summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-12-06 23:51:34 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-12-06 23:51:34 +0000
commit230e5ab6a084ed50470f101934782dbf54b0d06b (patch)
tree5dd821c8d33f450470588e7a543f74bf74306e9e /drivers
parentc9b1c8a64c6444d189856f1e26bdcb8b4cd0113a (diff)
Merge with Linux 2.1.67.
Diffstat (limited to 'drivers')
-rw-r--r--drivers/block/Config.in3
-rw-r--r--drivers/block/Makefile42
-rw-r--r--drivers/block/README.fd2
-rw-r--r--drivers/block/acsi_slm.c2
-rw-r--r--drivers/block/ataflop.c12
-rw-r--r--drivers/block/floppy.c93
-rw-r--r--drivers/block/ide-probe.c9
-rw-r--r--drivers/block/ide-tape.c22
-rw-r--r--drivers/block/linear.c3
-rw-r--r--drivers/block/ll_rw_blk.c71
-rw-r--r--drivers/block/md.c872
-rw-r--r--drivers/block/nbd.c435
-rw-r--r--drivers/block/raid0.c9
-rw-r--r--drivers/block/raid1.c870
-rw-r--r--drivers/block/raid5.c1652
-rw-r--r--drivers/block/rd.c27
-rw-r--r--drivers/block/swim3.c20
-rw-r--r--drivers/block/xd.c536
-rw-r--r--drivers/block/xd.h10
-rw-r--r--drivers/cdrom/cdu31a.c6
-rw-r--r--drivers/cdrom/mcdx.c4
-rw-r--r--drivers/char/ChangeLog44
-rw-r--r--drivers/char/Config.in27
-rw-r--r--drivers/char/Makefile52
-rw-r--r--drivers/char/README.cycladesZ8
-rw-r--r--drivers/char/acquirewdt.c4
-rw-r--r--drivers/char/apm_bios.c14
-rw-r--r--drivers/char/atixlmouse.c10
-rw-r--r--drivers/char/bt848.h319
-rw-r--r--drivers/char/bttv.c2005
-rw-r--r--drivers/char/bttv.h162
-rw-r--r--drivers/char/busmouse.c8
-rw-r--r--drivers/char/cyclades.c1409
-rw-r--r--drivers/char/epca.c2
-rw-r--r--drivers/char/esp.c148
-rw-r--r--drivers/char/ftape/Config.in38
-rw-r--r--drivers/char/ftape/Makefile110
-rw-r--r--drivers/char/ftape/README.PCI4
-rw-r--r--drivers/char/ftape/RELEASE-NOTES399
-rw-r--r--drivers/char/ftape/calibr.c183
-rw-r--r--drivers/char/ftape/compressor/Makefile48
-rw-r--r--drivers/char/ftape/compressor/lzrw3.c750
-rw-r--r--drivers/char/ftape/compressor/lzrw3.h253
-rw-r--r--drivers/char/ftape/compressor/zftape-compress.c1317
-rw-r--r--drivers/char/ftape/compressor/zftape-compress.h83
-rw-r--r--drivers/char/ftape/fdc-io.c1300
-rw-r--r--drivers/char/ftape/fdc-io.h182
-rw-r--r--drivers/char/ftape/fdc-isr.c813
-rw-r--r--drivers/char/ftape/ftape-bsm.c428
-rw-r--r--drivers/char/ftape/ftape-ctl.c883
-rw-r--r--drivers/char/ftape/ftape-ctl.h94
-rw-r--r--drivers/char/ftape/ftape-eof.c554
-rw-r--r--drivers/char/ftape/ftape-io.c1050
-rw-r--r--drivers/char/ftape/ftape-io.h77
-rw-r--r--drivers/char/ftape/ftape-read.c677
-rw-r--r--drivers/char/ftape/ftape-rw.c953
-rw-r--r--drivers/char/ftape/ftape-rw.h173
-rw-r--r--drivers/char/ftape/ftape-write.c723
-rw-r--r--drivers/char/ftape/kernel-interface.c358
-rw-r--r--drivers/char/ftape/lowlevel/.cvsignore1
-rw-r--r--drivers/char/ftape/lowlevel/Makefile60
-rw-r--r--drivers/char/ftape/lowlevel/fc-10.c (renamed from drivers/char/ftape/fc-10.c)137
-rw-r--r--drivers/char/ftape/lowlevel/fc-10.h (renamed from drivers/char/ftape/fc-10.h)11
-rw-r--r--drivers/char/ftape/lowlevel/fdc-io.c1439
-rw-r--r--drivers/char/ftape/lowlevel/fdc-io.h257
-rw-r--r--drivers/char/ftape/lowlevel/fdc-isr.c1185
-rw-r--r--drivers/char/ftape/lowlevel/fdc-isr.h (renamed from drivers/char/ftape/fdc-isr.h)27
-rw-r--r--drivers/char/ftape/lowlevel/ftape-bsm.c490
-rw-r--r--drivers/char/ftape/lowlevel/ftape-bsm.h (renamed from drivers/char/ftape/ftape-bsm.h)49
-rw-r--r--drivers/char/ftape/lowlevel/ftape-buffer.c147
-rw-r--r--drivers/char/ftape/lowlevel/ftape-buffer.h32
-rw-r--r--drivers/char/ftape/lowlevel/ftape-calibr.c289
-rw-r--r--drivers/char/ftape/lowlevel/ftape-calibr.h (renamed from drivers/char/ftape/calibr.h)28
-rw-r--r--drivers/char/ftape/lowlevel/ftape-ctl.c901
-rw-r--r--drivers/char/ftape/lowlevel/ftape-ctl.h172
-rw-r--r--drivers/char/ftape/lowlevel/ftape-ecc.c (renamed from drivers/char/ftape/ecc.c)623
-rw-r--r--drivers/char/ftape/lowlevel/ftape-ecc.h (renamed from drivers/char/ftape/ecc.h)69
-rw-r--r--drivers/char/ftape/lowlevel/ftape-format.c342
-rw-r--r--drivers/char/ftape/lowlevel/ftape-format.h37
-rw-r--r--drivers/char/ftape/lowlevel/ftape-init.c188
-rw-r--r--drivers/char/ftape/lowlevel/ftape-init.h (renamed from drivers/char/ftape/kernel-interface.h)38
-rw-r--r--drivers/char/ftape/lowlevel/ftape-io.c1018
-rw-r--r--drivers/char/ftape/lowlevel/ftape-io.h94
-rw-r--r--drivers/char/ftape/lowlevel/ftape-proc.c431
-rw-r--r--drivers/char/ftape/lowlevel/ftape-proc.h37
-rw-r--r--drivers/char/ftape/lowlevel/ftape-read.c614
-rw-r--r--drivers/char/ftape/lowlevel/ftape-read.h (renamed from drivers/char/ftape/ftape-read.h)34
-rw-r--r--drivers/char/ftape/lowlevel/ftape-rw.c1091
-rw-r--r--drivers/char/ftape/lowlevel/ftape-rw.h121
-rw-r--r--drivers/char/ftape/lowlevel/ftape-setup.c105
-rw-r--r--drivers/char/ftape/lowlevel/ftape-tracing.c118
-rw-r--r--drivers/char/ftape/lowlevel/ftape-tracing.h180
-rw-r--r--drivers/char/ftape/lowlevel/ftape-write.c337
-rw-r--r--drivers/char/ftape/lowlevel/ftape-write.h (renamed from drivers/char/ftape/ftape-write.h)35
-rw-r--r--drivers/char/ftape/lowlevel/ftape_syms.c103
-rw-r--r--drivers/char/ftape/lowlevel/ftape_syms.h39
-rw-r--r--drivers/char/ftape/qic117.h280
-rw-r--r--drivers/char/ftape/tracing.c103
-rw-r--r--drivers/char/ftape/tracing.h99
-rw-r--r--drivers/char/ftape/vendors.h127
-rw-r--r--drivers/char/ftape/zftape/.cvsignore1
-rw-r--r--drivers/char/ftape/zftape/Makefile54
-rw-r--r--drivers/char/ftape/zftape/zftape-buffers.c160
-rw-r--r--drivers/char/ftape/zftape/zftape-buffers.h56
-rw-r--r--drivers/char/ftape/zftape/zftape-ctl.c1502
-rw-r--r--drivers/char/ftape/zftape/zftape-ctl.h60
-rw-r--r--drivers/char/ftape/zftape/zftape-eof.c207
-rw-r--r--drivers/char/ftape/zftape/zftape-eof.h (renamed from drivers/char/ftape/ftape-eof.h)45
-rw-r--r--drivers/char/ftape/zftape/zftape-init.c512
-rw-r--r--drivers/char/ftape/zftape/zftape-init.h85
-rw-r--r--drivers/char/ftape/zftape/zftape-read.c390
-rw-r--r--drivers/char/ftape/zftape/zftape-read.h53
-rw-r--r--drivers/char/ftape/zftape/zftape-rw.c377
-rw-r--r--drivers/char/ftape/zftape/zftape-rw.h102
-rw-r--r--drivers/char/ftape/zftape/zftape-vtbl.c758
-rw-r--r--drivers/char/ftape/zftape/zftape-vtbl.h229
-rw-r--r--drivers/char/ftape/zftape/zftape-write.c496
-rw-r--r--drivers/char/ftape/zftape/zftape-write.h38
-rw-r--r--drivers/char/ftape/zftape/zftape_syms.c60
-rw-r--r--drivers/char/ftape/zftape/zftape_syms.h39
-rw-r--r--drivers/char/istallion.c16
-rw-r--r--drivers/char/joystick.c1051
-rw-r--r--drivers/char/lp.c15
-rw-r--r--drivers/char/lp_m68k.c4
-rw-r--r--drivers/char/mem.c132
-rw-r--r--drivers/char/misc.c6
-rw-r--r--drivers/char/msbusmouse.c9
-rw-r--r--drivers/char/n_tty.c27
-rw-r--r--drivers/char/nvram.c15
-rw-r--r--drivers/char/pc110pad.c5
-rw-r--r--drivers/char/pcwd.c5
-rw-r--r--drivers/char/pcxx.c2
-rw-r--r--drivers/char/pms.c1069
-rw-r--r--drivers/char/psaux.c30
-rw-r--r--drivers/char/radio.c234
-rw-r--r--drivers/char/random.c60
-rw-r--r--drivers/char/riscom8.c2
-rw-r--r--drivers/char/rocket.c63
-rw-r--r--drivers/char/rtc.c35
-rw-r--r--drivers/char/rtrack.c213
-rw-r--r--drivers/char/rtrack.h25
-rw-r--r--drivers/char/serial.c230
-rw-r--r--drivers/char/softdog.c2
-rw-r--r--drivers/char/stallion.c2
-rw-r--r--drivers/char/tpqic02.c15
-rw-r--r--drivers/char/tty_io.c235
-rw-r--r--drivers/char/tty_ioctl.c21
-rw-r--r--drivers/char/tuner.h59
-rw-r--r--drivers/char/vc_screen.c22
-rw-r--r--drivers/char/videodev.c264
-rw-r--r--drivers/char/vt.c2
-rw-r--r--drivers/char/wdt.c6
-rw-r--r--drivers/isdn/avmb1/b1capi.c2
-rw-r--r--drivers/isdn/avmb1/capi.c85
-rw-r--r--drivers/isdn/hisax/avm_a1.c5
-rw-r--r--drivers/isdn/hisax/elsa.c5
-rw-r--r--drivers/isdn/hisax/isdnl1.c6
-rw-r--r--drivers/isdn/hisax/ix1_micro.c5
-rw-r--r--drivers/isdn/hisax/teles0.c5
-rw-r--r--drivers/isdn/hisax/teles3.c5
-rw-r--r--drivers/isdn/isdn_common.c14
-rw-r--r--drivers/isdn/isdn_tty.c2
-rw-r--r--drivers/macintosh/adb.c2
-rw-r--r--drivers/macintosh/macserial.c2
-rw-r--r--drivers/misc/TODO-parport2
-rw-r--r--drivers/misc/parport_pc.c6
-rw-r--r--drivers/net/3c501.c8
-rw-r--r--drivers/net/3c503.c11
-rw-r--r--drivers/net/3c505.c17
-rw-r--r--drivers/net/3c507.c149
-rw-r--r--drivers/net/3c509.c4
-rw-r--r--drivers/net/8390.c5
-rw-r--r--drivers/net/8390.h5
-rw-r--r--drivers/net/Config.in8
-rw-r--r--drivers/net/README.DLINK205
-rw-r--r--drivers/net/README.de4x542
-rw-r--r--drivers/net/README.dgrs52
-rw-r--r--drivers/net/README.eql528
-rw-r--r--drivers/net/README.ewrk345
-rw-r--r--drivers/net/README.ltpc98
-rw-r--r--drivers/net/README.multicast57
-rw-r--r--drivers/net/README.pt62
-rw-r--r--drivers/net/README.scc23
-rw-r--r--drivers/net/README.smc942
-rw-r--r--drivers/net/README.tunnel123
-rw-r--r--drivers/net/README.wanpipe148
-rw-r--r--drivers/net/README.wavelan33
-rw-r--r--drivers/net/README1.PLIP113
-rw-r--r--drivers/net/README2.PLIP78
-rw-r--r--drivers/net/Space.c100
-rw-r--r--drivers/net/a2065.c32
-rw-r--r--drivers/net/ac3200.c14
-rw-r--r--drivers/net/apricot.c23
-rw-r--r--drivers/net/arc-rimi.c13
-rw-r--r--drivers/net/arcnet.c33
-rw-r--r--drivers/net/ariadne.c14
-rw-r--r--drivers/net/at1700.c8
-rw-r--r--drivers/net/atarilance.c8
-rw-r--r--drivers/net/atp.c9
-rw-r--r--drivers/net/auto_irq.c71
-rw-r--r--drivers/net/bpqether.c14
-rw-r--r--drivers/net/bsd_comp.c28
-rw-r--r--drivers/net/com20020.c16
-rw-r--r--drivers/net/com90io.c16
-rw-r--r--drivers/net/com90xx.c13
-rw-r--r--drivers/net/cops.c153
-rw-r--r--drivers/net/cs89x0.c17
-rw-r--r--drivers/net/de4x5.c8632
-rw-r--r--drivers/net/de4x5.h110
-rw-r--r--drivers/net/de600.c8
-rw-r--r--drivers/net/de620.c8
-rw-r--r--drivers/net/defxx.c2
-rw-r--r--drivers/net/depca.c3195
-rw-r--r--drivers/net/dgrs.c4
-rw-r--r--drivers/net/dlci.c7
-rw-r--r--drivers/net/dummy.c3
-rw-r--r--drivers/net/e2100.c9
-rw-r--r--drivers/net/eepro.c14
-rw-r--r--drivers/net/eepro100.c198
-rw-r--r--drivers/net/eexpress.c9
-rw-r--r--drivers/net/eql.c6
-rw-r--r--drivers/net/es3210.c9
-rw-r--r--drivers/net/eth16i.c9
-rw-r--r--drivers/net/ethertap.c18
-rw-r--r--drivers/net/ewrk3.c3234
-rw-r--r--drivers/net/fmv18x.c8
-rw-r--r--drivers/net/hdlcdrv.c15
-rw-r--r--drivers/net/hp-plus.c7
-rw-r--r--drivers/net/hp.c9
-rw-r--r--drivers/net/hp100.c4363
-rw-r--r--drivers/net/hp100.h2
-rw-r--r--drivers/net/hydra.c10
-rw-r--r--drivers/net/ibmtr.c27
-rw-r--r--drivers/net/ibmtr.h3
-rw-r--r--drivers/net/iow.h6
-rw-r--r--drivers/net/ipddp.c5
-rw-r--r--drivers/net/lance.c10
-rw-r--r--drivers/net/lapbether.c14
-rw-r--r--drivers/net/loopback.c10
-rw-r--r--drivers/net/ltpc.c39
-rw-r--r--drivers/net/mace.c23
-rw-r--r--drivers/net/mkiss.c18
-rw-r--r--drivers/net/myri_sbus.c8
-rw-r--r--drivers/net/ne.c12
-rw-r--r--drivers/net/net_init.c4
-rw-r--r--drivers/net/ni5010.c835
-rw-r--r--drivers/net/ni5010.h144
-rw-r--r--drivers/net/ni52.c16
-rw-r--r--drivers/net/ni65.c11
-rw-r--r--drivers/net/pcnet32.c8
-rw-r--r--drivers/net/pi2.c14
-rw-r--r--drivers/net/plip.c66
-rw-r--r--drivers/net/ppp.c62
-rw-r--r--drivers/net/ppp_deflate.c660
-rw-r--r--drivers/net/pt.c15
-rw-r--r--drivers/net/ptifddi.c268
-rw-r--r--drivers/net/ptifddi.h77
-rw-r--r--drivers/net/ptifddi_asm.h2543
-rw-r--r--drivers/net/scc.c5
-rw-r--r--drivers/net/sdla.c15
-rw-r--r--drivers/net/sdla_fr.c1008
-rw-r--r--drivers/net/sdla_ppp.c779
-rw-r--r--drivers/net/seeq8005.c11
-rw-r--r--drivers/net/sgiseeq.c15
-rw-r--r--drivers/net/shaper.c5
-rw-r--r--drivers/net/sk_g16.c19
-rw-r--r--drivers/net/skeleton.c15
-rw-r--r--drivers/net/slhc.c19
-rw-r--r--drivers/net/slip.c21
-rw-r--r--drivers/net/smc-mca.c7
-rw-r--r--drivers/net/smc-ultra.c7
-rw-r--r--drivers/net/smc9194.c8
-rw-r--r--drivers/net/sonic.c10
-rw-r--r--drivers/net/strip.c16
-rw-r--r--drivers/net/sunhme.c12
-rw-r--r--drivers/net/tlan.c4280
-rw-r--r--drivers/net/tlan.h267
-rw-r--r--drivers/net/tulip.c186
-rw-r--r--drivers/net/tunnel.c305
-rw-r--r--drivers/net/wavelan.c21
-rw-r--r--drivers/net/wd.c7
-rw-r--r--drivers/net/x25_asy.c12
-rw-r--r--drivers/net/zlib.c5380
-rw-r--r--drivers/net/zlib.h1010
-rw-r--r--drivers/net/znet.c7
-rw-r--r--drivers/pci/pci.c99
-rw-r--r--drivers/sbus/audio/audio.c8
-rw-r--r--drivers/sbus/char/pcikbd.c4
-rw-r--r--drivers/sbus/char/sab82532.c12
-rw-r--r--drivers/sbus/char/sunkbd.c2
-rw-r--r--drivers/sbus/char/sunmouse.c4
-rw-r--r--drivers/sbus/char/zs.c2
-rw-r--r--drivers/scsi/BusLogic.h7
-rw-r--r--drivers/scsi/ChangeLog.ncr53c8xx22
-rw-r--r--drivers/scsi/Config.in10
-rw-r--r--drivers/scsi/Makefile46
-rw-r--r--drivers/scsi/fdomain.c1
-rw-r--r--drivers/scsi/gdth.c3387
-rw-r--r--drivers/scsi/gdth.h720
-rw-r--r--drivers/scsi/gdth_ioctl.h86
-rw-r--r--drivers/scsi/gdth_proc.c635
-rw-r--r--drivers/scsi/gdth_proc.h24
-rw-r--r--drivers/scsi/hosts.c30
-rw-r--r--drivers/scsi/ibmmca.c1487
-rw-r--r--drivers/scsi/ibmmca.h8
-rw-r--r--drivers/scsi/ncr53c8xx.c145
-rw-r--r--drivers/scsi/ncr53c8xx.h2
-rw-r--r--drivers/scsi/pci2000.c660
-rw-r--r--drivers/scsi/pci2000.h226
-rw-r--r--drivers/scsi/pci2220i.c817
-rw-r--r--drivers/scsi/pci2220i.h345
-rw-r--r--drivers/scsi/ppa.c2255
-rw-r--r--drivers/scsi/ppa.h139
-rw-r--r--drivers/scsi/psi240i.c717
-rw-r--r--drivers/scsi/psi240i.h344
-rw-r--r--drivers/scsi/psi_chip.h195
-rw-r--r--drivers/scsi/psi_dale.h187
-rw-r--r--drivers/scsi/psi_roy.h314
-rw-r--r--drivers/scsi/scsi.c1
-rw-r--r--drivers/scsi/scsi.h3
-rw-r--r--drivers/scsi/scsi_ioctl.c5
-rw-r--r--drivers/scsi/scsi_module.c6
-rw-r--r--drivers/scsi/seagate.c22
-rw-r--r--drivers/scsi/sg.c27
-rw-r--r--drivers/scsi/sr.c108
-rw-r--r--drivers/scsi/sr.h19
-rw-r--r--drivers/scsi/sr_ioctl.c361
-rw-r--r--drivers/scsi/sr_vendor.c247
-rw-r--r--drivers/scsi/st.c84
-rw-r--r--drivers/scsi/st.h1
-rw-r--r--drivers/sound/.objects8
-rw-r--r--drivers/sound/.version2
-rw-r--r--drivers/sound/CHANGELOG12
-rw-r--r--drivers/sound/Config.in284
-rw-r--r--drivers/sound/Makefile282
-rw-r--r--drivers/sound/README.FIRST7
-rw-r--r--drivers/sound/Readme25
-rw-r--r--drivers/sound/Readme.cards100
-rw-r--r--drivers/sound/Readme.linux4
-rw-r--r--drivers/sound/ad1848.c4003
-rw-r--r--drivers/sound/adlib_card.c59
-rw-r--r--drivers/sound/audio.c1635
-rw-r--r--drivers/sound/configure.c2728
-rw-r--r--drivers/sound/cs4232.c458
-rw-r--r--drivers/sound/dev_table.c917
-rw-r--r--drivers/sound/dev_table.h80
-rw-r--r--drivers/sound/dmabuf.c2491
-rw-r--r--drivers/sound/dmasound.c5693
-rw-r--r--drivers/sound/gus_card.c306
-rw-r--r--drivers/sound/gus_midi.c363
-rw-r--r--drivers/sound/gus_vol.c165
-rw-r--r--drivers/sound/gus_wave.c5672
-rw-r--r--drivers/sound/ics2101.c354
-rw-r--r--drivers/sound/local.h.master228
-rw-r--r--drivers/sound/lowlevel/ChangeLog.awe82
-rw-r--r--drivers/sound/lowlevel/Makefile35
-rw-r--r--drivers/sound/lowlevel/README.awe165
-rw-r--r--drivers/sound/lowlevel/aedsp16.c2
-rw-r--r--drivers/sound/lowlevel/awe_compat.h190
-rw-r--r--drivers/sound/lowlevel/awe_config.h40
-rw-r--r--drivers/sound/lowlevel/awe_hw.h4
-rw-r--r--drivers/sound/lowlevel/awe_version.h24
-rw-r--r--drivers/sound/lowlevel/awe_voice.h490
-rw-r--r--drivers/sound/lowlevel/awe_wave.c3376
-rw-r--r--drivers/sound/mad16.c1210
-rw-r--r--drivers/sound/maui.c706
-rw-r--r--drivers/sound/midi_synth.c1091
-rw-r--r--drivers/sound/midibuf.c748
-rw-r--r--drivers/sound/mpu401.c2838
-rw-r--r--drivers/sound/opl3.c1876
-rw-r--r--drivers/sound/opl3sa.c277
-rw-r--r--drivers/sound/os.h17
-rw-r--r--drivers/sound/pas2_card.c487
-rw-r--r--drivers/sound/pas2_midi.c326
-rw-r--r--drivers/sound/pas2_mixer.c547
-rw-r--r--drivers/sound/pas2_pcm.c592
-rw-r--r--drivers/sound/pnp.c36
-rw-r--r--drivers/sound/pnp.h112
-rw-r--r--drivers/sound/pss.c1347
-rw-r--r--drivers/sound/sb.h4
-rw-r--r--drivers/sound/sb_audio.c1781
-rw-r--r--drivers/sound/sb_card.c138
-rw-r--r--drivers/sound/sb_common.c1958
-rw-r--r--drivers/sound/sb_midi.c281
-rw-r--r--drivers/sound/sb_mixer.c677
-rw-r--r--drivers/sound/sb_mixer.h11
-rw-r--r--drivers/sound/sequencer.c3191
-rw-r--r--drivers/sound/softoss.c1580
-rw-r--r--drivers/sound/softoss.h161
-rw-r--r--drivers/sound/softoss_rs.c136
-rw-r--r--drivers/sound/sound_calls.h13
-rw-r--r--drivers/sound/sound_config.h20
-rw-r--r--drivers/sound/sound_firmware.c58
-rw-r--r--drivers/sound/sound_firmware.h2
-rw-r--r--drivers/sound/sound_pnp.c1513
-rw-r--r--drivers/sound/sound_switch.c998
-rw-r--r--drivers/sound/sound_syms.c78
-rw-r--r--drivers/sound/sound_timer.c487
-rw-r--r--drivers/sound/soundcard.c1045
-rw-r--r--drivers/sound/soundmodule.h41
-rw-r--r--drivers/sound/soundvers.h2
-rw-r--r--drivers/sound/sscape.c1492
-rw-r--r--drivers/sound/sys_timer.c434
-rw-r--r--drivers/sound/trix.c800
-rw-r--r--drivers/sound/uart401.c656
-rw-r--r--drivers/sound/uart6850.c373
-rw-r--r--drivers/video/.cvsignore1
-rw-r--r--drivers/video/Config.in76
-rw-r--r--drivers/video/Makefile199
-rw-r--r--drivers/video/amifb.c3520
-rw-r--r--drivers/video/atafb.c3152
-rw-r--r--drivers/video/cyberfb.c1129
-rw-r--r--drivers/video/dn_fb.c389
-rw-r--r--drivers/video/fbcmap.c323
-rw-r--r--drivers/video/fbcon-afb.c304
-rw-r--r--drivers/video/fbcon-cfb16.c233
-rw-r--r--drivers/video/fbcon-cfb8.c218
-rw-r--r--drivers/video/fbcon-cyber.c230
-rw-r--r--drivers/video/fbcon-ilbm.c301
-rw-r--r--drivers/video/fbcon-iplan2p2.c443
-rw-r--r--drivers/video/fbcon-iplan2p4.c474
-rw-r--r--drivers/video/fbcon-iplan2p8.c532
-rw-r--r--drivers/video/fbcon-mfb.c203
-rw-r--r--drivers/video/fbcon-retz3.c261
-rw-r--r--drivers/video/fbcon.c1475
-rw-r--r--drivers/video/fbcon.h337
-rw-r--r--drivers/video/font.h35
-rw-r--r--drivers/video/font_8x16.c4625
-rw-r--r--drivers/video/font_8x8.c2577
-rw-r--r--drivers/video/fonts.c113
-rw-r--r--drivers/video/offb.c454
-rw-r--r--drivers/video/pearl_8x8.c2582
-rw-r--r--drivers/video/retz3fb.c1646
-rw-r--r--drivers/video/retz3fb.h286
-rw-r--r--drivers/video/s3blit.h74
-rw-r--r--drivers/video/tgafb.c938
-rw-r--r--drivers/video/txtcon.c153
-rw-r--r--drivers/video/vfb.c601
-rw-r--r--drivers/video/vgacon.c591
439 files changed, 127044 insertions, 57447 deletions
diff --git a/drivers/block/Config.in b/drivers/block/Config.in
index 59486adaa..9b52e48c1 100644
--- a/drivers/block/Config.in
+++ b/drivers/block/Config.in
@@ -49,10 +49,13 @@ fi
comment 'Additional Block Devices'
tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
+#tristate 'Network block device support' CONFIG_BLK_DEV_NBD
bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD
if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
tristate ' Linear (append) mode' CONFIG_MD_LINEAR
tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED
+ tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
+ tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5
fi
tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index de13e072a..89ce8a004 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -200,26 +200,30 @@ else
endif
endif
-#ifeq ($(CONFIG_MD_RAID1),y)
-#L_OBJS += raid1.o
-#else
-# ifeq ($(CONFIG_MD_SUPPORT_RAID1),y)
-# ifeq ($(CONFIG_MD_RAID1),m)
-# M_OBJS += raid1.o
-# endif
-# endif
-#endif
-#
-#ifeq ($(CONFIG_MD_RAID5),y)
-#L_OBJS += raid5.o
-#else
-# ifeq ($(CONFIG_MD_SUPPORT_RAID5),y)
-# ifeq ($(CONFIG_MD_RAID5),m)
-# M_OBJS += raid5.o
-# endif
-# endif
-#endif
+ifeq ($(CONFIG_MD_MIRRORING),y)
+L_OBJS += raid1.o
+else
+ ifeq ($(CONFIG_MD_MIRRORING),m)
+ M_OBJS += raid1.o
+ endif
+endif
+
+ifeq ($(CONFIG_MD_RAID5),y)
+L_OBJS += raid5.o
+else
+ ifeq ($(CONFIG_MD_RAID5),m)
+ M_OBJS += raid5.o
+ endif
+endif
+
+endif
+ifeq ($(CONFIG_BLK_DEV_NBD),y)
+L_OBJS += nbd.o
+else
+ ifeq ($(CONFIG_BLK_DEV_NBD),m)
+ M_OBJS += nbd.o
+ endif
endif
include $(TOPDIR)/Rules.make
diff --git a/drivers/block/README.fd b/drivers/block/README.fd
index d05120c59..045dbe63e 100644
--- a/drivers/block/README.fd
+++ b/drivers/block/README.fd
@@ -4,7 +4,7 @@ FAQ list:
=========
A FAQ list may be found in the fdutils package (see below), and also
-at http://www.club.innet.lu/~year3160/floppy/FAQ.html
+at http://poboxes.com/Alain.Knaff/floppy/FAQ.html
Lilo config options (Thinkpad users, read this)
diff --git a/drivers/block/acsi_slm.c b/drivers/block/acsi_slm.c
index 97957c0ea..64a401140 100644
--- a/drivers/block/acsi_slm.c
+++ b/drivers/block/acsi_slm.c
@@ -634,7 +634,7 @@ static long slm_write( struct inode *node, struct file *file,
while( SLMState == PRINTING ||
(SLMState == FILLING && SLMBufOwner != device) ) {
interruptible_sleep_on( &slm_wait );
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return( -ERESTARTSYS );
}
if (SLMState == IDLE) {
diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c
index 77add0b3a..3e0c28379 100644
--- a/drivers/block/ataflop.c
+++ b/drivers/block/ataflop.c
@@ -1978,10 +1978,14 @@ static int floppy_release( struct inode * inode, struct file * filp )
drive = inode->i_rdev & 3;
- if (!filp || (filp->f_mode & (2 | OPEN_WRITE_BIT)))
- /* if the file is mounted OR (writable now AND writable at open
- time) Linus: Does this cover all cases? */
- block_fsync (filp, filp->f_dentry);
+ /*
+ * If filp is NULL, we're being called from blkdev_release
+ * or after a failed mount attempt. In the former case the
+ * device has already been sync'ed, and in the latter no
+ * sync is required. Otherwise, sync if filp is writable.
+ */
+ if (filp && (filp->f_mode & (2 | OPEN_WRITE_BIT)))
+ block_fsync (filp, filp->f_dentry);
if (fd_ref[drive] < 0)
fd_ref[drive] = 0;
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index 7a4d082f0..d374415e4 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -415,7 +415,7 @@ static int probing = 0;
static volatile int command_status = FD_COMMAND_NONE, fdc_busy = 0;
static struct wait_queue *fdc_wait = NULL, *command_done = NULL;
-#define NO_SIGNAL (!(current->signal & ~current->blocked) || !interruptible)
+#define NO_SIGNAL (!interruptible || !signal_pending(current))
#define CALL(x) if ((x) == -EINTR) return -EINTR
#define ECALL(x) if ((ret = (x))) return ret;
#define _WAIT(x,i) CALL(ret=wait_til_done((x),i))
@@ -3274,7 +3274,7 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
return 0;
case BLKRAGET:
return put_user(read_ahead[MAJOR(inode->i_rdev)],
- (int *) param);
+ (long *) param);
case BLKFLSBUF:
if(!suser()) return -EACCES;
fsync_dev(inode->i_rdev);
@@ -3283,7 +3283,7 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
case BLKGETSIZE:
ECALL(get_floppy_geometry(drive, type, &g));
- return put_user(g->size, (int *) param);
+ return put_user(g->size, (long *) param);
/* BLKRRPART is not defined as floppies don't have
* partition tables */
}
@@ -3463,20 +3463,22 @@ static void config_types(void)
printk("\n");
}
-static long floppy_read(struct inode * inode, struct file * filp,
- char * buf, unsigned long count)
+static ssize_t floppy_read(struct file * filp, char *buf,
+ size_t count, loff_t *ppos)
{
+ struct inode *inode = filp->f_dentry->d_inode;
int drive = DRIVE(inode->i_rdev);
check_disk_change(inode->i_rdev);
if (UTESTF(FD_DISK_CHANGED))
return -ENXIO;
- return block_read(inode, filp, buf, count);
+ return block_read(filp, buf, count, ppos);
}
-static long floppy_write(struct inode * inode, struct file * filp,
- const char * buf, unsigned long count)
+static ssize_t floppy_write(struct file * filp, const char * buf,
+ size_t count, loff_t *ppos)
{
+ struct inode * inode = filp->f_dentry->d_inode;
int block;
int ret;
int drive = DRIVE(inode->i_rdev);
@@ -3488,9 +3490,9 @@ static long floppy_write(struct inode * inode, struct file * filp,
return -ENXIO;
if (!UTESTF(FD_DISK_WRITABLE))
return -EROFS;
- block = (filp->f_pos + count) >> 9;
+ block = (*ppos + count) >> 9;
INFBOUND(UDRS->maxblock, block);
- ret= block_write(inode, filp, buf, count);
+ ret= block_write(filp, buf, count, ppos);
return ret;
}
@@ -3500,9 +3502,13 @@ static int floppy_release(struct inode * inode, struct file * filp)
drive = DRIVE(inode->i_rdev);
- if (!filp || (filp->f_mode & (2 | OPEN_WRITE_BIT)))
- /* if the file is mounted OR (writable now AND writable at
- * open time) Linus: Does this cover all cases? */
+ /*
+ * If filp is NULL, we're being called from blkdev_release
+ * or after a failed mount attempt. In the former case the
+ * device has already been sync'ed, and in the latter no
+ * sync is required. Otherwise, sync if filp is writable.
+ */
+ if (filp && (filp->f_mode & (2 | OPEN_WRITE_BIT)))
block_fsync(filp, filp->f_dentry);
if (UDRS->fd_ref < 0)
@@ -4010,11 +4016,6 @@ __initfunc(int floppy_init(void))
continue;
}
- request_region(FDCS->address, 6, "floppy");
- request_region(FDCS->address+7, 1, "floppy DIR");
- /* address + 6 is reserved, and may be taken by IDE.
- * Unfortunately, Adaptec doesn't know this :-(, */
-
have_no_fdc = 0;
/* Not all FDCs seem to be able to handle the version command
* properly, so force a reset for the standard FDC clones,
@@ -4036,7 +4037,6 @@ __initfunc(int floppy_init(void))
static int floppy_grab_irq_and_dma(void)
{
- int i;
unsigned long flags;
INT_OFF;
@@ -4046,16 +4046,6 @@ static int floppy_grab_irq_and_dma(void)
}
INT_ON;
MOD_INC_USE_COUNT;
- for (i=0; i< N_FDC; i++){
- if (fdc_state[i].address != -1){
- fdc = i;
- reset_fdc_info(1);
- fd_outb(FDCS->dor, FD_DOR);
- }
- }
- fdc = 0;
- set_dor(0, ~0, 8); /* avoid immediate interrupt */
-
if (fd_request_irq(FLOPPY_IRQ)) {
DPRINT("Unable to grab IRQ%d for the floppy driver\n",
FLOPPY_IRQ);
@@ -4071,6 +4061,37 @@ static int floppy_grab_irq_and_dma(void)
usage_count--;
return -1;
}
+ for (fdc=0; fdc< N_FDC; fdc++){
+ if (FDCS->address != -1){
+ if (check_region(FDCS->address, 6) < 0 ||
+ check_region(FDCS->address+7, 1) < 0) {
+ DPRINT("Floppy io-port 0x%04x in use\n",
+ (unsigned int) FDCS->address);
+ fd_free_irq(FLOPPY_IRQ);
+ fd_free_dma(FLOPPY_DMA);
+ while(--fdc >= 0) {
+ release_region(FDCS->address, 6);
+ release_region(FDCS->address+7, 1);
+ }
+ MOD_DEC_USE_COUNT;
+ usage_count--;
+ return -1;
+ }
+ request_region(FDCS->address, 6, "floppy");
+ request_region(FDCS->address+7, 1, "floppy DIR");
+ /* address + 6 is reserved, and may be taken by IDE.
+ * Unfortunately, Adaptec doesn't know this :-(, */
+ }
+ }
+ for (fdc=0; fdc< N_FDC; fdc++){
+ if (FDCS->address != -1){
+ reset_fdc_info(1);
+ fd_outb(FDCS->dor, FD_DOR);
+ }
+ }
+ fdc = 0;
+ set_dor(0, ~0, 8); /* avoid immediate interrupt */
+
for (fdc = 0; fdc < N_FDC; fdc++)
if (FDCS->address != -1)
fd_outb(FDCS->dor, FD_DOR);
@@ -4081,6 +4102,7 @@ static int floppy_grab_irq_and_dma(void)
static void floppy_release_irq_and_dma(void)
{
+ int old_fdc;
#ifdef FLOPPY_SANITY_CHECK
#ifndef __sparc__
int drive;
@@ -4130,6 +4152,13 @@ static void floppy_release_irq_and_dma(void)
if (floppy_tq.sync)
printk("task queue still active\n");
#endif
+ old_fdc = fdc;
+ for (fdc = 0; fdc < N_FDC; fdc++)
+ if (FDCS->address != -1) {
+ release_region(FDCS->address, 6);
+ release_region(FDCS->address+7, 1);
+ }
+ fdc = old_fdc;
MOD_DEC_USE_COUNT;
}
@@ -4218,12 +4247,6 @@ void cleanup_module(void)
{
int fdc, dummy;
- for (fdc=0; fdc<2; fdc++)
- if (FDCS->address != -1){
- release_region(FDCS->address, 6);
- release_region(FDCS->address+7, 1);
- }
-
unregister_blkdev(MAJOR_NR, "fd");
blk_dev[MAJOR_NR].request_fn = 0;
diff --git a/drivers/block/ide-probe.c b/drivers/block/ide-probe.c
index ddaf1b0c6..3ce3e882a 100644
--- a/drivers/block/ide-probe.c
+++ b/drivers/block/ide-probe.c
@@ -123,11 +123,12 @@ static inline void do_identify (ide_drive_t *drive, byte cmd)
#endif /* CONFIG_BLK_DEV_PROMISE */
switch (type) {
case ide_floppy:
- if (strstr (id->model, "oppy") || strstr (id->model, "poyp")) {
+ if (!strstr(id->model, "oppy") && !strstr(id->model, "poyp") && !strstr(id->model, "ZIP"))
+ printk("cdrom or floppy?, assuming ");
+ if (drive->media != ide_cdrom) {
printk ("FLOPPY");
break;
}
- printk ("cdrom or floppy?, assuming ");
type = ide_cdrom; /* Early cdrom models used zero */
case ide_cdrom:
printk ("CDROM");
@@ -176,7 +177,7 @@ static int try_to_identify (ide_drive_t *drive, byte cmd)
int rc;
ide_ioreg_t hd_status;
unsigned long timeout;
- int irqs = 0;
+ unsigned long irqs = 0;
if (!HWIF(drive)->irq) { /* already got an IRQ? */
probe_irq_off(probe_irq_on()); /* clear dangling irqs */
@@ -235,7 +236,7 @@ static int try_to_identify (ide_drive_t *drive, byte cmd)
(void) GET_STAT(); /* clear drive IRQ */
} else { /* Mmmm.. multiple IRQs.. don't know which was ours */
- printk("%s: IRQ probe failed (%d)\n", drive->name, irqs);
+ printk("%s: IRQ probe failed (%ld)\n", drive->name, irqs);
#ifdef CONFIG_BLK_DEV_CMD640
#ifdef CMD640_DUMP_REGS
if (HWIF(drive)->chipset == ide_cmd640) {
diff --git a/drivers/block/ide-tape.c b/drivers/block/ide-tape.c
index 2fc5f0b9d..4df355141 100644
--- a/drivers/block/ide-tape.c
+++ b/drivers/block/ide-tape.c
@@ -2883,11 +2883,18 @@ static int idetape_space_over_filemarks (ide_drive_t *drive,short mt_op,int mt_c
* size will only result in a (slightly) increased driver overhead, but
* will no longer hit performance.
*/
-static long idetape_chrdev_read (struct inode *inode, struct file *file, char *buf, unsigned long count)
+static ssize_t idetape_chrdev_read (struct file *file, char *buf,
+ size_t count, loff_t *ppos)
{
+ struct inode *inode = file->f_dentry->d_inode;
ide_drive_t *drive = get_drive_ptr (inode->i_rdev);
idetape_tape_t *tape = drive->driver_data;
- int bytes_read,temp,actually_read=0;
+ ssize_t bytes_read,temp,actually_read=0;
+
+ if (ppos != &file->f_pos) {
+ /* "A request was outside the capabilities of the device." */
+ return -ENXIO;
+ }
#if IDETAPE_DEBUG_LOG
printk (KERN_INFO "Reached idetape_chrdev_read\n");
@@ -2953,11 +2960,18 @@ finish:
return (actually_read);
}
-static long idetape_chrdev_write (struct inode *inode, struct file *file, const char *buf, unsigned long count)
+static ssize_t idetape_chrdev_write (struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
{
+ struct inode *inode = file->f_dentry->d_inode;
ide_drive_t *drive = get_drive_ptr (inode->i_rdev);
idetape_tape_t *tape = drive->driver_data;
- int retval,actually_written=0;
+ ssize_t retval,actually_written=0;
+
+ if (ppos != &file->f_pos) {
+ /* "A request was outside the capabilities of the device." */
+ return -ENXIO;
+ }
#if IDETAPE_DEBUG_LOG
printk (KERN_INFO "Reached idetape_chrdev_write\n");
diff --git a/drivers/block/linear.c b/drivers/block/linear.c
index f0f9fec79..b6f72fd6a 100644
--- a/drivers/block/linear.c
+++ b/drivers/block/linear.c
@@ -163,6 +163,7 @@ static int linear_status (char *page, int minor, struct md_dev *mddev)
sz+=sprintf (page+sz, "\n");
#endif
+ sz+=sprintf (page+sz, " %dk rounding", 1<<FACTOR_SHIFT(FACTOR(mddev)));
return sz;
}
@@ -171,6 +172,8 @@ static struct md_personality linear_personality=
{
"linear",
linear_map,
+ NULL,
+ NULL,
linear_run,
linear_stop,
linear_status,
diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c
index 759785e85..07098974d 100644
--- a/drivers/block/ll_rw_blk.c
+++ b/drivers/block/ll_rw_blk.c
@@ -82,6 +82,11 @@ int * blksize_size[MAX_BLKDEV] = { NULL, NULL, };
*/
int * hardsect_size[MAX_BLKDEV] = { NULL, NULL, };
+/*
+ * The following tunes the read-ahead algorithm in mm/filemap.c
+ */
+int * max_readahead[MAX_BLKDEV] = { NULL, NULL, };
+
static inline struct request **get_queue(kdev_t dev)
{
int major = MAJOR(dev);
@@ -295,7 +300,27 @@ void add_request(struct blk_dev_struct * dev, struct request * req)
sti();
}
-static void make_request(int major,int rw, struct buffer_head * bh)
+#define MAX_SECTORS 244
+
+static inline void attempt_merge (struct request *req)
+{
+ struct request *next = req->next;
+
+ if (!next)
+ return;
+ if (req->sector + req->nr_sectors != next->sector)
+ return;
+ if (next->sem || req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors >= MAX_SECTORS)
+ return;
+ req->bhtail->b_reqnext = next->bh;
+ req->bhtail = next->bhtail;
+ req->nr_sectors += next->nr_sectors;
+ next->rq_status = RQ_INACTIVE;
+ req->next = next->next;
+ wake_up (&wait_for_request);
+}
+
+void make_request(int major,int rw, struct buffer_head * bh)
{
unsigned int sector, count;
struct request * req;
@@ -313,7 +338,7 @@ static void make_request(int major,int rw, struct buffer_head * bh)
if (blk_size[major])
if (blk_size[major][MINOR(bh->b_rdev)] < (sector + count)>>1) {
- bh->b_state &= (1 << BH_Lock) | (1 << BH_FreeOnIO);
+ bh->b_state &= (1 << BH_Lock);
/* This may well happen - the kernel calls bread()
without checking the size of the device, e.g.,
when mounting a device. */
@@ -323,8 +348,7 @@ static void make_request(int major,int rw, struct buffer_head * bh)
kdevname(bh->b_rdev), rw,
(sector + count)>>1,
blk_size[major][MINOR(bh->b_rdev)]);
- unlock_buffer(bh);
- return;
+ goto end_io;
}
rw_ahead = 0; /* normal case; gets changed below for READA/WRITEA */
@@ -333,10 +357,8 @@ static void make_request(int major,int rw, struct buffer_head * bh)
rw_ahead = 1;
rw = READ; /* drop into READ */
case READ:
- if (buffer_uptodate(bh)) {
- unlock_buffer(bh); /* Hmmph! Already have it */
- return;
- }
+ if (buffer_uptodate(bh)) /* Hmmph! Already have it */
+ goto end_io;
kstat.pgpgin++;
max_req = NR_REQUEST; /* reads take precedence */
break;
@@ -344,10 +366,8 @@ static void make_request(int major,int rw, struct buffer_head * bh)
rw_ahead = 1;
rw = WRITE; /* drop into WRITE */
case WRITE:
- if (!buffer_dirty(bh)) {
- unlock_buffer(bh); /* Hmmph! Nothing to write */
- return;
- }
+ if (!buffer_dirty(bh)) /* Hmmph! Nothing to write */
+ goto end_io;
/* We don't allow the write-requests to fill up the
* queue completely: we want some room for reads,
* as they take precedence. The last third of the
@@ -359,8 +379,7 @@ static void make_request(int major,int rw, struct buffer_head * bh)
default:
printk(KERN_ERR "make_request: bad block dev cmd,"
" must be R/W/RA/WA\n");
- unlock_buffer(bh);
- return;
+ goto end_io;
}
/* look for a free request. */
@@ -409,7 +428,7 @@ static void make_request(int major,int rw, struct buffer_head * bh)
continue;
if (req->cmd != rw)
continue;
- if (req->nr_sectors >= 244)
+ if (req->nr_sectors >= MAX_SECTORS)
continue;
if (req->rq_dev != bh->b_rdev)
continue;
@@ -417,6 +436,9 @@ static void make_request(int major,int rw, struct buffer_head * bh)
if (req->sector + req->nr_sectors == sector) {
req->bhtail->b_reqnext = bh;
req->bhtail = bh;
+ req->nr_sectors += count;
+ /* Can we now merge this req with the next? */
+ attempt_merge(req);
/* or to the beginning? */
} else if (req->sector - count == sector) {
bh->b_reqnext = req->bh;
@@ -424,10 +446,10 @@ static void make_request(int major,int rw, struct buffer_head * bh)
req->buffer = bh->b_data;
req->current_nr_sectors = count;
req->sector = sector;
+ req->nr_sectors += count;
} else
continue;
- req->nr_sectors += count;
mark_buffer_clean(bh);
sti();
return;
@@ -440,10 +462,8 @@ static void make_request(int major,int rw, struct buffer_head * bh)
/* if no request available: if rw_ahead, forget it; otherwise try again blocking.. */
if (!req) {
- if (rw_ahead) {
- unlock_buffer(bh);
- return;
- }
+ if (rw_ahead)
+ goto end_io;
req = __get_request_wait(max_req, bh->b_rdev);
}
@@ -459,6 +479,10 @@ static void make_request(int major,int rw, struct buffer_head * bh)
req->bhtail = bh;
req->next = NULL;
add_request(major+blk_dev,req);
+ return;
+
+end_io:
+ bh->b_end_io(bh, test_bit(BH_Uptodate, &bh->b_state));
}
/* This function can be used to request a number of buffers from a block
@@ -530,6 +554,12 @@ void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
for (i = 0; i < nr; i++) {
if (bh[i]) {
set_bit(BH_Req, &bh[i]->b_state);
+#ifdef CONFIG_BLK_DEV_MD
+ if (MAJOR(bh[i]->b_dev) == MD_MAJOR) {
+ md_make_request(MINOR (bh[i]->b_dev), rw, bh[i]);
+ continue;
+ }
+#endif
make_request(MAJOR(bh[i]->b_rdev), rw, bh[i]);
}
}
@@ -654,6 +684,7 @@ __initfunc(int blk_dev_init(void))
req->next = NULL;
}
memset(ro_bits,0,sizeof(ro_bits));
+ memset(max_readahead, 0, sizeof(max_readahead));
#ifdef CONFIG_AMIGA_Z2RAM
z2_init();
#endif
diff --git a/drivers/block/md.c b/drivers/block/md.c
index 8f8ba19d5..2018883ca 100644
--- a/drivers/block/md.c
+++ b/drivers/block/md.c
@@ -9,6 +9,9 @@
kerneld support by Boris Tobotras <boris@xtalk.msk.su>
+ RAID-1/RAID-5 extensions by:
+ Ingo Molnar, Miguel de Icaza, Gadi Oxman
+
This program is free software; you can redistribute 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)
@@ -19,6 +22,13 @@
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+/*
+ * Current RAID-1,4,5 parallel reconstruction speed limit is 1024 KB/sec, so
+ * the extra system load does not show up that much. Increase it if your
+ * system can take more.
+ */
+#define SPEED_LIMIT 1024
+
#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
@@ -31,20 +41,31 @@
#include <linux/proc_fs.h>
#include <linux/blkdev.h>
#include <linux/genhd.h>
+#include <linux/smp_lock.h>
#ifdef CONFIG_KERNELD
#include <linux/kerneld.h>
#endif
#include <linux/errno.h>
#include <linux/init.h>
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
#define MAJOR_NR MD_MAJOR
#define MD_DRIVER
#include <linux/blk.h>
#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
static struct hd_struct md_hd_struct[MAX_MD_DEV];
static int md_blocksizes[MAX_MD_DEV];
+int md_maxreadahead[MAX_MD_DEV];
+static struct md_thread md_threads[MAX_MD_THREADS];
+#if SUPPORT_RECONSTRUCTION
+static struct md_thread *md_sync_thread = NULL;
+#endif /* SUPPORT_RECONSTRUCTION */
int md_size[MAX_MD_DEV]={0, };
@@ -66,7 +87,6 @@ static struct gendisk md_gendisk=
};
static struct md_personality *pers[MAX_PERSONALITY]={NULL, };
-
struct md_dev md_dev[MAX_MD_DEV];
static struct gendisk *find_gendisk (kdev_t dev)
@@ -84,7 +104,6 @@ static struct gendisk *find_gendisk (kdev_t dev)
return (NULL);
}
-
char *partition_name (kdev_t dev)
{
static char name[40]; /* This should be long
@@ -93,49 +112,318 @@ char *partition_name (kdev_t dev)
if (!hd)
{
- printk ("No gendisk entry for dev %s\n", kdevname(dev));
- sprintf (name, "dev %s", kdevname(dev));
+ sprintf (name, "[dev %s]", kdevname(dev));
return (name);
}
return disk_name (hd, MINOR(dev), name); /* routine in genhd.c */
}
+static int legacy_raid_sb (int minor, int pnum)
+{
+ int i, factor;
+
+ factor = 1 << FACTOR_SHIFT(FACTOR((md_dev+minor)));
+
+ /*****
+ * do size and offset calculations.
+ */
+ for (i=0; i<md_dev[minor].nb_dev; i++) {
+ md_dev[minor].devices[i].size &= ~(factor - 1);
+ md_size[minor] += md_dev[minor].devices[i].size;
+ md_dev[minor].devices[i].offset=i ? (md_dev[minor].devices[i-1].offset +
+ md_dev[minor].devices[i-1].size) : 0;
+ }
+ if (pnum == RAID0 >> PERSONALITY_SHIFT)
+ md_maxreadahead[minor] = MD_DEFAULT_DISK_READAHEAD * md_dev[minor].nb_dev;
+ return 0;
+}
-static void set_ra (void)
+static void free_sb (struct md_dev *mddev)
{
- int i, j, minra=INT_MAX;
+ int i;
+ struct real_dev *realdev;
+
+ if (mddev->sb) {
+ free_page((unsigned long) mddev->sb);
+ mddev->sb = NULL;
+ }
+ for (i = 0; i <mddev->nb_dev; i++) {
+ realdev = mddev->devices + i;
+ if (realdev->sb) {
+ free_page((unsigned long) realdev->sb);
+ realdev->sb = NULL;
+ }
+ }
+}
- for (i=0; i<MAX_MD_DEV; i++)
- {
- if (!md_dev[i].pers)
- continue;
-
- for (j=0; j<md_dev[i].nb_dev; j++)
- if (read_ahead[MAJOR(md_dev[i].devices[j].dev)]<minra)
- minra=read_ahead[MAJOR(md_dev[i].devices[j].dev)];
- }
-
- read_ahead[MD_MAJOR]=minra;
+/*
+ * Check one RAID superblock for generic plausibility
+ */
+
+#define BAD_MAGIC KERN_ERR \
+"md: %s: invalid raid superblock magic (%x) on block %u\n"
+
+#define OUT_OF_MEM KERN_ALERT \
+"md: out of memory.\n"
+
+#define NO_DEVICE KERN_ERR \
+"md: disabled device %s\n"
+
+#define SUCCESS 0
+#define FAILURE -1
+
+static int analyze_one_sb (struct real_dev * rdev)
+{
+ int ret = FAILURE;
+ struct buffer_head *bh;
+ kdev_t dev = rdev->dev;
+ md_superblock_t *sb;
+
+ /*
+ * Read the superblock, it's at the end of the disk
+ */
+ rdev->sb_offset = MD_NEW_SIZE_BLOCKS (blk_size[MAJOR(dev)][MINOR(dev)]);
+ set_blocksize (dev, MD_SB_BYTES);
+ bh = bread (dev, rdev->sb_offset / MD_SB_BLOCKS, MD_SB_BYTES);
+
+ if (bh) {
+ sb = (md_superblock_t *) bh->b_data;
+ if (sb->md_magic != MD_SB_MAGIC) {
+ printk (BAD_MAGIC, kdevname(dev),
+ sb->md_magic, rdev->sb_offset);
+ goto abort;
+ }
+ rdev->sb = (md_superblock_t *) __get_free_page(GFP_KERNEL);
+ if (!rdev->sb) {
+ printk (OUT_OF_MEM);
+ goto abort;
+ }
+ memcpy (rdev->sb, bh->b_data, MD_SB_BYTES);
+
+ rdev->size = sb->size;
+ } else
+ printk (NO_DEVICE,kdevname(rdev->dev));
+ ret = SUCCESS;
+abort:
+ if (bh)
+ brelse (bh);
+ return ret;
+}
+
+#undef SUCCESS
+#undef FAILURE
+
+#undef BAD_MAGIC
+#undef OUT_OF_MEM
+#undef NO_DEVICE
+
+/*
+ * Check a full RAID array for plausibility
+ */
+
+#define INCONSISTENT KERN_ERR \
+"md: superblock inconsistency -- run ckraid\n"
+
+#define OUT_OF_DATE KERN_ERR \
+"md: superblock update time inconsistenty -- using the most recent one\n"
+
+#define OLD_VERSION KERN_ALERT \
+"md: %s: unsupported raid array version %d.%d.%d\n"
+
+#define NOT_CLEAN KERN_ERR \
+"md: %s: raid array is not clean -- run ckraid\n"
+
+#define NOT_CLEAN_IGNORE KERN_ERR \
+"md: %s: raid array is not clean -- reconstructing parity\n"
+
+#define UNKNOWN_LEVEL KERN_ERR \
+"md: %s: unsupported raid level %d\n"
+
+static int analyze_sbs (int minor, int pnum)
+{
+ struct md_dev *mddev = md_dev + minor;
+ int i, N = mddev->nb_dev, out_of_date = 0;
+ struct real_dev * disks = mddev->devices;
+ md_superblock_t *sb, *freshest = NULL;
+
+ /*
+ * RAID-0 and linear don't use a RAID superblock
+ */
+ if (pnum == RAID0 >> PERSONALITY_SHIFT ||
+ pnum == LINEAR >> PERSONALITY_SHIFT)
+ return legacy_raid_sb (minor, pnum);
+
+ /*
+ * Verify the RAID superblock on each real device
+ */
+ for (i = 0; i < N; i++)
+ if (analyze_one_sb(disks+i))
+ goto abort;
+
+ /*
+ * The superblock constant part has to be the same
+ * for all disks in the array.
+ */
+ sb = NULL;
+ for (i = 0; i < N; i++) {
+ if (!disks[i].sb)
+ continue;
+ if (!sb) {
+ sb = disks[i].sb;
+ continue;
+ }
+ if (memcmp(sb,
+ disks[i].sb, MD_SB_GENERIC_CONSTANT_WORDS * 4)) {
+ printk (INCONSISTENT);
+ goto abort;
+ }
+ }
+
+ /*
+ * Ok, we have all disks and the array is ready to run. Lets
+ * find the freshest superblock, that one will be the superblock
+ * that represents the whole array.
+ */
+ if ((sb = mddev->sb = (md_superblock_t *) __get_free_page (GFP_KERNEL)) == NULL)
+ goto abort;
+ freshest = NULL;
+ for (i = 0; i < N; i++) {
+ if (!disks[i].sb)
+ continue;
+ if (!freshest) {
+ freshest = disks[i].sb;
+ continue;
+ }
+ /*
+ * Find the newest superblock version
+ */
+ if (disks[i].sb->utime != freshest->utime) {
+ out_of_date = 1;
+ if (disks[i].sb->utime > freshest->utime)
+ freshest = disks[i].sb;
+ }
+ }
+ if (out_of_date)
+ printk(OUT_OF_DATE);
+ memcpy (sb, freshest, sizeof(*freshest));
+
+ /*
+ * Check if we can support this RAID array
+ */
+ if (sb->major_version != MD_MAJOR_VERSION ||
+ sb->minor_version > MD_MINOR_VERSION) {
+
+ printk (OLD_VERSION, kdevname(MKDEV(MD_MAJOR, minor)),
+ sb->major_version, sb->minor_version,
+ sb->patch_version);
+ goto abort;
+ }
+
+ /*
+ * We need to add this as a superblock option.
+ */
+#if SUPPORT_RECONSTRUCTION
+ if (sb->state != (1 << MD_SB_CLEAN)) {
+ if (sb->level == 1) {
+ printk (NOT_CLEAN, kdevname(MKDEV(MD_MAJOR, minor)));
+ goto abort;
+ } else
+ printk (NOT_CLEAN_IGNORE, kdevname(MKDEV(MD_MAJOR, minor)));
+ }
+#else
+ if (sb->state != (1 << MD_SB_CLEAN)) {
+ printk (NOT_CLEAN, kdevname(MKDEV(MD_MAJOR, minor)));
+ goto abort;
+ }
+#endif /* SUPPORT_RECONSTRUCTION */
+
+ switch (sb->level) {
+ case 1:
+ md_size[minor] = sb->size;
+ md_maxreadahead[minor] = MD_DEFAULT_DISK_READAHEAD;
+ break;
+ case 4:
+ case 5:
+ md_size[minor] = sb->size * (sb->raid_disks - 1);
+ md_maxreadahead[minor] = MD_DEFAULT_DISK_READAHEAD * (sb->raid_disks - 1);
+ break;
+ default:
+ printk (UNKNOWN_LEVEL, kdevname(MKDEV(MD_MAJOR, minor)),
+ sb->level);
+ goto abort;
+ }
+ return 0;
+abort:
+ free_sb(mddev);
+ return 1;
}
+#undef INCONSISTENT
+#undef OUT_OF_DATE
+#undef OLD_VERSION
+#undef NOT_CLEAN
+#undef OLD_LEVEL
+
+int md_update_sb(int minor)
+{
+ struct md_dev *mddev = md_dev + minor;
+ struct buffer_head *bh;
+ md_superblock_t *sb = mddev->sb;
+ struct real_dev *realdev;
+ kdev_t dev;
+ int i;
+ u32 sb_offset;
+
+ sb->utime = CURRENT_TIME;
+ for (i = 0; i < mddev->nb_dev; i++) {
+ realdev = mddev->devices + i;
+ if (!realdev->sb)
+ continue;
+ dev = realdev->dev;
+ sb_offset = realdev->sb_offset;
+ set_blocksize(dev, MD_SB_BYTES);
+ printk("md: updating raid superblock on device %s, sb_offset == %u\n", kdevname(dev), sb_offset);
+ bh = getblk(dev, sb_offset / MD_SB_BLOCKS, MD_SB_BYTES);
+ if (bh) {
+ sb = (md_superblock_t *) bh->b_data;
+ memcpy(sb, mddev->sb, MD_SB_BYTES);
+ memcpy(&sb->descriptor, sb->disks + realdev->sb->descriptor.number, MD_SB_DESCRIPTOR_WORDS * 4);
+ mark_buffer_uptodate(bh, 1);
+ mark_buffer_dirty(bh, 1);
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ bforget(bh);
+ fsync_dev(dev);
+ invalidate_buffers(dev);
+ } else
+ printk(KERN_ERR "md: getblk failed for device %s\n", kdevname(dev));
+ }
+ return 0;
+}
static int do_md_run (int minor, int repart)
{
- int pnum, i, min, current_ra, err;
-
+ int pnum, i, min, factor, err;
+
if (!md_dev[minor].nb_dev)
return -EINVAL;
if (md_dev[minor].pers)
return -EBUSY;
-
+
md_dev[minor].repartition=repart;
- if ((pnum=PERSONALITY(md_dev+minor) >> (PERSONALITY_SHIFT))
+ if ((pnum=PERSONALITY(&md_dev[minor]) >> (PERSONALITY_SHIFT))
>= MAX_PERSONALITY)
return -EINVAL;
-
+
+ /* Only RAID-1 and RAID-5 can have MD devices as underlying devices */
+ if (pnum != (RAID1 >> PERSONALITY_SHIFT) && pnum != (RAID5 >> PERSONALITY_SHIFT)){
+ for (i = 0; i < md_dev [minor].nb_dev; i++)
+ if (MAJOR (md_dev [minor].devices [i].dev) == MD_MAJOR)
+ return -EINVAL;
+ }
if (!pers[pnum])
{
#ifdef CONFIG_KERNELD
@@ -147,7 +435,7 @@ static int do_md_run (int minor, int repart)
return -EINVAL;
}
- min=1 << FACTOR_SHIFT(FACTOR((md_dev+minor)));
+ factor = min = 1 << FACTOR_SHIFT(FACTOR((md_dev+minor)));
for (i=0; i<md_dev[minor].nb_dev; i++)
if (md_dev[minor].devices[i].size<min)
@@ -156,118 +444,170 @@ static int do_md_run (int minor, int repart)
partition_name (md_dev[minor].devices[i].dev), min);
return -EINVAL;
}
+
+ for (i=0; i<md_dev[minor].nb_dev; i++) {
+ fsync_dev(md_dev[minor].devices[i].dev);
+ invalidate_buffers(md_dev[minor].devices[i].dev);
+ }
/* Resize devices according to the factor. It is used to align
partitions size on a given chunk size. */
md_size[minor]=0;
-
- for (i=0; i<md_dev[minor].nb_dev; i++)
- {
- md_dev[minor].devices[i].size &= ~(min - 1);
- md_size[minor] += md_dev[minor].devices[i].size;
- md_dev[minor].devices[i].offset=i ? (md_dev[minor].devices[i-1].offset + md_dev[minor].devices[i-1].size) : 0;
- }
+
+ /*
+ * Analyze the raid superblock
+ */
+ if (analyze_sbs(minor, pnum))
+ return -EINVAL;
md_dev[minor].pers=pers[pnum];
if ((err=md_dev[minor].pers->run (minor, md_dev+minor)))
{
md_dev[minor].pers=NULL;
+ free_sb(md_dev + minor);
return (err);
}
-
+
+ if (pnum != RAID0 >> PERSONALITY_SHIFT && pnum != LINEAR >> PERSONALITY_SHIFT)
+ {
+ md_dev[minor].sb->state &= ~(1 << MD_SB_CLEAN);
+ md_update_sb(minor);
+ }
+
/* FIXME : We assume here we have blocks
that are twice as large as sectors.
THIS MAY NOT BE TRUE !!! */
md_hd_struct[minor].start_sect=0;
md_hd_struct[minor].nr_sects=md_size[minor]<<1;
- /* It would be better to have a per-md-dev read_ahead. Currently,
- we only use the smallest read_ahead among md-attached devices */
-
- current_ra=read_ahead[MD_MAJOR];
-
- for (i=0; i<md_dev[minor].nb_dev; i++)
- if (current_ra>read_ahead[MAJOR(md_dev[minor].devices[i].dev)])
- current_ra=read_ahead[MAJOR(md_dev[minor].devices[i].dev)];
-
- read_ahead[MD_MAJOR]=current_ra;
-
- printk ("START_DEV md%x %s\n", minor, md_dev[minor].pers->name);
+ read_ahead[MD_MAJOR] = 128;
return (0);
}
-
static int do_md_stop (int minor, struct inode *inode)
{
- int i;
+ int i;
- if (inode->i_count > 1 || md_dev[minor].busy>1) /* ioctl : one open channel */
- {
- printk ("STOP_MD md%x failed : i_count=%d, busy=%d\n", minor,
- inode->i_count, md_dev[minor].busy);
- return -EBUSY;
- }
+ if (inode->i_count>1 || md_dev[minor].busy>1) {
+ /*
+ * ioctl : one open channel
+ */
+ printk ("STOP_MD md%x failed : i_count=%d, busy=%d\n",
+ minor, inode->i_count, md_dev[minor].busy);
+ return -EBUSY;
+ }
- if (md_dev[minor].pers)
- {
- /* The device won't exist anymore -> flush it now */
- fsync_dev (inode->i_rdev);
- invalidate_buffers (inode->i_rdev);
- md_dev[minor].pers->stop (minor, md_dev+minor);
- }
-
- /* Remove locks. */
- for (i=0; i<md_dev[minor].nb_dev; i++)
- clear_inode (md_dev[minor].devices[i].inode);
+ if (md_dev[minor].pers) {
+ /*
+ * It is safe to call stop here, it only frees private
+ * data. Also, it tells us if a device is unstoppable
+ * (eg. resyncing is in progress)
+ */
+ if (md_dev[minor].pers->stop (minor, md_dev+minor))
+ return -EBUSY;
+ /*
+ * The device won't exist anymore -> flush it now
+ */
+ fsync_dev (inode->i_rdev);
+ invalidate_buffers (inode->i_rdev);
+ if (md_dev[minor].sb) {
+ md_dev[minor].sb->state |= 1 << MD_SB_CLEAN;
+ md_update_sb(minor);
+ }
+ }
- md_dev[minor].nb_dev=md_size[minor]=0;
- md_hd_struct[minor].nr_sects=0;
- md_dev[minor].pers=NULL;
+ /* Remove locks. */
+ if (md_dev[minor].sb)
+ free_sb(md_dev + minor);
+ for (i=0; i<md_dev[minor].nb_dev; i++)
+ clear_inode (md_dev[minor].devices[i].inode);
+
+ md_dev[minor].nb_dev=md_size[minor]=0;
+ md_hd_struct[minor].nr_sects=0;
+ md_dev[minor].pers=NULL;
- set_ra (); /* calculate new read_ahead */
+ read_ahead[MD_MAJOR] = 128;
- printk ("STOP_DEV md%x\n", minor);
- return (0);
+ return (0);
}
-
static int do_md_add (int minor, kdev_t dev)
{
- struct gendisk *gen_real;
- int i;
-
- if (MAJOR(dev)==MD_MAJOR || md_dev[minor].nb_dev==MAX_REAL)
- return -EINVAL;
-
- if (!fs_may_mount (dev) || md_dev[minor].pers)
- return -EBUSY;
-
- if (!(gen_real=find_gendisk (dev)))
- return -ENOENT;
+ int i;
+ int hot_add=0;
+ struct real_dev *realdev;
+
+ if (md_dev[minor].nb_dev==MAX_REAL)
+ return -EINVAL;
+
+ if (!fs_may_mount (dev))
+ return -EBUSY;
+
+ if (blk_size[MAJOR(dev)] == NULL || blk_size[MAJOR(dev)][MINOR(dev)] == 0) {
+ printk("md_add(): zero device size, huh, bailing out.\n");
+ return -EINVAL;
+ }
+
+ if (md_dev[minor].pers) {
+ /*
+ * The array is already running, hot-add the drive, or
+ * bail out:
+ */
+ if (!md_dev[minor].pers->hot_add_disk)
+ return -EBUSY;
+ else
+ hot_add=1;
+ }
+
+ /*
+ * Careful. We cannot increase nb_dev for a running array.
+ */
+ i=md_dev[minor].nb_dev;
+ realdev = &md_dev[minor].devices[i];
+ realdev->dev=dev;
- i=md_dev[minor].nb_dev++;
- md_dev[minor].devices[i].dev=dev;
+ /* Lock the device by inserting a dummy inode. This doesn't
+ smell very good, but I need to be consistent with the
+ mount stuff, specially with fs_may_mount. If someone have
+ a better idea, please help ! */
- /* Lock the device by inserting a dummy inode. This doesn't
- smell very good, but I need to be consistent with the
- mount stuff, specially with fs_may_mount. If someone have
- a better idea, please help ! */
+ realdev->inode=get_empty_inode ();
+ realdev->inode->i_dev=dev; /* don't care about other fields */
+ insert_inode_hash (realdev->inode);
- md_dev[minor].devices[i].inode=get_empty_inode ();
- md_dev[minor].devices[i].inode->i_dev=dev; /* don't care about
- other fields */
- insert_inode_hash (md_dev[minor].devices[i].inode);
+ /* Sizes are now rounded at run time */
- /* Sizes are now rounded at run time */
-
- md_dev[minor].devices[i].size=gen_real->sizes[MINOR(dev)];
-
- printk ("REGISTER_DEV %s to md%x done\n", partition_name(dev), minor);
- return (0);
+/* md_dev[minor].devices[i].size=gen_real->sizes[MINOR(dev)]; HACKHACK*/
+
+ realdev->size=blk_size[MAJOR(dev)][MINOR(dev)];
+
+ if (hot_add) {
+ /*
+ * Check the superblock for consistency.
+ * the personality itself has to check wether it's getting
+ * added with the proper flags ... also, personality has to
+ * be checked too ;)
+ */
+ if (analyze_one_sb (realdev))
+ return -EINVAL;
+ /*
+ * hot_add has to bump up nb_dev itself
+ */
+ if (md_dev[minor].pers->hot_add_disk (&md_dev[minor], dev)) {
+ /*
+ * FIXME: here we should free up the inode and stuff
+ */
+ printk ("FIXME\n");
+ return -EINVAL;
+ }
+ } else
+ md_dev[minor].nb_dev++;
+
+ printk ("REGISTER_DEV %s to md%x done\n", partition_name(dev), minor);
+ return (0);
}
-
static int md_ioctl (struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
@@ -354,7 +694,6 @@ static int md_ioctl (struct inode *inode, struct file *file,
return (0);
}
-
static int md_open (struct inode *inode, struct file *file)
{
int minor=MINOR(inode->i_rdev);
@@ -374,26 +713,26 @@ static int md_release (struct inode *inode, struct file *file)
}
-static long md_read (struct inode *inode, struct file *file,
- char *buf, unsigned long count)
+static ssize_t md_read (struct file *file, char *buf, size_t count,
+ loff_t *ppos)
{
- int minor=MINOR(inode->i_rdev);
+ int minor=MINOR(file->f_dentry->d_inode->i_rdev);
if (!md_dev[minor].pers) /* Check if device is being run */
return -ENXIO;
- return block_read (inode, file, buf, count);
+ return block_read(file, buf, count, ppos);
}
-static long md_write (struct inode *inode, struct file *file,
- const char *buf, unsigned long count)
+static ssize_t md_write (struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
{
- int minor=MINOR(inode->i_rdev);
+ int minor=MINOR(file->f_dentry->d_inode->i_rdev);
if (!md_dev[minor].pers) /* Check if device is being run */
return -ENXIO;
- return block_write (inode, file, buf, count);
+ return block_write(file, buf, count, ppos);
}
static struct file_operations md_fops=
@@ -427,6 +766,30 @@ int md_map (int minor, kdev_t *rdev, unsigned long *rsector, unsigned long size)
return (md_dev[minor].pers->map(md_dev+minor, rdev, rsector, size));
}
+int md_make_request (int minor, int rw, struct buffer_head * bh)
+{
+ if (md_dev [minor].pers->make_request) {
+ if (buffer_locked(bh))
+ return 0;
+ set_bit(BH_Lock, &bh->b_state);
+ if (rw == WRITE || rw == WRITEA) {
+ if (!buffer_dirty(bh)) {
+ bh->b_end_io(bh, test_bit(BH_Uptodate, &bh->b_state));
+ return 0;
+ }
+ }
+ if (rw == READ || rw == READA) {
+ if (buffer_uptodate(bh)) {
+ bh->b_end_io(bh, test_bit(BH_Uptodate, &bh->b_state));
+ return 0;
+ }
+ }
+ return (md_dev[minor].pers->make_request(md_dev+minor, rw, bh));
+ } else {
+ make_request (MAJOR(bh->b_rdev), rw, bh);
+ return 0;
+ }
+}
static void do_md_request (void)
{
@@ -434,10 +797,51 @@ static void do_md_request (void)
return;
}
+/*
+ * We run MAX_MD_THREADS from md_init() and arbitrate them in run time.
+ * This is not so elegant, but how can we use kernel_thread() from within
+ * loadable modules?
+ */
+struct md_thread *md_register_thread (void (*run) (void *), void *data)
+{
+ int i;
+ for (i = 0; i < MAX_MD_THREADS; i++) {
+ if (md_threads[i].run == NULL) {
+ md_threads[i].run = run;
+ md_threads[i].data = data;
+ return md_threads + i;
+ }
+ }
+ return NULL;
+}
+
+void md_unregister_thread (struct md_thread *thread)
+{
+ thread->run = NULL;
+ thread->data = NULL;
+ thread->flags = 0;
+}
+
+void md_wakeup_thread(struct md_thread *thread)
+{
+ set_bit(THREAD_WAKEUP, &thread->flags);
+ wake_up(&thread->wqueue);
+}
+
+
EXPORT_SYMBOL(md_size);
+EXPORT_SYMBOL(md_maxreadahead);
EXPORT_SYMBOL(register_md_personality);
EXPORT_SYMBOL(unregister_md_personality);
EXPORT_SYMBOL(partition_name);
+EXPORT_SYMBOL(md_dev);
+EXPORT_SYMBOL(md_error);
+EXPORT_SYMBOL(md_register_thread);
+EXPORT_SYMBOL(md_unregister_thread);
+EXPORT_SYMBOL(md_update_sb);
+EXPORT_SYMBOL(md_map);
+EXPORT_SYMBOL(md_wakeup_thread);
+EXPORT_SYMBOL(md_do_sync);
static struct proc_dir_entry proc_md = {
PROC_MD, 6, "mdstat",
@@ -451,16 +855,36 @@ static void md_geninit (struct gendisk *gdisk)
for(i=0;i<MAX_MD_DEV;i++)
{
md_blocksizes[i] = 1024;
+ md_maxreadahead[i] = MD_DEFAULT_DISK_READAHEAD;
md_gendisk.part[i].start_sect=-1; /* avoid partition check */
md_gendisk.part[i].nr_sects=0;
md_dev[i].pers=NULL;
}
- blksize_size[MAJOR_NR] = md_blocksizes;
+ blksize_size[MD_MAJOR] = md_blocksizes;
+ max_readahead[MD_MAJOR] = md_maxreadahead;
proc_register(&proc_root, &proc_md);
}
+int md_error (kdev_t mddev, kdev_t rdev)
+{
+ unsigned int minor = MINOR (mddev);
+ int rc;
+
+ if (MAJOR(mddev) != MD_MAJOR || minor > MAX_MD_DEV)
+ panic ("md_error gets unknown device\n");
+ if (!md_dev [minor].pers)
+ panic ("md_error gets an error for an unknown device\n");
+ if (md_dev [minor].pers->error_handler) {
+ rc = md_dev [minor].pers->error_handler (md_dev+minor, rdev);
+#if SUPPORT_RECONSTRUCTION
+ md_wakeup_thread(md_sync_thread);
+#endif /* SUPPORT_RECONSTRUCTION */
+ return rc;
+ }
+ return 0;
+}
int get_md_status (char *page)
{
@@ -493,9 +917,13 @@ int get_md_status (char *page)
partition_name(md_dev[i].devices[j].dev));
size+=md_dev[i].devices[j].size;
}
-
- if (md_dev[i].nb_dev)
- sz+=sprintf (page+sz, " %d blocks", size);
+
+ if (md_dev[i].nb_dev) {
+ if (md_dev[i].pers)
+ sz+=sprintf (page+sz, " %d blocks", md_size[i]);
+ else
+ sz+=sprintf (page+sz, " %d blocks", size);
+ }
if (!md_dev[i].pers)
{
@@ -506,11 +934,8 @@ int get_md_status (char *page)
if (md_dev[i].pers->max_invalid_dev)
sz+=sprintf (page+sz, " maxfault=%ld", MAX_FAULT(md_dev+i));
- sz+=sprintf (page+sz, " %dk %s\n", 1<<FACTOR_SHIFT(FACTOR(md_dev+i)),
- md_dev[i].pers == pers[LINEAR>>PERSONALITY_SHIFT] ?
- "rounding" : "chunks");
-
sz+=md_dev[i].pers->status (page+sz, i, md_dev+i);
+ sz+=sprintf (page+sz, "\n");
}
return (sz);
@@ -543,6 +968,198 @@ int unregister_md_personality (int p_num)
return 0;
}
+int md_thread(void * arg)
+{
+ struct md_thread *thread = arg;
+
+ current->session = 1;
+ current->pgrp = 1;
+ sprintf(current->comm, "md_thread");
+
+ lock_kernel();
+ for (;;) {
+ sti();
+ clear_bit(THREAD_WAKEUP, &thread->flags);
+ if (thread->run) {
+ thread->run(thread->data);
+ run_task_queue(&tq_disk);
+ }
+ cli();
+ if (!test_bit(THREAD_WAKEUP, &thread->flags)) {
+ do {
+ current->signal = 0;
+ interruptible_sleep_on(&thread->wqueue);
+ } while (current->signal);
+ }
+ }
+}
+
+static md_descriptor_t *get_spare(struct md_dev *mddev)
+{
+ int i;
+ md_superblock_t *sb = mddev->sb;
+ md_descriptor_t *descriptor;
+ struct real_dev *realdev;
+
+ for (i = 0; i < mddev->nb_dev; i++) {
+ realdev = &mddev->devices[i];
+ if (!realdev->sb)
+ continue;
+ descriptor = &sb->disks[realdev->sb->descriptor.number];
+ if (descriptor->state & (1 << MD_FAULTY_DEVICE))
+ continue;
+ if (descriptor->state & (1 << MD_ACTIVE_DEVICE))
+ continue;
+ return descriptor;
+ }
+ return NULL;
+}
+
+/*
+ * parallel resyncing thread.
+ *
+ * FIXME: - make it abort with a dirty array on mdstop, now it just blocks
+ * - fix read error handing
+ */
+
+int md_do_sync(struct md_dev *mddev)
+{
+ struct buffer_head *bh;
+ int max_blocks, blocksize, curr_bsize, percent=1, j;
+ kdev_t read_disk = MKDEV(MD_MAJOR, mddev - md_dev);
+ int major = MAJOR(read_disk), minor = MINOR(read_disk);
+ unsigned long starttime;
+
+ blocksize = blksize_size[major][minor];
+ max_blocks = blk_size[major][minor] / (blocksize >> 10);
+
+ printk("... resync log\n");
+ printk(" .... mddev->nb_dev: %d\n", mddev->nb_dev);
+ printk(" .... raid array: %s\n", kdevname(read_disk));
+ printk(" .... max_blocks: %d blocksize: %d\n", max_blocks, blocksize);
+ printk("md: syncing RAID array %s\n", kdevname(read_disk));
+
+ mddev->busy++;
+
+ starttime=jiffies;
+ for (j = 0; j < max_blocks; j++) {
+
+ /*
+ * B careful. When some1 mounts a non-'blocksize' filesystem
+ * then we get the blocksize changed right under us. Go deal
+ * with it transparently, recalculate 'blocksize', 'j' and
+ * 'max_blocks':
+ */
+ curr_bsize = blksize_size[major][minor];
+ if (curr_bsize != blocksize) {
+diff_blocksize:
+ if (curr_bsize > blocksize)
+ /*
+ * this is safe, rounds downwards.
+ */
+ j /= curr_bsize/blocksize;
+ else
+ j *= blocksize/curr_bsize;
+
+ blocksize = curr_bsize;
+ max_blocks = blk_size[major][minor] / (blocksize >> 10);
+ }
+ if ((bh = breada (read_disk, j, blocksize, j * blocksize,
+ max_blocks * blocksize)) != NULL) {
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ } else {
+ /*
+ * FIXME: Ugly, but set_blocksize() isnt safe ...
+ */
+ curr_bsize = blksize_size[major][minor];
+ if (curr_bsize != blocksize)
+ goto diff_blocksize;
+
+ /*
+ * It's a real read problem. FIXME, handle this
+ * a better way.
+ */
+ printk ( KERN_ALERT
+ "read error, stopping reconstruction.\n");
+ mddev->busy--;
+ return 1;
+ }
+
+ /*
+ * Lets sleep some if we are faster than our speed limit:
+ */
+ while (blocksize*j/(jiffies-starttime+1)*HZ/1024 > SPEED_LIMIT)
+ {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies+1;
+ schedule();
+ }
+
+ /*
+ * FIXME: put this status bar thing into /proc
+ */
+ if (!(j%(max_blocks/100))) {
+ if (!(percent%10))
+ printk (" %03d%% done.\n",percent);
+ else
+ printk (".");
+ percent++;
+ }
+ }
+ fsync_dev(read_disk);
+ printk("md: %s: sync done.\n", kdevname(read_disk));
+ mddev->busy--;
+ return 0;
+}
+
+/*
+ * This is a kernel thread which: syncs a spare disk with the active array
+ *
+ * the amount of foolproofing might seem to be a tad excessive, but an
+ * early (not so error-safe) version of raid1syncd synced the first 0.5 gigs
+ * of my root partition with the first 0.5 gigs of my /home partition ... so
+ * i'm a bit nervous ;)
+ */
+void mdsyncd (void *data)
+{
+ int i;
+ struct md_dev *mddev;
+ md_superblock_t *sb;
+ md_descriptor_t *spare;
+ unsigned long flags;
+
+ for (i = 0, mddev = md_dev; i < MAX_MD_DEV; i++, mddev++) {
+ if ((sb = mddev->sb) == NULL)
+ continue;
+ if (sb->active_disks == sb->raid_disks)
+ continue;
+ if (!sb->spare_disks)
+ continue;
+ if ((spare = get_spare(mddev)) == NULL)
+ continue;
+ if (!mddev->pers->mark_spare)
+ continue;
+ if (mddev->pers->mark_spare(mddev, spare, SPARE_WRITE))
+ continue;
+ if (md_do_sync(mddev) || (spare->state & (1 << MD_FAULTY_DEVICE))) {
+ mddev->pers->mark_spare(mddev, spare, SPARE_INACTIVE);
+ continue;
+ }
+ save_flags(flags);
+ cli();
+ mddev->pers->mark_spare(mddev, spare, SPARE_ACTIVE);
+ spare->state |= (1 << MD_SYNC_DEVICE);
+ spare->state |= (1 << MD_ACTIVE_DEVICE);
+ sb->spare_disks--;
+ sb->active_disks++;
+ mddev->sb_dirty = 1;
+ md_update_sb(mddev - md_dev);
+ restore_flags(flags);
+ }
+
+}
+
void linear_init (void);
void raid0_init (void);
void raid1_init (void);
@@ -550,7 +1167,11 @@ void raid5_init (void);
__initfunc(int md_init (void))
{
- printk ("md driver %s MAX_MD_DEV=%d, MAX_REAL=%d\n", MD_VERSION, MAX_MD_DEV, MAX_REAL);
+ int i;
+
+ printk ("md driver %d.%d.%d MAX_MD_DEV=%d, MAX_REAL=%d\n",
+ MD_MAJOR_VERSION, MD_MINOR_VERSION, MD_PATCHLEVEL_VERSION,
+ MAX_MD_DEV, MAX_REAL);
if (register_blkdev (MD_MAJOR, "md", &md_fops))
{
@@ -558,19 +1179,40 @@ __initfunc(int md_init (void))
return (-1);
}
+ memset(md_threads, 0, MAX_MD_THREADS * sizeof(struct md_thread));
+ printk("md: starting %d kernel threads\n", MAX_MD_THREADS);
+ for (i = 0; i < MAX_MD_THREADS; i++) {
+ md_threads[i].run = NULL;
+ init_waitqueue(&md_threads[i].wqueue);
+ md_threads[i].flags = 0;
+ kernel_thread (md_thread, md_threads + i, 0);
+ }
+
blk_dev[MD_MAJOR].request_fn=DEVICE_REQUEST;
blk_dev[MD_MAJOR].current_request=NULL;
read_ahead[MD_MAJOR]=INT_MAX;
+ memset(md_dev, 0, MAX_MD_DEV * sizeof (struct md_dev));
md_gendisk.next=gendisk_head;
gendisk_head=&md_gendisk;
+#if SUPPORT_RECONSTRUCTION
+ if ((md_sync_thread = md_register_thread(mdsyncd, NULL)) == NULL)
+ printk("md: bug: md_sync_thread == NULL\n");
+#endif /* SUPPORT_RECONSTRUCTION */
+
#ifdef CONFIG_MD_LINEAR
linear_init ();
#endif
#ifdef CONFIG_MD_STRIPED
raid0_init ();
#endif
+#ifdef CONFIG_MD_MIRRORING
+ raid1_init ();
+#endif
+#ifdef CONFIG_MD_RAID5
+ raid5_init ();
+#endif
return (0);
}
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
new file mode 100644
index 000000000..54920a912
--- /dev/null
+++ b/drivers/block/nbd.c
@@ -0,0 +1,435 @@
+/*
+ * Network block device - make block devices work over TCP
+ *
+ * Note that you can not swap over this thing, yet. Seems to work but
+ * deadlocks sometimes - you can not swap over TCP in general.
+ *
+ * Copyright 1997 Pavel Machek <pavel@atrey.karlin.mff.cuni.cz>
+ *
+ * (part of code stolen from loop.c)
+ *
+ * 97-3-25 compiled 0-th version, not yet tested it
+ * (it did not work, BTW) (later that day) HEY! it works!
+ * (bit later) hmm, not that much... 2:00am next day:
+ * yes, it works, but it gives something like 50kB/sec
+ * 97-4-01 complete rewrite to make it possible for many requests at
+ * once to be processed
+ * 97-4-11 Making protocol independent of endianity etc.
+ * 97-9-13 Cosmetic changes
+ *
+ * possible FIXME: make set_sock / set_blksize / set_size / do_it one syscall
+ * why not: would need verify_area and friends, would share yet another
+ * structure with userland
+ */
+
+#define PARANOIA
+#include <linux/major.h>
+#define MAJOR_NR NBD_MAJOR
+#include <linux/nbd.h>
+
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+
+static int nbd_blksizes[MAX_NBD] = {1024, 1024,};
+static int nbd_sizes[MAX_NBD] = {0x7fffffff, 0x7fffffff,};
+
+static struct nbd_device nbd_dev[MAX_NBD];
+
+#define DEBUG( s )
+/* #define DEBUG( s ) printk( s )
+ */
+
+#ifdef PARANOIA
+static int requests_in;
+static int requests_out;
+#endif
+
+static int nbd_open(struct inode *inode, struct file *file)
+{
+ int dev;
+
+ if (!inode)
+ return -EINVAL;
+ dev = MINOR(inode->i_rdev);
+ if (dev >= MAX_NBD)
+ return -ENODEV;
+ nbd_dev[dev].refcnt++;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * Send or receive packet.
+ */
+static
+int nbd_xmit(int send, struct socket *sock, char *buf, int size)
+{
+ unsigned long oldfs;
+ int result;
+ struct msghdr msg;
+ struct iovec iov;
+
+ oldfs = get_fs();
+ set_fs(get_ds());
+ do {
+ int save;
+
+ iov.iov_base = buf;
+ iov.iov_len = size;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_namelen = 0;
+ msg.msg_flags = 0;
+
+ save = current->blocked;
+ current->blocked = ~0UL;
+ if (send)
+ result = sock_sendmsg(sock, &msg, size);
+ else
+ result = sock_recvmsg(sock, &msg, size, 0);
+ current->blocked = save;
+ if (result <= 0) {
+#ifdef PARANOIA
+ printk(KERN_ERR "NBD: %s - sock=%d at buf=%d, size=%d returned %d.\n",
+ send ? "send" : "receive", (int) sock, (int) buf, size, result);
+#endif
+ break;
+ }
+ size -= result;
+ buf += result;
+ } while (size > 0);
+ set_fs(oldfs);
+ return result;
+}
+
+#define FAIL( s ) { printk( KERN_ERR "NBD: " s "(result %d)\n", result ); goto error_out; }
+
+void nbd_send_req(struct socket *sock, struct request *req)
+{
+ int result;
+ struct nbd_request request;
+
+ DEBUG("NBD: sending control, ");
+ request.magic = htonl(NBD_REQUEST_MAGIC);
+ request.type = htonl(req->cmd);
+ request.from = htonl(req->sector * 512);
+ request.len = htonl(req->current_nr_sectors << 9);
+ memcpy(request.handle, &req, sizeof(req));
+
+ result = nbd_xmit(1, sock, (char *) &request, sizeof(request));
+ if (result <= 0)
+ FAIL("Sendmsg failed for control.");
+
+ if (req->cmd == WRITE) {
+ DEBUG("data, ");
+ result = nbd_xmit(1, sock, req->buffer, req->current_nr_sectors << 9);
+ if (result <= 0)
+ FAIL("Send data failed.");
+ }
+ return;
+
+ error_out:
+ req->errors++;
+}
+
+#define HARDFAIL( s ) { printk( KERN_ERR "NBD: " s "(result %d)\n", result ); lo->harderror = result; return NULL; }
+struct request * /* NULL returned = something went wrong, inform userspace */
+ nbd_read_stat(struct nbd_device *lo)
+{
+ int result;
+ struct nbd_reply reply;
+ struct request *xreq, *req;
+
+ DEBUG("reading control, ");
+ reply.magic = 0;
+ result = nbd_xmit(0, lo->sock, (char *) &reply, sizeof(reply));
+ req = lo->tail;
+ if (result <= 0)
+ HARDFAIL("Recv control failed.");
+ memcpy(&xreq, reply.handle, sizeof(xreq));
+
+ if (xreq != req)
+ FAIL("Unexpected handle received.\n");
+
+ DEBUG("ok, ");
+ if (ntohl(reply.magic) != NBD_REPLY_MAGIC)
+ HARDFAIL("Not enough magic.");
+ if (ntohl(reply.error))
+ FAIL("Other side returned error.");
+ if (req->cmd == READ) {
+ DEBUG("data, ");
+ result = nbd_xmit(0, lo->sock, req->buffer, req->current_nr_sectors << 9);
+ if (result <= 0)
+ HARDFAIL("Recv data failed.");
+ }
+ DEBUG("done.\n");
+ return req;
+
+/* Can we get here? Yes, if other side returns error */
+ error_out:
+ req->errors++;
+ return req;
+}
+
+void nbd_do_it(struct nbd_device *lo)
+{
+ struct request *req;
+
+ while (1) {
+ req = nbd_read_stat(lo);
+ if (!req)
+ return;
+#ifdef PARANOIA
+ if (req != lo->tail) {
+ printk(KERN_ALERT "NBD: I have problem...\n");
+ }
+ if (lo != &nbd_dev[MINOR(req->rq_dev)]) {
+ printk(KERN_ALERT "NBD: request corrupted!\n");
+ continue;
+ }
+ if (lo->magic != LO_MAGIC) {
+ printk(KERN_ALERT "NBD: nbd_dev[] corrupted: Not enough magic\n");
+ return;
+ }
+#endif
+ nbd_end_request(req);
+ if (lo->tail == lo->head) {
+#ifdef PARANOIA
+ if (lo->tail->next)
+ printk(KERN_ERR "NBD: I did not expect this\n");
+#endif
+ lo->head = NULL;
+ }
+ lo->tail = lo->tail->next;
+ }
+}
+
+void nbd_clear_que(struct nbd_device *lo)
+{
+ struct request *req;
+
+ while (1) {
+ req = lo->tail;
+ if (!req)
+ return;
+#ifdef PARANOIA
+ if (lo != &nbd_dev[MINOR(req->rq_dev)]) {
+ printk(KERN_ALERT "NBD: request corrupted when clearing!\n");
+ continue;
+ }
+ if (lo->magic != LO_MAGIC) {
+ printk(KERN_ERR "NBD: nbd_dev[] corrupted: Not enough magic when clearing!\n");
+ return;
+ }
+#endif
+ req->errors++;
+ nbd_end_request(req);
+ if (lo->tail == lo->head) {
+#ifdef PARANOIA
+ if (lo->tail->next)
+ printk(KERN_ERR "NBD: I did not assume this\n");
+#endif
+ lo->head = NULL;
+ }
+ lo->tail = lo->tail->next;
+ }
+}
+
+/*
+ * We always wait for result of write, for now. It would be nice to make it optional
+ * in future
+ * if ((req->cmd == WRITE) && (lo->flags & NBD_WRITE_NOCHK))
+ * { printk( "Warning: Ignoring result!\n"); nbd_end_request( req ); }
+ */
+
+#undef FAIL
+#define FAIL( s ) { printk( KERN_ERR "NBD, minor %d: " s "\n", dev ); goto error_out; }
+
+static void do_nbd_request(void)
+{
+ struct request *req;
+ int dev;
+ struct nbd_device *lo;
+
+ while (CURRENT) {
+ req = CURRENT;
+ dev = MINOR(req->rq_dev);
+#ifdef PARANOIA
+ if (dev >= MAX_NBD)
+ FAIL("Minor too big."); /* Probably can not happen */
+#endif
+ lo = &nbd_dev[dev];
+ if (!lo->file)
+ FAIL("Request when not-ready.");
+ if ((req->cmd == WRITE) && (lo->flags && NBD_READ_ONLY))
+ FAIL("Write on read-only");
+#ifdef PARANOIA
+ if (lo->magic != LO_MAGIC)
+ FAIL("nbd[] is not magical!");
+ requests_in++;
+#endif
+ req->errors = 0;
+
+ nbd_send_req(lo->sock, req); /* Why does this block? */
+ CURRENT = CURRENT->next;
+ req->next = NULL;
+ if (lo->head == NULL) {
+ lo->head = req;
+ lo->tail = req;
+ } else {
+ lo->head->next = req;
+ lo->head = req;
+ }
+ continue;
+
+ error_out:
+ req->errors++;
+ nbd_end_request(req);
+ CURRENT = CURRENT->next;
+ }
+ return;
+}
+
+static int nbd_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct nbd_device *lo;
+ int dev;
+
+ if (!suser())
+ return -EPERM;
+ if (!inode)
+ return -EINVAL;
+ dev = MINOR(inode->i_rdev);
+ if (dev >= MAX_NBD)
+ return -ENODEV;
+ lo = &nbd_dev[dev];
+ switch (cmd) {
+ case NBD_CLEAR_SOCK:
+ if (lo->head || lo->tail) {
+ printk(KERN_ERR "nbd: Some requests are in progress -> can not turn off.\n");
+ return -EBUSY;
+ }
+ if (!lo->file)
+ return -EINVAL;
+ lo->file->f_count--;
+ lo->file = NULL;
+ lo->sock = NULL;
+ return 0;
+ case NBD_SET_SOCK:
+ file = current->files->fd[arg];
+ inode = file->f_dentry->d_inode;
+ file->f_count++;
+ lo->sock = &inode->u.socket_i;
+ lo->file = file;
+ return 0;
+ case NBD_SET_BLKSIZE:
+ if ((arg & 511) || (arg > PAGE_SIZE))
+ return -EINVAL;
+ nbd_blksizes[dev] = arg;
+ return 0;
+ case NBD_SET_SIZE:
+ nbd_sizes[dev] = arg;
+ return 0;
+ case NBD_DO_IT:
+ if (!lo->file)
+ return -EINVAL;
+ nbd_do_it(lo);
+ return lo->harderror;
+ case NBD_CLEAR_QUE:
+ nbd_clear_que(lo);
+ return 0;
+#ifdef PARANOIA
+ case NBD_PRINT_DEBUG:
+ printk(KERN_INFO "NBD device %d: head = %x, tail = %x. Global: in %d, out %d\n",
+ dev, (int) lo->head, (int) lo->tail, requests_in, requests_out);
+ return 0;
+#endif
+ }
+ return -EINVAL;
+}
+
+static int nbd_release(struct inode *inode, struct file *file)
+{
+ struct nbd_device *lo;
+ int dev;
+
+ if (!inode)
+ return -ENODEV;
+ dev = MINOR(inode->i_rdev);
+ if (dev >= MAX_NBD)
+ return -ENODEV;
+ fsync_dev(inode->i_rdev);
+ lo = &nbd_dev[dev];
+ if (lo->refcnt <= 0)
+ printk(KERN_ALERT "nbd_release: refcount(%d) <= 0\n", lo->refcnt);
+ lo->refcnt--;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct file_operations nbd_fops =
+{
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ nbd_ioctl, /* ioctl */
+ NULL, /* mmap */
+ nbd_open, /* open */
+ nbd_release /* release */
+};
+
+/*
+ * And here should be modules and kernel interface
+ * (Just smiley confuses emacs :-)
+ */
+
+#ifdef MODULE
+#define nbd_init init_module
+#endif
+
+int nbd_init(void)
+{
+ int i;
+
+ if (register_blkdev(MAJOR_NR, "nbd", &nbd_fops)) {
+ printk("Unable to get major number %d for NBD\n",
+ MAJOR_NR);
+ return -EIO;
+ }
+#ifdef MODULE
+ printk("nbd: registered device at major %d\n", MAJOR_NR);
+#endif
+ blksize_size[MAJOR_NR] = nbd_blksizes;
+ blk_size[MAJOR_NR] = nbd_sizes;
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ for (i = 0; i < MAX_NBD; i++) {
+ nbd_dev[i].refcnt = 0;
+ nbd_dev[i].file = NULL;
+ nbd_dev[i].magic = LO_MAGIC;
+ nbd_dev[i].flags = 0;
+ }
+ return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ if (unregister_blkdev(MAJOR_NR, "nbd") != 0)
+ printk("nbd: cleanup_module failed\n");
+ else
+ printk("nbd: module cleaned up.\n");
+}
+#endif
diff --git a/drivers/block/raid0.c b/drivers/block/raid0.c
index 6d269745b..7b8aea1c9 100644
--- a/drivers/block/raid0.c
+++ b/drivers/block/raid0.c
@@ -250,6 +250,7 @@ static int raid0_status (char *page, int minor, struct md_dev *mddev)
data->strip_zone[j].size);
}
#endif
+ sz+=sprintf (page+sz, " %dk chunks", 1<<FACTOR_SHIFT(FACTOR(mddev)));
return sz;
}
@@ -258,11 +259,17 @@ static struct md_personality raid0_personality=
{
"raid0",
raid0_map,
+ NULL, /* no special make_request */
+ NULL, /* no special end_request */
raid0_run,
raid0_stop,
raid0_status,
NULL, /* no ioctls */
- 0
+ 0,
+ NULL, /* no error_handler */
+ NULL, /* hot_add_disk */
+ NULL, /* hot_remove_disk */
+ NULL /* mark_spare */
};
diff --git a/drivers/block/raid1.c b/drivers/block/raid1.c
new file mode 100644
index 000000000..e85d8fd69
--- /dev/null
+++ b/drivers/block/raid1.c
@@ -0,0 +1,870 @@
+/************************************************************************
+ * raid1.c : Multiple Devices driver for Linux
+ * Copyright (C) 1996 Ingo Molnar, Miguel de Icaza, Gadi Oxman
+ *
+ * RAID-1 management functions.
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example /usr/src/linux/COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/locks.h>
+#include <linux/malloc.h>
+#include <linux/md.h>
+#include <linux/raid1.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
+
+#define MAJOR_NR MD_MAJOR
+#define MD_DRIVER
+#define MD_PERSONALITY
+
+/*
+ * The following can be used to debug the driver
+ */
+/*#define RAID1_DEBUG*/
+#ifdef RAID1_DEBUG
+#define PRINTK(x) do { printk x; } while (0);
+#else
+#define PRINTK(x) do { ; } while (0);
+#endif
+
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+static struct md_personality raid1_personality;
+static struct md_thread *raid1_thread = NULL;
+struct buffer_head *raid1_retry_list = NULL;
+
+static int __raid1_map (struct md_dev *mddev, kdev_t *rdev,
+ unsigned long *rsector, unsigned long size)
+{
+ struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+ int i, n = raid_conf->raid_disks;
+
+ /*
+ * Later we do read balancing on the read side
+ * now we use the first available disk.
+ */
+
+ PRINTK(("raid1_map().\n"));
+
+ for (i=0; i<n; i++) {
+ if (raid_conf->mirrors[i].operational) {
+ *rdev = raid_conf->mirrors[i].dev;
+ return (0);
+ }
+ }
+
+ printk (KERN_ERR "raid1_map(): huh, no more operational devices?\n");
+ return (-1);
+}
+
+static int raid1_map (struct md_dev *mddev, kdev_t *rdev,
+ unsigned long *rsector, unsigned long size)
+{
+ return 0;
+}
+
+void raid1_reschedule_retry (struct buffer_head *bh)
+{
+ struct raid1_bh * r1_bh = (struct raid1_bh *)(bh->b_dev_id);
+
+ PRINTK(("raid1_reschedule_retry().\n"));
+
+ r1_bh->next_retry = raid1_retry_list;
+ raid1_retry_list = bh;
+ md_wakeup_thread(raid1_thread);
+}
+
+/*
+ * raid1_end_buffer_io() is called when we have finished servicing a mirrored
+ * operation and are ready to return a success/failure code to the buffer
+ * cache layer.
+ */
+static inline void raid1_end_buffer_io(struct raid1_bh *r1_bh, int uptodate)
+{
+ struct buffer_head *bh = r1_bh->master_bh;
+
+ bh->b_end_io(bh, uptodate);
+ kfree(r1_bh);
+}
+
+int raid1_one_error=0;
+
+void raid1_end_request (struct buffer_head *bh, int uptodate)
+{
+ struct raid1_bh * r1_bh = (struct raid1_bh *)(bh->b_dev_id);
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ PRINTK(("raid1_end_request().\n"));
+
+ if (raid1_one_error) {
+ raid1_one_error=0;
+ uptodate=0;
+ }
+ /*
+ * this branch is our 'one mirror IO has finished' event handler:
+ */
+ if (!uptodate)
+ md_error (bh->b_dev, bh->b_rdev);
+ else {
+ /*
+ * Set BH_Uptodate in our master buffer_head, so that
+ * we will return a good error code for to the higher
+ * levels even if IO on some other mirrored buffer fails.
+ *
+ * The 'master' represents the complex operation to
+ * user-side. So if something waits for IO, then it will
+ * wait for the 'master' buffer_head.
+ */
+ set_bit (BH_Uptodate, &r1_bh->state);
+ }
+
+ /*
+ * We split up the read and write side, imho they are
+ * conceptually different.
+ */
+
+ if ( (r1_bh->cmd == READ) || (r1_bh->cmd == READA) ) {
+
+ PRINTK(("raid1_end_request(), read branch.\n"));
+
+ /*
+ * we have only one buffer_head on the read side
+ */
+ if (uptodate) {
+ PRINTK(("raid1_end_request(), read branch, uptodate.\n"));
+ raid1_end_buffer_io(r1_bh, uptodate);
+ restore_flags(flags);
+ return;
+ }
+ /*
+ * oops, read error:
+ */
+ printk(KERN_ERR "raid1: %s: rescheduling block %lu\n",
+ kdevname(bh->b_dev), bh->b_blocknr);
+ raid1_reschedule_retry (bh);
+ restore_flags(flags);
+ return;
+ }
+
+ /*
+ * WRITE or WRITEA.
+ */
+ PRINTK(("raid1_end_request(), write branch.\n"));
+
+ /*
+ * lets see if all mirrored write operations have finished
+ * already [we have irqs off, so we can decrease]:
+ */
+
+ if (!--r1_bh->remaining) {
+ struct md_dev *mddev = r1_bh->mddev;
+ struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+ int i, n = raid_conf->raid_disks;
+
+ PRINTK(("raid1_end_request(), remaining == 0.\n"));
+
+ for ( i=0; i<n; i++)
+ if (r1_bh->mirror_bh[i]) kfree(r1_bh->mirror_bh[i]);
+
+ raid1_end_buffer_io(r1_bh, test_bit(BH_Uptodate, &r1_bh->state));
+ }
+ else PRINTK(("raid1_end_request(), remaining == %u.\n", r1_bh->remaining));
+ restore_flags(flags);
+}
+
+/* This routine checks if the undelying device is an md device and in that
+ * case it maps the blocks before putting the request on the queue
+ */
+static inline void
+map_and_make_request (int rw, struct buffer_head *bh)
+{
+ if (MAJOR (bh->b_rdev) == MD_MAJOR)
+ md_map (MINOR (bh->b_rdev), &bh->b_rdev, &bh->b_rsector, bh->b_size >> 9);
+ clear_bit(BH_Lock, &bh->b_state);
+ make_request (MAJOR (bh->b_rdev), rw, bh);
+}
+
+static int
+raid1_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh)
+{
+
+ struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+ struct buffer_head *mirror_bh[MD_SB_DISKS], *bh_req;
+ struct raid1_bh * r1_bh;
+ int n = raid_conf->raid_disks, i, sum_bhs = 0, switch_disks = 0, sectors;
+ struct mirror_info *mirror;
+
+ PRINTK(("raid1_make_request().\n"));
+
+ while (!( /* FIXME: now we are rather fault tolerant than nice */
+ r1_bh = kmalloc (sizeof (struct raid1_bh), GFP_KERNEL)
+ ) )
+ printk ("raid1_make_request(#1): out of memory\n");
+ memset (r1_bh, 0, sizeof (struct raid1_bh));
+
+/*
+ * make_request() can abort the operation when READA or WRITEA are being
+ * used and no empty request is available.
+ *
+ * Currently, just replace the command with READ/WRITE.
+ */
+ if (rw == READA) rw = READ;
+ if (rw == WRITEA) rw = WRITE;
+
+ if (rw == WRITE || rw == WRITEA)
+ mark_buffer_clean(bh); /* Too early ? */
+
+/*
+ * i think the read and write branch should be separated completely, since we want
+ * to do read balancing on the read side for example. Comments? :) --mingo
+ */
+
+ r1_bh->master_bh=bh;
+ r1_bh->mddev=mddev;
+ r1_bh->cmd = rw;
+
+ if (rw==READ || rw==READA) {
+ int last_used = raid_conf->last_used;
+ PRINTK(("raid1_make_request(), read branch.\n"));
+ mirror = raid_conf->mirrors + last_used;
+ bh->b_rdev = mirror->dev;
+ sectors = bh->b_size >> 9;
+ if (bh->b_blocknr * sectors == raid_conf->next_sect) {
+ raid_conf->sect_count += sectors;
+ if (raid_conf->sect_count >= mirror->sect_limit)
+ switch_disks = 1;
+ } else
+ switch_disks = 1;
+ raid_conf->next_sect = (bh->b_blocknr + 1) * sectors;
+ if (switch_disks) {
+ PRINTK(("read-balancing: switching %d -> %d (%d sectors)\n", last_used, mirror->next, raid_conf->sect_count));
+ raid_conf->sect_count = 0;
+ last_used = raid_conf->last_used = mirror->next;
+ /*
+ * Do not switch to write-only disks ... resyncing
+ * is in progress
+ */
+ while (raid_conf->mirrors[last_used].write_only)
+ raid_conf->last_used = raid_conf->mirrors[last_used].next;
+ }
+ PRINTK (("raid1 read queue: %d %d\n", MAJOR (bh->b_rdev), MINOR (bh->b_rdev)));
+ bh_req = &r1_bh->bh_req;
+ memcpy(bh_req, bh, sizeof(*bh));
+ bh_req->b_end_io = raid1_end_request;
+ bh_req->b_dev_id = r1_bh;
+ map_and_make_request (rw, bh_req);
+ return 0;
+ }
+
+ /*
+ * WRITE or WRITEA.
+ */
+ PRINTK(("raid1_make_request(n=%d), write branch.\n",n));
+
+ for (i = 0; i < n; i++) {
+
+ if (!raid_conf->mirrors [i].operational) {
+ /*
+ * the r1_bh->mirror_bh[i] pointer remains NULL
+ */
+ mirror_bh[i] = NULL;
+ continue;
+ }
+
+ /*
+ * We should use a private pool (size depending on NR_REQUEST),
+ * to avoid writes filling up the memory with bhs
+ *
+ * Such pools are much faster than kmalloc anyways (so we waste almost
+ * nothing by not using the master bh when writing and win alot of cleanness)
+ *
+ * but for now we are cool enough. --mingo
+ *
+ * It's safe to sleep here, buffer heads cannot be used in a shared
+ * manner in the write branch. Look how we lock the buffer at the beginning
+ * of this function to grok the difference ;)
+ */
+ while (!( /* FIXME: now we are rather fault tolerant than nice */
+ mirror_bh[i] = kmalloc (sizeof (struct buffer_head), GFP_KERNEL)
+ ) )
+ printk ("raid1_make_request(#2): out of memory\n");
+ memset (mirror_bh[i], 0, sizeof (struct buffer_head));
+
+ /*
+ * prepare mirrored bh (fields ordered for max mem throughput):
+ */
+ mirror_bh [i]->b_blocknr = bh->b_blocknr;
+ mirror_bh [i]->b_dev = bh->b_dev;
+ mirror_bh [i]->b_rdev = raid_conf->mirrors [i].dev;
+ mirror_bh [i]->b_rsector = bh->b_rsector;
+ mirror_bh [i]->b_state = (1<<BH_Req) |
+ (1<<BH_Touched) | (1<<BH_Dirty);
+ mirror_bh [i]->b_count = 1;
+ mirror_bh [i]->b_size = bh->b_size;
+ mirror_bh [i]->b_data = bh->b_data;
+ mirror_bh [i]->b_list = BUF_LOCKED;
+ mirror_bh [i]->b_end_io = raid1_end_request;
+ mirror_bh [i]->b_dev_id = r1_bh;
+
+ r1_bh->mirror_bh[i] = mirror_bh[i];
+ sum_bhs++;
+ }
+
+ r1_bh->remaining = sum_bhs;
+
+ PRINTK(("raid1_make_request(), write branch, sum_bhs=%d.\n",sum_bhs));
+
+ /*
+ * We have to be a bit careful about the semaphore above, thats why we
+ * start the requests separately. Since kmalloc() could fail, sleep and
+ * make_request() can sleep too, this is the safer solution. Imagine,
+ * end_request decreasing the semaphore before we could have set it up ...
+ * We could play tricks with the semaphore (presetting it and correcting
+ * at the end if sum_bhs is not 'n' but we have to do end_request by hand
+ * if all requests finish until we had a chance to set up the semaphore
+ * correctly ... lots of races).
+ */
+ for (i = 0; i < n; i++)
+ if (mirror_bh [i] != NULL)
+ map_and_make_request (rw, mirror_bh [i]);
+
+ return (0);
+}
+
+static int raid1_status (char *page, int minor, struct md_dev *mddev)
+{
+ struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+ int sz = 0, i;
+
+ sz += sprintf (page+sz, " [%d/%d] [", raid_conf->raid_disks, raid_conf->working_disks);
+ for (i = 0; i < raid_conf->raid_disks; i++)
+ sz += sprintf (page+sz, "%s", raid_conf->mirrors [i].operational ? "U" : "_");
+ sz += sprintf (page+sz, "]");
+ return sz;
+}
+
+static void raid1_fix_links (struct raid1_data *raid_conf, int failed_index)
+{
+ int disks = raid_conf->raid_disks;
+ int j;
+
+ for (j = 0; j < disks; j++)
+ if (raid_conf->mirrors [j].next == failed_index)
+ raid_conf->mirrors [j].next = raid_conf->mirrors [failed_index].next;
+}
+
+#define LAST_DISK KERN_ALERT \
+"raid1: only one disk left and IO error.\n"
+
+#define NO_SPARE_DISK KERN_ALERT \
+"raid1: no spare disk left, degrading mirror level by one.\n"
+
+#define DISK_FAILED KERN_ALERT \
+"raid1: Disk failure on %s, disabling device. \n" \
+" Operation continuing on %d devices\n"
+
+#define START_SYNCING KERN_ALERT \
+"raid1: start syncing spare disk.\n"
+
+#define ALREADY_SYNCING KERN_INFO \
+"raid1: syncing already in progress.\n"
+
+static int raid1_error (struct md_dev *mddev, kdev_t dev)
+{
+ struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+ struct mirror_info *mirror;
+ md_superblock_t *sb = mddev->sb;
+ int disks = raid_conf->raid_disks;
+ int i;
+
+ PRINTK(("raid1_error called\n"));
+
+ if (raid_conf->working_disks == 1) {
+ /*
+ * Uh oh, we can do nothing if this is our last disk, but
+ * first check if this is a queued request for a device
+ * which has just failed.
+ */
+ for (i = 0, mirror = raid_conf->mirrors; i < disks;
+ i++, mirror++)
+ if (mirror->dev == dev && !mirror->operational)
+ return 0;
+ printk (LAST_DISK);
+ } else {
+ /* Mark disk as unusable */
+ for (i = 0, mirror = raid_conf->mirrors; i < disks;
+ i++, mirror++) {
+ if (mirror->dev == dev && mirror->operational){
+ mirror->operational = 0;
+ raid1_fix_links (raid_conf, i);
+ sb->disks[mirror->number].state |=
+ (1 << MD_FAULTY_DEVICE);
+ sb->disks[mirror->number].state &=
+ ~(1 << MD_SYNC_DEVICE);
+ sb->disks[mirror->number].state &=
+ ~(1 << MD_ACTIVE_DEVICE);
+ sb->active_disks--;
+ sb->working_disks--;
+ sb->failed_disks++;
+ mddev->sb_dirty = 1;
+ md_wakeup_thread(raid1_thread);
+ raid_conf->working_disks--;
+ printk (DISK_FAILED, kdevname (dev),
+ raid_conf->working_disks);
+ }
+ }
+ }
+ return 0;
+}
+
+#undef LAST_DISK
+#undef NO_SPARE_DISK
+#undef DISK_FAILED
+#undef START_SYNCING
+
+/*
+ * This is the personality-specific hot-addition routine
+ */
+
+#define NO_SUPERBLOCK KERN_ERR \
+"raid1: cannot hot-add disk to the array with no RAID superblock\n"
+
+#define WRONG_LEVEL KERN_ERR \
+"raid1: hot-add: level of disk is not RAID-1\n"
+
+#define HOT_ADD_SUCCEEDED KERN_INFO \
+"raid1: device %s hot-added\n"
+
+static int raid1_hot_add_disk (struct md_dev *mddev, kdev_t dev)
+{
+ unsigned long flags;
+ struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+ struct mirror_info *mirror;
+ md_superblock_t *sb = mddev->sb;
+ struct real_dev * realdev;
+ int n;
+
+ /*
+ * The device has it's superblock already read and it was found
+ * to be consistent for generic RAID usage, now we check wether
+ * it's usable for RAID-1 hot addition.
+ */
+
+ n = mddev->nb_dev++;
+ realdev = &mddev->devices[n];
+ if (!realdev->sb) {
+ printk (NO_SUPERBLOCK);
+ return -EINVAL;
+ }
+ if (realdev->sb->level != 1) {
+ printk (WRONG_LEVEL);
+ return -EINVAL;
+ }
+ /* FIXME: are there other things left we could sanity-check? */
+
+ /*
+ * We have to disable interrupts, as our RAID-1 state is used
+ * from irq handlers as well.
+ */
+ save_flags(flags);
+ cli();
+
+ raid_conf->raid_disks++;
+ mirror = raid_conf->mirrors+n;
+
+ mirror->number=n;
+ mirror->raid_disk=n;
+ mirror->dev=dev;
+ mirror->next=0; /* FIXME */
+ mirror->sect_limit=128;
+
+ mirror->operational=0;
+ mirror->spare=1;
+ mirror->write_only=0;
+
+ sb->disks[n].state |= (1 << MD_FAULTY_DEVICE);
+ sb->disks[n].state &= ~(1 << MD_SYNC_DEVICE);
+ sb->disks[n].state &= ~(1 << MD_ACTIVE_DEVICE);
+ sb->nr_disks++;
+ sb->spare_disks++;
+
+ restore_flags(flags);
+
+ md_update_sb(MINOR(dev));
+
+ printk (HOT_ADD_SUCCEEDED, kdevname(realdev->dev));
+
+ return 0;
+}
+
+#undef NO_SUPERBLOCK
+#undef WRONG_LEVEL
+#undef HOT_ADD_SUCCEEDED
+
+/*
+ * Insert the spare disk into the drive-ring
+ */
+static void add_ring(struct raid1_data *raid_conf, struct mirror_info *mirror)
+{
+ int j, next;
+ struct mirror_info *p = raid_conf->mirrors;
+
+ for (j = 0; j < raid_conf->raid_disks; j++, p++)
+ if (p->operational && !p->write_only) {
+ next = p->next;
+ p->next = mirror->raid_disk;
+ mirror->next = next;
+ return;
+ }
+ printk("raid1: bug: no read-operational devices\n");
+}
+
+static int raid1_mark_spare(struct md_dev *mddev, md_descriptor_t *spare,
+ int state)
+{
+ int i = 0, failed_disk = -1;
+ struct raid1_data *raid_conf = mddev->private;
+ struct mirror_info *mirror = raid_conf->mirrors;
+ md_descriptor_t *descriptor;
+ unsigned long flags;
+
+ for (i = 0; i < MD_SB_DISKS; i++, mirror++) {
+ if (mirror->spare && mirror->number == spare->number)
+ goto found;
+ }
+ return 1;
+found:
+ for (i = 0, mirror = raid_conf->mirrors; i < raid_conf->raid_disks;
+ i++, mirror++)
+ if (!mirror->operational)
+ failed_disk = i;
+
+ save_flags(flags);
+ cli();
+ switch (state) {
+ case SPARE_WRITE:
+ mirror->operational = 1;
+ mirror->write_only = 1;
+ raid_conf->raid_disks = MAX(raid_conf->raid_disks,
+ mirror->raid_disk + 1);
+ break;
+ case SPARE_INACTIVE:
+ mirror->operational = 0;
+ mirror->write_only = 0;
+ break;
+ case SPARE_ACTIVE:
+ mirror->spare = 0;
+ mirror->write_only = 0;
+ raid_conf->working_disks++;
+ add_ring(raid_conf, mirror);
+
+ if (failed_disk != -1) {
+ descriptor = &mddev->sb->disks[raid_conf->mirrors[failed_disk].number];
+ i = spare->raid_disk;
+ spare->raid_disk = descriptor->raid_disk;
+ descriptor->raid_disk = i;
+ }
+ break;
+ default:
+ printk("raid1_mark_spare: bug: state == %d\n", state);
+ restore_flags(flags);
+ return 1;
+ }
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * This is a kernel thread which:
+ *
+ * 1. Retries failed read operations on working mirrors.
+ * 2. Updates the raid superblock when problems encounter.
+ */
+void raid1d (void *data)
+{
+ struct buffer_head *bh;
+ kdev_t dev;
+ unsigned long flags;
+ struct raid1_bh * r1_bh;
+ struct md_dev *mddev;
+
+ PRINTK(("raid1d() active\n"));
+ save_flags(flags);
+ cli();
+ while (raid1_retry_list) {
+ bh = raid1_retry_list;
+ r1_bh = (struct raid1_bh *)(bh->b_dev_id);
+ raid1_retry_list = r1_bh->next_retry;
+ restore_flags(flags);
+
+ mddev = md_dev + MINOR(bh->b_dev);
+ if (mddev->sb_dirty) {
+ printk("dirty sb detected, updating.\n");
+ mddev->sb_dirty = 0;
+ md_update_sb(MINOR(bh->b_dev));
+ }
+ dev = bh->b_rdev;
+ __raid1_map (md_dev + MINOR(bh->b_dev), &bh->b_rdev, &bh->b_rsector, bh->b_size >> 9);
+ if (bh->b_rdev == dev) {
+ printk (KERN_ALERT
+ "raid1: %s: unrecoverable I/O read error for block %lu\n",
+ kdevname(bh->b_dev), bh->b_blocknr);
+ raid1_end_buffer_io(r1_bh, 0);
+ } else {
+ printk (KERN_ERR "raid1: %s: redirecting sector %lu to another mirror\n",
+ kdevname(bh->b_dev), bh->b_blocknr);
+ map_and_make_request (r1_bh->cmd, bh);
+ }
+ cli();
+ }
+ restore_flags(flags);
+}
+
+/*
+ * This will catch the scenario in which one of the mirrors was
+ * mounted as a normal device rather than as a part of a raid set.
+ */
+static int __check_consistency (struct md_dev *mddev, int row)
+{
+ struct raid1_data *raid_conf = mddev->private;
+ kdev_t dev;
+ struct buffer_head *bh = NULL;
+ int i, rc = 0;
+ char *buffer = NULL;
+
+ for (i = 0; i < raid_conf->raid_disks; i++) {
+ if (!raid_conf->mirrors[i].operational)
+ continue;
+ dev = raid_conf->mirrors[i].dev;
+ set_blocksize(dev, 4096);
+ if ((bh = bread(dev, row / 4, 4096)) == NULL)
+ break;
+ if (!buffer) {
+ buffer = (char *) __get_free_page(GFP_KERNEL);
+ if (!buffer)
+ break;
+ memcpy(buffer, bh->b_data, 4096);
+ } else if (memcmp(buffer, bh->b_data, 4096)) {
+ rc = 1;
+ break;
+ }
+ bforget(bh);
+ fsync_dev(dev);
+ invalidate_buffers(dev);
+ bh = NULL;
+ }
+ if (buffer)
+ free_page((unsigned long) buffer);
+ if (bh) {
+ dev = bh->b_dev;
+ bforget(bh);
+ fsync_dev(dev);
+ invalidate_buffers(dev);
+ }
+ return rc;
+}
+
+static int check_consistency (struct md_dev *mddev)
+{
+ int size = mddev->sb->size;
+ int row;
+
+ for (row = 0; row < size; row += size / 8)
+ if (__check_consistency(mddev, row))
+ return 1;
+ return 0;
+}
+
+static int raid1_run (int minor, struct md_dev *mddev)
+{
+ struct raid1_data *raid_conf;
+ int i, j, raid_disk;
+ md_superblock_t *sb = mddev->sb;
+ md_descriptor_t *descriptor;
+ struct real_dev *realdev;
+
+ MOD_INC_USE_COUNT;
+
+ if (sb->level != 1) {
+ printk("raid1: %s: raid level not set to mirroring (%d)\n",
+ kdevname(MKDEV(MD_MAJOR, minor)), sb->level);
+ MOD_DEC_USE_COUNT;
+ return -EIO;
+ }
+ /****
+ * copy the now verified devices into our private RAID1 bookkeeping
+ * area. [whatever we allocate in raid1_run(), should be freed in
+ * raid1_stop()]
+ */
+
+ while (!( /* FIXME: now we are rather fault tolerant than nice */
+ mddev->private = kmalloc (sizeof (struct raid1_data), GFP_KERNEL)
+ ) )
+ printk ("raid1_run(): out of memory\n");
+ raid_conf = mddev->private;
+ memset(raid_conf, 0, sizeof(*raid_conf));
+
+ PRINTK(("raid1_run(%d) called.\n", minor));
+
+ for (i = 0; i < mddev->nb_dev; i++) {
+ realdev = &mddev->devices[i];
+ if (!realdev->sb) {
+ printk(KERN_ERR "raid1: disabled mirror %s (couldn't access raid superblock)\n", kdevname(realdev->dev));
+ continue;
+ }
+
+ /*
+ * This is important -- we are using the descriptor on
+ * the disk only to get a pointer to the descriptor on
+ * the main superblock, which might be more recent.
+ */
+ descriptor = &sb->disks[realdev->sb->descriptor.number];
+ if (descriptor->state & (1 << MD_FAULTY_DEVICE)) {
+ printk(KERN_ERR "raid1: disabled mirror %s (errors detected)\n", kdevname(realdev->dev));
+ continue;
+ }
+ if (descriptor->state & (1 << MD_ACTIVE_DEVICE)) {
+ if (!(descriptor->state & (1 << MD_SYNC_DEVICE))) {
+ printk(KERN_ERR "raid1: disabled mirror %s (not in sync)\n", kdevname(realdev->dev));
+ continue;
+ }
+ raid_disk = descriptor->raid_disk;
+ if (descriptor->number > sb->nr_disks || raid_disk > sb->raid_disks) {
+ printk(KERN_ERR "raid1: disabled mirror %s (inconsistent descriptor)\n", kdevname(realdev->dev));
+ continue;
+ }
+ if (raid_conf->mirrors[raid_disk].operational) {
+ printk(KERN_ERR "raid1: disabled mirror %s (mirror %d already operational)\n", kdevname(realdev->dev), raid_disk);
+ continue;
+ }
+ printk(KERN_INFO "raid1: device %s operational as mirror %d\n", kdevname(realdev->dev), raid_disk);
+ raid_conf->mirrors[raid_disk].number = descriptor->number;
+ raid_conf->mirrors[raid_disk].raid_disk = raid_disk;
+ raid_conf->mirrors[raid_disk].dev = mddev->devices [i].dev;
+ raid_conf->mirrors[raid_disk].operational = 1;
+ raid_conf->mirrors[raid_disk].sect_limit = 128;
+ raid_conf->working_disks++;
+ } else {
+ /*
+ * Must be a spare disk ..
+ */
+ printk(KERN_INFO "raid1: spare disk %s\n", kdevname(realdev->dev));
+ raid_disk = descriptor->raid_disk;
+ raid_conf->mirrors[raid_disk].number = descriptor->number;
+ raid_conf->mirrors[raid_disk].raid_disk = raid_disk;
+ raid_conf->mirrors[raid_disk].dev = mddev->devices [i].dev;
+ raid_conf->mirrors[raid_disk].sect_limit = 128;
+
+ raid_conf->mirrors[raid_disk].operational = 0;
+ raid_conf->mirrors[raid_disk].write_only = 0;
+ raid_conf->mirrors[raid_disk].spare = 1;
+ }
+ }
+ if (!raid_conf->working_disks) {
+ printk(KERN_ERR "raid1: no operational mirrors for %s\n", kdevname(MKDEV(MD_MAJOR, minor)));
+ kfree(raid_conf);
+ mddev->private = NULL;
+ MOD_DEC_USE_COUNT;
+ return -EIO;
+ }
+
+ raid_conf->raid_disks = sb->raid_disks;
+ raid_conf->mddev = mddev;
+
+ for (j = 0; !raid_conf->mirrors[j].operational; j++);
+ raid_conf->last_used = j;
+ for (i = raid_conf->raid_disks - 1; i >= 0; i--) {
+ if (raid_conf->mirrors[i].operational) {
+ PRINTK(("raid_conf->mirrors[%d].next == %d\n", i, j));
+ raid_conf->mirrors[i].next = j;
+ j = i;
+ }
+ }
+
+ if (check_consistency(mddev)) {
+ printk(KERN_ERR "raid1: detected mirror differences -- run ckraid\n");
+ sb->state |= 1 << MD_SB_ERRORS;
+ kfree(raid_conf);
+ mddev->private = NULL;
+ MOD_DEC_USE_COUNT;
+ return -EIO;
+ }
+
+ /*
+ * Regenerate the "device is in sync with the raid set" bit for
+ * each device.
+ */
+ for (i = 0; i < sb->nr_disks ; i++) {
+ sb->disks[i].state &= ~(1 << MD_SYNC_DEVICE);
+ for (j = 0; j < sb->raid_disks; j++) {
+ if (!raid_conf->mirrors[j].operational)
+ continue;
+ if (sb->disks[i].number == raid_conf->mirrors[j].number)
+ sb->disks[i].state |= 1 << MD_SYNC_DEVICE;
+ }
+ }
+ sb->active_disks = raid_conf->working_disks;
+
+ printk("raid1: raid set %s active with %d out of %d mirrors\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks);
+ /* Ok, everything is just fine now */
+ return (0);
+}
+
+static int raid1_stop (int minor, struct md_dev *mddev)
+{
+ struct raid1_data *raid_conf = (struct raid1_data *) mddev->private;
+
+ kfree (raid_conf);
+ mddev->private = NULL;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct md_personality raid1_personality=
+{
+ "raid1",
+ raid1_map,
+ raid1_make_request,
+ raid1_end_request,
+ raid1_run,
+ raid1_stop,
+ raid1_status,
+ NULL, /* no ioctls */
+ 0,
+ raid1_error,
+ raid1_hot_add_disk,
+ /* raid1_hot_remove_drive */ NULL,
+ raid1_mark_spare
+};
+
+int raid1_init (void)
+{
+ if ((raid1_thread = md_register_thread(raid1d, NULL)) == NULL)
+ return -EBUSY;
+ return register_md_personality (RAID1, &raid1_personality);
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+ return raid1_init();
+}
+
+void cleanup_module (void)
+{
+ md_unregister_thread (raid1_thread);
+ unregister_md_personality (RAID1);
+}
+#endif
diff --git a/drivers/block/raid5.c b/drivers/block/raid5.c
new file mode 100644
index 000000000..918dd2ee2
--- /dev/null
+++ b/drivers/block/raid5.c
@@ -0,0 +1,1652 @@
+/*****************************************************************************
+ * raid5.c : Multiple Devices driver for Linux
+ * Copyright (C) 1996, 1997 Ingo Molnar, Miguel de Icaza, Gadi Oxman
+ *
+ * RAID-5 management functions.
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example /usr/src/linux/COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/locks.h>
+#include <linux/malloc.h>
+#include <linux/md.h>
+#include <linux/raid5.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
+
+static struct md_personality raid5_personality;
+
+/*
+ * Stripe cache
+ */
+#define NR_STRIPES 128
+#define HASH_PAGES 1
+#define HASH_PAGES_ORDER 0
+#define NR_HASH (HASH_PAGES * PAGE_SIZE / sizeof(struct stripe_head *))
+#define HASH_MASK (NR_HASH - 1)
+#define stripe_hash(raid_conf, sect, size) ((raid_conf)->stripe_hashtbl[((sect) / (size >> 9)) & HASH_MASK])
+
+/*
+ * The following can be used to debug the driver
+ */
+#define RAID5_DEBUG 0
+
+#if RAID5_DEBUG
+#define PRINTK(x) do { printk x; } while (0);
+#else
+#define PRINTK(x) do { ; } while (0)
+#endif
+
+static inline int stripe_locked(struct stripe_head *sh)
+{
+ return test_bit(STRIPE_LOCKED, &sh->state);
+}
+
+static inline int stripe_error(struct stripe_head *sh)
+{
+ return test_bit(STRIPE_ERROR, &sh->state);
+}
+
+/*
+ * Stripes are locked whenever new buffers can't be added to them.
+ */
+static inline void lock_stripe(struct stripe_head *sh)
+{
+ struct raid5_data *raid_conf = sh->raid_conf;
+ if (!test_and_set_bit(STRIPE_LOCKED, &sh->state)) {
+ PRINTK(("locking stripe %lu\n", sh->sector));
+ raid_conf->nr_locked_stripes++;
+ }
+}
+
+static inline void unlock_stripe(struct stripe_head *sh)
+{
+ struct raid5_data *raid_conf = sh->raid_conf;
+ if (test_and_clear_bit(STRIPE_LOCKED, &sh->state)) {
+ PRINTK(("unlocking stripe %lu\n", sh->sector));
+ raid_conf->nr_locked_stripes--;
+ wake_up(&sh->wait);
+ }
+}
+
+static inline void finish_stripe(struct stripe_head *sh)
+{
+ struct raid5_data *raid_conf = sh->raid_conf;
+ unlock_stripe(sh);
+ sh->cmd = STRIPE_NONE;
+ sh->phase = PHASE_COMPLETE;
+ raid_conf->nr_pending_stripes--;
+ raid_conf->nr_cached_stripes++;
+ wake_up(&raid_conf->wait_for_stripe);
+}
+
+void __wait_on_stripe(struct stripe_head *sh)
+{
+ struct wait_queue wait = { current, NULL };
+
+ PRINTK(("wait_on_stripe %lu\n", sh->sector));
+ sh->count++;
+ add_wait_queue(&sh->wait, &wait);
+repeat:
+ current->state = TASK_UNINTERRUPTIBLE;
+ if (stripe_locked(sh)) {
+ schedule();
+ goto repeat;
+ }
+ PRINTK(("wait_on_stripe %lu done\n", sh->sector));
+ remove_wait_queue(&sh->wait, &wait);
+ sh->count--;
+ current->state = TASK_RUNNING;
+}
+
+static inline void wait_on_stripe(struct stripe_head *sh)
+{
+ if (stripe_locked(sh))
+ __wait_on_stripe(sh);
+}
+
+static inline void remove_hash(struct raid5_data *raid_conf, struct stripe_head *sh)
+{
+ PRINTK(("remove_hash(), stripe %lu\n", sh->sector));
+
+ if (sh->hash_pprev) {
+ if (sh->hash_next)
+ sh->hash_next->hash_pprev = sh->hash_pprev;
+ *sh->hash_pprev = sh->hash_next;
+ sh->hash_pprev = NULL;
+ raid_conf->nr_hashed_stripes--;
+ }
+}
+
+static inline void insert_hash(struct raid5_data *raid_conf, struct stripe_head *sh)
+{
+ struct stripe_head **shp = &stripe_hash(raid_conf, sh->sector, sh->size);
+
+ PRINTK(("insert_hash(), stripe %lu, nr_hashed_stripes %d\n", sh->sector, raid_conf->nr_hashed_stripes));
+
+ if ((sh->hash_next = *shp) != NULL)
+ (*shp)->hash_pprev = &sh->hash_next;
+ *shp = sh;
+ sh->hash_pprev = shp;
+ raid_conf->nr_hashed_stripes++;
+}
+
+static struct buffer_head *get_free_buffer(struct stripe_head *sh, int b_size)
+{
+ struct buffer_head *bh;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if ((bh = sh->buffer_pool) == NULL)
+ return NULL;
+ sh->buffer_pool = bh->b_next;
+ bh->b_size = b_size;
+ restore_flags(flags);
+ return bh;
+}
+
+static struct buffer_head *get_free_bh(struct stripe_head *sh)
+{
+ struct buffer_head *bh;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if ((bh = sh->bh_pool) == NULL)
+ return NULL;
+ sh->bh_pool = bh->b_next;
+ restore_flags(flags);
+ return bh;
+}
+
+static void put_free_buffer(struct stripe_head *sh, struct buffer_head *bh)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ bh->b_next = sh->buffer_pool;
+ sh->buffer_pool = bh;
+ restore_flags(flags);
+}
+
+static void put_free_bh(struct stripe_head *sh, struct buffer_head *bh)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ bh->b_next = sh->bh_pool;
+ sh->bh_pool = bh;
+ restore_flags(flags);
+}
+
+static struct stripe_head *get_free_stripe(struct raid5_data *raid_conf)
+{
+ struct stripe_head *sh;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if ((sh = raid_conf->free_sh_list) == NULL) {
+ restore_flags(flags);
+ return NULL;
+ }
+ raid_conf->free_sh_list = sh->free_next;
+ raid_conf->nr_free_sh--;
+ if (!raid_conf->nr_free_sh && raid_conf->free_sh_list)
+ printk ("raid5: bug: free_sh_list != NULL, nr_free_sh == 0\n");
+ restore_flags(flags);
+ if (sh->hash_pprev || sh->nr_pending || sh->count)
+ printk("get_free_stripe(): bug\n");
+ return sh;
+}
+
+static void put_free_stripe(struct raid5_data *raid_conf, struct stripe_head *sh)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ sh->free_next = raid_conf->free_sh_list;
+ raid_conf->free_sh_list = sh;
+ raid_conf->nr_free_sh++;
+ restore_flags(flags);
+}
+
+static void shrink_buffers(struct stripe_head *sh, int num)
+{
+ struct buffer_head *bh;
+
+ while (num--) {
+ if ((bh = get_free_buffer(sh, -1)) == NULL)
+ return;
+ free_page((unsigned long) bh->b_data);
+ kfree(bh);
+ }
+}
+
+static void shrink_bh(struct stripe_head *sh, int num)
+{
+ struct buffer_head *bh;
+
+ while (num--) {
+ if ((bh = get_free_bh(sh)) == NULL)
+ return;
+ kfree(bh);
+ }
+}
+
+static int grow_buffers(struct stripe_head *sh, int num, int b_size, int priority)
+{
+ struct buffer_head *bh;
+
+ while (num--) {
+ if ((bh = kmalloc(sizeof(struct buffer_head), priority)) == NULL)
+ return 1;
+ memset(bh, 0, sizeof (struct buffer_head));
+ bh->b_data = (char *) __get_free_page(priority);
+ if (!bh->b_data) {
+ kfree(bh);
+ return 1;
+ }
+ bh->b_size = b_size;
+ put_free_buffer(sh, bh);
+ }
+ return 0;
+}
+
+static int grow_bh(struct stripe_head *sh, int num, int priority)
+{
+ struct buffer_head *bh;
+
+ while (num--) {
+ if ((bh = kmalloc(sizeof(struct buffer_head), priority)) == NULL)
+ return 1;
+ memset(bh, 0, sizeof (struct buffer_head));
+ put_free_bh(sh, bh);
+ }
+ return 0;
+}
+
+static void raid5_kfree_buffer(struct stripe_head *sh, struct buffer_head *bh)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ put_free_buffer(sh, bh);
+ restore_flags(flags);
+}
+
+static void raid5_kfree_bh(struct stripe_head *sh, struct buffer_head *bh)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ put_free_bh(sh, bh);
+ restore_flags(flags);
+}
+
+static void raid5_kfree_old_bh(struct stripe_head *sh, int i)
+{
+ if (!sh->bh_old[i]) {
+ printk("raid5_kfree_old_bh: bug: sector %lu, index %d not present\n", sh->sector, i);
+ return;
+ }
+ raid5_kfree_buffer(sh, sh->bh_old[i]);
+ sh->bh_old[i] = NULL;
+}
+
+static void raid5_update_old_bh(struct stripe_head *sh, int i)
+{
+ PRINTK(("stripe %lu, idx %d, updating cache copy\n", sh->sector, i));
+ if (!sh->bh_copy[i]) {
+ printk("raid5_update_old_bh: bug: sector %lu, index %d not present\n", sh->sector, i);
+ return;
+ }
+ if (sh->bh_old[i])
+ raid5_kfree_old_bh(sh, i);
+ sh->bh_old[i] = sh->bh_copy[i];
+ sh->bh_copy[i] = NULL;
+}
+
+static void kfree_stripe(struct stripe_head *sh)
+{
+ struct raid5_data *raid_conf = sh->raid_conf;
+ int disks = raid_conf->raid_disks, j;
+
+ PRINTK(("kfree_stripe called, stripe %lu\n", sh->sector));
+ if (sh->phase != PHASE_COMPLETE || stripe_locked(sh) || sh->count) {
+ printk("raid5: kfree_stripe(), sector %lu, phase %d, locked %d, count %d\n", sh->sector, sh->phase, stripe_locked(sh), sh->count);
+ return;
+ }
+ for (j = 0; j < disks; j++) {
+ if (sh->bh_old[j])
+ raid5_kfree_old_bh(sh, j);
+ if (sh->bh_new[j] || sh->bh_copy[j])
+ printk("raid5: bug: sector %lu, new %p, copy %p\n", sh->sector, sh->bh_new[j], sh->bh_copy[j]);
+ }
+ remove_hash(raid_conf, sh);
+ put_free_stripe(raid_conf, sh);
+}
+
+static int shrink_stripe_cache(struct raid5_data *raid_conf, int nr)
+{
+ struct stripe_head *sh;
+ int i, count = 0;
+
+ PRINTK(("shrink_stripe_cache called, %d/%d, clock %d\n", nr, raid_conf->nr_hashed_stripes, raid_conf->clock));
+ for (i = 0; i < NR_HASH; i++) {
+repeat:
+ sh = raid_conf->stripe_hashtbl[(i + raid_conf->clock) & HASH_MASK];
+ for (; sh; sh = sh->hash_next) {
+ if (sh->phase != PHASE_COMPLETE)
+ continue;
+ if (stripe_locked(sh))
+ continue;
+ if (sh->count)
+ continue;
+ kfree_stripe(sh);
+ if (++count == nr) {
+ PRINTK(("shrink completed, nr_hashed_stripes %d\n", raid_conf->nr_hashed_stripes));
+ raid_conf->clock = (i + raid_conf->clock) & HASH_MASK;
+ return nr;
+ }
+ goto repeat;
+ }
+ }
+ PRINTK(("shrink completed, nr_hashed_stripes %d\n", raid_conf->nr_hashed_stripes));
+ return count;
+}
+
+static struct stripe_head *find_stripe(struct raid5_data *raid_conf, unsigned long sector, int size)
+{
+ struct stripe_head *sh;
+
+ if (raid_conf->buffer_size != size) {
+ PRINTK(("switching size, %d --> %d\n", raid_conf->buffer_size, size));
+ shrink_stripe_cache(raid_conf, raid_conf->max_nr_stripes);
+ raid_conf->buffer_size = size;
+ }
+
+ PRINTK(("find_stripe, sector %lu\n", sector));
+ for (sh = stripe_hash(raid_conf, sector, size); sh; sh = sh->hash_next)
+ if (sh->sector == sector && sh->raid_conf == raid_conf) {
+ if (sh->size == size) {
+ PRINTK(("found stripe %lu\n", sector));
+ return sh;
+ } else {
+ PRINTK(("switching size for %lu, %d --> %d\n", sector, sh->size, size));
+ kfree_stripe(sh);
+ break;
+ }
+ }
+ PRINTK(("stripe %lu not in cache\n", sector));
+ return NULL;
+}
+
+static int grow_stripes(struct raid5_data *raid_conf, int num, int priority)
+{
+ struct stripe_head *sh;
+
+ while (num--) {
+ if ((sh = kmalloc(sizeof(struct stripe_head), priority)) == NULL)
+ return 1;
+ memset(sh, 0, sizeof(*sh));
+ if (grow_buffers(sh, 2 * raid_conf->raid_disks, PAGE_SIZE, priority)) {
+ shrink_buffers(sh, 2 * raid_conf->raid_disks);
+ kfree(sh);
+ return 1;
+ }
+ if (grow_bh(sh, raid_conf->raid_disks, priority)) {
+ shrink_buffers(sh, 2 * raid_conf->raid_disks);
+ shrink_bh(sh, raid_conf->raid_disks);
+ kfree(sh);
+ return 1;
+ }
+ put_free_stripe(raid_conf, sh);
+ raid_conf->nr_stripes++;
+ }
+ return 0;
+}
+
+static void shrink_stripes(struct raid5_data *raid_conf, int num)
+{
+ struct stripe_head *sh;
+
+ while (num--) {
+ sh = get_free_stripe(raid_conf);
+ if (!sh)
+ break;
+ shrink_buffers(sh, raid_conf->raid_disks * 2);
+ shrink_bh(sh, raid_conf->raid_disks);
+ kfree(sh);
+ raid_conf->nr_stripes--;
+ }
+}
+
+static struct stripe_head *kmalloc_stripe(struct raid5_data *raid_conf, unsigned long sector, int size)
+{
+ struct stripe_head *sh = NULL, *tmp;
+ struct buffer_head *buffer_pool, *bh_pool;
+
+ PRINTK(("kmalloc_stripe called\n"));
+
+ while ((sh = get_free_stripe(raid_conf)) == NULL) {
+ shrink_stripe_cache(raid_conf, raid_conf->max_nr_stripes / 8);
+ if ((sh = get_free_stripe(raid_conf)) != NULL)
+ break;
+ if (!raid_conf->nr_pending_stripes)
+ printk("raid5: bug: nr_free_sh == 0, nr_pending_stripes == 0\n");
+ md_wakeup_thread(raid_conf->thread);
+ PRINTK(("waiting for some stripes to complete\n"));
+ sleep_on(&raid_conf->wait_for_stripe);
+ }
+
+ /*
+ * The above might have slept, so perhaps another process
+ * already created the stripe for us..
+ */
+ if ((tmp = find_stripe(raid_conf, sector, size)) != NULL) {
+ put_free_stripe(raid_conf, sh);
+ wait_on_stripe(tmp);
+ return tmp;
+ }
+ if (sh) {
+ buffer_pool = sh->buffer_pool;
+ bh_pool = sh->bh_pool;
+ memset(sh, 0, sizeof(*sh));
+ sh->buffer_pool = buffer_pool;
+ sh->bh_pool = bh_pool;
+ sh->phase = PHASE_COMPLETE;
+ sh->cmd = STRIPE_NONE;
+ sh->raid_conf = raid_conf;
+ sh->sector = sector;
+ sh->size = size;
+ raid_conf->nr_cached_stripes++;
+ insert_hash(raid_conf, sh);
+ } else printk("raid5: bug: kmalloc_stripe() == NULL\n");
+ return sh;
+}
+
+static struct stripe_head *get_stripe(struct raid5_data *raid_conf, unsigned long sector, int size)
+{
+ struct stripe_head *sh;
+
+ PRINTK(("get_stripe, sector %lu\n", sector));
+ sh = find_stripe(raid_conf, sector, size);
+ if (sh)
+ wait_on_stripe(sh);
+ else
+ sh = kmalloc_stripe(raid_conf, sector, size);
+ return sh;
+}
+
+
+static struct buffer_head *raid5_kmalloc_buffer(struct stripe_head *sh, int b_size)
+{
+ struct buffer_head *bh;
+
+ if ((bh = get_free_buffer(sh, b_size)) == NULL)
+ printk("raid5: bug: raid5_kmalloc_buffer() == NULL\n");
+ return bh;
+}
+
+static struct buffer_head *raid5_kmalloc_bh(struct stripe_head *sh)
+{
+ struct buffer_head *bh;
+
+ if ((bh = get_free_bh(sh)) == NULL)
+ printk("raid5: bug: raid5_kmalloc_bh() == NULL\n");
+ return bh;
+}
+
+static inline void raid5_end_buffer_io (struct stripe_head *sh, int i, int uptodate)
+{
+ struct buffer_head *bh = sh->bh_new[i];
+
+ sh->bh_new[i] = NULL;
+ raid5_kfree_bh(sh, sh->bh_req[i]);
+ sh->bh_req[i] = NULL;
+ bh->b_end_io(bh, uptodate);
+ if (!uptodate)
+ printk(KERN_ALERT "raid5: %s: unrecoverable I/O error for "
+ "block %lu\n", kdevname(bh->b_dev), bh->b_blocknr);
+}
+
+static inline void raid5_mark_buffer_uptodate (struct buffer_head *bh, int uptodate)
+{
+ if (uptodate)
+ set_bit(BH_Uptodate, &bh->b_state);
+ else
+ clear_bit(BH_Uptodate, &bh->b_state);
+}
+
+static void raid5_end_request (struct buffer_head * bh, int uptodate)
+{
+ struct stripe_head *sh = bh->b_dev_id;
+ struct raid5_data *raid_conf = sh->raid_conf;
+ int disks = raid_conf->raid_disks, i;
+ unsigned long flags;
+
+ PRINTK(("end_request %lu, nr_pending %d\n", sh->sector, sh->nr_pending));
+ save_flags(flags);
+ cli();
+ raid5_mark_buffer_uptodate(bh, uptodate);
+ --sh->nr_pending;
+ if (!sh->nr_pending) {
+ md_wakeup_thread(raid_conf->thread);
+ atomic_inc(&raid_conf->nr_handle);
+ }
+ if (!uptodate)
+ md_error(bh->b_dev, bh->b_rdev);
+ if (raid_conf->failed_disks) {
+ for (i = 0; i < disks; i++) {
+ if (raid_conf->disks[i].operational)
+ continue;
+ if (bh != sh->bh_old[i] && bh != sh->bh_req[i] && bh != sh->bh_copy[i])
+ continue;
+ if (bh->b_rdev != raid_conf->disks[i].dev)
+ continue;
+ set_bit(STRIPE_ERROR, &sh->state);
+ }
+ }
+ restore_flags(flags);
+}
+
+static int raid5_map (struct md_dev *mddev, kdev_t *rdev,
+ unsigned long *rsector, unsigned long size)
+{
+ /* No complex mapping used: the core of the work is done in the
+ * request routine
+ */
+ return 0;
+}
+
+static void raid5_build_block (struct stripe_head *sh, struct buffer_head *bh, int i)
+{
+ struct raid5_data *raid_conf = sh->raid_conf;
+ struct md_dev *mddev = raid_conf->mddev;
+ int minor = (int) (mddev - md_dev);
+ char *b_data;
+ kdev_t dev = MKDEV(MD_MAJOR, minor);
+ int block = sh->sector / (sh->size >> 9);
+
+ b_data = ((volatile struct buffer_head *) bh)->b_data;
+ memset (bh, 0, sizeof (struct buffer_head));
+ init_buffer(bh, dev, block, raid5_end_request, sh);
+ ((volatile struct buffer_head *) bh)->b_data = b_data;
+
+ bh->b_rdev = raid_conf->disks[i].dev;
+ bh->b_rsector = sh->sector;
+
+ bh->b_state = (1 << BH_Req);
+ bh->b_size = sh->size;
+ bh->b_list = BUF_LOCKED;
+}
+
+static int raid5_error (struct md_dev *mddev, kdev_t dev)
+{
+ struct raid5_data *raid_conf = (struct raid5_data *) mddev->private;
+ md_superblock_t *sb = mddev->sb;
+ struct disk_info *disk;
+ int i;
+
+ PRINTK(("raid5_error called\n"));
+ raid_conf->resync_parity = 0;
+ for (i = 0, disk = raid_conf->disks; i < raid_conf->raid_disks; i++, disk++)
+ if (disk->dev == dev && disk->operational) {
+ disk->operational = 0;
+ sb->disks[disk->number].state |= (1 << MD_FAULTY_DEVICE);
+ sb->disks[disk->number].state &= ~(1 << MD_SYNC_DEVICE);
+ sb->disks[disk->number].state &= ~(1 << MD_ACTIVE_DEVICE);
+ sb->active_disks--;
+ sb->working_disks--;
+ sb->failed_disks++;
+ mddev->sb_dirty = 1;
+ raid_conf->working_disks--;
+ raid_conf->failed_disks++;
+ md_wakeup_thread(raid_conf->thread);
+ printk (KERN_ALERT
+ "RAID5: Disk failure on %s, disabling device."
+ "Operation continuing on %d devices\n",
+ kdevname (dev), raid_conf->working_disks);
+ }
+ return 0;
+}
+
+/*
+ * Input: a 'big' sector number,
+ * Output: index of the data and parity disk, and the sector # in them.
+ */
+static inline unsigned long
+raid5_compute_sector (int r_sector, unsigned int raid_disks, unsigned int data_disks,
+ unsigned int * dd_idx, unsigned int * pd_idx,
+ struct raid5_data *raid_conf)
+{
+ unsigned int stripe;
+ int chunk_number, chunk_offset;
+ unsigned long new_sector;
+ int sectors_per_chunk = raid_conf->chunk_size >> 9;
+
+ /* First compute the information on this sector */
+
+ /*
+ * Compute the chunk number and the sector offset inside the chunk
+ */
+ chunk_number = r_sector / sectors_per_chunk;
+ chunk_offset = r_sector % sectors_per_chunk;
+
+ /*
+ * Compute the stripe number
+ */
+ stripe = chunk_number / data_disks;
+
+ /*
+ * Compute the data disk and parity disk indexes inside the stripe
+ */
+ *dd_idx = chunk_number % data_disks;
+
+ /*
+ * Select the parity disk based on the user selected algorithm.
+ */
+ if (raid_conf->level == 4)
+ *pd_idx = data_disks;
+ else switch (raid_conf->algorithm) {
+ case ALGORITHM_LEFT_ASYMMETRIC:
+ *pd_idx = data_disks - stripe % raid_disks;
+ if (*dd_idx >= *pd_idx)
+ (*dd_idx)++;
+ break;
+ case ALGORITHM_RIGHT_ASYMMETRIC:
+ *pd_idx = stripe % raid_disks;
+ if (*dd_idx >= *pd_idx)
+ (*dd_idx)++;
+ break;
+ case ALGORITHM_LEFT_SYMMETRIC:
+ *pd_idx = data_disks - stripe % raid_disks;
+ *dd_idx = (*pd_idx + 1 + *dd_idx) % raid_disks;
+ break;
+ case ALGORITHM_RIGHT_SYMMETRIC:
+ *pd_idx = stripe % raid_disks;
+ *dd_idx = (*pd_idx + 1 + *dd_idx) % raid_disks;
+ break;
+ default:
+ printk ("raid5: unsupported algorithm %d\n", raid_conf->algorithm);
+ }
+
+ /*
+ * Finally, compute the new sector number
+ */
+ new_sector = stripe * sectors_per_chunk + chunk_offset;
+
+#if 0
+ if ( *dd_idx > data_disks || *pd_idx > data_disks ||
+ chunk_offset + bh->b_size / 512 > sectors_per_chunk )
+
+ printk ("raid5: bug: dd_idx == %d, pd_idx == %d, chunk_offset == %d\n",
+ *dd_idx, *pd_idx, chunk_offset);
+#endif
+
+ return new_sector;
+}
+
+static unsigned long compute_blocknr(struct stripe_head *sh, int i)
+{
+ struct raid5_data *raid_conf = sh->raid_conf;
+ int raid_disks = raid_conf->raid_disks, data_disks = raid_disks - 1;
+ unsigned long new_sector = sh->sector, check;
+ int sectors_per_chunk = raid_conf->chunk_size >> 9;
+ unsigned long stripe = new_sector / sectors_per_chunk;
+ int chunk_offset = new_sector % sectors_per_chunk;
+ int chunk_number, dummy1, dummy2, dd_idx = i;
+ unsigned long r_sector, blocknr;
+
+ switch (raid_conf->algorithm) {
+ case ALGORITHM_LEFT_ASYMMETRIC:
+ case ALGORITHM_RIGHT_ASYMMETRIC:
+ if (i > sh->pd_idx)
+ i--;
+ break;
+ case ALGORITHM_LEFT_SYMMETRIC:
+ case ALGORITHM_RIGHT_SYMMETRIC:
+ if (i < sh->pd_idx)
+ i += raid_disks;
+ i -= (sh->pd_idx + 1);
+ break;
+ default:
+ printk ("raid5: unsupported algorithm %d\n", raid_conf->algorithm);
+ }
+
+ chunk_number = stripe * data_disks + i;
+ r_sector = chunk_number * sectors_per_chunk + chunk_offset;
+ blocknr = r_sector / (sh->size >> 9);
+
+ check = raid5_compute_sector (r_sector, raid_disks, data_disks, &dummy1, &dummy2, raid_conf);
+ if (check != sh->sector || dummy1 != dd_idx || dummy2 != sh->pd_idx) {
+ printk("compute_blocknr: map not correct\n");
+ return 0;
+ }
+ return blocknr;
+}
+
+static void xor_block(struct buffer_head *dest, struct buffer_head *source)
+{
+ int lines = dest->b_size / (sizeof (int)) / 8, i;
+ int *destp = (int *) dest->b_data, *sourcep = (int *) source->b_data;
+
+ for (i = lines; i > 0; i--) {
+ *(destp + 0) ^= *(sourcep + 0);
+ *(destp + 1) ^= *(sourcep + 1);
+ *(destp + 2) ^= *(sourcep + 2);
+ *(destp + 3) ^= *(sourcep + 3);
+ *(destp + 4) ^= *(sourcep + 4);
+ *(destp + 5) ^= *(sourcep + 5);
+ *(destp + 6) ^= *(sourcep + 6);
+ *(destp + 7) ^= *(sourcep + 7);
+ destp += 8;
+ sourcep += 8;
+ }
+}
+
+static void compute_block(struct stripe_head *sh, int dd_idx)
+{
+ struct raid5_data *raid_conf = sh->raid_conf;
+ int i, disks = raid_conf->raid_disks;
+
+ PRINTK(("compute_block, stripe %lu, idx %d\n", sh->sector, dd_idx));
+
+ if (sh->bh_old[dd_idx] == NULL)
+ sh->bh_old[dd_idx] = raid5_kmalloc_buffer(sh, sh->size);
+ raid5_build_block(sh, sh->bh_old[dd_idx], dd_idx);
+
+ memset(sh->bh_old[dd_idx]->b_data, 0, sh->size);
+ for (i = 0; i < disks; i++) {
+ if (i == dd_idx)
+ continue;
+ if (sh->bh_old[i]) {
+ xor_block(sh->bh_old[dd_idx], sh->bh_old[i]);
+ continue;
+ } else
+ printk("compute_block() %d, stripe %lu, %d not present\n", dd_idx, sh->sector, i);
+ }
+ raid5_mark_buffer_uptodate(sh->bh_old[dd_idx], 1);
+}
+
+static void compute_parity(struct stripe_head *sh, int method)
+{
+ struct raid5_data *raid_conf = sh->raid_conf;
+ int i, pd_idx = sh->pd_idx, disks = raid_conf->raid_disks;
+
+ PRINTK(("compute_parity, stripe %lu, method %d\n", sh->sector, method));
+ for (i = 0; i < disks; i++) {
+ if (i == pd_idx || !sh->bh_new[i])
+ continue;
+ if (!sh->bh_copy[i])
+ sh->bh_copy[i] = raid5_kmalloc_buffer(sh, sh->size);
+ raid5_build_block(sh, sh->bh_copy[i], i);
+ mark_buffer_clean(sh->bh_new[i]);
+ memcpy(sh->bh_copy[i]->b_data, sh->bh_new[i]->b_data, sh->size);
+ }
+ if (sh->bh_copy[pd_idx] == NULL)
+ sh->bh_copy[pd_idx] = raid5_kmalloc_buffer(sh, sh->size);
+ raid5_build_block(sh, sh->bh_copy[pd_idx], sh->pd_idx);
+
+ if (method == RECONSTRUCT_WRITE) {
+ memset(sh->bh_copy[pd_idx]->b_data, 0, sh->size);
+ for (i = 0; i < disks; i++) {
+ if (i == sh->pd_idx)
+ continue;
+ if (sh->bh_new[i]) {
+ xor_block(sh->bh_copy[pd_idx], sh->bh_copy[i]);
+ continue;
+ }
+ if (sh->bh_old[i]) {
+ xor_block(sh->bh_copy[pd_idx], sh->bh_old[i]);
+ continue;
+ }
+ }
+ } else if (method == READ_MODIFY_WRITE) {
+ memcpy(sh->bh_copy[pd_idx]->b_data, sh->bh_old[pd_idx]->b_data, sh->size);
+ for (i = 0; i < disks; i++) {
+ if (i == sh->pd_idx)
+ continue;
+ if (sh->bh_new[i] && sh->bh_old[i]) {
+ xor_block(sh->bh_copy[pd_idx], sh->bh_copy[i]);
+ xor_block(sh->bh_copy[pd_idx], sh->bh_old[i]);
+ continue;
+ }
+ }
+ }
+ raid5_mark_buffer_uptodate(sh->bh_copy[pd_idx], 1);
+}
+
+static void add_stripe_bh (struct stripe_head *sh, struct buffer_head *bh, int dd_idx, int rw)
+{
+ struct raid5_data *raid_conf = sh->raid_conf;
+ struct buffer_head *bh_req;
+
+ if (sh->bh_new[dd_idx])
+ printk("raid5: bug: stripe->bh_new[%d], sector %lu exists\n", dd_idx, sh->sector);
+
+ set_bit(BH_Lock, &bh->b_state);
+
+ bh_req = raid5_kmalloc_bh(sh);
+ raid5_build_block(sh, bh_req, dd_idx);
+ bh_req->b_data = bh->b_data;
+
+ if (sh->phase == PHASE_COMPLETE && sh->cmd == STRIPE_NONE) {
+ sh->phase = PHASE_BEGIN;
+ sh->cmd = (rw == READ) ? STRIPE_READ : STRIPE_WRITE;
+ raid_conf->nr_pending_stripes++;
+ atomic_inc(&raid_conf->nr_handle);
+ }
+ sh->bh_new[dd_idx] = bh;
+ sh->bh_req[dd_idx] = bh_req;
+ sh->cmd_new[dd_idx] = rw;
+ sh->new[dd_idx] = 1;
+}
+
+static void complete_stripe(struct stripe_head *sh)
+{
+ struct raid5_data *raid_conf = sh->raid_conf;
+ int disks = raid_conf->raid_disks;
+ int i, new = 0;
+
+ PRINTK(("complete_stripe %lu\n", sh->sector));
+ for (i = 0; i < disks; i++) {
+ if (sh->cmd == STRIPE_WRITE && i == sh->pd_idx)
+ raid5_update_old_bh(sh, i);
+ if (sh->bh_new[i]) {
+ if (!sh->new[i]) {
+#if 0
+ if (sh->cmd == STRIPE_WRITE) {
+ if (memcmp(sh->bh_new[i]->b_data, sh->bh_copy[i]->b_data, sh->size)) {
+ printk("copy differs, %s, sector %lu ",
+ test_bit(BH_Dirty, &sh->bh_new[i]->b_state) ? "dirty" : "clean",
+ sh->sector);
+ } else if (test_bit(BH_Dirty, &sh->bh_new[i]->b_state))
+ printk("sector %lu dirty\n", sh->sector);
+ }
+#endif
+ if (sh->cmd == STRIPE_WRITE)
+ raid5_update_old_bh(sh, i);
+ raid5_end_buffer_io(sh, i, 1);
+ continue;
+ } else
+ new++;
+ }
+ if (new && sh->cmd == STRIPE_WRITE)
+ printk("raid5: bug, completed STRIPE_WRITE with new == %d\n", new);
+ }
+ if (!new)
+ finish_stripe(sh);
+ else {
+ PRINTK(("stripe %lu, new == %d\n", sh->sector, new));
+ sh->phase = PHASE_BEGIN;
+ }
+}
+
+/*
+ * handle_stripe() is our main logic routine. Note that:
+ *
+ * 1. lock_stripe() should be used whenever we can't accept additonal
+ * buffers, either during short sleeping in handle_stripe() or
+ * during io operations.
+ *
+ * 2. We should be careful to set sh->nr_pending whenever we sleep,
+ * to prevent re-entry of handle_stripe() for the same sh.
+ *
+ * 3. raid_conf->failed_disks and disk->operational can be changed
+ * from an interrupt. This complicates things a bit, but it allows
+ * us to stop issuing requests for a failed drive as soon as possible.
+ */
+static void handle_stripe(struct stripe_head *sh)
+{
+ struct raid5_data *raid_conf = sh->raid_conf;
+ struct md_dev *mddev = raid_conf->mddev;
+ int minor = (int) (mddev - md_dev);
+ struct buffer_head *bh;
+ int disks = raid_conf->raid_disks;
+ int i, nr = 0, nr_read = 0, nr_write = 0;
+ int nr_cache = 0, nr_cache_other = 0, nr_cache_overwrite = 0, parity = 0;
+ int nr_failed_other = 0, nr_failed_overwrite = 0, parity_failed = 0;
+ int reading = 0, nr_writing = 0;
+ int method1 = INT_MAX, method2 = INT_MAX;
+ int block;
+ unsigned long flags;
+ int operational[MD_SB_DISKS], failed_disks = raid_conf->failed_disks;
+
+ PRINTK(("handle_stripe(), stripe %lu\n", sh->sector));
+ if (sh->nr_pending) {
+ printk("handle_stripe(), stripe %lu, io still pending\n", sh->sector);
+ return;
+ }
+ if (sh->phase == PHASE_COMPLETE) {
+ printk("handle_stripe(), stripe %lu, already complete\n", sh->sector);
+ return;
+ }
+
+ atomic_dec(&raid_conf->nr_handle);
+
+ if (test_and_clear_bit(STRIPE_ERROR, &sh->state)) {
+ printk("raid5: restarting stripe %lu\n", sh->sector);
+ sh->phase = PHASE_BEGIN;
+ }
+
+ if ((sh->cmd == STRIPE_WRITE && sh->phase == PHASE_WRITE) ||
+ (sh->cmd == STRIPE_READ && sh->phase == PHASE_READ)) {
+ /*
+ * Completed
+ */
+ complete_stripe(sh);
+ if (sh->phase == PHASE_COMPLETE)
+ return;
+ }
+
+ save_flags(flags);
+ cli();
+ for (i = 0; i < disks; i++) {
+ operational[i] = raid_conf->disks[i].operational;
+ if (i == sh->pd_idx && raid_conf->resync_parity)
+ operational[i] = 0;
+ }
+ failed_disks = raid_conf->failed_disks;
+ restore_flags(flags);
+
+ if (failed_disks > 1) {
+ for (i = 0; i < disks; i++) {
+ if (sh->bh_new[i]) {
+ raid5_end_buffer_io(sh, i, 0);
+ continue;
+ }
+ }
+ finish_stripe(sh);
+ return;
+ }
+
+ for (i = 0; i < disks; i++) {
+ if (sh->bh_old[i])
+ nr_cache++;
+ if (i == sh->pd_idx) {
+ if (sh->bh_old[i])
+ parity = 1;
+ else if(!operational[i])
+ parity_failed = 1;
+ continue;
+ }
+ if (!sh->bh_new[i]) {
+ if (sh->bh_old[i])
+ nr_cache_other++;
+ else if (!operational[i])
+ nr_failed_other++;
+ continue;
+ }
+ sh->new[i] = 0;
+ nr++;
+ if (sh->cmd_new[i] == READ)
+ nr_read++;
+ if (sh->cmd_new[i] == WRITE)
+ nr_write++;
+ if (sh->bh_old[i])
+ nr_cache_overwrite++;
+ else if (!operational[i])
+ nr_failed_overwrite++;
+ }
+
+ if (nr_write && nr_read)
+ printk("raid5: bug, nr_write == %d, nr_read == %d, sh->cmd == %d\n", nr_write, nr_read, sh->cmd);
+
+ if (nr_write) {
+ /*
+ * Attempt to add entries :-)
+ */
+ if (nr_write != disks - 1) {
+ for (i = 0; i < disks; i++) {
+ if (i == sh->pd_idx)
+ continue;
+ if (sh->bh_new[i])
+ continue;
+ block = (int) compute_blocknr(sh, i);
+ bh = efind_buffer(MKDEV(MD_MAJOR, minor), block, sh->size);
+ if (bh && bh->b_count == 0 && buffer_dirty(bh) && !buffer_locked(bh)) {
+ PRINTK(("Whee.. sector %lu, index %d (%d) found in the buffer cache!\n", sh->sector, i, block));
+ add_stripe_bh(sh, bh, i, WRITE);
+ sh->new[i] = 0;
+ nr++; nr_write++;
+ if (sh->bh_old[i]) {
+ nr_cache_overwrite++;
+ nr_cache_other--;
+ } else if (!operational[i]) {
+ nr_failed_overwrite++;
+ nr_failed_other--;
+ }
+ }
+ }
+ }
+ PRINTK(("handle_stripe() -- begin writing, stripe %lu\n", sh->sector));
+ /*
+ * Writing, need to update parity buffer.
+ *
+ * Compute the number of I/O requests in the "reconstruct
+ * write" and "read modify write" methods.
+ */
+ if (!nr_failed_other)
+ method1 = (disks - 1) - (nr_write + nr_cache_other);
+ if (!nr_failed_overwrite && !parity_failed)
+ method2 = nr_write - nr_cache_overwrite + (1 - parity);
+
+ if (method1 == INT_MAX && method2 == INT_MAX)
+ printk("raid5: bug: method1 == method2 == INT_MAX\n");
+ PRINTK(("handle_stripe(), sector %lu, nr_write %d, method1 %d, method2 %d\n", sh->sector, nr_write, method1, method2));
+
+ if (!method1 || !method2) {
+ lock_stripe(sh);
+ sh->nr_pending++;
+ sh->phase = PHASE_WRITE;
+ compute_parity(sh, method1 <= method2 ? RECONSTRUCT_WRITE : READ_MODIFY_WRITE);
+ for (i = 0; i < disks; i++) {
+ if (!operational[i] && !raid_conf->spare && !raid_conf->resync_parity)
+ continue;
+ if (i == sh->pd_idx || sh->bh_new[i])
+ nr_writing++;
+ }
+
+ sh->nr_pending = nr_writing;
+ PRINTK(("handle_stripe() %lu, writing back %d\n", sh->sector, sh->nr_pending));
+
+ for (i = 0; i < disks; i++) {
+ if (!operational[i] && !raid_conf->spare && !raid_conf->resync_parity)
+ continue;
+ bh = sh->bh_copy[i];
+ if (i != sh->pd_idx && ((bh == NULL) ^ (sh->bh_new[i] == NULL)))
+ printk("raid5: bug: bh == %p, bh_new[%d] == %p\n", bh, i, sh->bh_new[i]);
+ if (i == sh->pd_idx && !bh)
+ printk("raid5: bug: bh == NULL, i == pd_idx == %d\n", i);
+ if (bh) {
+ bh->b_state |= (1<<BH_Dirty);
+ PRINTK(("making request for buffer %d\n", i));
+ clear_bit(BH_Lock, &bh->b_state);
+ if (!operational[i] && !raid_conf->resync_parity) {
+ bh->b_rdev = raid_conf->spare->dev;
+ make_request(MAJOR(raid_conf->spare->dev), WRITE, bh);
+ } else
+ make_request(MAJOR(raid_conf->disks[i].dev), WRITE, bh);
+ }
+ }
+ return;
+ }
+
+ lock_stripe(sh);
+ sh->nr_pending++;
+ if (method1 < method2) {
+ sh->write_method = RECONSTRUCT_WRITE;
+ for (i = 0; i < disks; i++) {
+ if (i == sh->pd_idx)
+ continue;
+ if (sh->bh_new[i] || sh->bh_old[i])
+ continue;
+ sh->bh_old[i] = raid5_kmalloc_buffer(sh, sh->size);
+ raid5_build_block(sh, sh->bh_old[i], i);
+ reading++;
+ }
+ } else {
+ sh->write_method = READ_MODIFY_WRITE;
+ for (i = 0; i < disks; i++) {
+ if (sh->bh_old[i])
+ continue;
+ if (!sh->bh_new[i] && i != sh->pd_idx)
+ continue;
+ sh->bh_old[i] = raid5_kmalloc_buffer(sh, sh->size);
+ raid5_build_block(sh, sh->bh_old[i], i);
+ reading++;
+ }
+ }
+ sh->phase = PHASE_READ_OLD;
+ sh->nr_pending = reading;
+ PRINTK(("handle_stripe() %lu, reading %d old buffers\n", sh->sector, sh->nr_pending));
+ for (i = 0; i < disks; i++) {
+ if (!sh->bh_old[i])
+ continue;
+ if (buffer_uptodate(sh->bh_old[i]))
+ continue;
+ clear_bit(BH_Lock, &sh->bh_old[i]->b_state);
+ make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_old[i]);
+ }
+ } else {
+ /*
+ * Reading
+ */
+ method1 = nr_read - nr_cache_overwrite;
+ lock_stripe(sh);
+ sh->nr_pending++;
+
+ PRINTK(("handle_stripe(), sector %lu, nr_read %d, nr_cache %d, method1 %d\n", sh->sector, nr_read, nr_cache, method1));
+ if (!method1 || (method1 == 1 && nr_cache == disks - 1)) {
+ PRINTK(("read %lu completed from cache\n", sh->sector));
+ for (i = 0; i < disks; i++) {
+ if (!sh->bh_new[i])
+ continue;
+ if (!sh->bh_old[i])
+ compute_block(sh, i);
+ memcpy(sh->bh_new[i]->b_data, sh->bh_old[i]->b_data, sh->size);
+ }
+ sh->nr_pending--;
+ complete_stripe(sh);
+ return;
+ }
+ if (nr_failed_overwrite) {
+ sh->phase = PHASE_READ_OLD;
+ sh->nr_pending = (disks - 1) - nr_cache;
+ PRINTK(("handle_stripe() %lu, phase READ_OLD, pending %d\n", sh->sector, sh->nr_pending));
+ for (i = 0; i < disks; i++) {
+ if (sh->bh_old[i])
+ continue;
+ if (!operational[i])
+ continue;
+ sh->bh_old[i] = raid5_kmalloc_buffer(sh, sh->size);
+ raid5_build_block(sh, sh->bh_old[i], i);
+ clear_bit(BH_Lock, &sh->bh_old[i]->b_state);
+ make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_old[i]);
+ }
+ } else {
+ sh->phase = PHASE_READ;
+ sh->nr_pending = nr_read - nr_cache_overwrite;
+ PRINTK(("handle_stripe() %lu, phase READ, pending %d\n", sh->sector, sh->nr_pending));
+ for (i = 0; i < disks; i++) {
+ if (!sh->bh_new[i])
+ continue;
+ if (sh->bh_old[i]) {
+ memcpy(sh->bh_new[i]->b_data, sh->bh_old[i]->b_data, sh->size);
+ continue;
+ }
+ make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_req[i]);
+ }
+ }
+ }
+}
+
+static int raid5_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh)
+{
+ struct raid5_data *raid_conf = (struct raid5_data *) mddev->private;
+ const unsigned int raid_disks = raid_conf->raid_disks;
+ const unsigned int data_disks = raid_disks - 1;
+ unsigned int dd_idx, pd_idx;
+ unsigned long new_sector;
+
+ struct stripe_head *sh;
+
+ if (rw == READA) rw = READ;
+ if (rw == WRITEA) rw = WRITE;
+
+ new_sector = raid5_compute_sector(bh->b_rsector, raid_disks, data_disks,
+ &dd_idx, &pd_idx, raid_conf);
+
+ PRINTK(("raid5_make_request, sector %lu\n", new_sector));
+repeat:
+ sh = get_stripe(raid_conf, new_sector, bh->b_size);
+ if ((rw == READ && sh->cmd == STRIPE_WRITE) || (rw == WRITE && sh->cmd == STRIPE_READ)) {
+ PRINTK(("raid5: lock contention, rw == %d, sh->cmd == %d\n", rw, sh->cmd));
+ lock_stripe(sh);
+ if (!sh->nr_pending)
+ handle_stripe(sh);
+ goto repeat;
+ }
+ sh->pd_idx = pd_idx;
+ if (sh->phase != PHASE_COMPLETE && sh->phase != PHASE_BEGIN)
+ PRINTK(("stripe %lu catching the bus!\n", sh->sector));
+ add_stripe_bh(sh, bh, dd_idx, rw);
+
+ md_wakeup_thread(raid_conf->thread);
+ return 0;
+}
+
+static void unplug_devices(struct stripe_head *sh)
+{
+#if 0
+ struct raid5_data *raid_conf = sh->raid_conf;
+ int i;
+
+ for (i = 0; i < raid_conf->raid_disks; i++)
+ unplug_device(blk_dev + MAJOR(raid_conf->disks[i].dev));
+#endif
+}
+
+/*
+ * This is our raid5 kernel thread.
+ *
+ * We scan the hash table for stripes which can be handled now.
+ * During the scan, completed stripes are saved for us by the interrupt
+ * handler, so that they will not have to wait for our next wakeup.
+ */
+static void raid5d (void *data)
+{
+ struct stripe_head *sh;
+ struct raid5_data *raid_conf = data;
+ struct md_dev *mddev = raid_conf->mddev;
+ int i, handled = 0, unplug = 0;
+ unsigned long flags;
+
+ PRINTK(("+++ raid5d active\n"));
+
+ if (mddev->sb_dirty) {
+ mddev->sb_dirty = 0;
+ md_update_sb((int) (mddev - md_dev));
+ }
+ for (i = 0; i < NR_HASH; i++) {
+repeat:
+ sh = raid_conf->stripe_hashtbl[i];
+ for (; sh; sh = sh->hash_next) {
+ if (sh->raid_conf != raid_conf)
+ continue;
+ if (sh->phase == PHASE_COMPLETE)
+ continue;
+ if (sh->nr_pending)
+ continue;
+ if (sh->sector == raid_conf->next_sector) {
+ raid_conf->sector_count += (sh->size >> 9);
+ if (raid_conf->sector_count >= 128)
+ unplug = 1;
+ } else
+ unplug = 1;
+ if (unplug) {
+ PRINTK(("unplugging devices, sector == %lu, count == %d\n", sh->sector, raid_conf->sector_count));
+ unplug_devices(sh);
+ unplug = 0;
+ raid_conf->sector_count = 0;
+ }
+ raid_conf->next_sector = sh->sector + (sh->size >> 9);
+ handled++;
+ handle_stripe(sh);
+ goto repeat;
+ }
+ }
+ if (raid_conf) {
+ PRINTK(("%d stripes handled, nr_handle %d\n", handled, atomic_read(&raid_conf->nr_handle)));
+ save_flags(flags);
+ cli();
+ if (!atomic_read(&raid_conf->nr_handle))
+ clear_bit(THREAD_WAKEUP, &raid_conf->thread->flags);
+ }
+ PRINTK(("--- raid5d inactive\n"));
+}
+
+#if SUPPORT_RECONSTRUCTION
+/*
+ * Private kernel thread for parity reconstruction after an unclean
+ * shutdown. Reconstruction on spare drives in case of a failed drive
+ * is done by the generic mdsyncd.
+ */
+static void raid5syncd (void *data)
+{
+ struct raid5_data *raid_conf = data;
+ struct md_dev *mddev = raid_conf->mddev;
+
+ if (!raid_conf->resync_parity)
+ return;
+ md_do_sync(mddev);
+ raid_conf->resync_parity = 0;
+}
+#endif /* SUPPORT_RECONSTRUCTION */
+
+static int __check_consistency (struct md_dev *mddev, int row)
+{
+ struct raid5_data *raid_conf = mddev->private;
+ kdev_t dev;
+ struct buffer_head *bh[MD_SB_DISKS], tmp;
+ int i, rc = 0, nr = 0;
+
+ if (raid_conf->working_disks != raid_conf->raid_disks)
+ return 0;
+ tmp.b_size = 4096;
+ if ((tmp.b_data = (char *) get_free_page(GFP_KERNEL)) == NULL)
+ return 0;
+ memset(bh, 0, MD_SB_DISKS * sizeof(struct buffer_head *));
+ for (i = 0; i < raid_conf->raid_disks; i++) {
+ dev = raid_conf->disks[i].dev;
+ set_blocksize(dev, 4096);
+ if ((bh[i] = bread(dev, row / 4, 4096)) == NULL)
+ break;
+ nr++;
+ }
+ if (nr == raid_conf->raid_disks) {
+ for (i = 1; i < nr; i++)
+ xor_block(&tmp, bh[i]);
+ if (memcmp(tmp.b_data, bh[0]->b_data, 4096))
+ rc = 1;
+ }
+ for (i = 0; i < raid_conf->raid_disks; i++) {
+ dev = raid_conf->disks[i].dev;
+ if (bh[i]) {
+ bforget(bh[i]);
+ bh[i] = NULL;
+ }
+ fsync_dev(dev);
+ invalidate_buffers(dev);
+ }
+ free_page((unsigned long) tmp.b_data);
+ return rc;
+}
+
+static int check_consistency (struct md_dev *mddev)
+{
+ int size = mddev->sb->size;
+ int row;
+
+ for (row = 0; row < size; row += size / 8)
+ if (__check_consistency(mddev, row))
+ return 1;
+ return 0;
+}
+
+static int raid5_run (int minor, struct md_dev *mddev)
+{
+ struct raid5_data *raid_conf;
+ int i, j, raid_disk, memory;
+ md_superblock_t *sb = mddev->sb;
+ md_descriptor_t *descriptor;
+ struct real_dev *realdev;
+
+ MOD_INC_USE_COUNT;
+
+ if (sb->level != 5 && sb->level != 4) {
+ printk("raid5: %s: raid level not set to 4/5 (%d)\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->level);
+ MOD_DEC_USE_COUNT;
+ return -EIO;
+ }
+
+ mddev->private = kmalloc (sizeof (struct raid5_data), GFP_KERNEL);
+ if ((raid_conf = mddev->private) == NULL)
+ goto abort;
+ memset (raid_conf, 0, sizeof (*raid_conf));
+ raid_conf->mddev = mddev;
+
+ if ((raid_conf->stripe_hashtbl = (struct stripe_head **) __get_free_pages(GFP_ATOMIC, HASH_PAGES_ORDER, 0)) == NULL)
+ goto abort;
+ memset(raid_conf->stripe_hashtbl, 0, HASH_PAGES * PAGE_SIZE);
+
+ init_waitqueue(&raid_conf->wait_for_stripe);
+ PRINTK(("raid5_run(%d) called.\n", minor));
+
+ for (i = 0; i < mddev->nb_dev; i++) {
+ realdev = &mddev->devices[i];
+ if (!realdev->sb) {
+ printk(KERN_ERR "raid5: disabled device %s (couldn't access raid superblock)\n", kdevname(realdev->dev));
+ continue;
+ }
+
+ /*
+ * This is important -- we are using the descriptor on
+ * the disk only to get a pointer to the descriptor on
+ * the main superblock, which might be more recent.
+ */
+ descriptor = &sb->disks[realdev->sb->descriptor.number];
+ if (descriptor->state & (1 << MD_FAULTY_DEVICE)) {
+ printk(KERN_ERR "raid5: disabled device %s (errors detected)\n", kdevname(realdev->dev));
+ continue;
+ }
+ if (descriptor->state & (1 << MD_ACTIVE_DEVICE)) {
+ if (!(descriptor->state & (1 << MD_SYNC_DEVICE))) {
+ printk(KERN_ERR "raid5: disabled device %s (not in sync)\n", kdevname(realdev->dev));
+ continue;
+ }
+ raid_disk = descriptor->raid_disk;
+ if (descriptor->number > sb->nr_disks || raid_disk > sb->raid_disks) {
+ printk(KERN_ERR "raid5: disabled device %s (inconsistent descriptor)\n", kdevname(realdev->dev));
+ continue;
+ }
+ if (raid_conf->disks[raid_disk].operational) {
+ printk(KERN_ERR "raid5: disabled device %s (device %d already operational)\n", kdevname(realdev->dev), raid_disk);
+ continue;
+ }
+ printk(KERN_INFO "raid5: device %s operational as raid disk %d\n", kdevname(realdev->dev), raid_disk);
+
+ raid_conf->disks[raid_disk].number = descriptor->number;
+ raid_conf->disks[raid_disk].raid_disk = raid_disk;
+ raid_conf->disks[raid_disk].dev = mddev->devices[i].dev;
+ raid_conf->disks[raid_disk].operational = 1;
+
+ raid_conf->working_disks++;
+ } else {
+ /*
+ * Must be a spare disk ..
+ */
+ printk(KERN_INFO "raid5: spare disk %s\n", kdevname(realdev->dev));
+ raid_disk = descriptor->raid_disk;
+ raid_conf->disks[raid_disk].number = descriptor->number;
+ raid_conf->disks[raid_disk].raid_disk = raid_disk;
+ raid_conf->disks[raid_disk].dev = mddev->devices [i].dev;
+
+ raid_conf->disks[raid_disk].operational = 0;
+ raid_conf->disks[raid_disk].write_only = 0;
+ raid_conf->disks[raid_disk].spare = 1;
+ }
+ }
+ raid_conf->raid_disks = sb->raid_disks;
+ raid_conf->failed_disks = raid_conf->raid_disks - raid_conf->working_disks;
+ raid_conf->mddev = mddev;
+ raid_conf->chunk_size = sb->chunk_size;
+ raid_conf->level = sb->level;
+ raid_conf->algorithm = sb->parity_algorithm;
+ raid_conf->max_nr_stripes = NR_STRIPES;
+
+ if (raid_conf->working_disks != sb->raid_disks && sb->state != (1 << MD_SB_CLEAN)) {
+ printk(KERN_ALERT "raid5: raid set %s not clean and not all disks are operational -- run ckraid\n", kdevname(MKDEV(MD_MAJOR, minor)));
+ goto abort;
+ }
+ if (!raid_conf->chunk_size || raid_conf->chunk_size % 4) {
+ printk(KERN_ERR "raid5: invalid chunk size %d for %s\n", raid_conf->chunk_size, kdevname(MKDEV(MD_MAJOR, minor)));
+ goto abort;
+ }
+ if (raid_conf->algorithm > ALGORITHM_RIGHT_SYMMETRIC) {
+ printk(KERN_ERR "raid5: unsupported parity algorithm %d for %s\n", raid_conf->algorithm, kdevname(MKDEV(MD_MAJOR, minor)));
+ goto abort;
+ }
+ if (raid_conf->failed_disks > 1) {
+ printk(KERN_ERR "raid5: not enough operational devices for %s (%d/%d failed)\n", kdevname(MKDEV(MD_MAJOR, minor)), raid_conf->failed_disks, raid_conf->raid_disks);
+ goto abort;
+ }
+
+ if ((sb->state & (1 << MD_SB_CLEAN)) && check_consistency(mddev)) {
+ printk(KERN_ERR "raid5: detected raid-5 xor inconsistenty -- run ckraid\n");
+ sb->state |= 1 << MD_SB_ERRORS;
+ goto abort;
+ }
+
+ if ((raid_conf->thread = md_register_thread(raid5d, raid_conf)) == NULL) {
+ printk(KERN_ERR "raid5: couldn't allocate thread for %s\n", kdevname(MKDEV(MD_MAJOR, minor)));
+ goto abort;
+ }
+
+#if SUPPORT_RECONSTRUCTION
+ if ((raid_conf->resync_thread = md_register_thread(raid5syncd, raid_conf)) == NULL) {
+ printk(KERN_ERR "raid5: couldn't allocate thread for %s\n", kdevname(MKDEV(MD_MAJOR, minor)));
+ goto abort;
+ }
+#endif /* SUPPORT_RECONSTRUCTION */
+
+ memory = raid_conf->max_nr_stripes * (sizeof(struct stripe_head) +
+ raid_conf->raid_disks * (sizeof(struct buffer_head) +
+ 2 * (sizeof(struct buffer_head) + PAGE_SIZE))) / 1024;
+ if (grow_stripes(raid_conf, raid_conf->max_nr_stripes, GFP_KERNEL)) {
+ printk(KERN_ERR "raid5: couldn't allocate %dkB for buffers\n", memory);
+ shrink_stripes(raid_conf, raid_conf->max_nr_stripes);
+ goto abort;
+ } else
+ printk(KERN_INFO "raid5: allocated %dkB for %s\n", memory, kdevname(MKDEV(MD_MAJOR, minor)));
+
+ /*
+ * Regenerate the "device is in sync with the raid set" bit for
+ * each device.
+ */
+ for (i = 0; i < sb->nr_disks ; i++) {
+ sb->disks[i].state &= ~(1 << MD_SYNC_DEVICE);
+ for (j = 0; j < sb->raid_disks; j++) {
+ if (!raid_conf->disks[j].operational)
+ continue;
+ if (sb->disks[i].number == raid_conf->disks[j].number)
+ sb->disks[i].state |= 1 << MD_SYNC_DEVICE;
+ }
+ }
+ sb->active_disks = raid_conf->working_disks;
+
+ if (sb->active_disks == sb->raid_disks)
+ printk("raid5: raid level %d set %s active with %d out of %d devices, algorithm %d\n", raid_conf->level, kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks, raid_conf->algorithm);
+ else
+ printk(KERN_ALERT "raid5: raid level %d set %s active with %d out of %d devices, algorithm %d\n", raid_conf->level, kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks, raid_conf->algorithm);
+
+ if ((sb->state & (1 << MD_SB_CLEAN)) == 0) {
+ printk("raid5: raid set %s not clean; re-constructing parity\n", kdevname(MKDEV(MD_MAJOR, minor)));
+ raid_conf->resync_parity = 1;
+#if SUPPORT_RECONSTRUCTION
+ md_wakeup_thread(raid_conf->resync_thread);
+#endif /* SUPPORT_RECONSTRUCTION */
+ }
+
+ /* Ok, everything is just fine now */
+ return (0);
+abort:
+ if (raid_conf) {
+ if (raid_conf->stripe_hashtbl)
+ free_pages((unsigned long) raid_conf->stripe_hashtbl, HASH_PAGES_ORDER);
+ kfree(raid_conf);
+ }
+ mddev->private = NULL;
+ printk(KERN_ALERT "raid5: failed to run raid set %s\n", kdevname(MKDEV(MD_MAJOR, minor)));
+ MOD_DEC_USE_COUNT;
+ return -EIO;
+}
+
+static int raid5_stop (int minor, struct md_dev *mddev)
+{
+ struct raid5_data *raid_conf = (struct raid5_data *) mddev->private;
+
+ shrink_stripe_cache(raid_conf, raid_conf->max_nr_stripes);
+ shrink_stripes(raid_conf, raid_conf->max_nr_stripes);
+ md_unregister_thread(raid_conf->thread);
+#if SUPPORT_RECONSTRUCTION
+ md_unregister_thread(raid_conf->resync_thread);
+#endif /* SUPPORT_RECONSTRUCTION */
+ free_pages((unsigned long) raid_conf->stripe_hashtbl, HASH_PAGES_ORDER);
+ kfree(raid_conf);
+ mddev->private = NULL;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int raid5_status (char *page, int minor, struct md_dev *mddev)
+{
+ struct raid5_data *raid_conf = (struct raid5_data *) mddev->private;
+ md_superblock_t *sb = mddev->sb;
+ int sz = 0, i;
+
+ sz += sprintf (page+sz, " level %d, %dk chunk, algorithm %d", sb->level, sb->chunk_size >> 10, sb->parity_algorithm);
+ sz += sprintf (page+sz, " [%d/%d] [", raid_conf->raid_disks, raid_conf->working_disks);
+ for (i = 0; i < raid_conf->raid_disks; i++)
+ sz += sprintf (page+sz, "%s", raid_conf->disks[i].operational ? "U" : "_");
+ sz += sprintf (page+sz, "]");
+ return sz;
+}
+
+static int raid5_mark_spare(struct md_dev *mddev, md_descriptor_t *spare, int state)
+{
+ int i = 0, failed_disk = -1;
+ struct raid5_data *raid_conf = mddev->private;
+ struct disk_info *disk = raid_conf->disks;
+ unsigned long flags;
+ md_superblock_t *sb = mddev->sb;
+ md_descriptor_t *descriptor;
+
+ for (i = 0; i < MD_SB_DISKS; i++, disk++) {
+ if (disk->spare && disk->number == spare->number)
+ goto found;
+ }
+ return 1;
+found:
+ for (i = 0, disk = raid_conf->disks; i < raid_conf->raid_disks; i++, disk++)
+ if (!disk->operational)
+ failed_disk = i;
+ if (failed_disk == -1)
+ return 1;
+ save_flags(flags);
+ cli();
+ switch (state) {
+ case SPARE_WRITE:
+ disk->operational = 1;
+ disk->write_only = 1;
+ raid_conf->spare = disk;
+ break;
+ case SPARE_INACTIVE:
+ disk->operational = 0;
+ disk->write_only = 0;
+ raid_conf->spare = NULL;
+ break;
+ case SPARE_ACTIVE:
+ disk->spare = 0;
+ disk->write_only = 0;
+
+ descriptor = &sb->disks[raid_conf->disks[failed_disk].number];
+ i = spare->raid_disk;
+ disk->raid_disk = spare->raid_disk = descriptor->raid_disk;
+ if (disk->raid_disk != failed_disk)
+ printk("raid5: disk->raid_disk != failed_disk");
+ descriptor->raid_disk = i;
+
+ raid_conf->spare = NULL;
+ raid_conf->working_disks++;
+ raid_conf->failed_disks--;
+ raid_conf->disks[failed_disk] = *disk;
+ break;
+ default:
+ printk("raid5_mark_spare: bug: state == %d\n", state);
+ restore_flags(flags);
+ return 1;
+ }
+ restore_flags(flags);
+ return 0;
+}
+
+static struct md_personality raid5_personality=
+{
+ "raid5",
+ raid5_map,
+ raid5_make_request,
+ raid5_end_request,
+ raid5_run,
+ raid5_stop,
+ raid5_status,
+ NULL, /* no ioctls */
+ 0,
+ raid5_error,
+ /* raid5_hot_add_disk, */ NULL,
+ /* raid1_hot_remove_drive */ NULL,
+ raid5_mark_spare
+};
+
+int raid5_init (void)
+{
+ return register_md_personality (RAID5, &raid5_personality);
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+ return raid5_init();
+}
+
+void cleanup_module (void)
+{
+ unregister_md_personality (RAID5);
+}
+#endif
diff --git a/drivers/block/rd.c b/drivers/block/rd.c
index 759dbbca8..b57fc797c 100644
--- a/drivers/block/rd.c
+++ b/drivers/block/rd.c
@@ -184,16 +184,16 @@ static int rd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un
#ifdef CONFIG_BLK_DEV_INITRD
-static long initrd_read(struct inode *inode,struct file *file,
- char *buf, unsigned long count)
+static ssize_t initrd_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
{
int left;
- left = initrd_end-initrd_start-file->f_pos;
+ left = initrd_end - initrd_start - *ppos;
if (count > left) count = left;
if (count == 0) return 0;
- copy_to_user(buf,(char *) initrd_start+file->f_pos,count);
- file->f_pos += count;
+ copy_to_user(buf, (char *)initrd_start + *ppos, count);
+ *ppos += count;
return count;
}
@@ -363,7 +363,7 @@ identify_ramdisk_image(kdev_t device, struct file *fp, int start_block))
fp->f_op->llseek(fp, start_block * BLOCK_SIZE, 0);
fp->f_pos = start_block * BLOCK_SIZE;
- fp->f_op->read(fp->f_dentry->d_inode, fp, buf, size);
+ fp->f_op->read(fp, buf, size, &fp->f_pos);
/*
* If it matches the gzip magic numbers, return -1
@@ -393,7 +393,7 @@ identify_ramdisk_image(kdev_t device, struct file *fp, int start_block))
fp->f_op->llseek(fp, (start_block+1) * BLOCK_SIZE, 0);
fp->f_pos = (start_block+1) * BLOCK_SIZE;
- fp->f_op->read(fp->f_dentry->d_inode, fp, buf, size);
+ fp->f_op->read(fp, buf, size, &fp->f_pos);
/* Try minix */
if (minixsb->s_magic == MINIX_SUPER_MAGIC ||
@@ -510,10 +510,8 @@ __initfunc(static void rd_load_image(kdev_t device,int offset))
printk(KERN_NOTICE "RAMDISK: Loading %d blocks into ram disk... ", nblocks);
for (i=0; i < nblocks; i++) {
- infile.f_op->read(infile.f_dentry->d_inode, &infile, buf,
- BLOCK_SIZE);
- outfile.f_op->write(outfile.f_dentry->d_inode, &outfile, buf,
- BLOCK_SIZE);
+ infile.f_op->read(&infile, buf, BLOCK_SIZE, &infile.f_pos);
+ outfile.f_op->write(&outfile, buf, BLOCK_SIZE, &outfile.f_pos);
if (!(i % 16)) {
printk("%c\b", rotator[rotate & 0x3]);
rotate++;
@@ -641,8 +639,8 @@ __initfunc(static int fill_inbuf(void))
{
if (exit_code) return -1;
- insize = crd_infp->f_op->read(crd_infp->f_dentry->d_inode, crd_infp,
- inbuf, INBUFSIZ);
+ insize = crd_infp->f_op->read(crd_infp, inbuf, INBUFSIZ,
+ &crd_infp->f_pos);
if (insize == 0) return -1;
inptr = 1;
@@ -660,8 +658,7 @@ __initfunc(static void flush_window(void))
unsigned n;
uch *in, ch;
- crd_outfp->f_op->write(crd_outfp->f_dentry->d_inode, crd_outfp, window,
- outcnt);
+ crd_outfp->f_op->write(crd_outfp, window, outcnt, &crd_outfp->f_pos);
in = window;
for (n = 0; n < outcnt; n++) {
ch = *in++;
diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c
index 12be2e362..6ce5c037d 100644
--- a/drivers/block/swim3.c
+++ b/drivers/block/swim3.c
@@ -683,8 +683,7 @@ static int grab_drive(struct floppy_state *fs, enum swim_state state,
if (fs->state != idle) {
++fs->wanted;
while (fs->state != available) {
- if (interruptible
- && (current->signal & ~current->blocked)) {
+ if (interruptible && signal_pending(current)) {
--fs->wanted;
restore_flags(flags);
return -EINTR;
@@ -720,7 +719,7 @@ static int fd_eject(struct floppy_state *fs)
for (n = 2*HZ; n > 0; --n) {
if (swim3_readbit(fs, RELAX))
break;
- if ((current->signal & ~current->blocked) != 0) {
+ if (signal_pending(current)) {
err = -EINTR;
break;
}
@@ -781,7 +780,7 @@ static int floppy_open(struct inode *inode, struct file *filp)
for (n = HZ; n > 0; --n) {
if (swim3_readbit(fs, SEEK_COMPLETE))
break;
- if ((current->signal & ~current->blocked) != 0) {
+ if (signal_pending(current)) {
err = -EINTR;
break;
}
@@ -840,9 +839,16 @@ static int floppy_release(struct inode *inode, struct file *filp)
if (MINOR(inode->i_rdev) != 0)
return -ENXIO;
+ /*
+ * If filp is NULL, we're being called from blkdev_release
+ * or after a failed mount attempt. In the former case the
+ * device has already been sync'ed, and in the latter no
+ * sync is required. Otherwise, sync if filp is writable.
+ */
+ if (filp && (filp->f_mode & (2 | OPEN_WRITE_BIT)))
+ block_fsync (filp, filp->f_dentry);
+
fs = &floppy_states[0];
- if (filp == 0 || (filp->f_mode & (2 | OPEN_WRITE_BIT)))
- block_fsync(filp, filp->f_dentry);
sw = fs->swim3;
if (fs->ref_count > 0 && --fs->ref_count == 0) {
swim3_action(fs, MOTOR_OFF);
@@ -880,7 +886,7 @@ static int floppy_revalidate(kdev_t dev)
for (n = HZ; n > 0; --n) {
if (swim3_readbit(fs, SEEK_COMPLETE))
break;
- if ((current->signal & ~current->blocked) != 0)
+ if (signal_pending(current))
break;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + 1;
diff --git a/drivers/block/xd.c b/drivers/block/xd.c
index 472959603..f4ba2f26e 100644
--- a/drivers/block/xd.c
+++ b/drivers/block/xd.c
@@ -20,6 +20,13 @@
*
* Modularized: 04/10/96 by Todd Fries, tfries@umr.edu
*
+ * Revised: 13/09/97 by Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl
+ * Fixed some problems with disk initialization and module initiation.
+ * Added support for manual geometry setting (except Seagate controllers)
+ * in form:
+ * xd_geo=<cyl_xda>,<head_xda>,<sec_xda>[,<cyl_xdb>,<head_xdb>,<sec_xdb>]
+ * Recovered DMA access. Abridged messages. Added support for DTC5051CX &
+ * WD1002-27X controllers. Added alternate jumper geometry setting.
*/
#include <linux/module.h>
@@ -28,6 +35,7 @@
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/kernel.h>
+#include <linux/timer.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/init.h>
@@ -42,6 +50,13 @@
#include "xd.h"
+#define XD_DONT_USE_DMA 0 /* Initial value. may be overriden using
+ "nodma" module option */
+#define XD_INIT_DISK_DELAY 3 /* 30 ms delay during disk initialization */
+
+/* Above may need to be increased if a problem with the 2nd drive detection
+ (ST11M controller) or resetting a controler (WD) appears */
+
XD_INFO xd_info[XD_MAXDRIVES];
/* If you try this driver and find that your card is not detected by the driver at bootup, you need to add your BIOS
@@ -68,27 +83,47 @@ XD_INFO xd_info[XD_MAXDRIVES];
NOTE: You can now specify your XT controller's parameters from the command line in the form xd=TYPE,IRQ,IO,DMA. The driver
should be able to detect your drive's geometry from this info. (eg: xd=0,5,0x320,3 is the "standard"). */
-static XD_SIGNATURE xd_sigs[] = {
+#include <asm/page.h>
+/* coppied from floppy.c */
+static inline int __get_order(unsigned long size)
+{
+ int order;
+
+ size = (size-1) >> (PAGE_SHIFT-1);
+ order = -1;
+ do {
+ size >>= 1;
+ order++;
+ } while (size);
+ return order;
+}
+#define xd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL,__get_order(size))
+#define xd_dma_mem_free(addr, size) free_pages(addr, __get_order(size))
+static char *xd_dma_buffer = 0;
+
+static XD_SIGNATURE xd_sigs[] __initdata = {
{ 0x0000,"Override geometry handler",NULL,xd_override_init_drive,"n unknown" }, /* Pat Mackinlay, pat@it.com.au */
+ { 0x0008,"[BXD06 (C) DTC 17-MAY-1985]",xd_dtc_init_controller,xd_dtc5150cx_init_drive," DTC 5150CX" }, /* Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl */
{ 0x000B,"CRD18A Not an IBM rom. (C) Copyright Data Technology Corp. 05/31/88",xd_dtc_init_controller,xd_dtc_init_drive," DTC 5150X" }, /* Todd Fries, tfries@umr.edu */
{ 0x000B,"CXD23A Not an IBM ROM (C)Copyright Data Technology Corp 12/03/88",xd_dtc_init_controller,xd_dtc_init_drive," DTC 5150X" }, /* Pat Mackinlay, pat@it.com.au */
- { 0x0008,"07/15/86 (C) Copyright 1986 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," Western Digital 1002AWX1" }, /* Ian Justman, citrus!ianj@csusac.ecs.csus.edu */
- { 0x0008,"06/24/88 (C) Copyright 1988 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," Western Digital 1004A27X" }, /* Dave Thaler, thalerd@engin.umich.edu */
- { 0x0008,"06/24/88(C) Copyright 1988 Western Digital Corp.",xd_wd_init_controller,xd_wd_init_drive," Western Digital WDXT-GEN2" }, /* Dan Newcombe, newcombe@aa.csc.peachnet.edu */
+ { 0x0008,"07/15/86 (C) Copyright 1986 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," Western Dig. 1002AWX1" }, /* Ian Justman, citrus!ianj@csusac.ecs.csus.edu */
+ { 0x0008,"07/15/86(C) Copyright 1986 Western Digital Corp.",xd_wd_init_controller,xd_wd_init_drive," Western Dig. 1002-27X" }, /* Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl */
+ { 0x0008,"06/24/88 (C) Copyright 1988 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," Western Dig. 1004A27X" }, /* Dave Thaler, thalerd@engin.umich.edu */
+ { 0x0008,"06/24/88(C) Copyright 1988 Western Digital Corp.",xd_wd_init_controller,xd_wd_init_drive," Western Dig. WDXT-GEN2" }, /* Dan Newcombe, newcombe@aa.csc.peachnet.edu */
{ 0x0015,"SEAGATE ST11 BIOS REVISION",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Salvador Abreu, spa@fct.unl.pt */
{ 0x0010,"ST11R BIOS",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Risto Kankkunen, risto.kankkunen@cs.helsinki.fi */
{ 0x0010,"ST11 BIOS v1.7",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11R" }, /* Alan Hourihane, alanh@fairlite.demon.co.uk */
{ 0x1000,"(c)Copyright 1987 SMS",xd_omti_init_controller,xd_omti_init_drive,"n OMTI 5520" }, /* Dirk Melchers, dirk@merlin.nbg.sub.org */
};
-static unsigned int xd_bases[] =
+static unsigned int xd_bases[] __initdata =
{
0xC8000, 0xCA000, 0xCC000,
0xCE000, 0xD0000, 0xD8000,
0xE0000
};
-static struct hd_struct xd[XD_MAXDRIVES << 6];
+static struct hd_struct xd_struct[XD_MAXDRIVES << 6];
static int xd_sizes[XD_MAXDRIVES << 6], xd_access[XD_MAXDRIVES] = { 0, 0 };
static int xd_blocksizes[XD_MAXDRIVES << 6];
static struct gendisk xd_gendisk = {
@@ -102,7 +137,7 @@ static struct gendisk xd_gendisk = {
#else
xd_geninit, /* init function */
#endif
- xd, /* hd struct */
+ xd_struct, /* hd struct */
xd_sizes, /* block sizes */
0, /* number */
(void *) xd_info, /* internal */
@@ -122,15 +157,26 @@ static struct file_operations xd_fops = {
};
static struct wait_queue *xd_wait_int = NULL, *xd_wait_open = NULL;
static u_char xd_valid[XD_MAXDRIVES] = { 0,0 };
-static u_char xd_drives = 0, xd_irq = 0, xd_dma = 0, xd_maxsectors;
-static u_char xd_override = 0, xd_type = 0;
-static u_short xd_iobase = 0;
+static u_char xd_drives = 0, xd_irq = 5, xd_dma = 3, xd_maxsectors;
+static u_char xd_override __initdata = 0, xd_type = 0;
+static u_short xd_iobase = 0x320;
+static int xd_geo[XD_MAXDRIVES*3] __initdata = { 0,0,0,0,0,0 };
+
+static volatile int xdc_busy = 0;
+static struct wait_queue *xdc_wait = NULL;
+
+typedef void (*timeout_fn)(unsigned long);
+static struct timer_list xd_timer = { NULL, NULL, 0, 0, (timeout_fn) xd_wakeup },
+ xd_watchdog_int = { NULL, NULL, 0, 0, (timeout_fn) xd_watchdog };
+
+static volatile u_char xd_error;
+static int nodma = XD_DONT_USE_DMA;
/* xd_init: register the block device number and set up pointer tables */
__initfunc(int xd_init (void))
{
if (register_blkdev(MAJOR_NR,"xd",&xd_fops)) {
- printk("xd_init: unable to get major number %d\n",MAJOR_NR);
+ printk("xd: Unable to get major number %d\n",MAJOR_NR);
return -1;
}
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
@@ -172,27 +218,29 @@ __initfunc(static void xd_geninit (struct gendisk *ignored))
if (xd_detect(&controller,&address)) {
- printk("xd_geninit: detected a%s controller (type %d) at address %06x\n",xd_sigs[controller].name,controller,address);
+ printk("Detected a%s controller (type %d) at address %06x\n",xd_sigs[controller].name,controller,address);
if (controller)
xd_sigs[controller].init_controller(address);
xd_drives = xd_initdrives(xd_sigs[controller].init_drive);
- printk("xd_geninit: detected %d hard drive%s (using IRQ%d & DMA%d)\n",xd_drives,xd_drives == 1 ? "" : "s",xd_irq,xd_dma);
+ printk("Detected %d hard drive%s (using IRQ%d & DMA%d)\n",xd_drives,xd_drives == 1 ? "" : "s",xd_irq,xd_dma);
for (i = 0; i < xd_drives; i++)
- printk("xd_geninit: drive %d geometry - heads = %d, cylinders = %d, sectors = %d\n",i,xd_info[i].heads,xd_info[i].cylinders,xd_info[i].sectors);
+ printk(" xd%c: CHS=%d/%d/%d\n",'a'+i,xd_info[i].cylinders,xd_info[i].heads,xd_info[i].sectors);
+ }
+ if (xd_drives) {
if (!request_irq(xd_irq,xd_interrupt_handler, 0, "XT harddisk", NULL)) {
if (request_dma(xd_dma,"xd")) {
- printk("xd_geninit: unable to get DMA%d\n",xd_dma);
+ printk("xd: unable to get DMA%d\n",xd_dma);
free_irq(xd_irq, NULL);
}
}
else
- printk("xd_geninit: unable to get IRQ%d\n",xd_irq);
+ printk("xd: unable to get IRQ%d\n",xd_irq);
}
for (i = 0; i < xd_drives; i++) {
- xd[i << 6].nr_sects = xd_info[i].heads * xd_info[i].cylinders * xd_info[i].sectors;
+ xd_struct[i << 6].nr_sects = xd_info[i].heads * xd_info[i].cylinders * xd_info[i].sectors;
xd_valid[i] = 1;
}
@@ -211,6 +259,10 @@ static int xd_open (struct inode *inode,struct file *file)
while (!xd_valid[dev])
sleep_on(&xd_wait_open);
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif /* MODULE */
+
xd_access[dev]++;
return (0);
@@ -226,13 +278,15 @@ static void do_xd_request (void)
int code;
sti();
+ if (xdc_busy)
+ return;
while (code = 0, CURRENT) {
INIT_REQUEST; /* do some checking on the request structure */
if (CURRENT_DEV < xd_drives
&& CURRENT->sector + CURRENT->nr_sectors
- <= xd[MINOR(CURRENT->rq_dev)].nr_sects) {
- block = CURRENT->sector + xd[MINOR(CURRENT->rq_dev)].start_sect;
+ <= xd_struct[MINOR(CURRENT->rq_dev)].nr_sects) {
+ block = CURRENT->sector + xd_struct[MINOR(CURRENT->rq_dev)].start_sect;
count = CURRENT->nr_sectors;
switch (CURRENT->cmd) {
@@ -242,7 +296,8 @@ static void do_xd_request (void)
code = xd_readwrite(CURRENT->cmd,CURRENT_DEV,CURRENT->buffer,block,count);
break;
default:
- printk("do_xd_request: unknown request\n"); break;
+ printk("do_xd_request: unknown request\n");
+ break;
}
}
end_request(code); /* wrap up, 0 = fail, 1 = success */
@@ -262,15 +317,14 @@ static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg)
switch (cmd) {
case HDIO_GETGEO:
{
+ struct hd_geometry g;
struct hd_geometry *geometry = (struct hd_geometry *) arg;
if (!geometry) return -EINVAL;
- if(put_user(xd_info[dev].heads, (char *) &geometry->heads)
- || put_user(xd_info[dev].sectors, (char *) &geometry->sectors)
- || put_user(xd_info[dev].cylinders, (short *) &geometry->cylinders)
- || put_user(xd[MINOR(inode->i_rdev)].start_sect,
- (unsigned long *) &geometry->start))
- return -EFAULT;
- return 0;
+ g.heads = xd_info[dev].heads;
+ g.sectors = xd_info[dev].sectors;
+ g.cylinders = xd_info[dev].cylinders;
+ g.start = xd_struct[MINOR(inode->i_rdev)].start_sect;
+ return copy_to_user(geometry, &g, sizeof g) ? -EFAULT : 0;
}
case BLKRASET:
if(!suser()) return -EACCES;
@@ -279,8 +333,7 @@ static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg)
return 0;
case BLKGETSIZE:
if (!arg) return -EINVAL;
- put_user(xd[MINOR(inode->i_rdev)].nr_sects,(long *) arg);
- return 0;
+ return put_user(xd_struct[MINOR(inode->i_rdev)].nr_sects,(long *) arg);
case BLKFLSBUF: /* Return devices size */
if(!suser()) return -EACCES;
fsync_dev(inode->i_rdev);
@@ -303,6 +356,11 @@ static int xd_release (struct inode *inode, struct file *file)
if (target < xd_drives) {
sync_dev(inode->i_rdev);
xd_access[target]--;
+
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif /* MODULE */
+
}
return 0;
}
@@ -326,8 +384,11 @@ static int xd_reread_partitions(kdev_t dev)
for (partition = xd_gendisk.max_p - 1; partition >= 0; partition--) {
int minor = (start | partition);
kdev_t devp = MKDEV(MAJOR_NR, minor);
+ struct super_block * sb = get_super(devp);
+
sync_dev(devp);
- invalidate_inodes(devp);
+ if (sb)
+ invalidate_inodes(sb);
invalidate_buffers(devp);
xd_gendisk.part[minor].start_sect = 0;
xd_gendisk.part[minor].nr_sects = 0;
@@ -347,13 +408,17 @@ static int xd_readwrite (u_char operation,u_char drive,char *buffer,u_int block,
{
u_char cmdblk[6],sense[4];
u_short track,cylinder;
- u_char head,sector,control,mode,temp;
+ u_char head,sector,control,mode = PIO_MODE,temp;
+ char **real_buffer;
+ register int i;
#ifdef DEBUG_READWRITE
printk("xd_readwrite: operation = %s, drive = %d, buffer = 0x%X, block = %d, count = %d\n",operation == READ ? "read" : "write",drive,buffer,block,count);
#endif /* DEBUG_READWRITE */
control = xd_info[drive].control;
+ if (!xd_dma_buffer)
+ xd_dma_buffer = (char *)xd_dma_mem_alloc(xd_maxsectors * 0x200);
while (count) {
temp = count < xd_maxsectors ? count : xd_maxsectors;
@@ -366,27 +431,47 @@ static int xd_readwrite (u_char operation,u_char drive,char *buffer,u_int block,
printk("xd_readwrite: drive = %d, head = %d, cylinder = %d, sector = %d, count = %d\n",drive,head,cylinder,sector,temp);
#endif /* DEBUG_READWRITE */
- mode = xd_setup_dma(operation == READ ? DMA_MODE_READ : DMA_MODE_WRITE,(u_char *)buffer,temp * 0x200);
+ if (xd_dma_buffer) {
+ mode = xd_setup_dma(operation == READ ? DMA_MODE_READ : DMA_MODE_WRITE,(u_char *)(xd_dma_buffer),temp * 0x200);
+ real_buffer = &xd_dma_buffer;
+ for (i=0; i < (temp * 0x200); i++)
+ xd_dma_buffer[i] = buffer[i];
+ }
+ else
+ real_buffer = &buffer;
+
xd_build(cmdblk,operation == READ ? CMD_READ : CMD_WRITE,drive,head,cylinder,sector,temp & 0xFF,control);
- switch (xd_command(cmdblk,mode,(u_char *) buffer,(u_char *) buffer,sense,XD_TIMEOUT)) {
+ switch (xd_command(cmdblk,mode,(u_char *)(*real_buffer),(u_char *)(*real_buffer),sense,XD_TIMEOUT)) {
case 1:
- printk("xd_readwrite: timeout, recalibrating drive\n");
+ printk("xd%c: %s timeout, recalibrating drive\n",'a'+drive,(operation == READ ? "read" : "write"));
xd_recalibrate(drive);
return (0);
case 2:
- switch ((sense[0] & 0x30) >> 4) {
- case 0: printk("xd_readwrite: drive error, code = 0x%X",sense[0] & 0x0F); break;
- case 1: printk("xd_readwrite: controller error, code = 0x%X",sense[0] & 0x0F); break;
- case 2: printk("xd_readwrite: command error, code = 0x%X",sense[0] & 0x0F); break;
- case 3: printk("xd_readwrite: miscellaneous error, code = 0x%X",sense[0] & 0x0F); break;
+ if (sense[0] & 0x30) {
+ printk("xd%c: %s - ",'a'+drive,(operation == READ ? "reading" : "writing"));
+ switch ((sense[0] & 0x30) >> 4) {
+ case 0: printk("drive error, code = 0x%X",sense[0] & 0x0F);
+ break;
+ case 1: printk("controller error, code = 0x%X",sense[0] & 0x0F);
+ break;
+ case 2: printk("command error, code = 0x%X",sense[0] & 0x0F);
+ break;
+ case 3: printk("miscellaneous error, code = 0x%X",sense[0] & 0x0F);
+ break;
+ }
}
if (sense[0] & 0x80)
- printk(" - drive = %d, head = %d, cylinder = %d, sector = %d\n",sense[1] & 0xE0,sense[1] & 0x1F,((sense[2] & 0xC0) << 2) | sense[3],sense[2] & 0x3F);
+ printk(" - CHS = %d/%d/%d\n",((sense[2] & 0xC0) << 2) | sense[3],sense[1] & 0x1F,sense[2] & 0x3F);
+ /* reported drive number = (sense[1] & 0xE0) >> 5 */
else
printk(" - no valid disk address\n");
return (0);
}
+ if (xd_dma_buffer)
+ for (i=0; i < (temp * 0x200); i++)
+ buffer[i] = xd_dma_buffer[i];
+
count -= temp, buffer += temp * 0x200, block += temp;
}
return (1);
@@ -399,7 +484,7 @@ static void xd_recalibrate (u_char drive)
xd_build(cmdblk,CMD_RECALIBRATE,drive,0,0,0,0,0);
if (xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 8))
- printk("xd_recalibrate: warning! error recalibrating, controller may be unstable\n");
+ printk("xd%c: warning! error recalibrating, controller may be unstable\n", 'a'+drive);
}
/* xd_interrupt_handler: interrupt service routine */
@@ -413,31 +498,27 @@ static void xd_interrupt_handler(int irq, void *dev_id, struct pt_regs * regs)
wake_up(&xd_wait_int); /* and wake up sleeping processes */
}
else
- printk("xd_interrupt_handler: unexpected interrupt\n");
+ printk("xd: unexpected interrupt\n");
}
-/* xd_dma: set up the DMA controller for a data transfer */
+/* xd_setup_dma: set up the DMA controller for a data transfer */
static u_char xd_setup_dma (u_char mode,u_char *buffer,u_int count)
{
- if (buffer < ((u_char *) 0x1000000 - count)) { /* transfer to address < 16M? */
- if (((u_int) buffer & 0xFFFF0000) != (((u_int) buffer + count) & 0xFFFF0000)) {
+ if (nodma)
+ return (PIO_MODE);
+ if (((u_int) buffer & 0xFFFF0000) != (((u_int) buffer + count) & 0xFFFF0000)) {
#ifdef DEBUG_OTHER
- printk("xd_setup_dma: using PIO, transfer overlaps 64k boundary\n");
+ printk("xd_setup_dma: using PIO, transfer overlaps 64k boundary\n");
#endif /* DEBUG_OTHER */
- return (PIO_MODE);
- }
- disable_dma(xd_dma);
- clear_dma_ff(xd_dma);
- set_dma_mode(xd_dma,mode);
- set_dma_addr(xd_dma,(u_int) buffer);
- set_dma_count(xd_dma,count);
-
- return (DMA_MODE); /* use DMA and INT */
+ return (PIO_MODE);
}
-#ifdef DEBUG_OTHER
- printk("xd_setup_dma: using PIO, cannot DMA above 16 meg\n");
-#endif /* DEBUG_OTHER */
- return (PIO_MODE);
+ disable_dma(xd_dma);
+ clear_dma_ff(xd_dma);
+ set_dma_mode(xd_dma,mode);
+ set_dma_addr(xd_dma,(u_int) buffer);
+ set_dma_count(xd_dma,count);
+
+ return (DMA_MODE); /* use DMA and INT */
}
/* xd_build: put stuff into an array in a format suitable for the controller */
@@ -453,15 +534,53 @@ static u_char *xd_build (u_char *cmdblk,u_char command,u_char drive,u_char head,
return (cmdblk);
}
+/* xd_wakeup is called from timer interrupt */
+static void xd_wakeup (void)
+{
+ wake_up(&xdc_wait);
+}
+
+/* xd_wakeup is called from timer interrupt */
+static void xd_watchdog (void)
+{
+ xd_error = 1;
+ wake_up(&xd_wait_int);
+}
+
/* xd_waitport: waits until port & mask == flags or a timeout occurs. return 1 for a timeout */
static inline u_char xd_waitport (u_short port,u_char flags,u_char mask,u_long timeout)
{
u_long expiry = jiffies + timeout;
+ int success;
+
+ xdc_busy = 1;
+ while ((success = ((inb(port) & mask) != flags)) && (jiffies < expiry)) {
+ xd_timer.expires = jiffies;
+ cli();
+ add_timer(&xd_timer);
+ sleep_on(&xdc_wait);
+ del_timer(&xd_timer);
+ sti();
+ }
+ xdc_busy = 0;
+ return (success);
+}
- while (((inb(port) & mask) != flags) && (jiffies < expiry))
- ;
-
- return (jiffies >= expiry);
+static inline u_int xd_wait_for_IRQ (void)
+{
+ xd_watchdog_int.expires = jiffies + 8 * HZ;
+ add_timer(&xd_watchdog_int);
+ enable_dma(xd_dma);
+ sleep_on(&xd_wait_int);
+ del_timer(&xd_watchdog_int);
+ xdc_busy = 0;
+ disable_dma(xd_dma);
+ if (xd_error) {
+ printk("xd: missed IRQ - command aborted\n");
+ xd_error = 0;
+ return (1);
+ }
+ return (0);
}
/* xd_command: handle all data transfers necessary for a single command */
@@ -478,24 +597,23 @@ static u_int xd_command (u_char *command,u_char mode,u_char *indata,u_char *outd
if (xd_waitport(XD_STATUS,STAT_SELECT,STAT_SELECT,timeout))
return (1);
-
+
while (!complete) {
if (xd_waitport(XD_STATUS,STAT_READY,STAT_READY,timeout))
return (1);
+
switch (inb(XD_STATUS) & (STAT_COMMAND | STAT_INPUT)) {
case 0:
if (mode == DMA_MODE) {
- enable_dma(xd_dma);
- sleep_on(&xd_wait_int);
- disable_dma(xd_dma);
+ if (xd_wait_for_IRQ())
+ return (1);
} else
outb(outdata ? *outdata++ : 0,XD_DATA);
break;
case STAT_INPUT:
if (mode == DMA_MODE) {
- enable_dma(xd_dma);
- sleep_on(&xd_wait_int);
- disable_dma(xd_dma);
+ if (xd_wait_for_IRQ())
+ return (1);
} else
if (indata)
*indata++ = inb(XD_DATA);
@@ -518,7 +636,7 @@ static u_int xd_command (u_char *command,u_char mode,u_char *indata,u_char *outd
if (csb & CSB_ERROR) { /* read sense data if error */
xd_build(cmdblk,CMD_SENSE,(csb & CSB_LUN) >> 5,0,0,0,0,0);
if (xd_command(cmdblk,0,sense,0,0,XD_TIMEOUT))
- printk("xd_command: warning! sense command failed!\n");
+ printk("xd: warning! sense command failed!\n");
}
#ifdef DEBUG_COMMAND
@@ -535,28 +653,92 @@ __initfunc(static u_char xd_initdrives (void (*init_drive)(u_char drive)))
for (i = 0; i < XD_MAXDRIVES; i++) {
xd_build(cmdblk,CMD_TESTREADY,i,0,0,0,0,0);
if (!xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 8)) {
+ xd_timer.expires = jiffies + XD_INIT_DISK_DELAY;
+ add_timer(&xd_timer);
+ sleep_on(&xdc_wait);
+
init_drive(count);
count++;
+
+ xd_timer.expires = jiffies + XD_INIT_DISK_DELAY;
+ add_timer(&xd_timer);
+ sleep_on(&xdc_wait);
}
}
return (count);
}
+__initfunc(static void xd_manual_geo_set (u_char drive))
+{
+ xd_info[drive].heads = (u_char)(xd_geo[3 * drive + 1]);
+ xd_info[drive].cylinders = (u_short)(xd_geo[3 * drive]);
+ xd_info[drive].sectors = (u_char)(xd_geo[3 * drive + 2]);
+}
+
__initfunc(static void xd_dtc_init_controller (unsigned int address))
{
switch (address) {
- case 0xC8000: xd_iobase = 0x320; break;
+ case 0x00000:
+ case 0xC8000: break; /*initial: 0x320 */
case 0xCA000: xd_iobase = 0x324; break;
+ case 0xD0000: /*5150CX*/
+ case 0xD8000: break; /*5150CX*/
default: printk("xd_dtc_init_controller: unsupported BIOS address %06x\n",address);
- xd_iobase = 0x320; break;
+ break;
}
- xd_irq = 5; /* the IRQ _can_ be changed on this card, but requires a hardware mod */
- xd_dma = 3;
xd_maxsectors = 0x01; /* my card seems to have trouble doing multi-block transfers? */
outb(0,XD_RESET); /* reset the controller */
}
+
+__initfunc(static void xd_dtc5150cx_init_drive (u_char drive))
+{
+ /* values from controller's BIOS - BIOS chip may be removed */
+ static u_short geometry_table[][4] = {
+ {0x200,8,0x200,0x100},
+ {0x267,2,0x267,0x267},
+ {0x264,4,0x264,0x80},
+ {0x132,4,0x132,0x0},
+ {0x132,2,0x80, 0x132},
+ {0x177,8,0x177,0x0},
+ {0x132,8,0x84, 0x0},
+ {}, /* not used */
+ {0x132,6,0x80, 0x100},
+ {0x200,6,0x100,0x100},
+ {0x264,2,0x264,0x80},
+ {0x280,4,0x280,0x100},
+ {0x2B9,3,0x2B9,0x2B9},
+ {0x2B9,5,0x2B9,0x2B9},
+ {0x280,6,0x280,0x100},
+ {0x132,4,0x132,0x0}};
+ u_char n;
+
+ n = inb(XD_JUMPER);
+ n = (drive ? n : (n >> 2)) & 0x33;
+ n = (n | (n >> 2)) & 0x0F;
+ if (xd_geo[3*drive])
+ xd_manual_geo_set(drive);
+ else
+ if (n != 7) {
+ xd_info[drive].heads = (u_char)(geometry_table[n][1]); /* heads */
+ xd_info[drive].cylinders = geometry_table[n][0]; /* cylinders */
+ xd_info[drive].sectors = 17; /* sectors */
+#if 0
+ xd_info[drive].rwrite = geometry_table[n][2]; /* reduced write */
+ xd_info[drive].precomp = geometry_table[n][3] /* write precomp */
+ xd_info[drive].ecc = 0x0B; /* ecc length */
+#endif /* 0 */
+ }
+ else {
+ printk("xd%c: undetermined drive geometry\n",'a'+drive);
+ return;
+ }
+ xd_info[drive].control = 5; /* control byte */
+ xd_setparam(CMD_DTCSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,geometry_table[n][2],geometry_table[n][3],0x0B);
+ xd_recalibrate(drive);
+}
+
__initfunc(static void xd_dtc_init_drive (u_char drive))
{
u_char cmdblk[6],buf[64];
@@ -566,6 +748,8 @@ __initfunc(static void xd_dtc_init_drive (u_char drive))
xd_info[drive].heads = buf[0x0A]; /* heads */
xd_info[drive].cylinders = ((u_short *) (buf))[0x04]; /* cylinders */
xd_info[drive].sectors = 17; /* sectors */
+ if (xd_geo[3*drive])
+ xd_manual_geo_set(drive);
#if 0
xd_info[drive].rwrite = ((u_short *) (buf + 1))[0x05]; /* reduced write */
xd_info[drive].precomp = ((u_short *) (buf + 1))[0x06]; /* write precomp */
@@ -576,65 +760,128 @@ __initfunc(static void xd_dtc_init_drive (u_char drive))
xd_setparam(CMD_DTCSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,((u_short *) (buf + 1))[0x05],((u_short *) (buf + 1))[0x06],buf[0x0F]);
xd_build(cmdblk,CMD_DTCSETSTEP,drive,0,0,0,0,7);
if (xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2))
- printk("xd_dtc_init_drive: error setting step rate for drive %d\n",drive);
+ printk("xd_dtc_init_drive: error setting step rate for xd%c\n", 'a'+drive);
}
else
- printk("xd_dtc_init_drive: error reading geometry for drive %d\n",drive);
+ printk("xd_dtc_init_drive: error reading geometry for xd%c\n", 'a'+drive);
}
__initfunc(static void xd_wd_init_controller (unsigned int address))
{
switch (address) {
- case 0xC8000: xd_iobase = 0x320; break;
+ case 0x00000:
+ case 0xC8000: break; /*initial: 0x320 */
case 0xCA000: xd_iobase = 0x324; break;
case 0xCC000: xd_iobase = 0x328; break;
case 0xCE000: xd_iobase = 0x32C; break;
- case 0xD0000: xd_iobase = 0x328; break;
- case 0xD8000: xd_iobase = 0x32C; break;
+ case 0xD0000: xd_iobase = 0x328; break; /* ? */
+ case 0xD8000: xd_iobase = 0x32C; break; /* ? */
default: printk("xd_wd_init_controller: unsupported BIOS address %06x\n",address);
- xd_iobase = 0x320; break;
+ break;
}
- xd_irq = 5; /* don't know how to auto-detect this yet */
- xd_dma = 3;
xd_maxsectors = 0x01; /* this one doesn't wrap properly either... */
- /* outb(0,XD_RESET); */ /* reset the controller */
+ outb(0,XD_RESET); /* reset the controller */
+
+ xd_timer.expires = jiffies + XD_INIT_DISK_DELAY;
+ add_timer(&xd_timer);
+ sleep_on(&xdc_wait);
}
__initfunc(static void xd_wd_init_drive (u_char drive))
{
- u_char cmdblk[6],buf[0x200];
+ /* values from controller's BIOS - BIOS may be disabled */
+ static u_short geometry_table[][4] = {
+ {0x264,4,0x1C2,0x1C2}, /* common part */
+ {0x132,4,0x099,0x0},
+ {0x267,2,0x1C2,0x1C2},
+ {0x267,4,0x1C2,0x1C2},
+
+ {0x334,6,0x335,0x335}, /* 1004 series RLL */
+ {0x30E,4,0x30F,0x3DC},
+ {0x30E,2,0x30F,0x30F},
+ {0x267,4,0x268,0x268},
+
+ {0x3D5,5,0x3D6,0x3D6}, /* 1002 series RLL */
+ {0x3DB,7,0x3DC,0x3DC},
+ {0x264,4,0x265,0x265},
+ {0x267,4,0x268,0x268}};
+ u_char cmdblk[6],buf[0x200];
+ u_char n = 0,rll,jumper_state,use_jumper_geo;
+ u_char wd_1002 = (xd_sigs[xd_type].string[7] == '6');
+
+ jumper_state = ~(inb(0x322));
+ if (jumper_state & 0x40)
+ xd_irq = 9;
+ rll = (jumper_state & 0x30) ? (0x04 << wd_1002) : 0;
xd_build(cmdblk,CMD_READ,drive,0,0,0,1,0);
if (!xd_command(cmdblk,PIO_MODE,buf,0,0,XD_TIMEOUT * 2)) {
xd_info[drive].heads = buf[0x1AF]; /* heads */
xd_info[drive].cylinders = ((u_short *) (buf + 1))[0xD6]; /* cylinders */
xd_info[drive].sectors = 17; /* sectors */
+ if (xd_geo[3*drive])
+ xd_manual_geo_set(drive);
#if 0
xd_info[drive].rwrite = ((u_short *) (buf))[0xD8]; /* reduced write */
xd_info[drive].wprecomp = ((u_short *) (buf))[0xDA]; /* write precomp */
xd_info[drive].ecc = buf[0x1B4]; /* ecc length */
#endif /* 0 */
xd_info[drive].control = buf[0x1B5]; /* control byte */
-
- xd_setparam(CMD_WDSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,((u_short *) (buf))[0xD8],((u_short *) (buf))[0xDA],buf[0x1B4]);
+ use_jumper_geo = !(xd_info[drive].heads) || !(xd_info[drive].cylinders);
+ if (xd_geo[3*drive]) {
+ xd_manual_geo_set(drive);
+ xd_info[drive].control = rll ? 7 : 5;
+ }
+ else if (use_jumper_geo) {
+ n = (((jumper_state & 0x0F) >> (drive << 1)) & 0x03) | rll;
+ xd_info[drive].cylinders = geometry_table[n][0];
+ xd_info[drive].heads = (u_char)(geometry_table[n][1]);
+ xd_info[drive].control = rll ? 7 : 5;
+#if 0
+ xd_info[drive].rwrite = geometry_table[n][2];
+ xd_info[drive].wprecomp = geometry_table[n][3];
+ xd_info[drive].ecc = 0x0B;
+#endif /* 0 */
+ }
+ if (!wd_1002)
+ if (use_jumper_geo)
+ xd_setparam(CMD_WDSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,
+ geometry_table[n][2],geometry_table[n][3],0x0B);
+ else
+ xd_setparam(CMD_WDSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,
+ ((u_short *) (buf))[0xD8],((u_short *) (buf))[0xDA],buf[0x1B4]);
+ /* 1002 based RLL controler requests converted adressing, but reports physical
+ (physical 26 sec., logical 17 sec.)
+ 1004 based ???? */
+ if (rll & wd_1002) {
+ if ((xd_info[drive].cylinders *= 26,
+ xd_info[drive].cylinders /= 17) > 1023)
+ xd_info[drive].cylinders = 1023; /* 1024 ? */
+#if 0
+ xd_info[drive].rwrite *= 26;
+ xd_info[drive].rwrite /= 17;
+ xd_info[drive].wprecomp *= 26
+ xd_info[drive].wprecomp /= 17;
+#endif /* 0 */
+ }
}
else
- printk("xd_wd_init_drive: error reading geometry for drive %d\n",drive);
+ printk("xd_wd_init_drive: error reading geometry for xd%c\n",'a'+drive);
+
}
__initfunc(static void xd_seagate_init_controller (unsigned int address))
{
switch (address) {
- case 0xC8000: xd_iobase = 0x320; break;
+ case 0x00000:
+ case 0xC8000: break; /*initial: 0x320 */
case 0xD0000: xd_iobase = 0x324; break;
case 0xD8000: xd_iobase = 0x328; break;
case 0xE0000: xd_iobase = 0x32C; break;
default: printk("xd_seagate_init_controller: unsupported BIOS address %06x\n",address);
- xd_iobase = 0x320; break;
+ break;
}
- xd_irq = 5; /* the IRQ and DMA channel are fixed on the Seagate controllers */
- xd_dma = 3;
xd_maxsectors = 0x40;
outb(0,XD_RESET); /* reset the controller */
@@ -652,23 +899,22 @@ __initfunc(static void xd_seagate_init_drive (u_char drive))
xd_info[drive].control = 0; /* control byte */
}
else
- printk("xd_seagate_init_drive: error reading geometry from drive %d\n",drive);
+ printk("xd_seagate_init_drive: error reading geometry from xd%c\n", 'a'+drive);
}
/* Omti support courtesy Dirk Melchers */
__initfunc(static void xd_omti_init_controller (unsigned int address))
{
switch (address) {
- case 0xC8000: xd_iobase = 0x320; break;
+ case 0x00000:
+ case 0xC8000: break; /*initial: 0x320 */
case 0xD0000: xd_iobase = 0x324; break;
case 0xD8000: xd_iobase = 0x328; break;
case 0xE0000: xd_iobase = 0x32C; break;
default: printk("xd_omti_init_controller: unsupported BIOS address %06x\n",address);
- xd_iobase = 0x320; break;
+ break;
}
- xd_irq = 5; /* the IRQ and DMA channel are fixed on the Omti controllers */
- xd_dma = 3;
xd_maxsectors = 0x40;
outb(0,XD_RESET); /* reset the controller */
@@ -690,36 +936,63 @@ __initfunc(static void xd_override_init_drive (u_char drive))
u_short min[] = { 0,0,0 },max[] = { 16,1024,64 },test[] = { 0,0,0 };
u_char cmdblk[6],i;
- for (i = 0; i < 3; i++) {
- while (min[i] != max[i] - 1) {
- test[i] = (min[i] + max[i]) / 2;
- xd_build(cmdblk,CMD_SEEK,drive,(u_char) test[0],(u_short) test[1],(u_char) test[2],0,0);
- if (!xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2))
- min[i] = test[i];
- else
- max[i] = test[i];
+ if (xd_geo[3*drive])
+ xd_manual_geo_set(drive);
+ else {
+ for (i = 0; i < 3; i++) {
+ while (min[i] != max[i] - 1) {
+ test[i] = (min[i] + max[i]) / 2;
+ xd_build(cmdblk,CMD_SEEK,drive,(u_char) test[0],(u_short) test[1],(u_char) test[2],0,0);
+ if (!xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2))
+ min[i] = test[i];
+ else
+ max[i] = test[i];
+ }
+ test[i] = min[i];
}
- test[i] = min[i];
+ xd_info[drive].heads = (u_char) min[0] + 1;
+ xd_info[drive].cylinders = (u_short) min[1] + 1;
+ xd_info[drive].sectors = (u_char) min[2] + 1;
}
- xd_info[drive].heads = (u_char) min[0] + 1;
- xd_info[drive].cylinders = (u_short) min[1] + 1;
- xd_info[drive].sectors = (u_char) min[2] + 1;
xd_info[drive].control = 0;
}
-/* xd_setup: initialise from command line parameters */
+/* xd_setup: initialise controler from command line parameters */
__initfunc(void xd_setup (char *command,int *integers))
{
- xd_override = 1;
-
- xd_type = integers[1];
- xd_irq = integers[2];
- xd_iobase = integers[3];
- xd_dma = integers[4];
-
+ switch (integers[0]) {
+ case 4: if (integers[4] < 0)
+ nodma = 1;
+ else if (integers[4] < 8)
+ xd_dma = integers[4];
+ case 3: if ((integers[3] > 0) && (integers[3] <= 0x3FC))
+ xd_iobase = integers[3];
+ case 2: if ((integers[2] > 0) && (integers[2] < 16))
+ xd_irq = integers[2];
+ case 1: xd_override = 1;
+ if ((integers[1] >= 0) && (integers[1] < (sizeof(xd_sigs) / sizeof(xd_sigs[0]))))
+ xd_type = integers[1];
+ case 0: break;
+ default:printk("xd: too many parameters for xd\n");
+ }
xd_maxsectors = 0x01;
}
+#ifndef MODULE
+/* xd_manual_geo_init: initialise drive geometry from command line parameters
+ (used only for WD drives) */
+__initfunc(void xd_manual_geo_init (char *command,int *integers))
+{
+ int i;
+ if (integers[0]%3 != 0) {
+ printk("xd: incorrect number of parameters for xd_geo\n");
+ return;
+ }
+ for (i = 0; (i < integers[0]) && (i < 3*XD_MAXDRIVES); i++)
+ xd_geo[i] = integers[i+1];
+}
+#endif /* MODULE */
+
/* xd_setparam: set the drive characteristics */
__initfunc(static void xd_setparam (u_char command,u_char drive,u_char heads,u_short cylinders,u_short rwrite,u_short wprecomp,u_char ecc))
{
@@ -736,18 +1009,37 @@ __initfunc(static void xd_setparam (u_char command,u_char drive,u_char heads,u_s
cmdblk[13] = ecc;
if (xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2))
- printk("xd_setparam: error setting characteristics for drive %d\n",drive);
+ printk("xd: error setting characteristics for xd%c\n", 'a'+drive);
}
#ifdef MODULE
+static int xd[5] = { -1,-1,-1,-1, };
+
+MODULE_PARM(xd, "1-4i");
+MODULE_PARM(xd_geo, "3-6i");
+MODULE_PARM(nodma, "i");
+
int init_module(void)
{
+ int i,count = 0;
int error = xd_init();
if (!error)
{
printk(KERN_INFO "XD: Loaded as a module.\n");
+ for (i = 4; i > 0; i--)
+ if(((xd[i] = xd[i-1]) >= 0) && !count)
+ count = i;
+ if((xd[0] = count));
+ xd_setup(NULL, xd);
xd_geninit(&(struct gendisk) { 0,0,0,0,0,0,0,0,0,0,0 });
+ if (!xd_drives) {
+ /* no drives detected - unload module */
+ unregister_blkdev(MAJOR_NR, "xd");
+ return (-1);
+ }
+ for (i = 0; i < xd_drives; i++)
+ resetup_one_dev(&xd_gendisk, i);
}
return error;
@@ -756,8 +1048,12 @@ int init_module(void)
void cleanup_module(void)
{
unregister_blkdev(MAJOR_NR, "xd");
- free_irq(xd_irq, NULL);
- free_dma(xd_dma);
+ if (xd_drives) {
+ free_irq(xd_irq, NULL);
+ free_dma(xd_dma);
+ if (xd_dma_buffer)
+ xd_dma_mem_free((unsigned long)xd_dma_buffer, xd_maxsectors * 0x200);
+ }
}
#endif /* MODULE */
diff --git a/drivers/block/xd.h b/drivers/block/xd.h
index b3531e5cf..6fff452b7 100644
--- a/drivers/block/xd.h
+++ b/drivers/block/xd.h
@@ -35,7 +35,7 @@
#define CMD_SEEK 0x0B /* seek */
/* Controller specific commands */
-#define CMD_DTCSETPARAM 0x0C /* set drive parameters (DTC 5150X only?) */
+#define CMD_DTCSETPARAM 0x0C /* set drive parameters (DTC 5150X & CX only?) */
#define CMD_DTCGETECC 0x0D /* get ecc error length (DTC 5150X only?) */
#define CMD_DTCREADBUF 0x0E /* read sector buffer (DTC 5150X only?) */
#define CMD_DTCWRITEBUF 0x0F /* write sector buffer (DTC 5150X only?) */
@@ -85,8 +85,6 @@ typedef struct {
u_char control;
} XD_INFO;
-#define HDIO_GETGEO 0x0301 /* get drive geometry */
-
/* this structure is returned to the HDIO_GETGEO ioctl */
typedef struct {
__u8 heads;
@@ -105,6 +103,9 @@ typedef struct {
} XD_SIGNATURE;
void xd_setup (char *command,int *integers);
+#ifndef MODULE
+void xd_manual_geo_init (char *command,int *integers);
+#endif /* MODULE */
static u_char xd_detect (u_char *controller, unsigned int *address);
static u_char xd_initdrives (void (*init_drive)(u_char drive));
static void xd_geninit (struct gendisk *);
@@ -120,11 +121,14 @@ static void xd_recalibrate (u_char drive);
static void xd_interrupt_handler (int irq, void *dev_id, struct pt_regs *regs);
static u_char xd_setup_dma (u_char opcode,u_char *buffer,u_int count);
static u_char *xd_build (u_char *cmdblk,u_char command,u_char drive,u_char head,u_short cylinder,u_char sector,u_char count,u_char control);
+static void xd_wakeup (void);
+static void xd_watchdog (void);
static inline u_char xd_waitport (u_short port,u_char flags,u_char mask,u_long timeout);
static u_int xd_command (u_char *command,u_char mode,u_char *indata,u_char *outdata,u_char *sense,u_long timeout);
/* card specific setup and geometry gathering code */
static void xd_dtc_init_controller (unsigned int address);
+static void xd_dtc5150cx_init_drive (u_char drive);
static void xd_dtc_init_drive (u_char drive);
static void xd_wd_init_controller (unsigned int address);
static void xd_wd_init_drive (u_char drive);
diff --git a/drivers/cdrom/cdu31a.c b/drivers/cdrom/cdu31a.c
index e2c269bcf..8e4960525 100644
--- a/drivers/cdrom/cdu31a.c
+++ b/drivers/cdrom/cdu31a.c
@@ -844,7 +844,7 @@ do_sony_cd_cmd(unsigned char cmd,
while (sony_inuse)
{
interruptible_sleep_on(&sony_wait);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
{
result_buffer[0] = 0x20;
result_buffer[1] = SONY_SIGNAL_OP_ERR;
@@ -1556,7 +1556,7 @@ do_cdu31a_request(void)
while (sony_inuse)
{
interruptible_sleep_on(&sony_wait);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
{
restore_flags(flags);
if (CURRENT && CURRENT->rq_status != RQ_INACTIVE)
@@ -2262,7 +2262,7 @@ read_audio(struct cdrom_read_audio *ra,
while (sony_inuse)
{
interruptible_sleep_on(&sony_wait);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
{
restore_flags(flags);
return -EAGAIN;
diff --git a/drivers/cdrom/mcdx.c b/drivers/cdrom/mcdx.c
index 242508f9d..dea152349 100644
--- a/drivers/cdrom/mcdx.c
+++ b/drivers/cdrom/mcdx.c
@@ -947,7 +947,7 @@ static void mcdx_delay(struct s_drive_stuff *stuff, long jifs)
xtrace(SLEEP, "*** delay: sleepq\n");
interruptible_sleep_on(&stuff->sleepq);
xtrace(SLEEP, "delay awoken\n");
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
xtrace(SLEEP, "got signal\n");
}
}
@@ -1410,7 +1410,7 @@ static int mcdx_xfer(struct s_drive_stuff *stuffp,
if (!stuffp->introk) { xtrace(XFER, "error via interrupt\n"); }
else if (current->timeout == 0) { xtrace(XFER, "timeout\n"); }
- else if (current->signal & ~current->blocked) {
+ else if (signal_pending(current)) {
xtrace(XFER, "signal\n");
} else continue;
diff --git a/drivers/char/ChangeLog b/drivers/char/ChangeLog
index 3083c0c54..0e61fb819 100644
--- a/drivers/char/ChangeLog
+++ b/drivers/char/ChangeLog
@@ -1,3 +1,47 @@
+Mon Nov 24 10:37:49 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
+
+ * serial.c, esp.c, rocket.c: Change drivers to take advantage of
+ tty_get_baud_rate().
+
+ * tty_io.c (tty_get_baud_rate): New function which computes the
+ correct baud rate for the tty. More factoring out of
+ common code out of the serial driver to the high-level tty
+ functions....
+
+Sat Nov 22 07:53:36 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
+
+ * serial.c, esp.c, rocket.c: Add tty->driver.break() routine, and
+ allow high-level tty code to handle the break and soft
+ carrier ioctls.
+
+ * tty_ioctl.c (n_tty_ioctl): Support TIOCGSOFTCAR and
+ TIOCSSOFTCAR, so that device drivers don't have to support
+ it.
+
+ * serial.c (autoconfig): Change 16750 test to hopefully eliminate
+ false results by people with strange 16550A's being
+ detected as 16750's. Hopefully 16750's will still be
+ detected as 16750, and other wierd UART's won't get poorly
+ autodetected. If this doesn't work, I'll have to disable
+ the auto identification for the 16750....
+
+ * tty_io.c (tty_hangup): Now do actually do the tty hangup
+ processing during the timer processing, and disable
+ interrupts while doing the hangup processing. This avoids
+ several nasty race conditions which happened when the
+ hangup processing was done asynchronously.
+ (tty_ioctl): Do break handling in the tty driver if
+ driver's break function is supported.
+ (tty_flip_buffer_push): New exported function which should
+ be used by drivers to push characters in the flip buffer
+ to the tty handler. This may either be done using a task
+ queue function for better CPU efficiency, or directly for
+ low latency operation.
+
+ * serial.c (rs_set_termios): Fix bug rs_set_termios when
+ transitioning away from B0, submitted by Stanislav
+ Voronyi.
+
Thu Jun 19 20:05:58 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
* serial.c (begin_break, end_break, rs_ioctl): Applied patch
diff --git a/drivers/char/Config.in b/drivers/char/Config.in
index eee723670..9b13a4d2d 100644
--- a/drivers/char/Config.in
+++ b/drivers/char/Config.in
@@ -76,11 +76,6 @@ if [ "$CONFIG_QIC02_TAPE" != "n" ]; then
fi
fi
-tristate 'Ftape (QIC-80/Travan) support' CONFIG_FTAPE
-if [ "$CONFIG_FTAPE" != "n" ]; then
- comment 'Set IObase/IRQ/DMA for ftape in ./drivers/char/ftape/Makefile'
-fi
-
bool 'Advanced Power Management BIOS support' CONFIG_APM
if [ "$CONFIG_APM" = "y" ]; then
bool ' Ignore USER SUSPEND' CONFIG_APM_IGNORE_USER_SUSPEND
@@ -104,9 +99,29 @@ if [ "$CONFIG_WATCHDOG" != "n" ]; then
tristate ' Acquire SBC Watchdog Timer' CONFIG_ACQUIRE_WDT
fi
bool 'Enhanced Real Time Clock Support' CONFIG_RTC
-if [ "$CONFIG_PPC" = "y" ]; then
+if [ "$CONFIG_ALPHA_BOOK1" = "y" ]; then
bool 'Tadpole ANA H8 Support' CONFIG_H8
fi
+tristate 'Video For Linux' CONFIG_VIDEO_DEV
+dep_tristate 'BT848 Video For Linux' CONFIG_VIDEO_BT848 $CONFIG_VIDEO_DEV
+#dep_tristate 'Quickcam BW Video For Linux' CONFIG_VIDEO_BWQCAM $CONFIG_VIDEO_DEV
+dep_tristate 'Mediavision Pro Movie Studio Video For Linux' CONFIG_VIDEO_PMS $CONFIG_VIDEO_DEV
tristate '/dev/nvram support' CONFIG_NVRAM
tristate 'PC joystick support' CONFIG_JOYSTICK
+bool 'Radio Device Support' CONFIG_MISC_RADIO
+if [ "$CONFIG_MISC_RADIO" != "n" ]; then
+ bool ' AIMSlab RadioTrack (aka RadioReveal) support' CONFIG_RADIO_RTRACK
+ if [ "$CONFIG_RADIO_RTRACK" != "n" ]; then
+ hex ' RadioTrack i/o port (0x20f or 0x30f)' CONFIG_RADIO_RTRACK_PORT 0x20f
+ fi
+fi
+
+mainmenu_option next_comment
+comment 'Ftape, the floppy tape device driver'
+tristate 'Ftape (QIC-80/Travan) support' CONFIG_FTAPE
+if [ "$CONFIG_FTAPE" != "n" ]; then
+ source drivers/char/ftape/Config.in
+fi
+endmenu
+
endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index e0cb4dd44..58ca9aa09 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -279,11 +279,54 @@ endif
ifeq ($(CONFIG_NVRAM),y)
M = y
-L_OBJS += nvram.o
+ ifeq ($(CONFIG_PMAC)$(CONFIG_CHRP),)
+ L_OBJS += nvram.o
+ endif
else
ifeq ($(CONFIG_NVRAM),m)
MM = m
+ ifeq ($(CONFIG_PMAC)$(CONFIG_CHRP),)
M_OBJS += nvram.o
+ endif
+ endif
+endif
+
+ifeq ($(CONFIG_VIDEO_DEV),y)
+LX_OBJS += videodev.o
+else
+ ifeq ($(CONFIG_VIDEO_DEV),m)
+ MX_OBJS += videodev.o
+ endif
+endif
+
+ifeq ($(CONFIG_VIDEO_BT848),y)
+L_OBJS += bttv.o
+else
+ ifeq ($(CONFIG_VIDEO_BT848),m)
+ M_OBJS += bttv.o
+ endif
+endif
+
+ifeq ($(CONFIG_VIDEO_BWQCAM),y)
+L_OBJS += bw-qcam.o
+else
+ ifeq ($(CONFIG_VIDEO_BWQCAM),m)
+ M_OBJS += bw-qcam.o
+ endif
+endif
+
+ifeq ($(CONFIG_VIDEO_PMS),y)
+L_OBJS += pms.o
+else
+ ifeq ($(CONFIG_VIDEO_PMS),m)
+ M_OBJS += pms.o
+ endif
+endif
+
+ifeq ($(CONFIG_MISC_RADIO),y)
+L_OBJS += radio.o
+ ifeq ($(CONFIG_RADIO_RTRACK),y)
+ L_OBJS += rtrack.o
endif
endif
@@ -296,8 +339,11 @@ else
endif
ifeq ($(CONFIG_FTAPE),y)
-SUB_DIRS += ftape
-L_OBJS += ftape/ftape.o
+L_OBJS += ftape/ftape.o
+SUB_DIRS += ftape
+ifneq ($(CONFIG_ZFTAPE),n)
+MOD_SUB_DIRS += ftape
+endif
else
ifeq ($(CONFIG_FTAPE),m)
MOD_SUB_DIRS += ftape
diff --git a/drivers/char/README.cycladesZ b/drivers/char/README.cycladesZ
new file mode 100644
index 000000000..024a69443
--- /dev/null
+++ b/drivers/char/README.cycladesZ
@@ -0,0 +1,8 @@
+
+The Cyclades-Z must have firmware loaded onto the card before it will
+operate. This operation should be performed during system startup,
+
+The firmware, loader program and the latest device driver code are
+available from Cyclades at
+ ftp://ftp.cyclades.com/pub/cyclades/cyclades-z/linux/
+
diff --git a/drivers/char/acquirewdt.c b/drivers/char/acquirewdt.c
index aeb7b72fc..8d2600f41 100644
--- a/drivers/char/acquirewdt.c
+++ b/drivers/char/acquirewdt.c
@@ -62,7 +62,7 @@ static void acq_ping(void)
inb_p(WDT_START);
}
-static long acq_write(struct inode *inode, struct file *file, const char *buf, unsigned long count)
+static ssize_t acq_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
if(count)
{
@@ -72,7 +72,7 @@ static long acq_write(struct inode *inode, struct file *file, const char *buf, u
return 0;
}
-static long acq_read(struct inode *inode, struct file *file, char *buf, unsigned long count)
+static ssize_t acq_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
return -EINVAL;
}
diff --git a/drivers/char/apm_bios.c b/drivers/char/apm_bios.c
index 0a722b5e6..c19db0340 100644
--- a/drivers/char/apm_bios.c
+++ b/drivers/char/apm_bios.c
@@ -63,8 +63,8 @@
#include <asm/system.h>
#include <asm/uaccess.h>
-#include <asm/poll.h>
+#include <linux/poll.h>
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/timer.h>
@@ -307,7 +307,7 @@ static void do_apm_timer(unsigned long);
static int do_open(struct inode *, struct file *);
static int do_release(struct inode *, struct file *);
-static long do_read(struct inode *, struct file *, char *, unsigned long);
+static ssize_t do_read(struct file *, char *, size_t , loff_t *);
static unsigned int do_poll(struct file *, poll_table *);
static int do_ioctl(struct inode *, struct file *, u_int, u_long);
@@ -812,8 +812,7 @@ static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func)
return 0;
}
-static long do_read(struct inode *inode, struct file *fp,
- char *buf, unsigned long count)
+static ssize_t do_read(struct file *fp, char *buf, size_t count, loff_t *ppos)
{
struct apm_bios_struct * as;
int i;
@@ -831,8 +830,7 @@ static long do_read(struct inode *inode, struct file *fp,
add_wait_queue(&process_list, &wait);
repeat:
current->state = TASK_INTERRUPTIBLE;
- if (queue_empty(as)
- && !(current->signal & ~current->blocked)) {
+ if (queue_empty(as) && !signal_pending(current)) {
schedule();
goto repeat;
}
@@ -859,7 +857,7 @@ repeat:
}
if (i < count)
return count - i;
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -ERESTARTSYS;
return 0;
}
@@ -958,7 +956,7 @@ static int do_open(struct inode * inode, struct file * filp)
as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL);
if (as == NULL) {
- printk(KERN_ER "apm_bios: cannot allocate struct of size %d bytes",
+ printk(KERN_ERR "apm_bios: cannot allocate struct of size %d bytes",
sizeof(*as));
return -ENOMEM;
}
diff --git a/drivers/char/atixlmouse.c b/drivers/char/atixlmouse.c
index 7ffee43bc..51451a0a2 100644
--- a/drivers/char/atixlmouse.c
+++ b/drivers/char/atixlmouse.c
@@ -136,16 +136,16 @@ static int open_mouse(struct inode * inode, struct file * file)
}
-static long write_mouse(struct inode * inode, struct file * file,
- const char * buffer, unsigned long count)
+static ssize_t write_mouse(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos)
{
return -EINVAL;
}
-static long read_mouse(struct inode * inode, struct file * file,
- char * buffer, unsigned long count)
+static ssize_t read_mouse(struct file * file, char * buffer,
+ size_t count, loff_t *ppos)
{
- int i;
+ ssize_t i;
if (count < 3)
return -EINVAL;
diff --git a/drivers/char/bt848.h b/drivers/char/bt848.h
new file mode 100644
index 000000000..2171a1574
--- /dev/null
+++ b/drivers/char/bt848.h
@@ -0,0 +1,319 @@
+/*
+ bt848.h - Bt848 register offsets
+
+ Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _BT848_H_
+#define _BT848_H_
+
+#ifndef PCI_VENDOR_ID_BROOKTREE
+#define PCI_VENDOR_ID_BROOKTREE 0x109e
+#endif
+#ifndef PCI_DEVICE_ID_BT848
+#define PCI_DEVICE_ID_BT848 0x350
+#endif
+
+#define RISCMEM_LEN 131040
+
+/* Brooktree 848 registers */
+
+#define BT848_DSTATUS 0x000
+#define BT848_DSTATUS_PRES (1<<7)
+#define BT848_DSTATUS_HLOC (1<<6)
+#define BT848_DSTATUS_FIELD (1<<5)
+#define BT848_DSTATUS_NUML (1<<4)
+#define BT848_DSTATUS_CSEL (1<<3)
+#define BT848_DSTATUS_LOF (1<<1)
+#define BT848_DSTATUS_COF (1<<0)
+
+#define BT848_IFORM 0x004
+#define BT848_IFORM_HACTIVE (1<<7)
+#define BT848_IFORM_MUXSEL (3<<5)
+#define BT848_IFORM_MUX0 (2<<5)
+#define BT848_IFORM_MUX1 (3<<5)
+#define BT848_IFORM_MUX2 (1<<5)
+#define BT848_IFORM_XTSEL (3<<3)
+#define BT848_IFORM_XT0 (1<<3)
+#define BT848_IFORM_XT1 (2<<3)
+#define BT848_IFORM_XTAUTO (3<<3)
+#define BT848_IFORM_XTBOTH (3<<3)
+#define BT848_IFORM_NTSC 1
+#define BT848_IFORM_PAL_BDGHI 3
+#define BT848_IFORM_PAL_M 4
+#define BT848_IFORM_PAL_N 5
+#define BT848_IFORM_SECAM 6
+#define BT848_IFORM_AUTO 0
+#define BT848_IFORM_NORM 7
+
+#define BT848_TDEC 0x008
+#define BT848_TDEC_DEC_FIELD (1<<7)
+#define BT848_TDEC_FLDALIGN (1<<6)
+#define BT848_TDEC_DEC_RAT (0x1f)
+
+#define BT848_E_CROP 0x00C
+#define BT848_O_CROP 0x08C
+
+#define BT848_E_VDELAY_LO 0x010
+#define BT848_O_VDELAY_LO 0x090
+
+#define BT848_E_VACTIVE_LO 0x014
+#define BT848_O_VACTIVE_LO 0x094
+
+#define BT848_E_HDELAY_LO 0x018
+#define BT848_O_HDELAY_LO 0x098
+
+#define BT848_E_HACTIVE_LO 0x01C
+#define BT848_O_HACTIVE_LO 0x09C
+
+#define BT848_E_HSCALE_HI 0x020
+#define BT848_O_HSCALE_HI 0x0A0
+
+#define BT848_E_HSCALE_LO 0x024
+#define BT848_O_HSCALE_LO 0x0A4
+
+#define BT848_BRIGHT 0x028
+
+#define BT848_E_CONTROL 0x02C
+#define BT848_O_CONTROL 0x0AC
+#define BT848_CONTROL_LNOTCH (1<<7)
+#define BT848_CONTROL_COMP (1<<6)
+#define BT848_CONTROL_LDEC (1<<5)
+#define BT848_CONTROL_CBSENSE (1<<4)
+#define BT848_CONTROL_CON_MSB (1<<2)
+#define BT848_CONTROL_SAT_U_MSB (1<<1)
+#define BT848_CONTROL_SAT_V_MSB (1<<0)
+
+#define BT848_CONTRAST_LO 0x030
+#define BT848_SAT_U_LO 0x034
+#define BT848_SAT_V_LO 0x038
+#define BT848_HUE 0x03C
+
+#define BT848_E_SCLOOP 0x040
+#define BT848_O_SCLOOP 0x0C0
+#define BT848_SCLOOP_CAGC (1<<6)
+#define BT848_SCLOOP_CKILL (1<<5)
+#define BT848_SCLOOP_HFILT_AUTO (0<<3)
+#define BT848_SCLOOP_HFILT_CIF (1<<3)
+#define BT848_SCLOOP_HFILT_QCIF (2<<3)
+#define BT848_SCLOOP_HFILT_ICON (3<<3)
+
+#define BT848_OFORM 0x048
+#define BT848_OFORM_RANGE (1<<7)
+#define BT848_OFORM_CORE0 (0<<5)
+#define BT848_OFORM_CORE8 (1<<5)
+#define BT848_OFORM_CORE16 (2<<5)
+#define BT848_OFORM_CORE32 (3<<5)
+
+#define BT848_E_VSCALE_HI 0x04C
+#define BT848_O_VSCALE_HI 0x0CC
+#define BT848_VSCALE_YCOMB (1<<7)
+#define BT848_VSCALE_COMB (1<<6)
+#define BT848_VSCALE_INT (1<<5)
+#define BT848_VSCALE_HI 15
+
+#define BT848_E_VSCALE_LO 0x050
+#define BT848_O_VSCALE_LO 0x0D0
+#define BT848_TEST 0x054
+#define BT848_ADELAY 0x060
+#define BT848_BDELAY 0x064
+
+#define BT848_ADC 0x068
+#define BT848_ADC_RESERVED (2<<6)
+#define BT848_ADC_SYNC_T (1<<5)
+#define BT848_ADC_AGC_EN (1<<4)
+#define BT848_ADC_CLK_SLEEP (1<<3)
+#define BT848_ADC_Y_SLEEP (1<<2)
+#define BT848_ADC_C_SLEEP (1<<1)
+#define BT848_ADC_CRUSH (1<<0)
+
+#define BT848_E_VTC 0x06C
+#define BT848_O_VTC 0x0EC
+#define BT848_VTC_HSFMT (1<<7)
+#define BT848_VTC_VFILT_2TAP 0
+#define BT848_VTC_VFILT_3TAP 1
+#define BT848_VTC_VFILT_4TAP 2
+#define BT848_VTC_VFILT_5TAP 3
+
+#define BT848_SRESET 0x07C
+
+#define BT848_COLOR_FMT 0x0D4
+#define BT848_COLOR_FMT_O_RGB32 (0<<4)
+#define BT848_COLOR_FMT_O_RGB24 (1<<4)
+#define BT848_COLOR_FMT_O_RGB16 (2<<4)
+#define BT848_COLOR_FMT_O_RGB15 (3<<4)
+#define BT848_COLOR_FMT_O_YUY2 (4<<4)
+#define BT848_COLOR_FMT_O_BtYUV (5<<4)
+#define BT848_COLOR_FMT_O_Y8 (6<<4)
+#define BT848_COLOR_FMT_O_RGB8 (7<<4)
+#define BT848_COLOR_FMT_O_YCrCb422 (8<<4)
+#define BT848_COLOR_FMT_O_YCrCb411 (9<<4)
+#define BT848_COLOR_FMT_O_RAW (14<<4)
+#define BT848_COLOR_FMT_E_RGB32 0
+#define BT848_COLOR_FMT_E_RGB24 1
+#define BT848_COLOR_FMT_E_RGB16 2
+#define BT848_COLOR_FMT_E_RGB15 3
+#define BT848_COLOR_FMT_E_YUY2 4
+#define BT848_COLOR_FMT_E_BtYUV 5
+#define BT848_COLOR_FMT_E_Y8 6
+#define BT848_COLOR_FMT_E_RGB8 7
+#define BT848_COLOR_FMT_E_YCrCb422 8
+#define BT848_COLOR_FMT_E_YCrCb411 9
+#define BT848_COLOR_FMT_E_RAW 14
+
+#define BT848_COLOR_FMT_RGB32 0x00
+#define BT848_COLOR_FMT_RGB24 0x11
+#define BT848_COLOR_FMT_RGB16 0x22
+#define BT848_COLOR_FMT_RGB15 0x33
+#define BT848_COLOR_FMT_YUY2 0x44
+#define BT848_COLOR_FMT_BtYUV 0x55
+#define BT848_COLOR_FMT_Y8 0x66
+#define BT848_COLOR_FMT_RGB8 0x77
+#define BT848_COLOR_FMT_YCrCb422 0x88
+#define BT848_COLOR_FMT_YCrCb411 0x99
+#define BT848_COLOR_FMT_RAW 0xee
+
+#define BT848_COLOR_CTL 0x0D8
+#define BT848_COLOR_CTL_EXT_FRMRATE (1<<7)
+#define BT848_COLOR_CTL_COLOR_BARS (1<<6)
+#define BT848_COLOR_CTL_RGB_DED (1<<5)
+#define BT848_COLOR_CTL_GAMMA (1<<4)
+#define BT848_COLOR_CTL_WSWAP_ODD (1<<3)
+#define BT848_COLOR_CTL_WSWAP_EVEN (1<<2)
+#define BT848_COLOR_CTL_BSWAP_ODD (1<<1)
+#define BT848_COLOR_CTL_BSWAP_EVEN (1<<0)
+
+#define BT848_CAP_CTL 0x0DC
+#define BT848_CAP_CTL_DITH_FRAME (1<<4)
+#define BT848_CAP_CTL_CAPTURE_VBI_ODD (1<<3)
+#define BT848_CAP_CTL_CAPTURE_VBI_EVEN (1<<2)
+#define BT848_CAP_CTL_CAPTURE_ODD (1<<1)
+#define BT848_CAP_CTL_CAPTURE_EVEN (1<<0)
+
+#define BT848_VBI_PACK_SIZE 0x0E0
+
+#define BT848_VBI_PACK_DEL 0x0E4
+#define BT848_VBI_PACK_DEL_VBI_HDELAY 0xfc
+#define BT848_VBI_PACK_DEL_EXT_FRAME 2
+#define BT848_VBI_PACK_DEL_VBI_PKT_HI 1
+
+#define BT848_INT_STAT 0x100
+#define BT848_INT_MASK 0x104
+
+#define BT848_INT_ETBF (1<<23)
+
+#define BT848_INT_RISCS (0xf<<28)
+#define BT848_INT_RISC_EN (1<<27)
+#define BT848_INT_RACK (1<<25)
+#define BT848_INT_FIELD (1<<24)
+#define BT848_INT_SCERR (1<<19)
+#define BT848_INT_OCERR (1<<18)
+#define BT848_INT_PABORT (1<<17)
+#define BT848_INT_RIPERR (1<<16)
+#define BT848_INT_PPERR (1<<15)
+#define BT848_INT_FDSR (1<<14)
+#define BT848_INT_FTRGT (1<<13)
+#define BT848_INT_FBUS (1<<12)
+#define BT848_INT_RISCI (1<<11)
+#define BT848_INT_GPINT (1<<9)
+#define BT848_INT_I2CDONE (1<<8)
+#define BT848_INT_VPRES (1<<5)
+#define BT848_INT_HLOCK (1<<4)
+#define BT848_INT_OFLOW (1<<3)
+#define BT848_INT_HSYNC (1<<2)
+#define BT848_INT_VSYNC (1<<1)
+#define BT848_INT_FMTCHG (1<<0)
+
+
+#define BT848_GPIO_DMA_CTL 0x10C
+#define BT848_GPIO_DMA_CTL_GPINTC (1<<15)
+#define BT848_GPIO_DMA_CTL_GPINTI (1<<14)
+#define BT848_GPIO_DMA_CTL_GPWEC (1<<13)
+#define BT848_GPIO_DMA_CTL_GPIOMODE (3<<11)
+#define BT848_GPIO_DMA_CTL_GPCLKMODE (1<<10)
+#define BT848_GPIO_DMA_CTL_PLTP23_4 (0<<6)
+#define BT848_GPIO_DMA_CTL_PLTP23_8 (1<<6)
+#define BT848_GPIO_DMA_CTL_PLTP23_16 (2<<6)
+#define BT848_GPIO_DMA_CTL_PLTP23_32 (3<<6)
+#define BT848_GPIO_DMA_CTL_PLTP1_4 (0<<4)
+#define BT848_GPIO_DMA_CTL_PLTP1_8 (1<<4)
+#define BT848_GPIO_DMA_CTL_PLTP1_16 (2<<4)
+#define BT848_GPIO_DMA_CTL_PLTP1_32 (3<<4)
+#define BT848_GPIO_DMA_CTL_PKTP_4 (0<<2)
+#define BT848_GPIO_DMA_CTL_PKTP_8 (1<<2)
+#define BT848_GPIO_DMA_CTL_PKTP_16 (2<<2)
+#define BT848_GPIO_DMA_CTL_PKTP_32 (3<<2)
+#define BT848_GPIO_DMA_CTL_RISC_ENABLE (1<<1)
+#define BT848_GPIO_DMA_CTL_FIFO_ENABLE (1<<0)
+
+#define BT848_I2C 0x110
+#define BT848_I2C_DIV (0xf<<4)
+#define BT848_I2C_SYNC (1<<3)
+#define BT848_I2C_W3B (1<<2)
+#define BT848_I2C_SCL (1<<1)
+#define BT848_I2C_SDA (1<<0)
+
+
+#define BT848_RISC_STRT_ADD 0x114
+#define BT848_GPIO_OUT_EN 0x118
+#define BT848_GPIO_REG_INP 0x11C
+#define BT848_RISC_COUNT 0x120
+#define BT848_GPIO_DATA 0x200
+
+
+/* Bt848 RISC commands */
+
+/* only for the SYNC RISC command */
+#define BT848_FIFO_STATUS_FM1 0x06
+#define BT848_FIFO_STATUS_FM3 0x0e
+#define BT848_FIFO_STATUS_SOL 0x02
+#define BT848_FIFO_STATUS_EOL4 0x01
+#define BT848_FIFO_STATUS_EOL3 0x0d
+#define BT848_FIFO_STATUS_EOL2 0x09
+#define BT848_FIFO_STATUS_EOL1 0x05
+#define BT848_FIFO_STATUS_VRE 0x04
+#define BT848_FIFO_STATUS_VRO 0x0c
+#define BT848_FIFO_STATUS_PXV 0x00
+
+#define BT848_RISC_RESYNC (1<<15)
+
+/* WRITE and SKIP */
+/* disable which bytes of each DWORD */
+#define BT848_RISC_BYTE0 (1<<12)
+#define BT848_RISC_BYTE1 (1<<13)
+#define BT848_RISC_BYTE2 (1<<14)
+#define BT848_RISC_BYTE3 (1<<15)
+#define BT848_RISC_BYTE_ALL (0x0f<<12)
+#define BT848_RISC_BYTE_NONE 0
+/* cause RISCI */
+#define BT848_RISC_IRQ (1<<24)
+/* RISC command is last one in this line */
+#define BT848_RISC_EOL (1<<26)
+/* RISC command is first one in this line */
+#define BT848_RISC_SOL (1<<27)
+
+#define BT848_RISC_WRITE (0x01<<28)
+#define BT848_RISC_SKIP (0x02<<28)
+#define BT848_RISC_WRITEC (0x05<<28)
+#define BT848_RISC_JUMP (0x07<<28)
+#define BT848_RISC_SYNC (0x08<<28)
+
+#define BT848_RISC_WRITE123 (0x09<<28)
+#define BT848_RISC_SKIP123 (0x0a<<28)
+#define BT848_RISC_WRITE1S23 (0x0b<<28)
+
+#endif
diff --git a/drivers/char/bttv.c b/drivers/char/bttv.c
new file mode 100644
index 000000000..56d38d7a2
--- /dev/null
+++ b/drivers/char/bttv.c
@@ -0,0 +1,2005 @@
+/*
+ bttv - Bt848 frame grabber driver
+
+ Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Modified to put the RISC code writer in the kernel and to fit a
+ common (and I hope safe) kernel interface. When we have an X extension
+ all will now be really sweet.
+*/
+
+#include <linux/module.h>
+#include <linux/bios32.h>
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/signal.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <asm/segment.h>
+#include <linux/types.h>
+#include <linux/videodev.h>
+
+#include <linux/version.h>
+#include <asm/uaccess.h>
+
+#include "bttv.h"
+#include "tuner.h"
+
+#define DEBUG(x) /* Debug driver */
+#define IDEBUG(x) /* Debug interrupt handler */
+
+static unsigned int remap=0;
+static unsigned int vidmem=0;
+static unsigned int tuner=0; /* Default tuner */
+
+static int find_vga(void);
+static void bt848_set_risc_jmps(struct bttv *btv);
+
+/* Anybody who uses more than four? */
+#define BTTV_MAX 4
+
+static int bttv_num;
+static struct bttv bttvs[BTTV_MAX];
+
+#define I2C_TIMING (0x7<<4)
+#define I2C_COMMAND (I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA)
+
+#define AUDIO_MUTE_DELAY 10000
+#define FREQ_CHANGE_DELAY 20000
+#define EEPROM_WRITE_DELAY 20000
+
+
+/*******************************/
+/* Memory management functions */
+/*******************************/
+
+/* convert virtual user memory address to physical address */
+/* (virt_to_phys only works for kmalloced kernel memory) */
+
+static inline ulong uvirt_to_phys(ulong adr)
+{
+ pgd_t *pgd;
+ pmd_t *pmd;
+ pte_t *ptep, pte;
+
+/* printk("adr: 0x%08x\n",adr);*/
+ pgd = pgd_offset(current->mm, adr);
+/* printk("pgd: 0x%08x\n",pgd);*/
+ if (pgd_none(*pgd))
+ return 0;
+ pmd = pmd_offset(pgd, adr);
+/* printk("pmd: 0x%08x\n",pmd); */
+ if (pmd_none(*pmd))
+ return 0;
+ ptep = pte_offset(pmd, adr&(~PGDIR_MASK));
+ pte = *ptep;
+ if(pte_present(pte))
+ return (pte_page(pte)|(adr&(PAGE_SIZE-1)));
+ return 0;
+}
+
+/* convert virtual kernel memory address to physical address */
+/* (virt_to_phys only works for kmalloced kernel memory) */
+
+static inline ulong kvirt_to_phys(ulong adr)
+{
+ return uvirt_to_phys(VMALLOC_VMADDR(adr));
+}
+
+static inline ulong kvirt_to_bus(ulong adr)
+{
+ return virt_to_bus(phys_to_virt(kvirt_to_phys(adr)));
+}
+
+
+/*****************/
+/* I2C functions */
+/*****************/
+
+static int I2CRead(struct bttv *btv, int addr)
+{
+ u32 i;
+ u32 stat;
+
+ /* clear status bit ; BT848_INT_RACK is ro */
+ btwrite(BT848_INT_I2CDONE, BT848_INT_STAT);
+
+ btwrite(((addr & 0xff) << 24) | I2C_COMMAND, BT848_I2C);
+
+ for (i=0x7fffffff; i; i--)
+ {
+ stat=btread(BT848_INT_STAT);
+ if (stat & BT848_INT_I2CDONE)
+ break;
+ }
+
+ if (!i)
+ return -1;
+ if (!(stat & BT848_INT_RACK))
+ return -2;
+
+ i=(btread(BT848_I2C)>>8)&0xff;
+ return i;
+}
+
+/* set both to write both bytes, reset it to write only b1 */
+
+static int I2CWrite(struct bttv *btv, unchar addr, unchar b1,
+ unchar b2, int both)
+{
+ u32 i;
+ u32 data;
+ u32 stat;
+
+ /* clear status bit; BT848_INT_RACK is ro */
+ btwrite(BT848_INT_I2CDONE, BT848_INT_STAT);
+
+ data=((addr & 0xff) << 24) | ((b1 & 0xff) << 16) | I2C_COMMAND;
+ if (both)
+ {
+ data|=((b2 & 0xff) << 8);
+ data|=BT848_I2C_W3B;
+ }
+
+ btwrite(data, BT848_I2C);
+
+ for (i=0x7fffffff; i; i--)
+ {
+ stat=btread(BT848_INT_STAT);
+ if (stat & BT848_INT_I2CDONE)
+ break;
+ }
+
+ if (!i)
+ return -1;
+ if (!(stat & BT848_INT_RACK))
+ return -2;
+
+ return 0;
+}
+
+static void readee(struct bttv *btv, unchar *eedata)
+{
+ int i, k;
+
+ if (I2CWrite(btv, 0xa0, 0, -1, 0)<0)
+ {
+ printk(KERN_WARNING "bttv: readee error\n");
+ return;
+ }
+
+ for (i=0; i<256; i++)
+ {
+ k=I2CRead(btv, 0xa1);
+ if (k<0)
+ {
+ printk(KERN_WARNING "bttv: readee error\n");
+ break;
+ }
+ eedata[i]=k;
+ }
+}
+
+static void writeee(struct bttv *btv, unchar *eedata)
+{
+ int i;
+
+ for (i=0; i<256; i++)
+ {
+ if (I2CWrite(btv, 0xa0, i, eedata[i], 1)<0)
+ {
+ printk(KERN_WARNING "bttv: writeee error (%d)\n", i);
+ break;
+ }
+ udelay(EEPROM_WRITE_DELAY);
+ }
+}
+
+/*
+ * Tuner, internal, external and mute
+ */
+
+static unchar audiomuxs[][4] =
+{
+ { 0x00, 0x00, 0x00, 0x00}, /* unknown */
+ { 0x02, 0x00, 0x00, 0x0a}, /* MIRO */
+ { 0x00, 0x02, 0x03, 0x04}, /* Hauppauge */
+ { 0x04, 0x02, 0x03, 0x01}, /* STB */
+ { 0x01, 0x02, 0x03, 0x04}, /* Intel??? */
+ { 0x01, 0x02, 0x03, 0x04}, /* Diamond DTV2000??? */
+};
+
+static void audio(struct bttv *btv, int mode)
+{
+ btwrite(0x0f, BT848_GPIO_OUT_EN);
+ btwrite(0x00, BT848_GPIO_REG_INP);
+
+ switch (mode)
+ {
+ case AUDIO_UNMUTE:
+ btv->audio&=~AUDIO_MUTE;
+ mode=btv->audio;
+ break;
+ case AUDIO_OFF:
+ mode=AUDIO_OFF;
+ break;
+ case AUDIO_ON:
+ mode=btv->audio;
+ break;
+ default:
+ btv->audio&=AUDIO_MUTE;
+ btv->audio|=mode;
+ break;
+ }
+ if ((btv->audio&AUDIO_MUTE) || !(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC))
+ mode=AUDIO_OFF;
+ btaor(audiomuxs[btv->type][mode] , ~0x0f, BT848_GPIO_DATA);
+}
+
+
+extern inline void bt848_dma(struct bttv *btv, uint state)
+{
+ if (state)
+ btor(3, BT848_GPIO_DMA_CTL);
+ else
+ btand(~3, BT848_GPIO_DMA_CTL);
+}
+
+
+static void bt848_cap(struct bttv *btv, uint state)
+{
+ if (state)
+ {
+ btv->cap|=3;
+ bt848_set_risc_jmps(btv);
+ }
+ else
+ {
+ btv->cap&=~3;
+ bt848_set_risc_jmps(btv);
+ }
+}
+
+static void bt848_muxsel(struct bttv *btv, uint input)
+{
+ input&=3;
+
+ /* This seems to get rid of some synchronization problems */
+ btand(~(3<<5), BT848_IFORM);
+ udelay(10000);
+
+ if (input==3)
+ {
+ btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
+ btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
+ }
+ else
+ {
+ btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
+ btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
+ }
+ if (input==2)
+ input=3;
+ btaor(((input+2)&3)<<5, ~(3<<5), BT848_IFORM);
+ audio(btv, input ? AUDIO_EXTERN : AUDIO_TUNER);
+}
+
+
+#define VBIBUF_SIZE 65536
+
+static void make_vbitab(struct bttv *btv)
+{
+ int i;
+ dword *po=(dword *) btv->vbi_odd;
+ dword *pe=(dword *) btv->vbi_even;
+
+ DEBUG(printk(KERN_DEBUG "vbiodd: 0x%08x\n",(int)btv->vbi_odd));
+ DEBUG(printk(KERN_DEBUG "vbievn: 0x%08x\n",(int)btv->vbi_even));
+ DEBUG(printk(KERN_DEBUG "po: 0x%08x\n",(int)po));
+ DEBUG(printk(KERN_DEBUG "pe: 0x%08x\n",(int)pe));
+
+ *(po++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(po++)=0;
+ for (i=0; i<16; i++)
+ {
+ *(po++)=BT848_RISC_WRITE|2044|BT848_RISC_EOL|BT848_RISC_SOL|(13<<20);
+ *(po++)=kvirt_to_bus((ulong)btv->vbibuf+i*2048);
+ }
+ *(po++)=BT848_RISC_JUMP;
+ *(po++)=virt_to_bus(btv->risc_jmp+4);
+
+ *(pe++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(pe++)=0;
+ for (i=16; i<32; i++)
+ {
+ *(pe++)=BT848_RISC_WRITE|2044|BT848_RISC_EOL|BT848_RISC_SOL;
+ *(pe++)=kvirt_to_bus((ulong)btv->vbibuf+i*2048);
+ }
+ *(pe++)=BT848_RISC_JUMP|BT848_RISC_IRQ|(0x01<<16);
+ *(pe++)=virt_to_bus(btv->risc_jmp+10);
+}
+
+/*
+ * Set the registers for the size we have specified. Don't bother
+ * trying to understand this without the BT848 manual in front of
+ * you [AC].
+ *
+ * PS: The manual is free for download in .pdf format from
+ * www.brooktree.com - nicely done those folks.
+ */
+
+static void bt848_set_size(struct bttv *btv)
+{
+ u16 vscale, hscale;
+ u32 xsf, sr;
+ u16 hdelay, vdelay;
+ u16 hactive, vactive;
+ u16 inter;
+ u8 crop;
+
+ /*
+ * No window , no try...
+ */
+
+ if (!btv->win.width)
+ return;
+ if (!btv->win.height)
+ return;
+
+ inter=(btv->win.interlace&1)^1;
+
+ switch (btv->win.bpp)
+ {
+ /*
+ * RGB8 seems to be a 9x5x5 GRB color cube starting at color 16
+ * Why the h... can't they even mention this in the datasheet???
+ */
+ case 1:
+ btwrite(BT848_COLOR_FMT_RGB8, BT848_COLOR_FMT);
+ btand(~0x10, BT848_CAP_CTL); // Dithering looks much better in this mode
+ break;
+ case 2:
+ btwrite(BT848_COLOR_FMT_RGB16, BT848_COLOR_FMT);
+ btor(0x10, BT848_CAP_CTL);
+ break;
+ case 3:
+ btwrite(BT848_COLOR_FMT_RGB24, BT848_COLOR_FMT);
+ btor(0x10, BT848_CAP_CTL);
+ break;
+ case 4:
+ btwrite(BT848_COLOR_FMT_RGB32, BT848_COLOR_FMT);
+ btor(0x10, BT848_CAP_CTL);
+ break;
+ }
+
+ /*
+ * Set things up according to the final picture width.
+ */
+
+ hactive=btv->win.width;
+ if (hactive < 193)
+ {
+ btwrite (2, BT848_E_VTC);
+ btwrite (2, BT848_O_VTC);
+ }
+ else if (hactive < 385)
+ {
+ btwrite (1, BT848_E_VTC);
+ btwrite (1, BT848_O_VTC);
+ }
+ else
+ {
+ btwrite (0, BT848_E_VTC);
+ btwrite (0, BT848_O_VTC);
+ }
+
+ /*
+ * Ok are we doing Never The Same Color or PAL ?
+ */
+
+ if (btv->win.norm==1)
+ {
+ btv->win.cropwidth=640;
+ btv->win.cropheight=480;
+ btwrite(0x68, BT848_ADELAY);
+ btwrite(0x5d, BT848_BDELAY);
+ btaor(BT848_IFORM_NTSC, ~7, BT848_IFORM);
+ btaor(BT848_IFORM_XT0, ~0x18, BT848_IFORM);
+ xsf = (btv->win.width*365625UL)/300000UL;
+ hscale = ((910UL*4096UL)/xsf-4096);
+ vdelay=btv->win.cropy+0x16;
+ hdelay=((hactive*135)/754+btv->win.cropx)&0x3fe;
+ }
+ else
+ {
+ btv->win.cropwidth=768;
+ btv->win.cropheight=576;
+ if (btv->win.norm==0)
+ {
+ btwrite(0x7f, BT848_ADELAY);
+ btwrite(0x72, BT848_BDELAY);
+ btaor(BT848_IFORM_PAL_BDGHI, ~BT848_IFORM_NORM, BT848_IFORM);
+ }
+ else
+ {
+ btwrite(0x7f, BT848_ADELAY);
+ btwrite(0x00, BT848_BDELAY);
+ btaor(BT848_IFORM_SECAM, ~BT848_IFORM_NORM, BT848_IFORM);
+ }
+ btaor(BT848_IFORM_XT1, ~0x18, BT848_IFORM);
+ xsf = (btv->win.width*36875UL)/30000UL;
+ hscale = ((1135UL*4096UL)/xsf-4096);
+ vdelay=btv->win.cropy+0x20;
+ hdelay=((hactive*186)/922+btv->win.cropx)&0x3fe;
+ }
+ sr=((btv->win.cropheight>>inter)*512)/btv->win.height-512;
+ vscale=(0x10000UL-sr)&0x1fff;
+ vactive=btv->win.cropheight;
+
+#if 0
+ printk("bttv: hscale=0x%04x, ",hscale);
+ printk("bttv: vscale=0x%04x\n",vscale);
+
+ printk("bttv: hdelay =0x%04x\n",hdelay);
+ printk("bttv: hactive=0x%04x\n",hactive);
+ printk("bttv: vdelay =0x%04x\n",vdelay);
+ printk("bttv: vactive=0x%04x\n",vactive);
+#endif
+
+ /*
+ * Interlace is set elsewhere according to the final image
+ * size we desire
+ */
+
+ if (btv->win.interlace)
+ {
+ btor(BT848_VSCALE_INT, BT848_E_VSCALE_HI);
+ btor(BT848_VSCALE_INT, BT848_O_VSCALE_HI);
+ }
+ else
+ {
+ btand(~BT848_VSCALE_INT, BT848_E_VSCALE_HI);
+ btand(~BT848_VSCALE_INT, BT848_O_VSCALE_HI);
+ }
+
+ /*
+ * Load her up
+ */
+
+ btwrite(hscale>>8, BT848_E_HSCALE_HI);
+ btwrite(hscale>>8, BT848_O_HSCALE_HI);
+ btwrite(hscale&0xff, BT848_E_HSCALE_LO);
+ btwrite(hscale&0xff, BT848_O_HSCALE_LO);
+
+ btwrite((vscale>>8)|(btread(BT848_E_VSCALE_HI)&0xe0), BT848_E_VSCALE_HI);
+ btwrite((vscale>>8)|(btread(BT848_O_VSCALE_HI)&0xe0), BT848_O_VSCALE_HI);
+ btwrite(vscale&0xff, BT848_E_VSCALE_LO);
+ btwrite(vscale&0xff, BT848_O_VSCALE_LO);
+
+ btwrite(hactive&0xff, BT848_E_HACTIVE_LO);
+ btwrite(hactive&0xff, BT848_O_HACTIVE_LO);
+ btwrite(hdelay&0xff, BT848_E_HDELAY_LO);
+ btwrite(hdelay&0xff, BT848_O_HDELAY_LO);
+
+ btwrite(vactive&0xff, BT848_E_VACTIVE_LO);
+ btwrite(vactive&0xff, BT848_O_VACTIVE_LO);
+ btwrite(vdelay&0xff, BT848_E_VDELAY_LO);
+ btwrite(vdelay&0xff, BT848_O_VDELAY_LO);
+
+ crop=((hactive>>8)&0x03)|((hdelay>>6)&0x0c)|
+ ((vactive>>4)&0x30)|((vdelay>>2)&0xc0);
+ btwrite(crop, BT848_E_CROP);
+ btwrite(crop, BT848_O_CROP);
+}
+
+
+/*
+ * The floats in the tuner struct are computed at compile time
+ * by gcc and cast back to integers. Thus we don't violate the
+ * "no float in kernel" rule.
+ */
+
+static struct tunertype tuners[] = {
+ {"Temic PAL", TEMIC, PAL,
+ 16*140.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2},
+ {"Philips PAL_I", Philips, PAL_I,
+ 16*140.25,16*463.25,0x00,0x00,0x00,0x00,0x00},
+ {"Philips NTSC", Philips, NTSC,
+ 16*157.25,16*451.25,0xA0,0x90,0x30,0x8e,0xc0},
+ {"Philips SECAM", Philips, SECAM,
+ 16*168.25,16*447.25,0xA3,0x93,0x33,0x8e,0xc0},
+ {"NoTuner", NoTuner, NOTUNER,
+ 0 ,0 ,0x00,0x00,0x00,0x00,0x00},
+ {"Philips PAL", Philips, PAL,
+ 16*168.25,16*447.25,0xA0,0x90,0x30,0x8e,0xc0},
+ {"Temic NTSC", TEMIC, NTSC,
+ 16*157.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2},
+ {"TEMIC PAL_I", TEMIC, PAL_I,
+ 0 ,0 ,0x00,0x00,0x00,0x00,0xc2},
+};
+
+/*
+ * Set TSA5522 synthesizer frequency in 1/16 Mhz steps
+ */
+
+static void set_freq(struct bttv *btv, ushort freq)
+{
+ u8 config;
+ u16 div;
+ struct tunertype *tun=&tuners[btv->tuner];
+ int oldAudio = btv->audio;
+
+ audio(btv, AUDIO_MUTE);
+ udelay(AUDIO_MUTE_DELAY);
+ if (freq < tun->thresh1)
+ config = tun->VHF_L;
+ else if (freq < tun->thresh2)
+ config = tun->VHF_H;
+ else
+ config = tun->UHF;
+
+ div=freq+623; /* div=((freq+16*38.9));*/
+
+ div&=0x7fff;
+ if (I2CWrite(btv, btv->tuneradr, (div>>8)&0x7f, div&0xff, 1)<0)
+ return;
+ I2CWrite(btv, btv->tuneradr, tun->config, config, 1);
+ if (!(oldAudio & AUDIO_MUTE))
+ {
+ udelay(FREQ_CHANGE_DELAY);
+ audio(btv, AUDIO_UNMUTE);
+ }
+}
+
+static long bttv_write(struct video_device *v, const char *buf, unsigned long count, int nonblock)
+{
+ return -EINVAL;
+}
+
+static long bttv_read(struct video_device *v, char *buf, unsigned long count, int nonblock)
+{
+ struct bttv *btv= (struct bttv *)v;
+ int q,todo;
+
+ todo=count;
+ while (todo && todo>(q=VBIBUF_SIZE-btv->vbip))
+ {
+ if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, q))
+ return -EFAULT;
+ todo-=q;
+ buf+=q;
+
+/* btv->vbip=0; */
+ cli();
+ if (todo && q==VBIBUF_SIZE-btv->vbip)
+ {
+ if(nonblock)
+ {
+ sti();
+ if(count==todo)
+ return -EWOULDBLOCK;
+ return count-todo;
+ }
+ interruptible_sleep_on(&btv->vbiq);
+ sti();
+ if(current->signal & ~current->blocked)
+ {
+ if(todo==count)
+ return -EINTR;
+ else
+ return count-todo;
+ }
+ }
+ }
+ if (todo)
+ {
+ if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, todo))
+ return -EFAULT;
+ btv->vbip+=todo;
+ }
+ return count;
+}
+
+/*
+ * Open a bttv card. Right now the flags stuff is just playing
+ */
+
+static int bttv_open(struct video_device *dev, int flags)
+{
+ struct bttv *btv = (struct bttv *)dev;
+ int users, i;
+
+ switch (flags)
+ {
+ case 0:
+ if (btv->user)
+ return -EBUSY;
+ btv->user++;
+ audio(btv, AUDIO_UNMUTE);
+ for (i=users=0; i<bttv_num; i++)
+ users+=bttvs[i].user;
+ if (users==1)
+ find_vga();
+ break;
+ case 1:
+ break;
+ case 2:
+ btv->vbip=VBIBUF_SIZE;
+ btv->cap|=0x0c;
+ bt848_set_risc_jmps(btv);
+ break;
+ }
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void bttv_close(struct video_device *dev)
+{
+ struct bttv *btv=(struct bttv *)dev;
+
+ btv->user--;
+ audio(btv, AUDIO_MUTE);
+ btv->cap&=~3;
+#if 0 /* FIXME */
+ if(minor&0x20)
+ {
+ btv->cap&=~0x0c;
+ }
+#endif
+ bt848_set_risc_jmps(btv);
+
+ MOD_DEC_USE_COUNT;
+}
+
+/***********************************/
+/* ioctls and supporting functions */
+/***********************************/
+
+extern inline void bt848_bright(struct bttv *btv, uint bright)
+{
+ btwrite(bright&0xff, BT848_BRIGHT);
+}
+
+extern inline void bt848_hue(struct bttv *btv, uint hue)
+{
+ btwrite(hue&0xff, BT848_HUE);
+}
+
+extern inline void bt848_contrast(struct bttv *btv, uint cont)
+{
+ unsigned int conthi;
+
+ conthi=(cont>>6)&4;
+ btwrite(cont&0xff, BT848_CONTRAST_LO);
+ btaor(conthi, ~4, BT848_E_CONTROL);
+ btaor(conthi, ~4, BT848_O_CONTROL);
+}
+
+extern inline void bt848_sat_u(struct bttv *btv, ulong data)
+{
+ u32 datahi;
+
+ datahi=(data>>7)&2;
+ btwrite(data&0xff, BT848_SAT_U_LO);
+ btaor(datahi, ~2, BT848_E_CONTROL);
+ btaor(datahi, ~2, BT848_O_CONTROL);
+}
+
+static inline void bt848_sat_v(struct bttv *btv, ulong data)
+{
+ u32 datahi;
+
+ datahi=(data>>8)&1;
+ btwrite(data&0xff, BT848_SAT_V_LO);
+ btaor(datahi, ~1, BT848_E_CONTROL);
+ btaor(datahi, ~1, BT848_O_CONTROL);
+}
+
+/*
+ * Cliprect -> risc table.
+ *
+ * FIXME: This is generating wrong code when we have some kinds of
+ * rectangle lists. I don't currently understand why.
+ */
+
+static void write_risc_data(struct bttv *btv, struct video_clip *vp, int count)
+{
+ int i;
+ u32 yy, y, x, dx, ox;
+ u32 *rmem, *rmem2;
+ struct video_clip first, *cur, *cur2, *nx, first2, *prev, *nx2;
+ u32 *rp, rpo=0, rpe=0, p, bpsl;
+ u32 *rpp;
+ u32 mask;
+ int interlace;
+ int depth;
+
+ rmem=(u32 *)btv->risc_odd;
+ rmem2=(u32 *)btv->risc_even;
+ depth=btv->win.bpp;
+
+ /* create y-sorted list */
+
+ first.next=NULL;
+ for (i=0; i<count; i++)
+ {
+ cur=&first;
+ while ((nx=cur->next) && (vp[i].y > cur->next->y))
+ cur=nx;
+ cur->next=&(vp[i]);
+ vp[i].next=nx;
+ }
+ first2.next=NULL;
+
+ rmem[rpo++]=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; rmem[rpo++]=0;
+
+ rmem2[rpe++]=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; rmem2[rpe++]=0;
+
+
+ /*
+ * 32bit depth frame buffers need extra flags setting
+ */
+
+ if (depth==4)
+ mask=BT848_RISC_BYTE3;
+ else
+ mask=0;
+
+ bpsl=btv->win.width*btv->win.bpp;
+ p=btv->win.vidadr+btv->win.x*btv->win.bpp+
+ btv->win.y*btv->win.bpl;
+
+ interlace=btv->win.interlace;
+
+ /*
+ * Loop through all lines
+ */
+
+ for (yy=0; yy<(btv->win.height<<(1^interlace)); yy++)
+ {
+ y=yy>>(1^interlace);
+
+ /*
+ * Even or odd frame generation. We have to
+ * write the RISC instructions to the right stream.
+ */
+
+ if(!(y&1))
+ {
+ rp=&rpo;
+ rpp=rmem;
+ }
+ else
+ {
+ rp=&rpe;
+ rpp=rmem2;
+ }
+
+
+ /*
+ * first2 is the header of a list of "active" rectangles. We add
+ * rectangles as we hit their top and remove them as they fall off
+ * the bottom
+ */
+
+ /* remove rects with y2 > y */
+ if ((cur=first2.next))
+ {
+ prev=&first2;
+ do
+ {
+ if (cur->y+cur->height < y)
+ prev->next=cur->next;
+ else
+ prev=cur;
+ }
+ while ((cur=cur->next));
+ }
+
+ /* add rect to second (x-sorted) list if rect.y == y */
+ if ((cur=first.next))
+ {
+ while ((cur) && (cur->y == y))
+ {
+ first.next=cur->next;
+ cur2=&first2;
+ while ((nx2=cur2->next) && (cur->x > cur2->next->x))
+ cur2=nx2;
+ cur2->next=cur;
+ cur->next=nx2;
+ cur=first.next;
+ }
+ }
+
+
+ /*
+ * Begin writing the RISC script
+ */
+
+ dx=x=0;
+
+ /*
+ * Starting at x position 0 on a new scan line
+ * write to location p, don't yet write the number
+ * of pixels for the instruction
+ */
+
+ rpp[(*rp)++]=BT848_RISC_WRITE|BT848_RISC_SOL;
+ rpp[(*rp)++]=p;
+
+ /*
+ * For each rectangle we have in the "active" list - sorted left to
+ * right..
+ */
+
+ for (cur2=first2.next; cur2; cur2=cur2->next)
+ {
+ /*
+ * If we are to the left of the first drawing area
+ */
+
+ if (x+dx < cur2->x)
+ {
+ /* Bytes pending ? */
+ if (dx)
+ {
+ /* For a delta away from the start we need to write a SKIP */
+ if (x)
+ rpp[(*rp)++]=BT848_RISC_SKIP|(dx*depth);
+ else
+ /* Rewrite the start of line WRITE to a SKIP */
+ rpp[(*rp)-2]|=BT848_RISC_BYTE_ALL|(dx*depth);
+ /* Move X to the next point (drawing start) */
+ x=x+dx;
+ }
+ /* Ok how far are we from the start of the next rectangle ? */
+ dx=cur2->x-x;
+ /* dx is now the size of data to write */
+
+ /* If this isnt the left edge generate a "write continue" */
+ if (x)
+ rpp[(*rp)++]=BT848_RISC_WRITEC|(dx*depth)|mask;
+ else
+ /* Fill in the byte count on the initial WRITE */
+ rpp[(*rp)-2]|=(dx*depth)|mask;
+ /* Move to the start of the rectangle */
+ x=cur2->x;
+ /* x is our left dx is byte size of hole */
+ dx=cur2->width+1;
+ }
+ else
+ /* Already in a clip zone.. set dx */
+ if (x+dx < cur2->x+cur2->width)
+ dx=cur2->x+cur2->width-x+1;
+ }
+ /* now treat the rest of the line */
+ ox=x;
+ if (dx)
+ {
+ /* Complete the SKIP to eat to the end of the gap */
+ if (x)
+ rpp[(*rp)++]=BT848_RISC_SKIP|(dx*depth);
+ else
+ /* Rewrite to SKIP start to this point */
+ rpp[(*rp)-2]|=BT848_RISC_BYTE_ALL|(dx*depth);
+ x=x+dx;
+ }
+
+ /*
+ * Not at the right hand edge ?
+ */
+
+ if ((dx=btv->win.width-x)!=0)
+ {
+ /* Write to edge of display */
+ if (x)
+ rpp[(*rp)++]=BT848_RISC_WRITEC|(dx*depth)|BT848_RISC_EOL|mask;
+ else
+ /* Entire frame is a write - patch first order */
+ rpp[(*rp)-2]|=(dx*depth)|BT848_RISC_EOL|mask;
+ }
+ else
+ {
+ /* End of line if needed */
+ if (ox)
+ rpp[(*rp)-1]|=BT848_RISC_EOL|mask;
+ else
+ {
+ /* Skip the line : write a SKIP + start/end of line marks */
+ (*rp)--;
+ rpp[(*rp)-1]=BT848_RISC_SKIP|(btv->win.width*depth)|
+ BT848_RISC_EOL|BT848_RISC_SOL;
+ }
+ }
+ /*
+ * Move the video render pointer on a line
+ */
+ if (interlace||(y&1))
+ p+=btv->win.bpl;
+ }
+
+ /*
+ * Attach the interframe jumps
+ */
+
+ rmem[rpo++]=BT848_RISC_JUMP;
+ rmem[rpo++]=btv->bus_vbi_even;
+
+ rmem2[rpe++]=BT848_RISC_JUMP;
+ rmem2[rpe++]=btv->bus_vbi_odd;
+}
+
+/*
+ * Helper for adding clips.
+ */
+
+static void new_risc_clip(struct video_window *vw, struct video_clip *vcp, int x, int y, int w, int h)
+{
+ vcp[vw->clipcount].x=x;
+ vcp[vw->clipcount].y=y;
+ vcp[vw->clipcount].width=w;
+ vcp[vw->clipcount].height=h;
+ vw->clipcount++;
+}
+
+/*
+ * ioctl routine
+ */
+
+static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+ unsigned char eedata[256];
+/* unsigned long data;*/
+/* static ushort creg;*/
+ struct bttv *btv=(struct bttv *)dev;
+ static int lastchan=0;
+
+ switch (cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability b;
+ strcpy(b.name,btv->video_dev.name);
+ b.type = VID_TYPE_CAPTURE|
+ VID_TYPE_TUNER|
+ VID_TYPE_TELETEXT|
+ VID_TYPE_OVERLAY|
+ VID_TYPE_CLIPPING|
+ VID_TYPE_FRAMERAM|
+ VID_TYPE_SCALES;
+ b.channels = 4; /* tv , input, svhs */
+ b.audios = 4; /* tv, input, svhs */
+ b.maxwidth = 768;
+ b.maxheight = 576;
+ b.minwidth = 32;
+ b.minheight = 32;
+ if(copy_to_user(arg,&b,sizeof(b)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCGCHAN:
+ {
+ struct video_channel v;
+ if(copy_from_user(&v, arg,sizeof(v)))
+ return -EFAULT;
+ v.flags=VIDEO_VC_AUDIO;
+ v.tuners=0;
+ v.type=VIDEO_TYPE_CAMERA;
+ switch(v.channel)
+ {
+ case 0:
+ strcpy(v.name,"Television");
+ v.flags|=VIDEO_VC_TUNER;
+ v.type=VIDEO_TYPE_TV;
+ v.tuners=1;
+ break;
+ case 1:
+ strcpy(v.name,"Composite1");
+ break;
+ case 2:
+ strcpy(v.name,"Composite2");
+ break;
+ case 3:
+ strcpy(v.name,"SVHS");
+ break;
+ default:
+ return -EINVAL;
+ }
+ if(copy_to_user(arg,&v,sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ /*
+ * Each channel has 1 tuner
+ */
+ case VIDIOCSCHAN:
+ {
+ int v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ bt848_muxsel(btv, v);
+ lastchan=v;
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner v;
+ if(copy_from_user(&v,arg,sizeof(v))!=0)
+ return -EFAULT;
+ if(v.tuner||lastchan) /* Only tuner 0 */
+ return -EINVAL;
+ strcpy(v.name, "Television");
+ v.rangelow=0;
+ v.rangehigh=0xFFFFFFFF;
+ v.flags=VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC;
+ v.mode = btv->win.norm;
+ if(copy_to_user(arg,&v,sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ /* We have but tuner 0 */
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ /* Only channel 0 has a tuner */
+ if(v.tuner!=0 || lastchan)
+ return -EINVAL;
+ if(v.mode!=VIDEO_MODE_PAL||v.mode!=VIDEO_MODE_NTSC)
+ return -EOPNOTSUPP;
+ btv->win.norm = v.mode;
+ bt848_set_size(btv);
+ return 0;
+ }
+ case VIDIOCGPICT:
+ {
+ struct video_picture p=btv->picture;
+ if(btv->win.bpp==8)
+ p.palette=VIDEO_PALETTE_HI240;
+ if(btv->win.bpp==16)
+ p.palette=VIDEO_PALETTE_RGB565;
+ if(btv->win.bpp==24)
+ p.palette=VIDEO_PALETTE_RGB24;
+ if(btv->win.bpp==32)
+ p.palette=VIDEO_PALETTE_RGB32;
+ if(copy_to_user(arg, &p, sizeof(p)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSPICT:
+ {
+ struct video_picture p;
+ if(copy_from_user(&p, arg,sizeof(p)))
+ return -EFAULT;
+ /* We want -128 to 127 we get 0-65535 */
+ bt848_bright(btv, (p.brightness>>8)-128);
+ /* 0-511 for the colour */
+ bt848_sat_u(btv, p.colour>>7);
+ bt848_sat_v(btv, ((p.colour>>7)*201L)/237);
+ /* -128 to 127 */
+ bt848_hue(btv, (p.hue>>8)-128);
+ /* 0-511 */
+ bt848_contrast(btv, p.contrast>>7);
+ btv->picture=p;
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window vw;
+ struct video_clip *vcp;
+ int on;
+
+ if(copy_from_user(&vw,arg,sizeof(vw)))
+ return -EFAULT;
+
+ if(vw.flags)
+ return -EINVAL;
+
+ btv->win.x=vw.x;
+ btv->win.y=vw.y;
+ btv->win.width=vw.width;
+ btv->win.height=vw.height;
+
+ if(btv->win.height>btv->win.cropheight/2)
+ btv->win.interlace=1;
+ else
+ btv->win.interlace=0;
+
+ on=(btv->cap&3)?1:0;
+
+ bt848_cap(btv,0);
+ bt848_set_size(btv);
+
+ if(vw.clipcount>256)
+ return -EDOM; /* Too many! */
+
+ /*
+ * Do any clips.
+ */
+
+ vcp=vmalloc(sizeof(struct video_clip)*(vw.clipcount+4));
+ if(vcp==NULL)
+ return -ENOMEM;
+ if(vw.clipcount && copy_from_user(vcp,vw.clips,sizeof(struct video_clip)*vw.clipcount))
+ return -EFAULT;
+ /*
+ * Impose display clips
+ */
+ if(btv->win.x<0)
+ new_risc_clip(&vw, vcp, 0, 0, -btv->win.x, btv->win.height-1);
+ if(btv->win.y<0)
+ new_risc_clip(&vw, vcp, 0, 0, btv->win.width-1,-btv->win.y);
+ if(btv->win.x+btv->win.width> btv->win.swidth)
+ new_risc_clip(&vw, vcp, btv->win.swidth-btv->win.x, 0, btv->win.width-1, btv->win.height-1);
+ if(btv->win.y+btv->win.height > btv->win.sheight)
+ new_risc_clip(&vw, vcp, 0, btv->win.sheight-btv->win.y, btv->win.width-1, btv->win.height-1);
+ /*
+ * Question: Do we need to walk the clip list
+ * and saw off any clips outside the window
+ * frame or will write_risc_tab do the right
+ * thing ?
+ */
+ write_risc_data(btv,vcp, vw.clipcount);
+ vfree(vcp);
+ if(on)
+ bt848_cap(btv,1);
+ return 0;
+ }
+ case VIDIOCGWIN:
+ {
+ struct video_window vw;
+ /* Oh for a COBOL move corresponding .. */
+ vw.x=btv->win.x;
+ vw.y=btv->win.y;
+ vw.width=btv->win.width;
+ vw.height=btv->win.height;
+ vw.chromakey=0;
+ vw.flags=0;
+ if(btv->win.interlace)
+ vw.flags|=VIDEO_WINDOW_INTERLACE;
+ if(copy_to_user(arg,&vw,sizeof(vw)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCCAPTURE:
+ {
+ int v;
+ if(copy_from_user(&v, arg,sizeof(v)))
+ return -EFAULT;
+ if(btv->win.vidadr==0 || btv->win.width==0 || btv->win.height==0)
+ return -EINVAL;
+ if(v==0)
+ {
+ bt848_cap(btv,0);
+ }
+ else
+ {
+ bt848_cap(btv,1);
+ }
+ return 0;
+ }
+ case VIDIOCGFBUF:
+ {
+ struct video_buffer v;
+ v.base=(void *)btv->win.vidadr;
+ v.height=btv->win.sheight;
+ v.width=btv->win.swidth;
+ v.depth=btv->win.bpp*8;
+ v.bytesperline=btv->win.bpl;
+ if(copy_to_user(arg, &v,sizeof(v)))
+ return -EFAULT;
+ return 0;
+
+ }
+ case VIDIOCSFBUF:
+ {
+ struct video_buffer v;
+ if(!suser())
+ return -EPERM;
+ if(copy_from_user(&v, arg,sizeof(v)))
+ return -EFAULT;
+ if(v.depth!=8 && v.depth!=16 && v.depth!=24 && v.depth!=32)
+ return -EINVAL;
+ btv->win.vidadr=(int)v.base;
+ btv->win.sheight=v.height;
+ btv->win.swidth=v.width;
+ btv->win.bpp=v.depth/8;
+ btv->win.bpl=v.bytesperline;
+
+ DEBUG(printk("Display at %p is %d by %d, bytedepth %d, bpl %d\n",
+ v.base, v.width,v.height, btv->win.bpp, btv->win.bpl));
+ bt848_set_size(btv);
+ return 0;
+ }
+ case VIDIOCKEY:
+ {
+ /* Will be handled higher up .. */
+ return 0;
+ }
+ case VIDIOCGFREQ:
+ {
+ unsigned long v=btv->win.freq;
+ if(copy_to_user(arg,&v,sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSFREQ:
+ {
+ unsigned long v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ btv->win.freq=v;
+ set_freq(btv, btv->win.freq);
+ return 0;
+ }
+
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio vp;
+ vp=btv->audio_dev;
+ vp.flags&=~(VIDEO_AUDIO_MUTE|VIDEO_AUDIO_MUTABLE);
+ vp.flags|=VIDEO_AUDIO_MUTABLE;
+ return 0;
+ }
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio v;
+ if(copy_from_user(&v,arg, sizeof(v)))
+ return -EFAULT;
+ if(v.flags&VIDEO_AUDIO_MUTE)
+ audio(btv, AUDIO_MUTE);
+ if(v.audio<0||v.audio>2)
+ return -EINVAL;
+ bt848_muxsel(btv,v.audio);
+ if(!(v.flags&VIDEO_AUDIO_MUTE))
+ audio(btv, AUDIO_UNMUTE);
+ btv->audio_dev=v;
+ return 0;
+ }
+
+ case BTTV_WRITEEE:
+ if(copy_from_user((void *) eedata, (void *) arg, 256))
+ return -EFAULT;
+ writeee(btv, eedata);
+ break;
+
+ case BTTV_READEE:
+ readee(btv, eedata);
+ if(copy_to_user((void *) arg, (void *) eedata, 256))
+ return -EFAULT;
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int bttv_init_done(struct video_device *dev)
+{
+ return 0;
+}
+
+static struct video_device bttv_template=
+{
+ "UNSET",
+ VID_TYPE_TUNER|VID_TYPE_CAPTURE|VID_TYPE_OVERLAY|VID_TYPE_TELETEXT,
+ VID_HARDWARE_BT848,
+ bttv_open,
+ bttv_close,
+ bttv_read,
+ bttv_write,
+ bttv_ioctl,
+ NULL, /* no mmap yet */
+ bttv_init_done,
+ NULL,
+ 0,
+ 0
+};
+
+struct vidbases
+{
+ ushort vendor, device;
+ char *name;
+ uint badr;
+};
+
+static struct vidbases vbs[] = {
+ { PCI_VENDOR_ID_TSENG, 0, "TSENG", PCI_BASE_ADDRESS_0},
+ { PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL,
+ "Matrox Millennium", PCI_BASE_ADDRESS_1},
+ { PCI_VENDOR_ID_MATROX, 0x051a, "Matrox Mystique", PCI_BASE_ADDRESS_1},
+ { PCI_VENDOR_ID_S3, 0, "S3", PCI_BASE_ADDRESS_0},
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_210888GX,
+ "ATI MACH64 Winturbo", PCI_BASE_ADDRESS_0},
+ { PCI_VENDOR_ID_CIRRUS, 0, "Cirrus Logic", PCI_BASE_ADDRESS_0},
+ { PCI_VENDOR_ID_N9, PCI_DEVICE_ID_N9_I128,
+ "Number Nine Imagine 128", PCI_BASE_ADDRESS_0},
+ { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA,
+ "DEC DC21030", PCI_BASE_ADDRESS_0},
+};
+
+
+/* DEC TGA offsets stolen from XFree-3.2 */
+
+static uint dec_offsets[4] = {
+ 0x200000,
+ 0x804000,
+ 0,
+ 0x1004000
+};
+
+#define NR_CARDS (sizeof(vbs)/sizeof(struct vidbases))
+
+/* Scan for PCI display adapter
+ if more than one card is present the last one is used for now */
+
+static int find_vga(void)
+{
+ unsigned int devfn, class, vendev;
+ ushort vendor, device, badr;
+ int found=0, bus=0, i, tga_type;
+ unsigned int vidadr=0;
+
+
+ for (devfn = 0; devfn < 0xff; devfn++)
+ {
+ if (PCI_FUNC(devfn) != 0)
+ continue;
+ pcibios_read_config_dword(bus, devfn, PCI_VENDOR_ID, &vendev);
+ if (vendev == 0xffffffff || vendev == 0x00000000)
+ continue;
+ pcibios_read_config_word(bus, devfn, PCI_VENDOR_ID, &vendor);
+ pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &device);
+ pcibios_read_config_dword(bus, devfn, PCI_CLASS_REVISION, &class);
+ class = class >> 16;
+/* if (class == PCI_CLASS_DISPLAY_VGA) {*/
+ if ((class>>8) == PCI_BASE_CLASS_DISPLAY ||
+ /* Number 9 GXE64Pro needs this */
+ class == PCI_CLASS_NOT_DEFINED_VGA)
+ {
+ badr=0;
+ printk(KERN_INFO "bttv: PCI display adapter: ");
+ for (i=0; i<NR_CARDS; i++)
+ {
+ if (vendor==vbs[i].vendor)
+ {
+ if (vbs[i].device)
+ if (vbs[i].device!=device)
+ continue;
+ printk("%s.\n", vbs[i].name);
+ badr=vbs[i].badr;
+ break;
+ }
+ }
+ if (!badr)
+ {
+ printk(KERN_ERR "bttv: Unknown video memory base address.\n");
+ continue;
+ }
+ pcibios_read_config_dword(bus, devfn, badr, &vidadr);
+ if (vidadr & PCI_BASE_ADDRESS_SPACE_IO)
+ {
+ printk(KERN_ERR "bttv: Memory seems to be I/O memory.\n");
+ printk(KERN_ERR "bttv: Check entry for your card type in bttv.c vidbases struct.\n");
+ continue;
+ }
+ vidadr &= PCI_BASE_ADDRESS_MEM_MASK;
+ if (!vidadr)
+ {
+ printk(KERN_ERR "bttv: Memory @ 0, must be something wrong!");
+ continue;
+ }
+
+ if (vendor==PCI_VENDOR_ID_DEC)
+ if (device==PCI_DEVICE_ID_DEC_TGA)
+ {
+ tga_type = (readl((unsigned long)vidadr) >> 12) & 0x0f;
+ if (tga_type != 0 && tga_type != 1 && tga_type != 3)
+ {
+ printk(KERN_ERR "bttv: TGA type (0x%x) unrecognized!\n", tga_type);
+ found--;
+ }
+ vidadr+=dec_offsets[tga_type];
+ }
+
+ DEBUG(printk(KERN_DEBUG "bttv: memory @ 0x%08x, ", vidadr));
+ DEBUG(printk(KERN_DEBUG "devfn: 0x%04x.\n", devfn));
+ found++;
+ }
+ }
+
+ if (vidmem)
+ {
+ vidadr=vidmem<<20;
+ printk(KERN_INFO "bttv: Video memory override: 0x%08x\n", vidadr);
+ found=1;
+ }
+ for (i=0; i<BTTV_MAX; i++)
+ bttvs[i].win.vidadr=vidadr;
+
+ return found;
+}
+
+#define TRITON_PCON 0x50
+#define TRITON_BUS_CONCURRENCY (1<<0)
+#define TRITON_STREAMING (1<<1)
+#define TRITON_WRITE_BURST (1<<2)
+#define TRITON_PEER_CONCURRENCY (1<<3)
+
+static void handle_chipset(void)
+{
+ int index;
+
+ for (index = 0; index < 8; index++)
+ {
+ unsigned char bus, devfn;
+ unsigned char b, bo;
+
+ /* nothing wrong with this one, just checking buffer control config */
+
+ if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441,
+ index, &bus, &devfn))
+ {
+ pcibios_read_config_byte(bus, devfn, 0x53, &b);
+ DEBUG(printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, "));
+ DEBUG(printk("bufcon=0x%02x\n",b));
+ }
+
+ if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437,
+ index, &bus, &devfn))
+ {
+ printk(KERN_INFO "bttv: Host bridge 82437FX Triton PIIX\n");
+ pcibios_read_config_byte(bus, devfn, TRITON_PCON, &b);
+ bo=b;
+ DEBUG(printk(KERN_DEBUG "bttv: 82437FX: PCON: 0x%x\n",b));
+
+ /* 430FX (Triton I) freezes with bus concurrency on -> switch it off */
+ if(!(b & TRITON_BUS_CONCURRENCY))
+ {
+ printk(KERN_WARNING "bttv: 82437FX: disabling bus concurrency\n");
+ b |= TRITON_BUS_CONCURRENCY;
+ }
+
+ /* still freezes on other boards -> switch off even more */
+ if(b & TRITON_PEER_CONCURRENCY)
+ {
+ printk(KERN_WARNING "bttv: 82437FX: disabling peer concurrency\n");
+ b &= ~TRITON_PEER_CONCURRENCY;
+ }
+ if(!(b & TRITON_STREAMING))
+ {
+ printk(KERN_WARNING "bttv: 82437FX: disabling streaming\n");
+ b |= TRITON_STREAMING;
+ }
+
+ if (b!=bo)
+ {
+ pcibios_write_config_byte(bus, devfn, TRITON_PCON, b);
+ printk(KERN_DEBUG "bttv: 82437FX: PCON changed to: 0x%x\n",b);
+ }
+ }
+ }
+}
+
+static void init_tda9850(struct bttv *btv)
+{
+ I2CWrite(btv, I2C_TDA9850, TDA9850_CON3, 0, 1);
+}
+
+/* Figure out card and tuner type */
+
+static void idcard(struct bttv *btv)
+{
+ int i;
+
+ btwrite(0, BT848_GPIO_OUT_EN);
+ DEBUG(printk(KERN_DEBUG "bttv: GPIO: 0x%08x\n", btread(BT848_GPIO_DATA)));
+
+ btv->type=BTTV_MIRO;
+ btv->tuner=tuner;
+
+ if (I2CRead(btv, I2C_HAUPEE)>=0)
+ btv->type=BTTV_HAUPPAUGE;
+ else if (I2CRead(btv, I2C_STBEE)>=0)
+ btv->type=BTTV_STB;
+
+ for (i=0xc0; i<0xd0; i+=2)
+ {
+ if (I2CRead(btv, i)>=0)
+ {
+ btv->tuneradr=i;
+ break;
+ }
+ }
+
+ btv->dbx = I2CRead(btv, I2C_TDA9850) ? 0 : 1;
+
+ if (btv->dbx)
+ init_tda9850(btv);
+
+ /* How do I detect the tuner type for other cards but Miro ??? */
+ printk(KERN_INFO "bttv: model: ");
+ switch (btv->type)
+ {
+ case BTTV_MIRO:
+ btv->tuner=((btread(BT848_GPIO_DATA)>>10)-1)&7;
+ printk("MIRO");
+ strcpy(btv->video_dev.name,"BT848(Miro)");
+ break;
+ case BTTV_HAUPPAUGE:
+ printk("HAUPPAUGE");
+ strcpy(btv->video_dev.name,"BT848(Hauppauge)");
+ break;
+ case BTTV_STB:
+ printk("STB");
+ strcpy(btv->video_dev.name,"BT848(STB)");
+ break;
+ case BTTV_INTEL:
+ printk("Intel");
+ strcpy(btv->video_dev.name,"BT848(Intel)");
+ break;
+ case BTTV_DIAMOND:
+ printk("Diamond");
+ strcpy(btv->video_dev.name,"BT848(Diamond)");
+ break;
+ }
+ printk(" (%s @ 0x%02x)\n", tuners[btv->tuner].name, btv->tuneradr);
+ audio(btv, AUDIO_MUTE);
+}
+
+
+static void bt848_set_risc_jmps(struct bttv *btv)
+{
+ int flags=btv->cap;
+
+ btv->risc_jmp[0]=BT848_RISC_SYNC|BT848_RISC_RESYNC|BT848_FIFO_STATUS_VRE;
+ btv->risc_jmp[1]=0;
+
+ btv->risc_jmp[2]=BT848_RISC_JUMP;
+ if (flags&8)
+ btv->risc_jmp[3]=virt_to_bus(btv->vbi_odd);
+ else
+ btv->risc_jmp[3]=virt_to_bus(btv->risc_jmp+4);
+
+ btv->risc_jmp[4]=BT848_RISC_JUMP;
+ if (flags&2)
+ btv->risc_jmp[5]=virt_to_bus(btv->risc_odd);
+ else
+ btv->risc_jmp[5]=virt_to_bus(btv->risc_jmp+6);
+
+ btv->risc_jmp[6]=BT848_RISC_SYNC|BT848_RISC_RESYNC|BT848_FIFO_STATUS_VRO;
+ btv->risc_jmp[7]=0;
+
+ btv->risc_jmp[8]=BT848_RISC_JUMP;
+ if (flags&4)
+ btv->risc_jmp[9]=virt_to_bus(btv->vbi_even);
+ else
+ btv->risc_jmp[9]=virt_to_bus(btv->risc_jmp+10);
+
+ btv->risc_jmp[10]=BT848_RISC_JUMP;
+ if (flags&1)
+ btv->risc_jmp[11]=virt_to_bus(btv->risc_even);
+ else
+ btv->risc_jmp[11]=virt_to_bus(btv->risc_jmp);
+
+ btaor(flags, ~0x0f, BT848_CAP_CTL);
+ if (flags&0x0f)
+ bt848_dma(btv, 3);
+ else
+ bt848_dma(btv, 0);
+}
+
+
+static int init_bt848(struct bttv *btv)
+{
+ /* reset the bt848 */
+ btwrite(0,BT848_SRESET);
+ btv->user=0;
+
+ DEBUG(printk(KERN_DEBUG "bttv: bt848_mem: 0x%08x\n",(unsigned int) btv->bt848_mem));
+
+ /* default setup for max. PAL size in a 1024xXXX hicolor framebuffer */
+
+ btv->win.norm=0; /* change this to 1 for NTSC, 2 for SECAM */
+ btv->win.interlace=1;
+ btv->win.x=0;
+ btv->win.y=0;
+ btv->win.width=768; /* 640 */
+ btv->win.height=576; /* 480 */
+ btv->win.cropwidth=768; /* 640 */
+ btv->win.cropheight=576; /* 480 */
+ btv->win.cropx=0;
+ btv->win.cropy=0;
+ btv->win.bpp=2;
+ btv->win.bpl=1024*btv->win.bpp;
+ btv->win.swidth=1024;
+ btv->win.sheight=768;
+ btv->cap=0;
+
+ if (!(btv->risc_odd=(dword *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL)))
+ return -1;
+ if (!(btv->risc_even=(dword *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL)))
+ return -1;
+ if (!(btv->risc_jmp =(dword *) kmalloc(1024, GFP_KERNEL)))
+ return -1;
+ btv->vbi_odd=btv->risc_jmp+12;
+ btv->vbi_even=btv->vbi_odd+256;
+ btv->bus_vbi_odd=virt_to_bus(btv->risc_jmp);
+ btv->bus_vbi_even=virt_to_bus(btv->risc_jmp+6);
+
+ btwrite(virt_to_bus(btv->risc_jmp+2), BT848_RISC_STRT_ADD);
+ btv->vbibuf=(unchar *) vmalloc(VBIBUF_SIZE);
+ if (!btv->vbibuf)
+ return -1;
+
+ bt848_muxsel(btv, 1);
+ bt848_set_size(btv);
+
+/* btwrite(0, BT848_TDEC); */
+ btwrite(0x10, BT848_COLOR_CTL);
+ btwrite(0x00, BT848_CAP_CTL);
+
+ btwrite(0x0ff, BT848_VBI_PACK_SIZE);
+ btwrite(1, BT848_VBI_PACK_DEL);
+
+ btwrite(0xfc, BT848_GPIO_DMA_CTL);
+ btwrite(BT848_IFORM_MUX1 | BT848_IFORM_XTAUTO | BT848_IFORM_PAL_BDGHI,
+ BT848_IFORM);
+
+ bt848_bright(btv, 0x10);
+ btwrite(0xd8, BT848_CONTRAST_LO);
+
+ btwrite(0x60, BT848_E_VSCALE_HI);
+ btwrite(0x60, BT848_O_VSCALE_HI);
+ btwrite(/*BT848_ADC_SYNC_T|*/
+ BT848_ADC_RESERVED|BT848_ADC_CRUSH, BT848_ADC);
+
+ btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL);
+ btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL);
+ btwrite(0x00, BT848_E_SCLOOP);
+ btwrite(0x00, BT848_O_SCLOOP);
+
+ btwrite(0xffffffUL,BT848_INT_STAT);
+/* BT848_INT_PABORT|BT848_INT_RIPERR|BT848_INT_PPERR|BT848_INT_FDSR|
+ BT848_INT_FTRGT|BT848_INT_FBUS|*/
+ btwrite(BT848_INT_ETBF|
+ BT848_INT_SCERR|
+ BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES|
+ BT848_INT_FMTCHG|BT848_INT_HLOCK,
+ BT848_INT_MASK);
+
+/* make_risctab(btv); */
+ make_vbitab(btv);
+ bt848_set_risc_jmps(btv);
+
+ /*
+ * Now add the template and register the device unit.
+ */
+
+ memcpy(&btv->video_dev,&bttv_template,sizeof(bttv_template));
+ idcard(btv);
+ if(video_register_device(&btv->video_dev)<0)
+ return -1;
+ return 0;
+}
+
+
+static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs)
+{
+ u32 stat,astat;
+ u32 dstat;
+ int count;
+ struct bttv *btv;
+
+ btv=(struct bttv *)dev_id;
+ count=0;
+ while (1)
+ {
+ /* get/clear interrupt status bits */
+ stat=btread(BT848_INT_STAT);
+ astat=stat&btread(BT848_INT_MASK);
+ if (!astat)
+ return;
+ btwrite(astat,BT848_INT_STAT);
+ IDEBUG(printk ("bttv: astat %08x\n",astat));
+ IDEBUG(printk ("bttv: stat %08x\n",stat));
+
+ /* get device status bits */
+ dstat=btread(BT848_DSTATUS);
+
+ if (astat&BT848_INT_FMTCHG)
+ {
+ IDEBUG(printk ("bttv: IRQ_FMTCHG\n"));
+/* btv->win.norm&=(dstat&BT848_DSTATUS_NUML) ? (~1) : (~0); */
+ }
+ if (astat&BT848_INT_VPRES)
+ {
+ IDEBUG(printk ("bttv: IRQ_VPRES\n"));
+ }
+ if (astat&BT848_INT_VSYNC)
+ {
+ IDEBUG(printk ("bttv: IRQ_VSYNC\n"));
+ }
+ if (astat&BT848_INT_SCERR) {
+ IDEBUG(printk ("bttv: IRQ_SCERR\n"));
+ bt848_dma(btv, 0);
+ bt848_dma(btv, 1);
+ wake_up_interruptible(&btv->vbiq);
+ wake_up_interruptible(&btv->capq);
+ }
+ if (astat&BT848_INT_RISCI)
+ {
+ IDEBUG(printk ("bttv: IRQ_RISCI\n"));
+ /* printk ("bttv: IRQ_RISCI%d\n",stat>>28); */
+ if (stat&(1<<28))
+ {
+ btv->vbip=0;
+ wake_up_interruptible(&btv->vbiq);
+ }
+ if (stat&(2<<28))
+ {
+ bt848_set_risc_jmps(btv);
+ wake_up_interruptible(&btv->capq);
+ break;
+ }
+ }
+ if (astat&BT848_INT_OCERR)
+ {
+ IDEBUG(printk ("bttv: IRQ_OCERR\n"));
+ }
+ if (astat&BT848_INT_PABORT)
+ {
+ IDEBUG(printk ("bttv: IRQ_PABORT\n"));
+ }
+ if (astat&BT848_INT_RIPERR)
+ {
+ IDEBUG(printk ("bttv: IRQ_RIPERR\n"));
+ }
+ if (astat&BT848_INT_PPERR)
+ {
+ IDEBUG(printk ("bttv: IRQ_PPERR\n"));
+ }
+ if (astat&BT848_INT_FDSR)
+ {
+ IDEBUG(printk ("bttv: IRQ_FDSR\n"));
+ }
+ if (astat&BT848_INT_FTRGT)
+ {
+ IDEBUG(printk ("bttv: IRQ_FTRGT\n"));
+ }
+ if (astat&BT848_INT_FBUS)
+ {
+ IDEBUG(printk ("bttv: IRQ_FBUS\n"));
+ }
+ if (astat&BT848_INT_HLOCK)
+ {
+ if (dstat&BT848_DSTATUS_HLOC)
+ audio(btv, AUDIO_ON);
+ else
+ audio(btv, AUDIO_OFF);
+ }
+
+ if (astat&BT848_INT_I2CDONE)
+ {
+ }
+
+ count++;
+ if (count > 10)
+ printk (KERN_WARNING "bttv: irq loop %d\n", count);
+ if (count > 20)
+ {
+ btwrite(0, BT848_INT_MASK);
+ printk(KERN_ERR "bttv: IRQ lockup, cleared int mask\n");
+ }
+ }
+}
+
+
+/*
+ * Scan for a Bt848 card, request the irq and map the io memory
+ */
+
+static int find_bt848(void)
+{
+ short pci_index;
+ unsigned char command, latency;
+ int result;
+ unsigned char bus, devfn;
+ struct bttv *btv;
+
+ bttv_num=0;
+
+ if (!pcibios_present())
+ {
+ DEBUG(printk(KERN_DEBUG "bttv: PCI-BIOS not present or not accessable!\n"));
+ return 0;
+ }
+
+ for (pci_index = 0;
+ !pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848,
+ pci_index, &bus, &devfn);
+ ++pci_index)
+ {
+ btv=&bttvs[bttv_num];
+ btv->bus=bus;
+ btv->devfn=devfn;
+ btv->bt848_mem=NULL;
+ btv->vbibuf=NULL;
+ btv->risc_jmp=NULL;
+ btv->vbi_odd=NULL;
+ btv->vbi_even=NULL;
+ btv->vbiq=NULL;
+ btv->capq=NULL;
+ btv->vbip=VBIBUF_SIZE;
+
+ pcibios_read_config_byte(btv->bus, btv->devfn,
+ PCI_INTERRUPT_LINE, &btv->irq);
+ pcibios_read_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0,
+ &btv->bt848_adr);
+
+ if (remap&&(!bttv_num))
+ {
+ remap<<=20;
+ remap&=PCI_BASE_ADDRESS_MEM_MASK;
+ printk(KERN_INFO "Remapping to : 0x%08x.\n", remap);
+ remap|=btv->bt848_adr&(~PCI_BASE_ADDRESS_MEM_MASK);
+ pcibios_write_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0,
+ remap);
+ pcibios_read_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0,
+ &btv->bt848_adr);
+ }
+
+ btv->bt848_adr&=PCI_BASE_ADDRESS_MEM_MASK;
+ pcibios_read_config_byte(btv->bus, btv->devfn, PCI_CLASS_REVISION,
+ &btv->revision);
+ printk(KERN_INFO "bttv: Brooktree Bt848 (rev %d) ",btv->revision);
+ printk("bus: %d, devfn: %d, ",
+ btv->bus, btv->devfn);
+ printk("irq: %d, ",btv->irq);
+ printk("memory: 0x%08x.\n", btv->bt848_adr);
+
+ btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000);
+
+ result = request_irq(btv->irq, bttv_irq,
+ SA_SHIRQ | SA_INTERRUPT,"bttv",(void *)btv);
+ if (result==-EINVAL)
+ {
+ printk(KERN_ERR "bttv: Bad irq number or handler\n");
+ return -EINVAL;
+ }
+ if (result==-EBUSY)
+ {
+ printk(KERN_ERR "bttv: IRQ %d busy, change your PnP config in BIOS\n",btv->irq);
+ return result;
+ }
+ if (result < 0)
+ return result;
+
+ /* Enable bus-mastering */
+ pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command);
+ command|=PCI_COMMAND_MASTER;
+ pcibios_write_config_byte(btv->bus, btv->devfn, PCI_COMMAND, command);
+ pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command);
+ if (!(command&PCI_COMMAND_MASTER))
+ {
+ printk(KERN_ERR "bttv: PCI bus-mastering could not be enabled\n");
+ return -1;
+ }
+ pcibios_read_config_byte(btv->bus, btv->devfn, PCI_LATENCY_TIMER,
+ &latency);
+ if (!latency)
+ {
+ latency=32;
+ pcibios_write_config_byte(btv->bus, btv->devfn, PCI_LATENCY_TIMER,
+ latency);
+ }
+ DEBUG(printk(KERN_DEBUG "bttv: latency: %02x\n", latency));
+ bttv_num++;
+ }
+ if(bttv_num)
+ printk(KERN_INFO "bttv: %d Bt848 card(s) found.\n", bttv_num);
+ return bttv_num;
+}
+
+static void release_bttv(void)
+{
+ u8 command;
+ int i;
+ struct bttv *btv;
+
+ for (i=0;i<bttv_num; i++)
+ {
+ btv=&bttvs[i];
+ /* turn off all capturing, DMA and IRQs */
+
+ /* first disable interrupts before unmapping the memory! */
+ btwrite(0, BT848_INT_MASK);
+ btwrite(0xffffffffUL,BT848_INT_STAT);
+ btwrite(0x0, BT848_GPIO_OUT_EN);
+
+ bt848_cap(btv, 0);
+
+ /* disable PCI bus-mastering */
+ pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command);
+ command|=PCI_COMMAND_MASTER;
+ pcibios_write_config_byte(btv->bus, btv->devfn, PCI_COMMAND, command);
+
+ /* unmap and free memory */
+ if (btv->risc_odd)
+ kfree((void *) btv->risc_odd);
+
+ if (btv->risc_even)
+ kfree((void *) btv->risc_even);
+
+ DEBUG(printk(KERN_DEBUG "free: risc_jmp: 0x%08x.\n", btv->risc_jmp));
+ if (btv->risc_jmp)
+ kfree((void *) btv->risc_jmp);
+
+ DEBUG(printk(KERN_DEBUG "bt848_vbibuf: 0x%08x.\n", btv->vbibuf));
+ if (btv->vbibuf)
+ vfree((void *) btv->vbibuf);
+ free_irq(btv->irq,btv);
+ DEBUG(printk(KERN_DEBUG "bt848_mem: 0x%08x.\n", btv->bt848_mem));
+ if (btv->bt848_mem)
+ iounmap(btv->bt848_mem);
+ video_unregister_device(&btv->video_dev);
+ }
+}
+
+
+#ifdef MODULE
+
+int init_module(void)
+{
+#else
+int init_bttv_cards(struct video_init *unused)
+{
+#endif
+ int i;
+
+ handle_chipset();
+ if (find_bt848()<0)
+ return -EIO;
+
+ for (i=0; i<bttv_num; i++)
+ {
+ if (init_bt848(&bttvs[i])<0)
+ {
+ release_bttv();
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+#ifdef MODULE
+
+void cleanup_module(void)
+{
+ release_bttv();
+}
+
+#endif
diff --git a/drivers/char/bttv.h b/drivers/char/bttv.h
new file mode 100644
index 000000000..e1c54e94c
--- /dev/null
+++ b/drivers/char/bttv.h
@@ -0,0 +1,162 @@
+/*
+ bttv - Bt848 frame grabber driver
+
+ Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _BTTV_H_
+#define _BTTV_H_
+
+#define TEST_VBI
+
+#include <linux/types.h>
+#include <linux/wait.h>
+
+#include "bt848.h"
+
+typedef unsigned int dword;
+
+struct riscprog {
+ uint length;
+ dword *busadr;
+ dword *prog;
+};
+
+/* values that can be set by user programs */
+
+struct bttv_window {
+ int x, y;
+ ushort width, height;
+ ushort bpp, bpl;
+ ushort swidth, sheight;
+ short cropx, cropy;
+ ushort cropwidth, cropheight;
+ int vidadr;
+ ushort freq;
+ int norm;
+ int interlace;
+ int color_fmt;
+};
+
+/* private data that can only be read (or set indirectly) by user program */
+
+struct bttv {
+ struct video_device video_dev;
+ struct video_picture picture; /* Current picture params */
+ struct video_audio audio_dev; /* Current audio params */
+ u_char bus; /* PCI bus the Bt848 is on */
+ u_char devfn;
+ u_char revision;
+ u_char irq; /* IRQ used by Bt848 card */
+ uint bt848_adr; /* bus address of IO mem returned by PCI BIOS */
+ u_char *bt848_mem; /* pointer to mapped IO memory */
+ ulong busriscmem;
+ dword *riscmem;
+
+ u_char *vbibuf;
+ struct bttv_window win;
+ int type; /* card type */
+ int audio; /* audio mode */
+ int user;
+ int tuner;
+ int tuneradr;
+ int dbx;
+
+ dword *risc_jmp;
+ dword *vbi_odd;
+ dword *vbi_even;
+ dword bus_vbi_even;
+ dword bus_vbi_odd;
+ struct wait_queue *vbiq;
+ struct wait_queue *capq;
+ int vbip;
+
+ dword *risc_odd;
+ dword *risc_even;
+ int cap;
+};
+
+/*The following should be done in more portable way. It depends on define
+ of _ALPHA_BTTV in the Makefile.*/
+#ifdef _ALPHA_BTTV
+#define btwrite(dat,adr) writel((dat),(char *) (btv->bt848_adr+(adr)))
+#define btread(adr) readl(btv->bt848_adr+(adr))
+#else
+#define btwrite(dat,adr) writel((dat), (char *) (btv->bt848_mem+(adr)))
+#define btread(adr) readl(btv->bt848_mem+(adr))
+#endif
+
+#define btand(dat,adr) btwrite((dat) & btread(adr), adr)
+#define btor(dat,adr) btwrite((dat) | btread(adr), adr)
+#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
+
+/* bttv ioctls */
+#define BTTV_WRITE_BTREG 0x00
+#define BTTV_READ_BTREG 0x01
+#define BTTV_SET_BTREG 0x02
+#define BTTV_SETRISC 0x03
+#define BTTV_SETWTW 0x04
+#define BTTV_GETWTW 0x05
+#define BTTV_DMA 0x06
+#define BTTV_CAP_OFF 0x07
+#define BTTV_CAP_ON 0x08
+#define BTTV_GETBTTV 0x09
+#define BTTV_SETFREQ 0x0a
+#define BTTV_SETCHAN 0x0b
+#define BTTV_INPUT 0x0c
+#define BTTV_READEE 0x0d
+#define BTTV_WRITEEE 0x0e
+#define BTTV_BRIGHT 0x0f
+#define BTTV_HUE 0x10
+#define BTTV_COLOR 0x11
+#define BTTV_CONTRAST 0x12
+#define BTTV_SET_FFREQ 0x13
+#define BTTV_MUTE 0x14
+
+#define BTTV_GRAB 0x20
+#define BTTV_TESTM 0x20
+
+
+#define BTTV_UNKNOWN 0x00
+#define BTTV_MIRO 0x01
+#define BTTV_HAUPPAUGE 0x02
+#define BTTV_STB 0x03
+#define BTTV_INTEL 0x04
+#define BTTV_DIAMOND 0x05
+
+#define AUDIO_TUNER 0x00
+#define AUDIO_EXTERN 0x01
+#define AUDIO_INTERN 0x02
+#define AUDIO_OFF 0x03
+#define AUDIO_ON 0x04
+#define AUDIO_MUTE 0x80
+#define AUDIO_UNMUTE 0x81
+
+#define I2C_TSA5522 0xc2
+#define I2C_TDA9850 0xb6
+#define I2C_HAUPEE 0xa0
+#define I2C_STBEE 0xae
+
+#define TDA9850_CON1 0x04
+#define TDA9850_CON2 0x05
+#define TDA9850_CON3 0x06
+#define TDA9850_CON4 0x07
+#define TDA9850_ALI1 0x08
+#define TDA9850_ALI2 0x09
+#define TDA9850_ALI3 0x0a
+
+#endif
diff --git a/drivers/char/busmouse.c b/drivers/char/busmouse.c
index 9e160cb93..a33a6a8c2 100644
--- a/drivers/char/busmouse.c
+++ b/drivers/char/busmouse.c
@@ -159,8 +159,8 @@ static int open_mouse(struct inode * inode, struct file * file)
* writes are disallowed
*/
-static long write_mouse(struct inode * inode, struct file * file,
- const char * buffer, unsigned long count)
+static ssize_t write_mouse(struct file * file,
+ const char * buffer, size_t count, loff_t *ppos)
{
return -EINVAL;
}
@@ -169,8 +169,8 @@ static long write_mouse(struct inode * inode, struct file * file,
* read mouse data. Currently never blocks.
*/
-static long read_mouse(struct inode * inode, struct file * file,
- char * buffer, unsigned long count)
+static ssize_t read_mouse(struct file * file,
+ char * buffer, size_t count, loff_t *ppos)
{
int r;
int dx;
diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c
index fa4134dda..ed4ac9846 100644
--- a/drivers/char/cyclades.c
+++ b/drivers/char/cyclades.c
@@ -1,6 +1,6 @@
#define BLOCKMOVE
static char rcsid[] =
-"$Revision: 1.36.4.33 $$Date: 1997/06/27 19:00:00 $";
+"$Revision: 2.1 $$Date: 1997/11/01 17:42:41 $";
/*
* linux/drivers/char/cyclades.c
@@ -8,8 +8,9 @@ static char rcsid[] =
* This file contains the driver for the Cyclades Cyclom-Y multiport
* serial boards.
*
- * Maintained by Marcio Saito (marcio@cyclades.com) and
- * Randolph Bentson (bentson@grieg.seaslug.org)
+ * Maintained by Ivan Passos (ivan@cyclades.com),
+ * Marcio Saito (marcio@cyclades.com) and
+ * Randolph Bentson (bentson@grieg.seaslug.org).
*
* For Technical support and installation problems, please send e-mail
* to support@cyclades.com.
@@ -29,6 +30,18 @@ static char rcsid[] =
* void cleanup_module(void);
*
* $Log: cyclades.c,v $
+ * Revision 2.1 1997/11/01 17:42:41 ivan
+ * Changes in the driver to support Alpha systems (except 8Zo V_1);
+ * BREAK fix for the Cyclades-Z boards;
+ * driver inactivity control by FW implemented;
+ * introduction of flag that allows driver to take advantage of
+ * a special CD1400 feature related to HW flow control;
+ * added support for the CD1400 rev. J (Cyclom-Y boards);
+ * introduction of ioctls to:
+ * - control the rtsdtr_inv flag (Cyclom-Y);
+ * - control the rflow flag (Cyclom-Y);
+ * - adjust the polling interval (Cyclades-Z);
+ *
* Revision 1.36.4.33 1997/06/27 19:00:00 ivan
* Fixes related to kernel version conditional
* compilation.
@@ -435,7 +448,6 @@ static char rcsid[] =
constant in the definition below. No other change is necessary to
support more boards/ports. */
-//#define NR_PORTS 64
#define NR_PORTS 128
#define ZE_V1_NPORTS 64
@@ -463,18 +475,24 @@ static char rcsid[] =
#define cy_min(a,b) (((a)<(b))?(a):(b))
+#if 0
+/********
+ * For the next two macros, it is assumed that the buffer size is a
+ * power of 2
+ ********/
+
#define CHARS_IN_BUF(buf_ctrl) \
- ((buf_ctrl->rx_put - \
- buf_ctrl->rx_get + \
- buf_ctrl->rx_bufsize) % \
- buf_ctrl->rx_bufsize)
+ ((cy_readl(&buf_ctrl->rx_put) - \
+ cy_readl(&buf_ctrl->rx_get) + \
+ cy_readl(&buf_ctrl->rx_bufsize)) & \
+ (cy_readl(&buf_ctrl->rx_bufsize) - 1))
#define SPACE_IN_BUF(buf_ctrl) \
- ((buf_ctrl->tx_get - \
- buf_ctrl->tx_put + \
- buf_ctrl->tx_bufsize - 1) % \
- buf_ctrl->tx_bufsize)
-
+ ((cy_readl(&buf_ctrl->tx_get) - \
+ cy_readl(&buf_ctrl->tx_put) + \
+ cy_readl(&buf_ctrl->tx_bufsize) - 1) & \
+ (cy_readl(&buf_ctrl->tx_bufsize) - 1))
+#endif
#include <linux/module.h>
@@ -540,13 +558,14 @@ static unsigned long cy_get_user(unsigned long *addr)
#define IS_CYC_Z(card) ((card).num_chips == 1)
#define Z_FPGA_CHECK(card) \
- ((((struct RUNTIME_9060 *)((card).ctl_addr))->init_ctrl&(1<<17))!=0)
+ ((cy_readl(&((struct RUNTIME_9060 *) \
+ ((card).ctl_addr))->init_ctrl) & (1<<17)) != 0)
-#define ISZLOADED(card) (((ZO_V1==((struct RUNTIME_9060 *) \
- ((card).ctl_addr))->mail_box_0) || \
+#define ISZLOADED(card) (((ZO_V1==cy_readl(&((struct RUNTIME_9060 *) \
+ ((card).ctl_addr))->mail_box_0)) || \
Z_FPGA_CHECK(card)) && \
- (ZFIRM_ID==((struct FIRM_ID *) \
- ((card).base_addr+ID_ADDRESS))->signature))
+ (ZFIRM_ID==cy_readl(&((struct FIRM_ID *) \
+ ((card).base_addr+ID_ADDRESS))->signature)))
#define WAKEUP_CHARS (SERIAL_XMIT_SIZE-256)
@@ -563,7 +582,7 @@ struct tty_driver cy_serial_driver, cy_callout_driver;
static volatile int cy_irq_triggered;
static volatile int cy_triggered;
static int cy_wild_int_mask;
-static unsigned char *intr_base_addr;
+static volatile ucchar *intr_base_addr;
/* This is the address lookup table. The driver will probe for
@@ -632,26 +651,55 @@ static struct semaphore tmp_buf_sem = MUTEX;
* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
* 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
* HI VHI
+ * 20
*/
static int baud_table[] = {
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800,115200,150000,
- 0};
+ 230400, 0};
-static char baud_co[] = { /* 25 MHz clock option table */
+static char baud_co_25[] = { /* 25 MHz clock option table */
/* value => 00 01 02 03 04 */
/* divide by 8 32 128 512 2048 */
0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02,
0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-static char baud_bpr[] = { /* 25 MHz baud rate period table */
+static char baud_bpr_25[] = { /* 25 MHz baud rate period table */
0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3,
0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15};
+static char baud_co_60[] = { /* 60 MHz clock option table (CD1400 J) */
+ /* value => 00 01 02 03 04 */
+ /* divide by 8 32 128 512 2048 */
+ 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03,
+ 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00};
+
+static char baud_bpr_60[] = { /* 60 MHz baud rate period table (CD1400 J) */
+ 0x00, 0x82, 0x21, 0xff, 0xdb, 0xc3, 0x92, 0x62, 0xc3, 0x62,
+ 0x41, 0xc3, 0x62, 0xc3, 0x62, 0xc3, 0x82, 0x62, 0x41, 0x32,
+ 0x21};
+
static char baud_cor3[] = { /* receive threshold */
0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07};
+ 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07,
+ 0x07};
+
+/*
+ * The Cyclades driver implements HW flow control as any serial driver.
+ * The cyclades_port structure member rflow and the vector rflow_thr
+ * allows us to take advantage of a special feature in the CD1400 to avoid
+ * data loss even when the system interrupt latency is too high. These flags
+ * are to be used only with very special applications. Setting these flags
+ * requires the use of a special cable (DTR and RTS reversed). In the new
+ * CD1400-based boards (rev. 6.00 or later), there is no need for special
+ * cables.
+ */
+static char rflow_thr[] = { /* rflow threshold */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a};
/* The Cyclom-Ye has placed the sequential chips in non-sequential
* address order. This look-up table overcomes that problem.
@@ -689,6 +737,8 @@ static void cyz_poll(unsigned long);
static void show_status(int);
#endif
+/* The Cyclades-Z polling cycle is defined by this variable */
+static long cyz_polling_cycle = CZ_DEF_POLL;
static int cyz_timeron = 0;
static struct timer_list
@@ -696,13 +746,12 @@ cyz_timerlist = {
NULL, NULL, 0, 0, cyz_poll
};
-
/**************************************************
error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long));
-memcpy_tofs (to, from, count);
+copy_to_user (to, from, count);
***************************************************************
error = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned long *));
-memcpy_fromfs(to, from, count);
+copy_from_user(to, from, count);
**************************************************/
@@ -873,7 +922,7 @@ do_softint(void *private_)
didn't finish within the time limit.
*/
static int
-cyy_issue_cmd(u_char *base_addr, u_char cmd, int index)
+cyy_issue_cmd(volatile ucchar *base_addr, u_char cmd, int index)
{
unsigned long flags;
volatile int i;
@@ -881,7 +930,7 @@ cyy_issue_cmd(u_char *base_addr, u_char cmd, int index)
save_flags(flags); cli();
/* Check to see that the previous command has completed */
for(i = 0 ; i < 100 ; i++){
- if (base_addr[CyCCR<<index] == 0){
+ if (cy_readb(base_addr+(CyCCR<<index)) == 0){
break;
}
udelay(10L);
@@ -894,7 +943,7 @@ cyy_issue_cmd(u_char *base_addr, u_char cmd, int index)
}
/* Issue the new command */
- base_addr[CyCCR<<index] = cmd;
+ cy_writeb((u_long)base_addr+(CyCCR<<index), cmd);
restore_flags(flags);
return(0);
} /* cyy_issue_cmd */
@@ -932,8 +981,9 @@ free_all_interrupts(int irq_lines)
int i;
for (i = 0; i < 16; i++) {
- if (irq_lines & (1 << i))
+ if (irq_lines & (1 << i)) {
free_irq(i,NULL);
+ }
}
} /* free_all_interrupts */
@@ -959,13 +1009,13 @@ check_wild_interrupts(void)
* Delay for 0.1 seconds -- we use a busy loop since this may
* occur during the bootup sequence
*/
- timeout = jiffies+10;
+ timeout = jiffies+(HZ/10);
while (timeout >= jiffies)
;
cy_triggered = 0; /* Reset after letting things settle */
- timeout = jiffies+10;
+ timeout = jiffies+(HZ/10);
while (timeout >= jiffies)
;
@@ -986,11 +1036,11 @@ check_wild_interrupts(void)
* fool-proof, but it works a large part of the time.
*/
static int
-get_auto_irq(unsigned char *address)
+get_auto_irq(volatile ucchar *address)
{
- unsigned long timeout;
- unsigned char *base_addr;
- int index;
+ unsigned long timeout;
+ volatile ucchar *base_addr;
+ int index;
index = 0; /* IRQ probing is only for ISA */
base_addr = address;
@@ -1001,13 +1051,14 @@ get_auto_irq(unsigned char *address)
*/
cy_irq_triggered = 0;
cli();
- base_addr[CyCAR<<index] = 0;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), 0);
cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR,index);
- base_addr[CySRER<<index] |= CyTxMpty;
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
probe_ready = 1;
sti();
- timeout = jiffies+2;
+ timeout = jiffies+(HZ/50);
while (timeout >= jiffies) {
if (cy_irq_triggered)
break;
@@ -1021,7 +1072,7 @@ get_auto_irq(unsigned char *address)
* faked out by random interrupts
*/
static int
-do_auto_irq(unsigned char *address)
+do_auto_irq(volatile ucchar *address)
{
int irq_lines = 0;
int irq_try_1 = 0, irq_try_2 = 0;
@@ -1065,27 +1116,28 @@ cy_probe(int irq, void *dev_id, struct pt_regs *regs)
int index = 0; /* probing interrupts is only for ISA */
if (!probe_ready) {
- *(intr_base_addr + (Cy_ClrIntr<<index)) = 0;
+ cy_writeb((u_long)intr_base_addr+(Cy_ClrIntr<<index), 0);
return;
}
cy_irq_triggered = irq;
cy_triggered |= 1 << irq;
- if(intr_base_addr[CySVRR<<index] != 0) {
- save_xir = (u_char) intr_base_addr[CyTIR<<index];
- save_car = intr_base_addr[CyCAR<<index];
+ if(cy_readb(intr_base_addr+(CySVRR<<index)) != 0) {
+ save_xir = (u_char) cy_readb(intr_base_addr+(CyTIR<<index));
+ save_car = cy_readb(intr_base_addr+(CyCAR<<index));
if ((save_xir & 0x3) != 0){
SP("channel ");
CP8(save_xir);
SP(" requesting unexpected interrupt\n");
}
- intr_base_addr[CyCAR<<index] = (save_xir & 0x3);
- intr_base_addr[CySRER<<index] &= ~CyTxMpty;
- intr_base_addr[CyTIR<<index] = (save_xir & 0x3f);
- intr_base_addr[CyCAR<<index] = (save_car);
+ cy_writeb((u_long)intr_base_addr+(CyCAR<<index), (save_xir & 0x3));
+ cy_writeb((u_long)intr_base_addr+(CySRER<<index),
+ cy_readb(intr_base_addr+(CySRER<<index)) & ~CyTxMpty);
+ cy_writeb((u_long)intr_base_addr+(CyTIR<<index), (save_xir & 0x3f));
+ cy_writeb((u_long)intr_base_addr+(CyCAR<<index), (save_car));
}
- *(intr_base_addr + (Cy_ClrIntr<<index)) = 0;
+ cy_writeb((u_long)intr_base_addr+(Cy_ClrIntr<<index), 0);
/* Cy_ClrIntr is 0x1800 */
return;
} /* cy_probe */
@@ -1105,7 +1157,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
int chip;
int save_xir, channel, save_car;
char data;
- int char_count;
+ volatile int char_count;
int outch;
int i,j,index;
int too_many;
@@ -1114,12 +1166,16 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
int mdm_status;
if((cinfo = IRQ_cards[irq]) == 0){
+#ifdef CY_DEBUG_INTERRUPTS
+printk("cy_interrupt: spurious interrupt %d\n\r", irq);
+#endif
return; /* spurious interrupt */
}
card_base_addr = (unsigned char *)cinfo->base_addr;
index = cinfo->bus_index;
+
/* This loop checks all chips in the card. Make a note whenever
_any_ chip had some work to do, as this is considered an
indication that there will be more to do. Only when no chip
@@ -1131,7 +1187,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
base_addr = (unsigned char *)
(cinfo->base_addr + (cy_chip_offset[chip]<<index));
too_many = 0;
- while ( (status = base_addr[CySVRR<<index]) != 0x00) {
+ while ( (status = cy_readb(base_addr+(CySVRR<<index))) != 0x00) {
had_work++;
/* The purpose of the following test is to ensure that
no chip can monopolize the driver. This forces the
@@ -1142,31 +1198,34 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
break;
}
if (status & CySRReceive) { /* reception interrupt */
+#ifdef CY_DEBUG_INTERRUPTS
+printk("cy_interrupt: rcvd intr, chip %d\n\r", chip);
+#endif
/* determine the channel & change to that context */
- save_xir = (u_char) base_addr[CyRIR<<index];
+ save_xir = (u_char) cy_readb(base_addr+(CyRIR<<index));
channel = (u_short ) (save_xir & CyIRChannel);
i = channel + chip * 4 + cinfo->first_line;
info = &cy_port[i];
info->last_active = jiffies;
- save_car = base_addr[CyCAR<<index];
- base_addr[CyCAR<<index] = save_xir;
+ save_car = cy_readb(base_addr+(CyCAR<<index));
+ cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir);
/* if there is nowhere to put the data, discard it */
if(info->tty == 0){
- j = (base_addr[CyRIVR<<index] & CyIVRMask);
+ j = (cy_readb(base_addr+(CyRIVR<<index)) & CyIVRMask);
if ( j == CyIVRRxEx ) { /* exception */
- data = base_addr[CyRDSR<<index];
+ data = cy_readb(base_addr+(CyRDSR<<index));
} else { /* normal character reception */
- char_count = base_addr[CyRDCR<<index];
+ char_count = cy_readb(base_addr+(CyRDCR<<index));
while(char_count--){
- data = base_addr[CyRDSR<<index];
+ data = cy_readb(base_addr+(CyRDSR<<index));
}
}
}else{ /* there is an open port for this data */
tty = info->tty;
- j = (base_addr[CyRIVR<<index] & CyIVRMask);
+ j = (cy_readb(base_addr+(CyRIVR<<index)) & CyIVRMask);
if ( j == CyIVRRxEx ) { /* exception */
- data = base_addr[CyRDSR<<index];
+ data = cy_readb(base_addr+(CyRDSR<<index));
if(data & info->ignore_status_mask){
continue;
}
@@ -1177,7 +1236,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
*tty->flip.flag_buf_ptr++ =
TTY_BREAK;
*tty->flip.char_buf_ptr++ =
- base_addr[CyRDSR<<index];
+ cy_readb(base_addr+(CyRDSR<<index));
if (info->flags & ASYNC_SAK){
do_SAK(tty);
}
@@ -1185,12 +1244,12 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
*tty->flip.flag_buf_ptr++ =
TTY_FRAME;
*tty->flip.char_buf_ptr++ =
- base_addr[CyRDSR<<index];
+ cy_readb(base_addr+(CyRDSR<<index));
}else if(data & CyPARITY){
*tty->flip.flag_buf_ptr++ =
TTY_PARITY;
*tty->flip.char_buf_ptr++ =
- base_addr[CyRDSR<<index];
+ cy_readb(base_addr+(CyRDSR<<index));
}else if(data & CyOVERRUN){
*tty->flip.flag_buf_ptr++ =
TTY_OVERRUN;
@@ -1204,8 +1263,8 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
tty->flip.count++;
*tty->flip.flag_buf_ptr++ =
TTY_NORMAL;
- *tty->flip.char_buf_ptr++ =
- base_addr[CyRDSR<<index];
+ *tty->flip.char_buf_ptr++ =
+ cy_readb(base_addr+(CyRDSR<<index));
}
/* These two conditions may imply */
/* a normal read should be done. */
@@ -1226,7 +1285,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
}
} else { /* normal character reception */
/* load # chars available from the chip */
- char_count = base_addr[CyRDCR<<index];
+ char_count = cy_readb(base_addr+(CyRDCR<<index));
#ifdef CYCLOM_ENABLE_MONITORING
++info->mon.int_count;
@@ -1240,7 +1299,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
break;
}
tty->flip.count++;
- data = base_addr[CyRDSR<<index];
+ data = cy_readb(base_addr+(CyRDSR<<index));
*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
*tty->flip.char_buf_ptr++ = data;
#ifdef CYCLOM_16Y_HACK
@@ -1251,8 +1310,8 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
queue_task(&tty->flip.tqueue, &tq_timer);
}
/* end of service */
- base_addr[CyRIR<<index] = (save_xir & 0x3f);
- base_addr[CyCAR<<index] = (save_car);
+ cy_writeb((u_long)base_addr+(CyRIR<<index), (save_xir & 0x3f));
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (save_car));
}
@@ -1260,23 +1319,28 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/* Since we only get here when the transmit buffer
is empty, we know we can always stuff a dozen
characters. */
+#ifdef CY_DEBUG_INTERRUPTS
+printk("cy_interrupt: xmit intr, chip %d\n\r", chip);
+#endif
/* determine the channel & change to that context */
- save_xir = (u_char) base_addr[CyTIR<<index];
+ save_xir = (u_char) cy_readb(base_addr+(CyTIR<<index));
channel = (u_short ) (save_xir & CyIRChannel);
i = channel + chip * 4 + cinfo->first_line;
- save_car = base_addr[CyCAR<<index];
- base_addr[CyCAR<<index] = save_xir;
+ save_car = cy_readb(base_addr+(CyCAR<<index));
+ cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir);
/* validate the port# (as configured and open) */
if( (i < 0) || (NR_PORTS <= i) ){
- base_addr[CySRER<<index] &= ~CyTxMpty;
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
goto txend;
}
info = &cy_port[i];
info->last_active = jiffies;
if(info->tty == 0){
- base_addr[CySRER<<index] &= ~CyTxMpty;
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
goto txdone;
}
@@ -1286,7 +1350,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
if(info->x_char) { /* send special char */
outch = info->x_char;
- base_addr[CyTDR<<index] = outch;
+ cy_writeb((u_long)base_addr+(CyTDR<<index), outch);
char_count--;
info->x_char = 0;
}
@@ -1300,29 +1364,44 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
PPR runs at 200 Hz, so the delay is
duration * 200/HZ, and thus a break can
run from 1/100 sec to about 5/4 sec.
+ For CD1400 J or later, replace the 200 Hz
+ by 500 Hz.
*/
- base_addr[CyTDR<<index] = 0; /* start break */
- base_addr[CyTDR<<index] = 0x81;
- base_addr[CyTDR<<index] = 0; /* delay a bit */
- base_addr[CyTDR<<index] = 0x82;
- base_addr[CyTDR<<index] = info->x_break*200/HZ;
- base_addr[CyTDR<<index] = 0; /* finish break */
- base_addr[CyTDR<<index] = 0x83;
+ /* start break */
+ cy_writeb((u_long)base_addr + (CyTDR<<index), 0);
+ cy_writeb((u_long)base_addr + (CyTDR<<index), 0x81);
+ /* delay a bit */
+ cy_writeb((u_long)base_addr + (CyTDR<<index), 0);
+ cy_writeb((u_long)base_addr + (CyTDR<<index), 0x82);
+ if (cy_readb(base_addr + (CyGFRCR<<index)) >= 0x48 ) {
+ /* It is a CD1400 rev. J or later */
+ cy_writeb((u_long)base_addr + (CyTDR<<index),
+ info->x_break*500/HZ);
+ } else {
+ cy_writeb((u_long)base_addr + (CyTDR<<index),
+ info->x_break*200/HZ);
+ }
+ /* finish break */
+ cy_writeb((u_long)base_addr + (CyTDR<<index), 0);
+ cy_writeb((u_long)base_addr + (CyTDR<<index), 0x83);
char_count -= 7;
info->x_break = 0;
}
while (char_count-- > 0){
if (!info->xmit_cnt){
- base_addr[CySRER<<index] &= ~CyTxMpty;
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
goto txdone;
}
if (info->xmit_buf == 0){
- base_addr[CySRER<<index] &= ~CyTxMpty;
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
goto txdone;
}
if (info->tty->stopped || info->tty->hw_stopped){
- base_addr[CySRER<<index] &= ~CyTxMpty;
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
goto txdone;
}
/* Because the Embedded Transmit Commands have
@@ -1340,15 +1419,16 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
if( outch ){
info->xmit_cnt--;
info->xmit_tail = (info->xmit_tail + 1)
- & (PAGE_SIZE - 1);
- base_addr[CyTDR<<index] = outch;
+ & (SERIAL_XMIT_SIZE - 1);
+ cy_writeb((u_long)base_addr+(CyTDR<<index), outch);
}else{
if(char_count > 1){
info->xmit_cnt--;
info->xmit_tail = (info->xmit_tail + 1)
- & (PAGE_SIZE - 1);
- base_addr[CyTDR<<index] = outch;
- base_addr[CyTDR<<index] = 0;
+ & (SERIAL_XMIT_SIZE - 1);
+ cy_writeb((u_long)base_addr+(CyTDR<<index),
+ outch);
+ cy_writeb((u_long)base_addr+(CyTDR<<index), 0);
char_count--;
}else{
}
@@ -1362,23 +1442,24 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
txend:
/* end of service */
- base_addr[CyTIR<<index] = (save_xir & 0x3f);
- base_addr[CyCAR<<index] = (save_car);
+ cy_writeb((u_long)base_addr+(CyTIR<<index),
+ (save_xir & 0x3f));
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (save_car));
}
if (status & CySRModem) { /* modem interrupt */
/* determine the channel & change to that context */
- save_xir = (u_char) base_addr[CyMIR<<index];
+ save_xir = (u_char) cy_readb(base_addr+(CyMIR<<index));
channel = (u_short ) (save_xir & CyIRChannel);
info = &cy_port[channel + chip * 4
+ cinfo->first_line];
info->last_active = jiffies;
- save_car = base_addr[CyCAR<<index];
- base_addr[CyCAR<<index] = save_xir;
+ save_car = cy_readb(base_addr+(CyCAR<<index));
+ cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir);
- mdm_change = base_addr[CyMISR<<index];
- mdm_status = base_addr[CyMSVR1<<index];
+ mdm_change = cy_readb(base_addr+(CyMISR<<index));
+ mdm_status = cy_readb(base_addr+(CyMSVR1<<index));
if(info->tty == 0){/* no place for data, ignore it*/
;
@@ -1403,7 +1484,9 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/* cy_start isn't used
because... !!! */
info->tty->hw_stopped = 0;
- base_addr[CySRER<<index] |= CyTxMpty;
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) |
+ CyTxMpty);
cy_sched_event(info,
Cy_EVENT_WRITE_WAKEUP);
}
@@ -1412,8 +1495,9 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/* cy_stop isn't used
because ... !!! */
info->tty->hw_stopped = 1;
- base_addr[CySRER<<index] &=
- ~CyTxMpty;
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) &
+ ~CyTxMpty);
}
}
}
@@ -1423,15 +1507,16 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
}
}
/* end of service */
- base_addr[CyMIR<<index] = (save_xir & 0x3f);
- base_addr[CyCAR<<index] = save_car;
+ cy_writeb((u_long)base_addr+(CyMIR<<index),
+ (save_xir & 0x3f));
+ cy_writeb((u_long)base_addr+(CyCAR<<index), save_car);
}
} /* end while status != 0 */
} /* end loop for chips... */
} while(had_work);
/* clear interrupts */
- *(card_base_addr + (Cy_ClrIntr<<index)) = 0;
+ cy_writeb((u_long)card_base_addr + (Cy_ClrIntr<<index), 0);
/* Cy_ClrIntr is 0x1800 */
} /* cyy_interrupt */
@@ -1444,7 +1529,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
static int
cyz_fetch_msg( struct cyclades_card *cinfo,
- u_long *channel, u_char *cmd, u_long **param)
+ uclong *channel, ucchar *cmd, uclong *param)
{
struct FIRM_ID *firm_id;
struct ZFW_CTRL *zfw_ctrl;
@@ -1456,17 +1541,17 @@ cyz_fetch_msg( struct cyclades_card *cinfo,
return (-1);
}
zfw_ctrl = (struct ZFW_CTRL *)
- (cinfo->base_addr + firm_id->zfwctrl_addr);
+ (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
board_ctrl = &zfw_ctrl->board_ctrl;
- loc_doorbell = ((struct RUNTIME_9060 *)
- (cinfo->ctl_addr))->loc_doorbell;
+ loc_doorbell = cy_readl(&((struct RUNTIME_9060 *)
+ (cinfo->ctl_addr))->loc_doorbell);
if (loc_doorbell){
*cmd = (char)(0xff & loc_doorbell);
- *channel = board_ctrl->fwcmd_channel;
- *param = board_ctrl->fwcmd_param;
- ((struct RUNTIME_9060 *)
- (cinfo->ctl_addr))->loc_doorbell = 0xffffffff;
+ *channel = cy_readl(&board_ctrl->fwcmd_channel);
+ *param = (uclong)cy_readl(&board_ctrl->fwcmd_param);
+ cy_writel(&((struct RUNTIME_9060 *)(cinfo->ctl_addr))->loc_doorbell,
+ 0xffffffff);
return 1;
}
return 0;
@@ -1475,12 +1560,12 @@ cyz_fetch_msg( struct cyclades_card *cinfo,
static int
cyz_issue_cmd( struct cyclades_card *cinfo,
- u_long channel, u_char cmd, u_long *param)
+ uclong channel, ucchar cmd, uclong param)
{
struct FIRM_ID *firm_id;
struct ZFW_CTRL *zfw_ctrl;
struct BOARD_CTRL *board_ctrl;
- volatile unsigned long *pci_doorbell;
+ volatile uclong *pci_doorbell;
int index;
firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
@@ -1488,21 +1573,21 @@ cyz_issue_cmd( struct cyclades_card *cinfo,
return (-1);
}
zfw_ctrl = (struct ZFW_CTRL *)
- (cinfo->base_addr + firm_id->zfwctrl_addr);
+ (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
board_ctrl = &zfw_ctrl->board_ctrl;
index = 0;
- pci_doorbell = &((struct RUNTIME_9060 *)
- (cinfo->ctl_addr))->pci_doorbell;
- while( (*pci_doorbell & 0xff) != 0){
+ pci_doorbell = (uclong *)(&((struct RUNTIME_9060 *)
+ (cinfo->ctl_addr))->pci_doorbell);
+ while( (cy_readl(pci_doorbell) & 0xff) != 0){
if (index++ == 1000){
return(-1);
}
udelay(50L);
}
- board_ctrl->hcmd_channel = channel;
- board_ctrl->hcmd_param = param;
- *pci_doorbell = (long)cmd;
+ cy_writel((u_long)&board_ctrl->hcmd_channel, channel);
+ cy_writel((u_long)&board_ctrl->hcmd_param , param);
+ cy_writel((u_long)pci_doorbell, (long)cmd);
return(0);
} /* cyz_issue_cmd */
@@ -1521,11 +1606,11 @@ cyz_update_channel( struct cyclades_card *cinfo,
if (!ISZLOADED(*cinfo)){
return (-1);
}
- zfw_ctrl =
- (struct ZFW_CTRL *)(cinfo->base_addr + firm_id->zfwctrl_addr);
+ zfw_ctrl = (struct ZFW_CTRL *)
+ (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
ch_ctrl = zfw_ctrl->ch_ctrl;
- ch_ctrl[channel].op_mode = (long)mode;
+ cy_writel(&ch_ctrl[channel].op_mode, (uclong)mode);
return cyz_issue_cmd(cinfo, channel, cmd, 0L);
@@ -1542,42 +1627,53 @@ cyz_interrupt(int irq, void *dev_id, struct pt_regs *regs)
static void
cyz_poll(unsigned long arg)
{
- struct FIRM_ID *firm_id;
- struct ZFW_CTRL *zfw_ctrl;
- struct BOARD_CTRL *board_ctrl;
- struct CH_CTRL *ch_ctrl;
- struct BUF_CTRL *buf_ctrl;
+ static volatile struct FIRM_ID *firm_id;
+ static volatile struct ZFW_CTRL *zfw_ctrl;
+ static volatile struct BOARD_CTRL *board_ctrl;
+ static volatile struct CH_CTRL *ch_ctrl;
+ static volatile struct BUF_CTRL *buf_ctrl;
struct cyclades_card *cinfo;
struct cyclades_port *info;
struct tty_struct *tty;
int card, port;
- int char_count, small_count;
+ int char_count;
+#ifdef BLOCKMOVE
+ int small_count;
+#endif
char data;
- u_long channel;
- u_char cmd;
- u_long *param;
- u_long hw_ver, fw_ver;
-
- cyz_timerlist.expires = jiffies + 100;
-
+ uclong channel;
+ ucchar cmd;
+ uclong param;
+ uclong hw_ver, fw_ver;
+ volatile uclong tx_put, tx_get, tx_bufsize;
+ volatile uclong rx_put, rx_get, rx_bufsize;
+
+ cyz_timerlist.expires = jiffies + (HZ);
for (card = 0 ; card < NR_CARDS ; card++){
cinfo = &cy_card[card];
if (!IS_CYC_Z(*cinfo)) continue;
- firm_id = (struct FIRM_ID *)
- (cinfo->base_addr + ID_ADDRESS);
- if (!ISZLOADED(*cinfo)){
+
+ firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
+ if (!ISZLOADED(*cinfo)) {
+ cinfo->inact_ctrl = 0;
continue;
}
- zfw_ctrl =
- (struct ZFW_CTRL *)
- (cinfo->base_addr + firm_id->zfwctrl_addr);
- board_ctrl = &zfw_ctrl->board_ctrl;
- fw_ver = board_ctrl->fw_version;
- hw_ver = ((struct RUNTIME_9060 *)(cinfo->ctl_addr))->mail_box_0;
+ zfw_ctrl = (struct ZFW_CTRL *)
+ (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
+ board_ctrl = &(zfw_ctrl->board_ctrl);
+ fw_ver = cy_readl(&board_ctrl->fw_version);
+ hw_ver = cy_readl(&((struct RUNTIME_9060 *)
+ (cinfo->ctl_addr))->mail_box_0);
+
+ /* Enables the firmware inactivity control */
+ if ((fw_ver > 0x00000310L) && (!cinfo->inact_ctrl)) {
+ param = cyz_issue_cmd( &cy_card[card], 0L, C_CM_TINACT, 0L);
+ cinfo->inact_ctrl = 1;
+ }
- while( cyz_fetch_msg( cinfo, &channel, &cmd, &param) == 1){
+ while(cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1){
char_count = 0;
info = &cy_port[ channel + cinfo->first_line ];
if((tty = info->tty) == 0) continue;
@@ -1606,9 +1702,9 @@ cyz_poll(unsigned long arg)
break;
case C_CM_MDCD:
if (info->flags & ASYNC_CHECK_CD){
- if (((hw_ver != 0 || fw_ver > 241)
- ? ((u_long)param)
- : ch_ctrl[channel].rs_status) & C_RS_DCD) {
+ if ((fw_ver > 241 ?
+ ((u_long)param) :
+ cy_readl(&ch_ctrl[channel].rs_status)) & C_RS_DCD) {
/* SP("Open Wakeup\n"); */
cy_sched_event(info,
Cy_EVENT_OPEN_WAKEUP);
@@ -1625,7 +1721,7 @@ cyz_poll(unsigned long arg)
case C_CM_MCTS:
if (info->flags & ASYNC_CTS_FLOW) {
if(info->tty->hw_stopped){
- if( ch_ctrl[channel].rs_status & C_RS_DCD){
+ if( cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD){
/* cy_start isn't used because...
HW flow is handled by the board */
/* SP("Write Wakeup\n"); */
@@ -1633,7 +1729,7 @@ cyz_poll(unsigned long arg)
Cy_EVENT_WRITE_WAKEUP);
}
}else{
- if(!(ch_ctrl[channel].rs_status & C_RS_CTS)){
+ if(!(cy_readl(&ch_ctrl[channel].rs_status) & C_RS_CTS)){
/* cy_stop isn't used because
HW flow is handled by the board */
/* SP("Write stop\n"); */
@@ -1653,14 +1749,25 @@ cyz_poll(unsigned long arg)
queue_task(&tty->flip.tqueue, &tq_timer);
}
}
-
- for (port = 0; port < board_ctrl->n_channel; port++){
+ for (port = 0; port < cy_readl(&board_ctrl->n_channel); port++){
info = &cy_port[ port + cinfo->first_line ];
tty = info->tty;
ch_ctrl = &(zfw_ctrl->ch_ctrl[port]);
buf_ctrl = &(zfw_ctrl->buf_ctrl[port]);
- if ((char_count = CHARS_IN_BUF(buf_ctrl))){
+/* Removed due to compilation problems in Alpha systems */
+// if ((char_count = CHARS_IN_BUF(buf_ctrl))){
+
+ rx_get = cy_readl(&buf_ctrl->rx_get);
+ rx_put = cy_readl(&buf_ctrl->rx_put);
+ rx_bufsize = cy_readl(&buf_ctrl->rx_bufsize);
+ if (rx_put >= rx_get)
+ char_count = rx_put - rx_get;
+ else
+ char_count = rx_put - rx_get + rx_bufsize;
+
+ if ( char_count ){
+
info->last_active = jiffies;
info->jiffies[1] = jiffies;
@@ -1673,9 +1780,7 @@ cyz_poll(unsigned long arg)
#endif
if( tty == 0){
/* flush received characters */
- buf_ctrl->rx_get =
- (buf_ctrl->rx_get + char_count)
- % buf_ctrl->rx_bufsize;
+ rx_get = (rx_get + char_count) & (rx_bufsize - 1);
/* SP("-"); */
info->rflush_count++;
}else{
@@ -1684,22 +1789,22 @@ cyz_poll(unsigned long arg)
for performance, but because of buffer boundaries, there
may be several steps to the operation */
while(0 < (small_count
- = cy_min( (buf_ctrl->rx_bufsize - buf_ctrl->rx_get),
- cy_min( (TTY_FLIPBUF_SIZE - tty->flip.count),
- char_count)))){
- memcpy(tty->flip.char_buf_ptr,
- (char *)(cinfo->base_addr
- + buf_ctrl->rx_bufaddr
- + buf_ctrl->rx_get),
- small_count);
+ = cy_min((rx_bufsize - rx_get),
+ cy_min((TTY_FLIPBUF_SIZE - tty->flip.count),
+ char_count)))){
+
+ memcpy_fromio(tty->flip.char_buf_ptr,
+ (char *)(cinfo->base_addr
+ + cy_readl(&buf_ctrl->rx_bufaddr)
+ + rx_get),
+ small_count);
+
tty->flip.char_buf_ptr += small_count;
memset(tty->flip.flag_buf_ptr,
TTY_NORMAL,
small_count);
tty->flip.flag_buf_ptr += small_count;
- buf_ctrl->rx_get =
- (buf_ctrl->rx_get + small_count)
- % buf_ctrl->rx_bufsize;
+ rx_get = (rx_get + small_count) & (rx_bufsize - 1);
char_count -= small_count;
tty->flip.count += small_count;
}
@@ -1708,13 +1813,9 @@ cyz_poll(unsigned long arg)
if (tty->flip.count >= TTY_FLIPBUF_SIZE){
break;
}
- data = *(char *) (cinfo->base_addr +
- buf_ctrl->rx_bufaddr +
- buf_ctrl->rx_get);
- buf_ctrl->rx_get =
- (buf_ctrl->rx_get + 1)
- % buf_ctrl->rx_bufsize;
-
+ data = cy_readb(cinfo->base_addr +
+ cy_readl(&buf_ctrl->rx_bufaddr) + rx_get);
+ rx_get = (rx_get + 1) & (rx_bufsize - 1);
tty->flip.count++;
*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
*tty->flip.char_buf_ptr++ = data;
@@ -1722,22 +1823,33 @@ cyz_poll(unsigned long arg)
#endif
queue_task(&tty->flip.tqueue, &tq_timer);
}
+ /* Update rx_get */
+ cy_writel(&buf_ctrl->rx_get, rx_get);
}
- if ((char_count = SPACE_IN_BUF(buf_ctrl))){
- if( tty == 0){
+/* Removed due to compilation problems in Alpha systems */
+// if ((char_count = SPACE_IN_BUF(buf_ctrl))){
+
+ tx_get = cy_readl(&buf_ctrl->tx_get);
+ tx_put = cy_readl(&buf_ctrl->tx_put);
+ tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize);
+ if (tx_put >= tx_get)
+ char_count = tx_get - tx_put - 1 + tx_bufsize;
+ else
+ char_count = tx_get - tx_put - 1;
+
+ if ( char_count ){
+
+ if( tty == 0 ){
goto ztxdone;
}
if(info->x_char) { /* send special char */
data = info->x_char;
- *(char *) (cinfo->base_addr +
- buf_ctrl->tx_bufaddr +
- buf_ctrl->tx_put) = data;
- buf_ctrl->tx_put =
- (buf_ctrl->tx_put + 1)
- % buf_ctrl->tx_bufsize;
+ cy_writeb((cinfo->base_addr +
+ cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), data);
+ tx_put = (tx_put + 1) & (tx_bufsize - 1);
info->x_char = 0;
char_count--;
info->last_active = jiffies;
@@ -1751,21 +1863,20 @@ cyz_poll(unsigned long arg)
}
#ifdef BLOCKMOVE
while(0 < (small_count
- = cy_min( (buf_ctrl->tx_bufsize - buf_ctrl->tx_put),
- cy_min ( (PAGE_SIZE - info->xmit_tail),
- cy_min( info->xmit_cnt, char_count))))){
- memcpy((char *)(cinfo->base_addr
- + buf_ctrl->tx_bufaddr
- + buf_ctrl->tx_put),
- &info->xmit_buf[info->xmit_tail],
- small_count);
- buf_ctrl->tx_put =
- (buf_ctrl->tx_put + small_count)
- % buf_ctrl->tx_bufsize;
+ = cy_min((tx_bufsize - tx_put),
+ cy_min ((SERIAL_XMIT_SIZE - info->xmit_tail),
+ cy_min(info->xmit_cnt, char_count))))){
+
+ memcpy_toio((char *)(cinfo->base_addr
+ + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put),
+ &info->xmit_buf[info->xmit_tail],
+ small_count);
+
+ tx_put = (tx_put + small_count) & (tx_bufsize - 1);
char_count -= small_count;
info->xmit_cnt -= small_count;
info->xmit_tail =
- (info->xmit_tail + small_count) & (PAGE_SIZE - 1);
+ (info->xmit_tail + small_count) & (SERIAL_XMIT_SIZE - 1);
info->last_active = jiffies;
info->jiffies[2] = jiffies;
}
@@ -1774,28 +1885,33 @@ cyz_poll(unsigned long arg)
data = info->xmit_buf[info->xmit_tail];
info->xmit_cnt--;
info->xmit_tail =
- (info->xmit_tail + 1) & (PAGE_SIZE - 1);
-
- *(char *) (cinfo->base_addr +
- buf_ctrl->tx_bufaddr +
- buf_ctrl->tx_put) = data;
- buf_ctrl->tx_put =
- (buf_ctrl->tx_put + 1)
- % buf_ctrl->tx_bufsize;
+ (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1);
+
+ cy_writeb(cinfo->base_addr +
+ cy_readl(&buf_ctrl->tx_bufaddr) + tx_put,
+ data);
+ tx_put = (tx_put + 1) & (tx_bufsize - 1);
char_count--;
info->last_active = jiffies;
info->jiffies[2] = jiffies;
}
+
#endif
ztxdone:
if (info->xmit_cnt < WAKEUP_CHARS) {
cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
}
+ /* Update tx_put */
+ cy_writel(&buf_ctrl->tx_put, tx_put);
}
}
-
/* poll every 40 ms */
- cyz_timerlist.expires = jiffies + 4;
+ cyz_timerlist.expires = jiffies + cyz_polling_cycle;
+
+ /* refresh inactivity counter */
+ if (cinfo->inact_ctrl) {
+ cy_writel(&board_ctrl->inactivity, (uclong) ZF_TINACT);
+ }
}
add_timer(&cyz_timerlist);
@@ -1851,26 +1967,27 @@ startup(struct cyclades_port * info)
#endif
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
+ cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
- base_addr[CyRTPR<<index] = (info->default_timeout
+ cy_writeb((ulong)base_addr+(CyRTPR<<index), (info->default_timeout
? info->default_timeout
- : 0x02); /* 10ms rx timeout */
+ : 0x02)); /* 10ms rx timeout */
cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index);
- base_addr[CyCAR<<index] =
- (u_char)channel; /* !!! Is this needed? */
- base_addr[CyMSVR1<<index] = CyRTS;
- base_addr[CyMSVR2<<index] = CyDTR;
+ cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
+ cy_writeb((ulong)base_addr+(CyMSVR1<<index), CyRTS);
+ cy_writeb((ulong)base_addr+(CyMSVR2<<index), CyDTR);
#ifdef SERIAL_DEBUG_DTR
printk("cyc:startup raising DTR\n");
printk(" status: 0x%x, 0x%x\n",
- base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+ cy_readb(base_addr+(CyMSVR1<<index)),
+ cy_readb(base_addr+(CyMSVR2<<index)));
#endif
- base_addr[CySRER<<index] |= CyRxData;
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) | CyRxData);
info->flags |= ASYNC_INITIALIZED;
if (info->tty){
@@ -1894,7 +2011,7 @@ startup(struct cyclades_port * info)
zfw_ctrl =
(struct ZFW_CTRL *)
- (cy_card[card].base_addr + firm_id->zfwctrl_addr);
+ (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
board_ctrl = &zfw_ctrl->board_ctrl;
ch_ctrl = zfw_ctrl->ch_ctrl;
@@ -1903,8 +2020,8 @@ startup(struct cyclades_port * info)
card, channel, (long)base_addr);/**/
#endif
- ch_ctrl[channel].op_mode = C_CH_ENABLE;
- ch_ctrl[channel].intr_enable = C_IN_MDCD|C_IN_MCTS;
+ cy_writel(&ch_ctrl[channel].op_mode, C_CH_ENABLE);
+ cy_writel(&ch_ctrl[channel].intr_enable, C_IN_MDCD|C_IN_MCTS);
retval = cyz_issue_cmd( &cy_card[card],
channel, C_CM_IOCTL, 0L); /* was C_CM_RESET */
if (retval != 0){
@@ -1913,8 +2030,8 @@ startup(struct cyclades_port * info)
/* set timeout !!! */
/* set RTS and DTR !!! */
- ch_ctrl[channel].rs_control |=
- C_RS_RTS | C_RS_DTR ;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS | C_RS_DTR) ;
retval = cyz_issue_cmd(&cy_card[info->card],
channel, C_CM_IOCTLM, 0L);
if (retval != 0){
@@ -1959,8 +2076,9 @@ start_xmit( struct cyclades_port *info )
+ (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR<<index] = channel;
- base_addr[CySRER<<index] |= CyTxMpty;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), channel);
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
restore_flags(flags);
} else {
/* Don't have to do anything at this time */
@@ -2012,14 +2130,15 @@ shutdown(struct cyclades_port * info)
free_page((unsigned long) temp);
}
- base_addr[CyCAR<<index] = (u_char)channel;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
- base_addr[CyMSVR1<<index] = ~CyRTS;
- base_addr[CyMSVR2<<index] = ~CyDTR;
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
#ifdef SERIAL_DEBUG_DTR
printk("cyc shutdown dropping DTR\n");
printk(" status: 0x%x, 0x%x\n",
- base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+ cy_readb(base_addr+(CyMSVR1<<index)),
+ cy_readb(base_addr+(CyMSVR2<<index)));
#endif
}
cyy_issue_cmd(base_addr,CyCHAN_CTL|CyDIS_RCVR,index);
@@ -2051,10 +2170,11 @@ shutdown(struct cyclades_port * info)
zfw_ctrl =
(struct ZFW_CTRL *)
- (cy_card[card].base_addr + firm_id->zfwctrl_addr);
- board_ctrl = &zfw_ctrl->board_ctrl;
+ (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
+ board_ctrl = &(zfw_ctrl->board_ctrl);
ch_ctrl = zfw_ctrl->ch_ctrl;
+
save_flags(flags); cli();
if (info->xmit_buf){
unsigned char * temp;
@@ -2063,8 +2183,9 @@ shutdown(struct cyclades_port * info)
free_page((unsigned long) temp);
}
if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
- ch_ctrl[channel].rs_control &=
- ~(C_RS_RTS | C_RS_DTR );
+ cy_writel((u_long)&ch_ctrl[channel].rs_control,
+ (uclong)(cy_readl(&ch_ctrl[channel].rs_control) &
+ ~(C_RS_RTS | C_RS_DTR)));
retval = cyz_issue_cmd(&cy_card[info->card],
channel, C_CM_IOCTLM, 0L);
if (retval != 0){
@@ -2187,13 +2308,14 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
while (1) {
save_flags(flags); cli();
if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR1<<index] = CyRTS;
- base_addr[CyMSVR2<<index] = CyDTR;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
#ifdef SERIAL_DEBUG_DTR
printk("cyc:block_til_ready raising DTR\n");
printk(" status: 0x%x, 0x%x\n",
- base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+ cy_readb(base_addr+(CyMSVR1<<index)),
+ cy_readb(base_addr+(CyMSVR2<<index)));
#endif
}
restore_flags(flags);
@@ -2208,11 +2330,11 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
break;
}
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
if (!(info->flags & ASYNC_CALLOUT_ACTIVE)
&& !(info->flags & ASYNC_CLOSING)
&& (C_CLOCAL(tty)
- || (base_addr[CyMSVR1<<index] & CyDCD))) {
+ || (cy_readb(base_addr+(CyMSVR1<<index)) & CyDCD))) {
restore_flags(flags);
break;
}
@@ -2243,13 +2365,13 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
zfw_ctrl =
(struct ZFW_CTRL *)
- (base_addr + firm_id->zfwctrl_addr);
+ (base_addr + cy_readl(&firm_id->zfwctrl_addr));
board_ctrl = &zfw_ctrl->board_ctrl;
ch_ctrl = zfw_ctrl->ch_ctrl;
while (1) {
- ch_ctrl[channel].rs_control |=
- C_RS_RTS | C_RS_DTR ;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS | C_RS_DTR);
retval = cyz_issue_cmd(&cy_card[info->card],
channel, C_CM_IOCTLM, 0L);
if (retval != 0){
@@ -2272,7 +2394,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
if (!(info->flags & ASYNC_CALLOUT_ACTIVE)
&& !(info->flags & ASYNC_CLOSING)
&& (C_CLOCAL(tty)
- || (ch_ctrl[channel].rs_status & C_RS_DCD))) {
+ || (cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD))) {
break;
}
if (current->signal & ~current->blocked) {
@@ -2332,11 +2454,11 @@ cy_open(struct tty_struct *tty, struct file * filp)
*/
if (IS_CYC_Z(cy_card[info->card])) {
if (!ISZLOADED(cy_card[info->card])) {
- if (((ZE_V1 ==((struct RUNTIME_9060 *)
- ((cy_card[info->card]).ctl_addr))->mail_box_0) &&
+ if (((ZE_V1 ==cy_readl(&((struct RUNTIME_9060 *)
+ ((cy_card[info->card]).ctl_addr))->mail_box_0)) &&
Z_FPGA_CHECK(cy_card[info->card])) &&
- (ZFIRM_HLT==((struct FIRM_ID *)
- ((cy_card[info->card]).base_addr+ID_ADDRESS))->signature))
+ (ZFIRM_HLT==cy_readl(&((struct FIRM_ID *)
+ ((cy_card[info->card]).base_addr+ID_ADDRESS))->signature)))
{
printk ("Cyclades-Z Error: you need an external power supply for this number of ports.\n\rFirmware halted.\r\n");
} else {
@@ -2403,6 +2525,7 @@ cy_open(struct tty_struct *tty, struct file * filp)
#ifdef SERIAL_DEBUG_OPEN
printk(" cyc:cy_open done\n");/**/
#endif
+
return 0;
} /* cy_open */
@@ -2603,13 +2726,13 @@ cy_put_char(struct tty_struct *tty, unsigned char ch)
return;
save_flags(flags); cli();
- if (info->xmit_cnt >= PAGE_SIZE - 1) {
+ if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
restore_flags(flags);
return;
}
info->xmit_buf[info->xmit_head++] = ch;
- info->xmit_head &= PAGE_SIZE - 1;
+ info->xmit_head &= SERIAL_XMIT_SIZE - 1;
info->xmit_cnt++;
restore_flags(flags);
#if 0
@@ -2652,8 +2775,9 @@ cy_flush_chars(struct tty_struct *tty)
+ (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR<<index] = channel;
- base_addr[CySRER<<index] |= CyTxMpty;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), channel);
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
restore_flags(flags);
} else {
/* Since polling is already in place,
@@ -2680,7 +2804,7 @@ cy_write_room(struct tty_struct *tty)
if (serial_paranoia_check(info, tty->device, "cy_write_room"))
return 0;
- ret = PAGE_SIZE - info->xmit_cnt - 1;
+ ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
if (ret < 0)
ret = 0;
return ret;
@@ -2724,6 +2848,7 @@ set_line_char(struct cyclades_port * info)
unsigned cflag;
int i;
+
if (!info->tty || !info->tty->termios){
return;
}
@@ -2736,6 +2861,9 @@ set_line_char(struct cyclades_port * info)
channel = (info->line) - (cy_card[card].first_line);
if (!IS_CYC_Z(cy_card[card])) {
+
+ index = cy_card[card].bus_index;
+
/* baud rate */
i = cflag & CBAUD;
@@ -2744,6 +2872,11 @@ set_line_char(struct cyclades_port * info)
i = 16;
else if(i == B115200)
i = 18;
+ else if(i == B230400 &&
+ cy_readb(cy_card[card].base_addr+(CyGFRCR<<index)) >= 0x48) {
+ /* It is a CD1400 rev. J or later */
+ i = 20;
+ }
#ifdef B76800
else if(i == B76800)
i = 17;
@@ -2753,15 +2886,35 @@ set_line_char(struct cyclades_port * info)
}
if (i == 15) {
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- i += 1;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- i += 3;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ i += 1;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ i += 3;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){
+ switch(info->baud) {
+ case 57600:
+ i += 1; break;
+ case 115200:
+ i += 3; break;
+ case 230400:
+ i += 5; break;
+ default:
+ break;
+ }
+ }
+ }
+ if(cy_readb(cy_card[card].base_addr+(CyGFRCR<<index)) >= 0x48) {
+ /* It is a CD1400 rev. J or later */
+ info->tbpr = baud_bpr_60[i]; /* Tx BPR */
+ info->tco = baud_co_60[i]; /* Tx CO */
+ info->rbpr = baud_bpr_60[i]; /* Rx BPR */
+ info->rco = baud_co_60[i]; /* Rx CO */
+ } else {
+ info->tbpr = baud_bpr_25[i]; /* Tx BPR */
+ info->tco = baud_co_25[i]; /* Tx CO */
+ info->rbpr = baud_bpr_25[i]; /* Rx BPR */
+ info->rco = baud_co_25[i]; /* Rx CO */
}
- info->tbpr = baud_bpr[i]; /* Tx BPR */
- info->tco = baud_co[i]; /* Tx CO */
- info->rbpr = baud_bpr[i]; /* Rx BPR */
- info->rco = baud_co[i]; /* Rx CO */
if (baud_table[i] == 134) {
info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
/* get it right for 134.5 baud */
@@ -2834,70 +2987,94 @@ set_line_char(struct cyclades_port * info)
chip = channel>>2;
channel &= 0x03;
- index = cy_card[card].bus_index;
base_addr = (unsigned char*)
(cy_card[card].base_addr
+ (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
/* tx and rx baud rate */
- base_addr[CyTCOR<<index] = info->tco;
- base_addr[CyTBPR<<index] = info->tbpr;
- base_addr[CyRCOR<<index] = info->rco;
- base_addr[CyRBPR<<index] = info->rbpr;
+ cy_writeb((u_long)base_addr+(CyTCOR<<index), info->tco);
+ cy_writeb((u_long)base_addr+(CyTBPR<<index), info->tbpr);
+ cy_writeb((u_long)base_addr+(CyRCOR<<index), info->rco);
+ cy_writeb((u_long)base_addr+(CyRBPR<<index), info->rbpr);
/* set line characteristics according configuration */
- base_addr[CySCHR1<<index] = START_CHAR(info->tty);
- base_addr[CySCHR2<<index] = STOP_CHAR(info->tty);
- base_addr[CyCOR1<<index] = info->cor1;
- base_addr[CyCOR2<<index] = info->cor2;
- base_addr[CyCOR3<<index] = info->cor3;
- base_addr[CyCOR4<<index] = info->cor4;
- base_addr[CyCOR5<<index] = info->cor5;
+ cy_writeb((u_long)base_addr+(CySCHR1<<index),
+ START_CHAR(info->tty));
+ cy_writeb((u_long)base_addr+(CySCHR2<<index),
+ STOP_CHAR(info->tty));
+ cy_writeb((u_long)base_addr+(CyCOR1<<index), info->cor1);
+ cy_writeb((u_long)base_addr+(CyCOR2<<index), info->cor2);
+ cy_writeb((u_long)base_addr+(CyCOR3<<index), info->cor3);
+ cy_writeb((u_long)base_addr+(CyCOR4<<index), info->cor4);
+ cy_writeb((u_long)base_addr+(CyCOR5<<index), info->cor5);
cyy_issue_cmd(base_addr,
CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch,index);
- base_addr[CyCAR<<index] =
- (u_char)channel; /* !!! Is this needed? */
-
- base_addr[CyRTPR<<index] = (info->default_timeout
- ? info->default_timeout
- : 0x02); /* 10ms rx timeout */
+ cy_writeb((u_long)base_addr+(CyCAR<<index),
+ (u_char)channel); /* !!! Is this needed? */
+ cy_writeb((u_long)base_addr+(CyRTPR<<index), (info->default_timeout
+ ? info->default_timeout
+ : 0x02)); /* 10ms rx timeout */
if (C_CLOCAL(info->tty)) {
- base_addr[CySRER<<index] |= CyMdmCh; /* without modem intr */
+ /* without modem intr */
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) | CyMdmCh);
/* act on 1->0 modem transitions */
- base_addr[CyMCOR1<<index] = CyCTS;
+ if ((cflag & CRTSCTS) && info->rflow) {
+ cy_writeb((u_long)base_addr+(CyMCOR1<<index),
+ (CyCTS|rflow_thr[i]));
+ } else {
+ cy_writeb((u_long)base_addr+(CyMCOR1<<index), CyCTS);
+ }
/* act on 0->1 modem transitions */
- base_addr[CyMCOR2<<index] = CyCTS;
+ cy_writeb((u_long)base_addr+(CyMCOR2<<index), CyCTS);
} else {
- base_addr[CySRER<<index] |= CyMdmCh; /* with modem intr */
+ /* without modem intr */
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) | CyMdmCh);
/* act on 1->0 modem transitions */
- base_addr[CyMCOR1<<index] = CyDSR|CyCTS|CyRI|CyDCD;
+ if ((cflag & CRTSCTS) && info->rflow) {
+ cy_writeb((u_long)base_addr+(CyMCOR1<<index),
+ (CyDSR|CyCTS|CyRI|CyDCD|rflow_thr[i]));
+ } else {
+ cy_writeb((u_long)base_addr+(CyMCOR1<<index),
+ CyDSR|CyCTS|CyRI|CyDCD);
+ }
/* act on 0->1 modem transitions */
- base_addr[CyMCOR2<<index] = CyDSR|CyCTS|CyRI|CyDCD;
+ cy_writeb((u_long)base_addr+(CyMCOR2<<index),
+ CyDSR|CyCTS|CyRI|CyDCD);
}
if(i == 0){ /* baud rate is zero, turn off line */
- base_addr[CyMSVR2<<index] = ~CyDTR;
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+ }
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_line_char dropping DTR\n");
printk(" status: 0x%x,
- 0x%x\n", base_addr[CyMSVR1<<index],
- base_addr[CyMSVR2<<index]);
+ 0x%x\n", cy_readb(base_addr+(CyMSVR1<<index)),
+ cy_readb(base_addr+(CyMSVR2<<index)));
#endif
}else{
- base_addr[CyMSVR2<<index] = CyDTR;
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+ }
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_line_char raising DTR\n");
printk(" status: 0x%x, 0x%x\n",
- base_addr[CyMSVR1<<index],
- base_addr[CyMSVR2<<index]);
+ cy_readb(base_addr+(CyMSVR1<<index)),
+ cy_readb(base_addr+(CyMSVR2<<index)));
#endif
}
@@ -2920,82 +3097,84 @@ set_line_char(struct cyclades_port * info)
return;
}
- zfw_ctrl =
- (struct ZFW_CTRL *)
- (cy_card[card].base_addr + firm_id->zfwctrl_addr);
+ zfw_ctrl = (struct ZFW_CTRL *)
+ (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
board_ctrl = &zfw_ctrl->board_ctrl;
- ch_ctrl = &zfw_ctrl->ch_ctrl[channel];
+ ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]);
buf_ctrl = &zfw_ctrl->buf_ctrl[channel];
/* baud rate */
switch(i = cflag & CBAUD){
/*
- case B0: ch_ctrl->comm_baud = 0; break;
+ case B0: cy_writel(&ch_ctrl->comm_baud , 0); break;
*/
- case B50: ch_ctrl->comm_baud = 50; break;
- case B75: ch_ctrl->comm_baud = 75; break;
- case B110: ch_ctrl->comm_baud = 110; break;
- case B134: ch_ctrl->comm_baud = 134; break;
- case B150: ch_ctrl->comm_baud = 150; break;
- case B200: ch_ctrl->comm_baud = 200; break;
- case B300: ch_ctrl->comm_baud = 300; break;
- case B600: ch_ctrl->comm_baud = 600; break;
- case B1200: ch_ctrl->comm_baud = 1200; break;
- case B1800: ch_ctrl->comm_baud = 1800; break;
- case B2400: ch_ctrl->comm_baud = 2400; break;
- case B4800: ch_ctrl->comm_baud = 4800; break;
- case B9600: ch_ctrl->comm_baud = 9600; break;
- case B19200: ch_ctrl->comm_baud = 19200; break;
+ case B50: cy_writel(&ch_ctrl->comm_baud , 50); break;
+ case B75: cy_writel(&ch_ctrl->comm_baud , 75); break;
+ case B110: cy_writel(&ch_ctrl->comm_baud , 110); break;
+ case B134: cy_writel(&ch_ctrl->comm_baud , 134); break;
+ case B150: cy_writel(&ch_ctrl->comm_baud , 150); break;
+ case B200: cy_writel(&ch_ctrl->comm_baud , 200); break;
+ case B300: cy_writel(&ch_ctrl->comm_baud , 300); break;
+ case B600: cy_writel(&ch_ctrl->comm_baud , 600); break;
+ case B1200: cy_writel(&ch_ctrl->comm_baud , 1200); break;
+ case B1800: cy_writel(&ch_ctrl->comm_baud , 1800); break;
+ case B2400: cy_writel(&ch_ctrl->comm_baud , 2400); break;
+ case B4800: cy_writel(&ch_ctrl->comm_baud , 4800); break;
+ case B9600: cy_writel(&ch_ctrl->comm_baud , 9600); break;
+ case B19200: cy_writel(&ch_ctrl->comm_baud , 19200); break;
case B38400:
if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI){
- ch_ctrl->comm_baud = 57600;
+ cy_writel(&ch_ctrl->comm_baud , 57600);
}else if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI){
- ch_ctrl->comm_baud = 115200;
+ cy_writel(&ch_ctrl->comm_baud , 115200);
+ }else if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){
+ cy_writel(&ch_ctrl->comm_baud , info->baud);
}else{
- ch_ctrl->comm_baud = 38400;
+ cy_writel(&ch_ctrl->comm_baud , 38400);
}
break;
- case B57600: ch_ctrl->comm_baud = 57600; break;
+ case B57600: cy_writel(&ch_ctrl->comm_baud , 57600); break;
#ifdef B76800
- case B76800: ch_ctrl->comm_baud = 76800; break;
+ case B76800: cy_writel(&ch_ctrl->comm_baud , 76800); break;
#endif
- case B115200: ch_ctrl->comm_baud = 115200; break;
- case B230400: ch_ctrl->comm_baud = 230400; break;
- case B460800: ch_ctrl->comm_baud = 460800; break;
+ case B115200: cy_writel(&ch_ctrl->comm_baud , 115200); break;
+ case B230400: cy_writel(&ch_ctrl->comm_baud , 230400); break;
+ case B460800: cy_writel(&ch_ctrl->comm_baud , 460800); break;
}
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){
- ch_ctrl->comm_baud = info->baud;
- }
/* byte size and parity */
switch(cflag & CSIZE){
- case CS5: ch_ctrl->comm_data_l = C_DL_CS5; break;
- case CS6: ch_ctrl->comm_data_l = C_DL_CS6; break;
- case CS7: ch_ctrl->comm_data_l = C_DL_CS7; break;
- case CS8: ch_ctrl->comm_data_l = C_DL_CS8; break;
+ case CS5: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS5); break;
+ case CS6: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS6); break;
+ case CS7: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS7); break;
+ case CS8: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS8); break;
}
if(cflag & CSTOPB){
- ch_ctrl->comm_data_l |= C_DL_2STOP;
+ cy_writel(&ch_ctrl->comm_data_l,
+ cy_readl(&ch_ctrl->comm_data_l) | C_DL_2STOP);
}else{
- ch_ctrl->comm_data_l |= C_DL_1STOP;
+ cy_writel(&ch_ctrl->comm_data_l,
+ cy_readl(&ch_ctrl->comm_data_l) | C_DL_1STOP);
}
if (cflag & PARENB){
if (cflag & PARODD){
- ch_ctrl->comm_parity = C_PR_ODD;
+ cy_writel(&ch_ctrl->comm_parity , C_PR_ODD);
}else{
- ch_ctrl->comm_parity = C_PR_EVEN;
+ cy_writel(&ch_ctrl->comm_parity , C_PR_EVEN);
}
}else{
- ch_ctrl->comm_parity = C_PR_NONE;
+ cy_writel(&ch_ctrl->comm_parity , C_PR_NONE);
}
/* CTS flow control flag */
if (cflag & CRTSCTS){
info->flags |= ASYNC_CTS_FLOW;
- ch_ctrl->hw_flow |= C_RS_CTS | C_RS_RTS;
+ cy_writel(&ch_ctrl->hw_flow,
+ cy_readl(&ch_ctrl->hw_flow) | C_RS_CTS | C_RS_RTS);
}else{
info->flags &= ~ASYNC_CTS_FLOW;
- ch_ctrl->hw_flow &= ~(C_RS_CTS | C_RS_RTS);
+ cy_writel(&ch_ctrl->hw_flow,
+ cy_readl(&ch_ctrl->hw_flow) & ~(C_RS_CTS | C_RS_RTS));
}
retval = cyz_issue_cmd( &cy_card[card], channel, C_CM_IOCTL, 0L);
@@ -3012,12 +3191,14 @@ set_line_char(struct cyclades_port * info)
}
if(i == 0){ /* baud rate is zero, turn off line */
- ch_ctrl->rs_control &= ~C_RS_DTR;
+ cy_writel(&ch_ctrl->rs_control,
+ cy_readl(&ch_ctrl->rs_control) & ~C_RS_DTR);
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_line_char dropping Z DTR\n");
#endif
}else{
- ch_ctrl->rs_control |= C_RS_DTR;
+ cy_writel(&ch_ctrl->rs_control,
+ cy_readl(&ch_ctrl->rs_control) | C_RS_DTR);
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_line_char raising Z DTR\n");
#endif
@@ -3028,6 +3209,7 @@ set_line_char(struct cyclades_port * info)
printk("cyc:set_line_char retval at %d was %x\n",
__LINE__, retval);
}
+ cy_readl(&ch_ctrl->comm_baud);
if (info->tty){
clear_bit(TTY_IO_ERROR, &info->tty->flags);
@@ -3131,9 +3313,9 @@ get_modem_info(struct cyclades_port * info, unsigned int *value)
+ (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- status = base_addr[CyMSVR1<<index];
- status |= base_addr[CyMSVR2<<index];
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ status = cy_readb(base_addr+(CyMSVR1<<index));
+ status |= cy_readb(base_addr+(CyMSVR2<<index));
restore_flags(flags);
result = ((status & CyRTS) ? TIOCM_RTS : 0)
@@ -3152,12 +3334,11 @@ get_modem_info(struct cyclades_port * info, unsigned int *value)
firm_id = (struct FIRM_ID *)
(cy_card[card].base_addr + ID_ADDRESS);
if (ISZLOADED(cy_card[card])) {
- zfw_ctrl =
- (struct ZFW_CTRL *)
- (cy_card[card].base_addr + firm_id->zfwctrl_addr);
+ zfw_ctrl = (struct ZFW_CTRL *)
+ (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
board_ctrl = &zfw_ctrl->board_ctrl;
ch_ctrl = zfw_ctrl->ch_ctrl;
- lstatus = ch_ctrl[channel].rs_status;
+ lstatus = cy_readl(&ch_ctrl[channel].rs_status);
result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0)
| ((lstatus & C_RS_DTR) ? TIOCM_DTR : 0)
| ((lstatus & C_RS_DCD) ? TIOCM_CAR : 0)
@@ -3203,18 +3384,27 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
case TIOCMBIS:
if (arg & TIOCM_RTS){
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR1<<index] = CyRTS;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+ }
restore_flags(flags);
}
if (arg & TIOCM_DTR){
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR2<<index] = CyDTR;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+ }
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_modem_info raising DTR\n");
printk(" status: 0x%x, 0x%x\n",
- base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+ cy_readb(base_addr+(CyMSVR1<<index)),
+ cy_readb(base_addr+(CyMSVR2<<index)));
#endif
restore_flags(flags);
}
@@ -3222,18 +3412,28 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
case TIOCMBIC:
if (arg & TIOCM_RTS){
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR1<<index] = ~CyRTS;
+ cy_writeb((u_long)base_addr+(CyCAR<<index),
+ (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+ }
restore_flags(flags);
}
if (arg & TIOCM_DTR){
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR2<<index] = ~CyDTR;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+ }
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_modem_info dropping DTR\n");
printk(" status: 0x%x, 0x%x\n",
- base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+ cy_readb(base_addr+(CyMSVR1<<index)),
+ cy_readb(base_addr+(CyMSVR2<<index)));
#endif
restore_flags(flags);
}
@@ -3241,33 +3441,52 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
case TIOCMSET:
if (arg & TIOCM_RTS){
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR1<<index] = CyRTS;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+ }
restore_flags(flags);
}else{
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR1<<index] = ~CyRTS;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+ }
restore_flags(flags);
}
if (arg & TIOCM_DTR){
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR2<<index] = CyDTR;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+ }
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_modem_info raising DTR\n");
printk(" status: 0x%x, 0x%x\n",
- base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+ cy_readb(base_addr+(CyMSVR1<<index)),
+ cy_readb(base_addr+(CyMSVR2<<index)));
#endif
restore_flags(flags);
}else{
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR2<<index] = ~CyDTR;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+ }
+
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_modem_info dropping DTR\n");
printk(" status: 0x%x, 0x%x\n",
- base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+ cy_readb(base_addr+(CyMSVR1<<index)),
+ cy_readb(base_addr+(CyMSVR2<<index)));
#endif
restore_flags(flags);
}
@@ -3281,19 +3500,20 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
firm_id = (struct FIRM_ID *)
(cy_card[card].base_addr + ID_ADDRESS);
if (ISZLOADED(cy_card[card])) {
- zfw_ctrl =
- (struct ZFW_CTRL *)
- (cy_card[card].base_addr + firm_id->zfwctrl_addr);
+ zfw_ctrl = (struct ZFW_CTRL *)
+ (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr));
board_ctrl = &zfw_ctrl->board_ctrl;
ch_ctrl = zfw_ctrl->ch_ctrl;
switch (cmd) {
case TIOCMBIS:
if (arg & TIOCM_RTS){
- ch_ctrl[channel].rs_control |= C_RS_RTS;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS);
}
if (arg & TIOCM_DTR){
- ch_ctrl[channel].rs_control |= C_RS_DTR;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR);
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_modem_info raising Z DTR\n");
#endif
@@ -3301,10 +3521,12 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
break;
case TIOCMBIC:
if (arg & TIOCM_RTS){
- ch_ctrl[channel].rs_control &= ~C_RS_RTS;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_RTS);
}
if (arg & TIOCM_DTR){
- ch_ctrl[channel].rs_control &= ~C_RS_DTR;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR);
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_modem_info clearing Z DTR\n");
#endif
@@ -3312,17 +3534,21 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
break;
case TIOCMSET:
if (arg & TIOCM_RTS){
- ch_ctrl[channel].rs_control |= C_RS_RTS;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS);
}else{
- ch_ctrl[channel].rs_control &= ~C_RS_RTS;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_RTS);
}
if (arg & TIOCM_DTR){
- ch_ctrl[channel].rs_control |= C_RS_DTR;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR);
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_modem_info raising Z DTR\n");
#endif
}else{
- ch_ctrl[channel].rs_control &= ~C_RS_DTR;
+ cy_writel(&ch_ctrl[channel].rs_control,
+ cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR);
#ifdef SERIAL_DEBUG_DTR
printk("cyc:set_modem_info clearing Z DTR\n");
#endif
@@ -3362,8 +3588,8 @@ send_break( struct cyclades_port * info, int duration)
A better implementation will use C_CM_SET_BREAK
and C_CM_CLR_BREAK with the appropriate delay.
*/
-#if 0
-this appears to wedge the output data stream
+#if 1
+// this appears to wedge the output data stream
int retval;
retval = cyz_issue_cmd(&cy_card[info->card],
(info->line) - (cy_card[info->card].first_line),
@@ -3408,7 +3634,7 @@ set_threshold(struct cyclades_port * info, unsigned long value)
info->cor3 &= ~CyREC_FIFO;
info->cor3 |= value & CyREC_FIFO;
- base_addr[CyCOR3<<index] = info->cor3;
+ cy_writeb((u_long)base_addr+(CyCOR3<<index), info->cor3);
cyy_issue_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index);
} else {
// Nothing to do!
@@ -3434,7 +3660,7 @@ get_threshold(struct cyclades_port * info, unsigned long *value)
(cy_card[card].base_addr
+ (cy_chip_offset[chip]<<index));
- tmp = base_addr[CyCOR3<<index] & CyREC_FIFO;
+ tmp = cy_readb(base_addr+(CyCOR3<<index)) & CyREC_FIFO;
cy_put_user(tmp,value);
} else {
// Nothing to do!
@@ -3475,7 +3701,7 @@ set_timeout(struct cyclades_port * info, unsigned long value)
(cy_card[card].base_addr
+ (cy_chip_offset[chip]<<index));
- base_addr[CyRTPR<<index] = value & 0xff;
+ cy_writeb((u_long)base_addr+(CyRTPR<<index), value & 0xff);
} else {
// Nothing to do!
}
@@ -3500,7 +3726,7 @@ get_timeout(struct cyclades_port * info, unsigned long *value)
(cy_card[card].base_addr
+ (cy_chip_offset[chip]<<index));
- tmp = base_addr[CyRTPR<<index];
+ tmp = cy_readb(base_addr+(CyRTPR<<index));
cy_put_user(tmp,value);
} else {
// Nothing to do!
@@ -3524,7 +3750,6 @@ get_default_timeout(struct cyclades_port * info, unsigned long *value)
return 0;
}/* get_default_timeout */
-
/*
* This routine allows the tty driver to implement device-
* specific ioctl's. If the ioctl number passed in cmd is
@@ -3601,6 +3826,26 @@ cy_ioctl(struct tty_struct *tty, struct file * file,
case CYSETDEFTIMEOUT:
ret_val = set_default_timeout(info, (unsigned long)arg);
break;
+ case CYSETRFLOW:
+ info->rflow = 1;
+ ret_val = 0;
+ break;
+ case CYRESETRFLOW:
+ info->rflow = 0;
+ ret_val = 0;
+ break;
+ case CYSETRTSDTR_INV:
+ info->rtsdtr_inv = 1;
+ ret_val = 0;
+ break;
+ case CYRESETRTSDTR_INV:
+ info->rtsdtr_inv = 0;
+ ret_val = 0;
+ break;
+ case CYZPOLLCYCLE:
+ cyz_polling_cycle = (HZ * arg) / 1000;
+ ret_val = 0;
+ break;
case TCSBRK: /* SVID version: non-zero arg --> no break */
ret_val = tty_check_change(tty);
if (ret_val)
@@ -3745,7 +3990,7 @@ cy_throttle(struct tty_struct * tty)
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
- printk("cyc:throttle %s: %d....ttyC%d\n", _tty_name(tty, buf),
+ printk("cyc:throttle %s: %d....ttyC%d\n", tty_name(tty, buf),
tty->ldisc.chars_in_buffer(tty), info->line);
#endif
@@ -3769,8 +4014,12 @@ cy_throttle(struct tty_struct * tty)
+ (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR1<<index] = ~CyRTS;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+ }
restore_flags(flags);
} else {
// Nothing to do!
@@ -3796,7 +4045,7 @@ cy_unthrottle(struct tty_struct * tty)
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
- printk("cyc:throttle %s: %d....ttyC%d\n", _tty_name(tty, buf),
+ printk("cyc:throttle %s: %d....ttyC%d\n", tty_name(tty, buf),
tty->ldisc.chars_in_buffer(tty), info->line);
#endif
@@ -3820,8 +4069,12 @@ cy_unthrottle(struct tty_struct * tty)
+ (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyMSVR1<<index] = CyRTS;
+ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+ if (info->rtsdtr_inv) {
+ cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+ } else {
+ cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+ }
restore_flags(flags);
}else{
// Nothing to do!
@@ -3861,9 +4114,10 @@ cy_stop(struct tty_struct *tty)
+ (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR<<index] =
- (u_char)(channel & 0x0003); /* index channel */
- base_addr[CySRER<<index] &= ~CyTxMpty;
+ cy_writeb((u_long)base_addr+(CyCAR<<index),
+ (u_char)(channel & 0x0003)); /* index channel */
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
restore_flags(flags);
} else {
// Nothing to do!
@@ -3900,8 +4154,10 @@ cy_start(struct tty_struct *tty)
+ (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR<<index] = (u_char)(channel & 0x0003);
- base_addr[CySRER<<index] |= CyTxMpty;
+ cy_writeb((u_long)base_addr+(CyCAR<<index),
+ (u_char)(channel & 0x0003)); /* index channel */
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
restore_flags(flags);
} else {
// Nothing to do!
@@ -3968,24 +4224,25 @@ cy_flush_buffer(struct tty_struct *tty)
* ---------------------------------------------------------------------
*/
-
/* initialize chips on Cyclom-Y card -- return number of valid
chips (which is number of ports/4) */
-__initfunc(static int
-cyy_init_card(unsigned char *true_base_addr,int index))
+__initfunc(static unsigned short
+cyy_init_card(volatile ucchar *true_base_addr,int index))
{
unsigned int chip_number;
- unsigned char* base_addr;
+ volatile ucchar* base_addr;
- true_base_addr[Cy_HwReset<<index] = 0; /* Cy_HwReset is 0x1400 */
- true_base_addr[Cy_ClrIntr<<index] = 0; /* Cy_ClrIntr is 0x1800 */
+ cy_writeb((u_long)true_base_addr+(Cy_HwReset<<index), 0);
+ /* Cy_HwReset is 0x1400 */
+ cy_writeb((u_long)true_base_addr+(Cy_ClrIntr<<index), 0);
+ /* Cy_ClrIntr is 0x1800 */
udelay(500L);
- for(chip_number=0; chip_number<CyMaxChipsPerCard; chip_number++){
+ for(chip_number=0; chip_number<CyMAX_CHIPS_PER_CARD; chip_number++){
base_addr = true_base_addr
+ (cy_chip_offset[chip_number]<<index);
udelay(1000L);
- if(base_addr[CyCCR<<index] != 0x00){
+ if(cy_readb(base_addr+(CyCCR<<index)) != 0x00){
/*************
printk(" chip #%d at %#6lx is never idle (CCR != 0)\n",
chip_number, (unsigned long)base_addr);
@@ -3993,7 +4250,7 @@ cyy_init_card(unsigned char *true_base_addr,int index))
return chip_number;
}
- base_addr[CyGFRCR<<index] = 0;
+ cy_writeb((u_long)base_addr+(CyGFRCR<<index), 0);
udelay(10L);
/* The Cyclom-16Y does not decode address bit 9 and therefore
@@ -4003,16 +4260,16 @@ cyy_init_card(unsigned char *true_base_addr,int index))
and this must be a Cyclom-16Y, not a Cyclom-32Ye.
*/
if (chip_number == 4
- && *(true_base_addr
+ && cy_readb(true_base_addr
+ (cy_chip_offset[0]<<index)
+ (CyGFRCR<<index)) == 0){
return chip_number;
}
- base_addr[CyCCR<<index] = CyCHIP_RESET;
+ cy_writeb((u_long)base_addr+(CyCCR<<index), CyCHIP_RESET);
udelay(1000L);
- if(base_addr[CyGFRCR<<index] == 0x00){
+ if(cy_readb(base_addr+(CyGFRCR<<index)) == 0x00){
/*
printk(" chip #%d at %#6lx is not responding ",
chip_number, (unsigned long)base_addr);
@@ -4020,7 +4277,7 @@ cyy_init_card(unsigned char *true_base_addr,int index))
*/
return chip_number;
}
- if((0xf0 & base_addr[CyGFRCR<<index]) != 0x40){
+ if((0xf0 & (cy_readb(base_addr+(CyGFRCR<<index)))) != 0x40){
/*
printk(" chip #%d at %#6lx is not valid (GFRCR == %#2x)\n",
chip_number, (unsigned long)base_addr,
@@ -4028,10 +4285,16 @@ cyy_init_card(unsigned char *true_base_addr,int index))
*/
return chip_number;
}
- base_addr[CyGCR<<index] = CyCH0_SERIAL;
- base_addr[CyPPR<<index] = 244;
- /* better value than CyCLOCK_25_1MS * 5
- to run clock at 200 Hz */
+ cy_writeb((u_long)base_addr+(CyGCR<<index), CyCH0_SERIAL);
+ if (cy_readb(base_addr+(CyGFRCR<<index)) >= 0x48){
+ /* It is a CD1400 rev. J or later */
+ /* Impossible to reach 5ms with this chip.
+ Changed to 2ms instead (f = 500 Hz). */
+ cy_writeb((u_long)base_addr+(CyPPR<<index), CyCLOCK_60_2MS);
+ } else {
+ /* f = 200 Hz */
+ cy_writeb((u_long)base_addr+(CyPPR<<index), CyCLOCK_25_5MS);
+ }
/*
printk(" chip #%d at %#6lx is rev 0x%2x\n",
@@ -4039,11 +4302,9 @@ cyy_init_card(unsigned char *true_base_addr,int index))
base_addr[CyGFRCR<<index]);
*/
}
-
return chip_number;
} /* cyy_init_card */
-
/*
* ---------------------------------------------------------------------
* cy_detect_isa() - Probe for Cyclom-Y/ISA boards.
@@ -4053,9 +4314,9 @@ cyy_init_card(unsigned char *true_base_addr,int index))
__initfunc(static int
cy_detect_isa(void))
{
- unsigned int cy_isa_irq,nboard;
- unsigned char *cy_isa_address;
- unsigned short i,j,cy_isa_nchan;
+ unsigned short cy_isa_irq,nboard;
+ volatile ucchar *cy_isa_address;
+ unsigned short i,j,cy_isa_nchan;
nboard = 0;
@@ -4067,10 +4328,13 @@ cy_detect_isa(void))
}
/* probe for CD1400... */
-#if (LINUX_VERSION_CODE >= 0x020100)
- cy_isa_address = ioremap((unsigned int)cy_isa_address,0x2000);
+
+#if !defined(__alpha__) && (LINUX_VERSION_CODE >= 0x020100)
+ cy_isa_address = ioremap((unsigned int)cy_isa_address,
+ CyISA_Ywin);
#endif
- cy_isa_nchan = 4 * cyy_init_card(cy_isa_address,0);
+ cy_isa_nchan = CyPORTS_PER_CHIP *
+ cyy_init_card(cy_isa_address,0);
if (cy_isa_nchan == 0) {
continue;
}
@@ -4078,15 +4342,15 @@ cy_detect_isa(void))
/* find out the board's irq by probing */
cy_isa_irq = do_auto_irq(cy_isa_address);
if (cy_isa_irq == 0) {
- printk("Cyclom-Y/ISA found at 0x%x ",
- (unsigned int) cy_isa_address);
+ printk("Cyclom-Y/ISA found at 0x%lx ",
+ (unsigned long) cy_isa_address);
printk("but the IRQ could not be detected.\n");
continue;
}
if((cy_next_channel+cy_isa_nchan) > NR_PORTS) {
- printk("Cyclom-Y/ISA found at 0x%x ",
- (unsigned int) cy_isa_address);
+ printk("Cyclom-Y/ISA found at 0x%lx ",
+ (unsigned long) cy_isa_address);
printk("but no more channels are available.\n");
printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
return(nboard);
@@ -4096,8 +4360,8 @@ cy_detect_isa(void))
if (cy_card[j].base_addr == 0) break;
}
if (j == NR_CARDS) { /* no more cy_cards available */
- printk("Cyclom-Y/ISA found at 0x%x ",
- (unsigned int) cy_isa_address);
+ printk("Cyclom-Y/ISA found at 0x%lx ",
+ (unsigned long) cy_isa_address);
printk("but no more cards can be used .\n");
printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
return(nboard);
@@ -4107,15 +4371,15 @@ cy_detect_isa(void))
if(request_irq(cy_isa_irq, cyy_interrupt,
SA_INTERRUPT, "cyclomY", NULL))
{
- printk("Cyclom-Y/ISA found at 0x%x ",
- (unsigned int) cy_isa_address);
+ printk("Cyclom-Y/ISA found at 0x%lx ",
+ (unsigned long) cy_isa_address);
printk("but could not allocate IRQ#%d.\n",
cy_isa_irq);
return(nboard);
}
/* set cy_card */
- cy_card[j].base_addr = (int) cy_isa_address;
+ cy_card[j].base_addr = (u_long) cy_isa_address;
cy_card[j].ctl_addr = 0;
cy_card[j].irq = (int) cy_isa_irq;
cy_card[j].bus_index = 0;
@@ -4125,10 +4389,10 @@ cy_detect_isa(void))
nboard++;
/* print message */
- printk("Cyclom-Y/ISA #%d: 0x%x-0x%x, IRQ%d, ",
- j+1, (unsigned int) cy_isa_address,
- (unsigned int)(cy_isa_address + 0x1fff),
- cy_isa_irq);
+ printk("Cyclom-Y/ISA #%d: 0x%lx-0x%lx, IRQ%d, ",
+ j+1, (unsigned long) cy_isa_address,
+ (unsigned long)(cy_isa_address + (CyISA_Ywin - 1)),
+ cy_isa_irq);
printk("%d channels starting from port %d.\n",
cy_isa_nchan, cy_next_channel);
cy_next_channel += cy_isa_nchan;
@@ -4150,11 +4414,11 @@ cy_detect_pci(void))
unsigned char cyy_bus, cyy_dev_fn, cyy_rev_id;
unsigned long pci_intr_ctrl;
unsigned char cy_pci_irq;
- unsigned int cy_pci_addr0, cy_pci_addr1, cy_pci_addr2;
+ uclong cy_pci_addr0, cy_pci_addr1, cy_pci_addr2;
unsigned short i,j,cy_pci_nchan;
unsigned short device_id,dev_index = 0,board_index = 0;
- unsigned long mailbox;
- unsigned int Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0;
+ uclong mailbox;
+ uclong Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0;
if(pcibios_present() == 0) { /* PCI bus not present */
return(0);
@@ -4181,13 +4445,17 @@ cy_detect_pci(void))
pcibios_read_config_byte(cyy_bus, cyy_dev_fn,
PCI_INTERRUPT_LINE, &cy_pci_irq);
pcibios_read_config_dword(cyy_bus, cyy_dev_fn,
- PCI_BASE_ADDRESS_0, &cy_pci_addr0);
+ PCI_BASE_ADDRESS_0,
+ (unsigned int *) &cy_pci_addr0);
pcibios_read_config_dword(cyy_bus, cyy_dev_fn,
- PCI_BASE_ADDRESS_1, &cy_pci_addr1);
+ PCI_BASE_ADDRESS_1,
+ (unsigned int *) &cy_pci_addr1);
pcibios_read_config_dword(cyy_bus, cyy_dev_fn,
- PCI_BASE_ADDRESS_2, &cy_pci_addr2);
+ PCI_BASE_ADDRESS_2,
+ (unsigned int *) &cy_pci_addr2);
pcibios_read_config_byte(cyy_bus, cyy_dev_fn,
PCI_REVISION_ID, &cyy_rev_id);
+
if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo)
|| (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){
#ifdef CY_PCI_DEBUG
@@ -4195,34 +4463,48 @@ cy_detect_pci(void))
cyy_bus, cyy_dev_fn);
printk("rev_id=%d) IRQ%d\n",
cyy_rev_id, (int)cy_pci_irq);
- printk("Cyclom-Y/PCI:found winaddr=0x%x ioaddr=0x%x\n",
- cy_pci_addr2, cy_pci_addr1);
+ printk("Cyclom-Y/PCI:found winaddr=0x%lx ioaddr=0x%lx\n",
+ (ulong)cy_pci_addr2, (ulong)cy_pci_addr1);
#endif
cy_pci_addr1 &= 0xfffffffc;
cy_pci_addr2 &= 0xfffffff0;
-#if (LINUX_VERSION_CODE < 0x020100)
+#if defined(__alpha__)
+ if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */
+ printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ",
+ cyy_bus, cyy_dev_fn);
+ printk("rev_id=%d) IRQ%d\n",
+ cyy_rev_id, (int)cy_pci_irq);
+ printk("Cyclom-Y/PCI:found winaddr=0x%lx ioaddr=0x%lx\n",
+ (ulong)cy_pci_addr2, (ulong)cy_pci_addr1);
+ printk("Cyclom-Y/PCI not supported for low addresses in "
+ "Alpha systems.\n");
+ i--;
+ continue;
+ }
+#else
+#if (LINUX_VERSION_CODE < 0x020100)
if ((ulong)cy_pci_addr2 >= 0x100000) /* above 1M? */
#endif
- cy_pci_addr2 =
- (unsigned int) ioremap(cy_pci_addr2,CyPCI_Ywin);
+ cy_pci_addr2 = (ulong) ioremap(cy_pci_addr2, CyPCI_Ywin);
+#endif
#ifdef CY_PCI_DEBUG
- printk("Cyclom-Y/PCI: relocate winaddr=0x%x ioaddr=0x%x\n",
- cy_pci_addr2, cy_pci_addr1);
+ printk("Cyclom-Y/PCI: relocate winaddr=0x%lx ioaddr=0x%lx\n",
+ (u_long)cy_pci_addr2, (u_long)cy_pci_addr1);
#endif
- cy_pci_nchan = 4 * cyy_init_card((unsigned char *)
- cy_pci_addr2,1);
+ cy_pci_nchan = (unsigned short)(CyPORTS_PER_CHIP *
+ cyy_init_card((volatile ucchar *)cy_pci_addr2, 1));
if(cy_pci_nchan == 0) {
printk("Cyclom-Y PCI host card with ");
- printk("no Serial-Modules at 0x%x.\n",
- (unsigned int) cy_pci_addr2);
+ printk("no Serial-Modules at 0x%lx.\n",
+ (ulong) cy_pci_addr2);
i--;
continue;
}
if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
- printk("Cyclom-Y/PCI found at 0x%x ",
- (unsigned int) cy_pci_addr2);
+ printk("Cyclom-Y/PCI found at 0x%lx ",
+ (ulong) cy_pci_addr2);
printk("but no channels are available.\n");
printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
return(i);
@@ -4232,8 +4514,8 @@ cy_detect_pci(void))
if (cy_card[j].base_addr == 0) break;
}
if (j == NR_CARDS) { /* no more cy_cards available */
- printk("Cyclom-Y/PCI found at 0x%x ",
- (unsigned int) cy_pci_addr2);
+ printk("Cyclom-Y/PCI found at 0x%lx ",
+ (ulong) cy_pci_addr2);
printk("but no more cards can be used.\n");
printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
return(i);
@@ -4243,15 +4525,15 @@ cy_detect_pci(void))
if(request_irq(cy_pci_irq, cyy_interrupt,
SA_INTERRUPT, "cyclomY", NULL))
{
- printk("Cyclom-Y/PCI found at 0x%x ",
- (unsigned int) cy_pci_addr2);
+ printk("Cyclom-Y/PCI found at 0x%lx ",
+ (ulong) cy_pci_addr2);
printk("but could not allocate IRQ%d.\n",
cy_pci_irq);
return(i);
}
/* set cy_card */
- cy_card[j].base_addr = (int) cy_pci_addr2;
+ cy_card[j].base_addr = (ulong)cy_pci_addr2;
cy_card[j].ctl_addr = 0;
cy_card[j].irq = (int) cy_pci_irq;
cy_card[j].bus_index = 1;
@@ -4266,9 +4548,11 @@ cy_detect_pci(void))
| inw(cy_pci_addr1+0x6a)<<16);
/* print message */
- printk("Cyclom-Y/PCI #%d: 0x%x-0x%x, IRQ%d, ",
- j+1, cy_pci_addr2, (cy_pci_addr2 + CyPCI_Ywin - 1),
- (int)cy_pci_irq);
+ printk("Cyclom-Y/PCI #%d: 0x%lx-0x%lx, IRQ%d, ",
+ j+1,
+ (ulong)cy_pci_addr2,
+ (ulong)(cy_pci_addr2 + CyPCI_Ywin - 1),
+ (int)cy_pci_irq);
printk("%d channels starting from port %d.\n",
cy_pci_nchan, cy_next_channel);
@@ -4279,8 +4563,8 @@ cy_detect_pci(void))
cyy_bus, cyy_dev_fn);
printk("rev_id=%d) IRQ%d\n",
cyy_rev_id, (int)cy_pci_irq);
- printk("Cyclades-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n",
- cy_pci_addr2, cy_pci_addr0);
+ printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n",
+ (ulong)cy_pci_addr2, (ulong)cy_pci_addr0);
printk("Cyclades-Z/PCI not supported for low addresses\n");
break;
}else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){
@@ -4289,25 +4573,29 @@ cy_detect_pci(void))
cyy_bus, cyy_dev_fn);
printk("rev_id=%d) IRQ%d\n",
cyy_rev_id, (int)cy_pci_irq);
- printk("Cyclades-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n",
- cy_pci_addr2, cy_pci_addr0);
+ printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n",
+ (ulong)cy_pci_addr2, (ulong)cy_pci_addr0);
#endif
cy_pci_addr0 &= 0xfffffff0;
+#if !defined(__alpha__)
cy_pci_addr0 = (unsigned int) ioremap(
cy_pci_addr0 & PAGE_MASK,
PAGE_ALIGN(CyPCI_Zctl))
+ (cy_pci_addr0 & (PAGE_SIZE-1));
-
- mailbox = ((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0;
+#endif
+ mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *)
+ cy_pci_addr0)->mail_box_0);
cy_pci_addr2 &= 0xfffffff0;
if (mailbox == ZE_V1) {
+#if !defined(__alpha__)
cy_pci_addr2 = (unsigned int) ioremap(
cy_pci_addr2 & PAGE_MASK,
PAGE_ALIGN(CyPCI_Ze_win))
+ (cy_pci_addr2 & (PAGE_SIZE-1));
+#endif
if (ZeIndex == NR_CARDS) {
- printk("Cyclades-Z/PCI found at 0x%x ",
- (unsigned int) cy_pci_addr2);
+ printk("Cyclades-Ze/PCI found at 0x%lx ",
+ (ulong)cy_pci_addr2);
printk("but no more cards can be used.\n");
printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
} else {
@@ -4318,24 +4606,28 @@ cy_detect_pci(void))
i--;
continue;
} else {
+#if !defined(__alpha__)
cy_pci_addr2 = (unsigned int) ioremap(
cy_pci_addr2 & PAGE_MASK,
PAGE_ALIGN(CyPCI_Zwin))
+ (cy_pci_addr2 & (PAGE_SIZE-1));
+#endif
}
#ifdef CY_PCI_DEBUG
- printk("Cyclades-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n",
- cy_pci_addr2, cy_pci_addr0);
+ printk("Cyclades-Z/PCI: relocate winaddr=0x%lx ctladdr=0x%lx\n",
+ (ulong)cy_pci_addr2, (ulong)cy_pci_addr0);
if (mailbox == ZO_V1) {
- ((struct RUNTIME_9060 *)(cy_pci_addr0))
- ->loc_addr_base = WIN_CREG;
+ cy_writel(&((struct RUNTIME_9060 *)
+ (cy_pci_addr0))->loc_addr_base, WIN_CREG);
PAUSE
- printk("Cyclades-Z/PCI: FPGA id %lx, ver %lx\n",
- 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_id,
- 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_version);
- ((struct RUNTIME_9060 *)(cy_pci_addr0))
- ->loc_addr_base = WIN_RAM;
+ printk("Cyclades-8Zo/PCI: FPGA id %lx, ver %lx\n",
+ (ulong)(0xff & cy_readl(&((struct CUSTOM_REG *)
+ (cy_pci_addr2))->fpga_id)),
+ (ulong)(0xff & cy_readl(&((struct CUSTOM_REG *)
+ (cy_pci_addr2))->fpga_version)));
+ cy_writel(&((struct RUNTIME_9060 *)
+ (cy_pci_addr0))->loc_addr_base, WIN_RAM);
} else {
printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n");
}
@@ -4345,8 +4637,8 @@ cy_detect_pci(void))
until it has been properly initialized.
*/
PAUSE
- if (mailbox == ZO_V1)
- *(unsigned long *)(cy_pci_addr2+ID_ADDRESS) = 0L;
+ if ((mailbox == ZO_V1) || (mailbox == ZO_V2))
+ cy_writel((ulong)(cy_pci_addr2+ID_ADDRESS), 0L);
/* This must be a Cyclades-8Zo/PCI. The extendable
version will have a different device_id and will
@@ -4354,8 +4646,8 @@ cy_detect_pci(void))
cy_pci_nchan = 8;
if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
- printk("Cyclades-Z/PCI found at 0x%x ",
- (unsigned int) cy_pci_addr2);
+ printk("Cyclades-8Zo/PCI found at 0x%lx ",
+ (ulong)cy_pci_addr2);
printk("but no channels are available.\n");
printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
return(i);
@@ -4366,8 +4658,8 @@ cy_detect_pci(void))
if (cy_card[j].base_addr == 0) break;
}
if (j == NR_CARDS) { /* no more cy_cards available */
- printk("Cyclades-Z/PCI found at 0x%x ",
- (unsigned int) cy_pci_addr2);
+ printk("Cyclades-8Zo/PCI found at 0x%lx ",
+ (ulong)cy_pci_addr2);
printk("but no more cards can be used.\n");
printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
return(i);
@@ -4380,12 +4672,13 @@ cy_detect_pci(void))
{
printk("Could not allocate IRQ%d ",
cy_pci_irq);
- printk("for Cyclades-Z/PCI at 0x%x.\n",
- (unsigned int) cy_pci_addr2);
+ printk("for Cyclades-8Zo/PCI at 0x%lx.\n",
+ (ulong)cy_pci_addr2);
return(i);
}
}
+
/* set cy_card */
cy_card[j].base_addr = cy_pci_addr2;
cy_card[j].ctl_addr = cy_pci_addr0;
@@ -4398,14 +4691,14 @@ cy_detect_pci(void))
/* print message */
/* don't report IRQ if board is no IRQ */
if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) {
- printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ",
- j+1,cy_pci_addr2,
- (cy_pci_addr2 + CyPCI_Zwin - 1),
+ printk("Cyclades-8Zo/PCI #%d: 0x%lx-0x%lx, IRQ%d, ",
+ j+1,(ulong)cy_pci_addr2,
+ (ulong)(cy_pci_addr2 + CyPCI_Zwin - 1),
(int)cy_pci_irq);
}else{
- printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, ",
- j+1,cy_pci_addr2,
- (cy_pci_addr2 + CyPCI_Zwin - 1));
+ printk("Cyclades-8Zo/PCI #%d: 0x%lx-0x%lx, ",
+ j+1,(ulong)cy_pci_addr2,
+ (ulong)(cy_pci_addr2 + CyPCI_Zwin - 1));
}
printk("%d channels starting from port %d.\n",
cy_pci_nchan,cy_next_channel);
@@ -4421,10 +4714,11 @@ cy_detect_pci(void))
Ze_addr2[j] = Ze_addr2[j+1];
}
ZeIndex--;
- mailbox = ((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0;
+ mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *)
+ cy_pci_addr0)->mail_box_0);
#ifdef CY_PCI_DEBUG
- printk("Cyclades-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n",
- cy_pci_addr2, cy_pci_addr0);
+ printk("Cyclades-Z/PCI: relocate winaddr=0x%lx ctladdr=0x%lx\n",
+ (ulong)cy_pci_addr2, (ulong)cy_pci_addr0);
printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n");
#endif
/* The following clears the firmware id word. This ensures
@@ -4436,8 +4730,8 @@ cy_detect_pci(void))
cy_pci_nchan = ZE_V1_NPORTS;
if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
- printk("Cyclades-Z/PCI found at 0x%x ",
- (unsigned int) cy_pci_addr2);
+ printk("Cyclades-Ze/PCI found at 0x%lx ",
+ (ulong)cy_pci_addr2);
printk("but no channels are available.\n");
printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
return(i);
@@ -4448,8 +4742,8 @@ cy_detect_pci(void))
if (cy_card[j].base_addr == 0) break;
}
if (j == NR_CARDS) { /* no more cy_cards available */
- printk("Cyclades-Z/PCI found at 0x%x ",
- (unsigned int) cy_pci_addr2);
+ printk("Cyclades-Ze/PCI found at 0x%lx ",
+ (ulong)cy_pci_addr2);
printk("but no more cards can be used.\n");
printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
return(i);
@@ -4462,8 +4756,8 @@ cy_detect_pci(void))
{
printk("Could not allocate IRQ%d ",
cy_pci_irq);
- printk("for Cyclades-Z/PCI at 0x%x.\n",
- (unsigned int) cy_pci_addr2);
+ printk("for Cyclades-Ze/PCI at 0x%lx.\n",
+ (ulong) cy_pci_addr2);
return(i);
}
}
@@ -4480,21 +4774,21 @@ cy_detect_pci(void))
/* print message */
/* don't report IRQ if board is no IRQ */
if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) {
- printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ",
- j+1,cy_pci_addr2,
- (cy_pci_addr2 + CyPCI_Ze_win - 1),
+ printk("Cyclades-Ze/PCI #%d: 0x%lx-0x%lx, IRQ%d, ",
+ j+1,(ulong)cy_pci_addr2,
+ (ulong)(cy_pci_addr2 + CyPCI_Ze_win - 1),
(int)cy_pci_irq);
}else{
- printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, ",
- j+1,cy_pci_addr2,
- (cy_pci_addr2 + CyPCI_Ze_win - 1));
+ printk("Cyclades-Ze/PCI #%d: 0x%lx-0x%lx, ",
+ j+1,(ulong)cy_pci_addr2,
+ (ulong)(cy_pci_addr2 + CyPCI_Ze_win - 1));
}
printk("%d channels starting from port %d.\n",
cy_pci_nchan,cy_next_channel);
cy_next_channel += cy_pci_nchan;
}
if (ZeIndex != 0) {
- printk("Cyclades-Z/PCI found at 0x%x ",
+ printk("Cyclades-Ze/PCI found at 0x%x ",
(unsigned int) Ze_addr2[0]);
printk("but no more cards can be used.\n");
printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
@@ -4520,7 +4814,7 @@ show_version(void)
tmp = strrchr(rcsdate, ' '); *tmp = '\0';
printk("Cyclom driver %s %s\n",
rcsvers, rcsdate);
- printk("\tbuilt %s %s\n",
+ printk(" built %s %s\n",
__DATE__, __TIME__);
} /* show_version */
@@ -4549,7 +4843,7 @@ cy_init(void))
struct cyclades_port *info;
struct cyclades_card *cinfo;
int number_z_boards = 0;
- int board,port,i;
+ int board,port,i,index;
unsigned long mailbox;
int nports;
@@ -4623,7 +4917,7 @@ cy_init(void))
/* look for isa boards */
cy_isa_nboard = cy_detect_isa();
-
+
/* look for pci boards */
cy_pci_nboard = cy_detect_pci();
@@ -4649,10 +4943,10 @@ cy_init(void))
/* initialize per-port data structures for each valid board found */
for (board = 0 ; board < cy_nboard ; board++) {
cinfo = &cy_card[board];
- if (cinfo->num_chips == 1){ /* Cyclades-8Zo/PCI */
+ if (cinfo->num_chips == 1){ /* Cyclades-Z */
number_z_boards++;
- mailbox = ((struct RUNTIME_9060 *)
- cy_card[board].ctl_addr)->mail_box_0;
+ mailbox = cy_readl(&((struct RUNTIME_9060 *)
+ cy_card[board].ctl_addr)->mail_box_0);
nports = (mailbox == ZE_V1) ? ZE_V1_NPORTS : 8;
for (port = cinfo->first_line ;
port < cinfo->first_line + nports;
@@ -4680,7 +4974,7 @@ cy_init(void))
info->event = 0;
info->count = 0;
#ifdef SERIAL_DEBUG_COUNT
- printk("cyc:cy_init(1) setting Z count to 0\n");
+// printk("cyc:cy_init(1) setting Z count to 0\n");
#endif
info->blocked_open = 0;
info->default_threshold = 0;
@@ -4705,6 +4999,7 @@ cy_init(void))
}
continue;
}else{ /* Cyclom-Y of some kind*/
+ index = cinfo->bus_index;
for (port = cinfo->first_line ;
port < cinfo->first_line + 4*cinfo->num_chips ;
port++)
@@ -4722,16 +5017,28 @@ cy_init(void))
info->cor3 = 0x08; /* _very_ small rcv threshold */
info->cor4 = 0;
info->cor5 = 0;
- info->tbpr = baud_bpr[13]; /* Tx BPR */
- info->tco = baud_co[13]; /* Tx CO */
- info->rbpr = baud_bpr[13]; /* Rx BPR */
- info->rco = baud_co[13]; /* Rx CO */
info->close_delay = 0;
+ if (cy_readb(cinfo->base_addr+(CyGFRCR<<index)) >= 0x48) {
+ /* It is a CD1400 rev. J or later */
+ info->tbpr = baud_bpr_60[13]; /* Tx BPR */
+ info->tco = baud_co_60[13]; /* Tx CO */
+ info->rbpr = baud_bpr_60[13]; /* Rx BPR */
+ info->rco = baud_co_60[13]; /* Rx CO */
+ info->rflow = 0;
+ info->rtsdtr_inv = 1;
+ } else {
+ info->tbpr = baud_bpr_25[13]; /* Tx BPR */
+ info->tco = baud_co_25[13]; /* Tx CO */
+ info->rbpr = baud_bpr_25[13]; /* Rx BPR */
+ info->rco = baud_co_25[13]; /* Rx CO */
+ info->rflow = 0;
+ info->rtsdtr_inv = 0;
+ }
info->x_char = 0;
info->event = 0;
info->count = 0;
#ifdef SERIAL_DEBUG_COUNT
- printk("cyc:cy_init(2) setting Y count to 0\n");
+// printk("cyc:cy_init(2) setting Y count to 0\n");
#endif
info->blocked_open = 0;
info->default_threshold = 0;
@@ -4874,54 +5181,54 @@ show_status(int line_num)
/* Global Registers */
- printk(" CyGFRCR %x\n", base_addr[CyGFRCR<<index]);
- printk(" CyCAR %x\n", base_addr[CyCAR<<index]);
- printk(" CyGCR %x\n", base_addr[CyGCR<<index]);
- printk(" CySVRR %x\n", base_addr[CySVRR<<index]);
- printk(" CyRICR %x\n", base_addr[CyRICR<<index]);
- printk(" CyTICR %x\n", base_addr[CyTICR<<index]);
- printk(" CyMICR %x\n", base_addr[CyMICR<<index]);
- printk(" CyRIR %x\n", base_addr[CyRIR<<index]);
- printk(" CyTIR %x\n", base_addr[CyTIR<<index]);
- printk(" CyMIR %x\n", base_addr[CyMIR<<index]);
- printk(" CyPPR %x\n", base_addr[CyPPR<<index]);
+ printk(" CyGFRCR %x\n", cy_readb(base_addr + CyGFRCR<<index));
+ printk(" CyCAR %x\n", cy_readb(base_addr + CyCAR<<index));
+ printk(" CyGCR %x\n", cy_readb(base_addr + CyGCR<<index));
+ printk(" CySVRR %x\n", cy_readb(base_addr + CySVRR<<index));
+ printk(" CyRICR %x\n", cy_readb(base_addr + CyRICR<<index));
+ printk(" CyTICR %x\n", cy_readb(base_addr + CyTICR<<index));
+ printk(" CyMICR %x\n", cy_readb(base_addr + CyMICR<<index));
+ printk(" CyRIR %x\n", cy_readb(base_addr + CyRIR<<index));
+ printk(" CyTIR %x\n", cy_readb(base_addr + CyTIR<<index));
+ printk(" CyMIR %x\n", cy_readb(base_addr + CyMIR<<index));
+ printk(" CyPPR %x\n", cy_readb(base_addr + CyPPR<<index));
- base_addr[CyCAR<<index] = (u_char)channel;
+ cy_writeb(base_addr + CyCAR<<index, (u_char)channel);
/* Virtual Registers */
- printk(" CyRIVR %x\n", base_addr[CyRIVR<<index]);
- printk(" CyTIVR %x\n", base_addr[CyTIVR<<index]);
- printk(" CyMIVR %x\n", base_addr[CyMIVR<<index]);
- printk(" CyMISR %x\n", base_addr[CyMISR<<index]);
+ printk(" CyRIVR %x\n", cy_readb(base_addr + CyRIVR<<index));
+ printk(" CyTIVR %x\n", cy_readb(base_addr + CyTIVR<<index));
+ printk(" CyMIVR %x\n", cy_readb(base_addr + CyMIVR<<index));
+ printk(" CyMISR %x\n", cy_readb(base_addr + CyMISR<<index));
/* Channel Registers */
- printk(" CyCCR %x\n", base_addr[CyCCR<<index]);
- printk(" CySRER %x\n", base_addr[CySRER<<index]);
- printk(" CyCOR1 %x\n", base_addr[CyCOR1<<index]);
- printk(" CyCOR2 %x\n", base_addr[CyCOR2<<index]);
- printk(" CyCOR3 %x\n", base_addr[CyCOR3<<index]);
- printk(" CyCOR4 %x\n", base_addr[CyCOR4<<index]);
- printk(" CyCOR5 %x\n", base_addr[CyCOR5<<index]);
- printk(" CyCCSR %x\n", base_addr[CyCCSR<<index]);
- printk(" CyRDCR %x\n", base_addr[CyRDCR<<index]);
- printk(" CySCHR1 %x\n", base_addr[CySCHR1<<index]);
- printk(" CySCHR2 %x\n", base_addr[CySCHR2<<index]);
- printk(" CySCHR3 %x\n", base_addr[CySCHR3<<index]);
- printk(" CySCHR4 %x\n", base_addr[CySCHR4<<index]);
- printk(" CySCRL %x\n", base_addr[CySCRL<<index]);
- printk(" CySCRH %x\n", base_addr[CySCRH<<index]);
- printk(" CyLNC %x\n", base_addr[CyLNC<<index]);
- printk(" CyMCOR1 %x\n", base_addr[CyMCOR1<<index]);
- printk(" CyMCOR2 %x\n", base_addr[CyMCOR2<<index]);
- printk(" CyRTPR %x\n", base_addr[CyRTPR<<index]);
- printk(" CyMSVR1 %x\n", base_addr[CyMSVR1<<index]);
- printk(" CyMSVR2 %x\n", base_addr[CyMSVR2<<index]);
- printk(" CyRBPR %x\n", base_addr[CyRBPR<<index]);
- printk(" CyRCOR %x\n", base_addr[CyRCOR<<index]);
- printk(" CyTBPR %x\n", base_addr[CyTBPR<<index]);
- printk(" CyTCOR %x\n", base_addr[CyTCOR<<index]);
+ printk(" CyCCR %x\n", cy_readb(base_addr + CyCCR<<index));
+ printk(" CySRER %x\n", cy_readb(base_addr + CySRER<<index));
+ printk(" CyCOR1 %x\n", cy_readb(base_addr + CyCOR1<<index));
+ printk(" CyCOR2 %x\n", cy_readb(base_addr + CyCOR2<<index));
+ printk(" CyCOR3 %x\n", cy_readb(base_addr + CyCOR3<<index));
+ printk(" CyCOR4 %x\n", cy_readb(base_addr + CyCOR4<<index));
+ printk(" CyCOR5 %x\n", cy_readb(base_addr + CyCOR5<<index));
+ printk(" CyCCSR %x\n", cy_readb(base_addr + CyCCSR<<index));
+ printk(" CyRDCR %x\n", cy_readb(base_addr + CyRDCR<<index));
+ printk(" CySCHR1 %x\n", cy_readb(base_addr + CySCHR1<<index));
+ printk(" CySCHR2 %x\n", cy_readb(base_addr + CySCHR2<<index));
+ printk(" CySCHR3 %x\n", cy_readb(base_addr + CySCHR3<<index));
+ printk(" CySCHR4 %x\n", cy_readb(base_addr + CySCHR4<<index));
+ printk(" CySCRL %x\n", cy_readb(base_addr + CySCRL<<index));
+ printk(" CySCRH %x\n", cy_readb(base_addr + CySCRH<<index));
+ printk(" CyLNC %x\n", cy_readb(base_addr + CyLNC<<index));
+ printk(" CyMCOR1 %x\n", cy_readb(base_addr + CyMCOR1<<index));
+ printk(" CyMCOR2 %x\n", cy_readb(base_addr + CyMCOR2<<index));
+ printk(" CyRTPR %x\n", cy_readb(base_addr + CyRTPR<<index));
+ printk(" CyMSVR1 %x\n", cy_readb(base_addr + CyMSVR1<<index));
+ printk(" CyMSVR2 %x\n", cy_readb(base_addr + CyMSVR2<<index));
+ printk(" CyRBPR %x\n", cy_readb(base_addr + CyRBPR<<index));
+ printk(" CyRCOR %x\n", cy_readb(base_addr + CyRCOR<<index));
+ printk(" CyTBPR %x\n", cy_readb(base_addr + CyTBPR<<index));
+ printk(" CyTCOR %x\n", cy_readb(base_addr + CyTCOR<<index));
restore_flags(flags);
} /* show_status */
diff --git a/drivers/char/epca.c b/drivers/char/epca.c
index 63da8b5cb..bc25dca1d 100644
--- a/drivers/char/epca.c
+++ b/drivers/char/epca.c
@@ -1384,7 +1384,7 @@ static int block_til_ready(struct tty_struct *tty,
(do_clocal || (ch->imodem & ch->dcd)))
break;
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
{
retval = -ERESTARTSYS;
break;
diff --git a/drivers/char/esp.c b/drivers/char/esp.c
index 636c6369d..5fdcd8a77 100644
--- a/drivers/char/esp.c
+++ b/drivers/char/esp.c
@@ -47,6 +47,7 @@
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
+#include <linux/serialP.h>
#include <linux/serial_reg.h>
#include <linux/major.h>
#include <linux/string.h>
@@ -140,11 +141,8 @@ static void change_speed(struct esp_struct *info);
static void rs_wait_until_sent(struct tty_struct *, int);
/*
- * This assumes you have a 1.8432 MHz clock for your UART.
- *
- * It'd be nice if someone built a serial card with a 24.576 MHz
- * clock, since the 16550A is capable of handling a top speed of 1.5
- * megabits/second; but this requires the faster clock.
+ * The ESP card has a clock rate of 14.7456 MHz (that is, 2**ESPC_SCALE
+ * times the normal 1.8432 Mhz clock of most serial boards).
*/
#define BASE_BAUD ((1843200 / 16) * (1 << ESPC_SCALE))
@@ -192,15 +190,6 @@ static inline int serial_paranoia_check(struct esp_struct *info,
return 0;
}
-/*
- * This is used to figure out the divisor speeds
- */
-static int quot_table[] = {
-/* 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, */
- 0, 18432, 12288, 8378, 6878, 6144, 4608, 3072, 1536, 768,
-/* 1800,2400,4800,9600,19200,38400,57600,115200,230400,460800 */
- 512, 384, 192, 96, 48, 24, 16, 8, 4, 2, 0 };
-
static inline unsigned int serial_in(struct esp_struct *info, int offset)
{
return inb(info->port + offset);
@@ -1096,7 +1085,7 @@ static void change_speed(struct esp_struct *info)
unsigned short port;
int quot = 0;
unsigned cflag,cval;
- int i, bits;
+ int baud, bits;
unsigned char flow1 = 0, flow2 = 0;
unsigned long flags;
@@ -1104,27 +1093,7 @@ static void change_speed(struct esp_struct *info)
return;
cflag = info->tty->termios->c_cflag;
port = info->port;
- i = cflag & CBAUD;
- if (i & CBAUDEX) {
- i &= ~CBAUDEX;
- if (i < 1 || i > 2)
- info->tty->termios->c_cflag &= ~CBAUDEX;
- else
- i += 15;
- }
- if (i == 15) {
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- i += 1;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- i += 2;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
- i += 3;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
- i += 4;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
- quot = info->custom_divisor;
- }
-
+
/* byte size and parity */
switch (cflag & CSIZE) {
case CS5: cval = 0x00; bits = 7; break;
@@ -1148,14 +1117,20 @@ static void change_speed(struct esp_struct *info)
cval |= UART_LCR_SPAR;
#endif
- if (!quot) {
- quot = quot_table[i];
-
- /* default to 9600 bps */
- if (!quot)
- quot = BASE_BAUD / 9600;
- }
-
+ baud = tty_get_baud_rate(info->tty);
+ if (baud == 38400)
+ quot = info->custom_divisor;
+ else {
+ if (baud == 134)
+ /* Special case since 134 is really 134.5 */
+ quot = (2*BASE_BAUD / 269);
+ else if (baud)
+ quot = BASE_BAUD / baud;
+ }
+ /* If the quotient is ever zero, default to 9600 bps */
+ if (!quot)
+ quot = BASE_BAUD / 9600;
+
info->timeout = ((1024 * HZ * bits * quot) / BASE_BAUD) + (HZ / 50);
/* CTS flow control flag and modem status interrupts */
@@ -1632,8 +1607,17 @@ check_and_exit:
if (((old_info.flags & ASYNC_SPD_MASK) !=
(info->flags & ASYNC_SPD_MASK)) ||
(old_info.custom_divisor != info->custom_divisor) ||
- change_flow)
+ change_flow) {
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ info->tty->alt_speed = 57600;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ info->tty->alt_speed = 115200;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+ info->tty->alt_speed = 230400;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+ info->tty->alt_speed = 460800;
change_speed(info);
+ }
} else
retval = startup(info);
return retval;
@@ -1723,30 +1707,27 @@ static int set_modem_info(struct esp_struct * info, unsigned int cmd,
}
/*
- * This routine sends a break character out the serial port.
+ * rs_break() --- routine which turns the break handling on or off
*/
-static void send_break( struct esp_struct * info, int duration)
+static void esp_break(struct tty_struct *tty, int break_state)
{
- cli();
- serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
- serial_out(info, UART_ESI_CMD2, 0x01);
+ struct esp_struct * info = (struct esp_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "esp_break"))
+ return;
- interruptible_sleep_on(&info->break_wait);
+ save_flags(flags); cli();
+ if (break_state == -1) {
+ serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
+ serial_out(info, UART_ESI_CMD2, 0x01);
- if (current->signal & ~current->blocked) {
+ interruptible_sleep_on(&info->break_wait);
+ } else {
serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
serial_out(info, UART_ESI_CMD2, 0x00);
- sti();
- return;
}
-
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + duration;
- schedule();
-
- serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
- serial_out(info, UART_ESI_CMD2, 0x00);
- sti();
+ restore_flags(flags);
}
static int rs_ioctl(struct tty_struct *tty, struct file * file,
@@ -1754,7 +1735,6 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
{
int error;
struct esp_struct * info = (struct esp_struct *)tty->driver_data;
- int retval;
struct async_icount cprev, cnow; /* kernel counter temps */
struct serial_icounter_struct *p_cuser; /* user space */
@@ -1770,41 +1750,6 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
}
switch (cmd) {
- case TCSBRK: /* SVID version: non-zero arg --> no break */
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- if (current->signal & ~current->blocked)
- return -EINTR;
- if (!arg) {
- send_break(info, HZ/4); /* 1/4 second */
- if (current->signal & ~current->blocked)
- return -EINTR;
- }
- return 0;
- case TCSBRKP: /* support for POSIX tcsendbreak() */
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- if (current->signal & ~current->blocked)
- return -EINTR;
- send_break(info, arg ? arg*(HZ/10) : HZ/4);
- if (current->signal & ~current->blocked)
- return -EINTR;
- return 0;
- case TIOCGSOFTCAR:
- return put_user(C_CLOCAL(tty) ? 1 : 0,
- (int *) arg);
- case TIOCSSOFTCAR:
- error = get_user(arg, (unsigned int *)arg);
- if (error)
- return error;
- tty->termios->c_cflag =
- ((tty->termios->c_cflag & ~CLOCAL) |
- (arg ? CLOCAL : 0));
- return 0;
case TIOCMGET:
return get_modem_info(info, (unsigned int *) arg);
case TIOCMBIS:
@@ -1845,7 +1790,7 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
while (1) {
interruptible_sleep_on(&info->delta_msr_wait);
/* see if a signal did it */
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -ERESTARTSYS;
cli();
cnow = info->icount; /* atomic copy */
@@ -2086,7 +2031,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
current->timeout = jiffies + char_time;
schedule();
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
break;
if (timeout && ((orig_jiffies + timeout) < jiffies))
@@ -2243,7 +2188,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
!(info->flags & ASYNC_CLOSING) &&
(do_clocal))
break;
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
@@ -2527,6 +2472,7 @@ __initfunc(int espserial_init(void))
esp_driver.stop = rs_stop;
esp_driver.start = rs_start;
esp_driver.hangup = esp_hangup;
+ esp_driver.break_ctl = esp_break;
esp_driver.wait_until_sent = rs_wait_until_sent;
/*
diff --git a/drivers/char/ftape/Config.in b/drivers/char/ftape/Config.in
new file mode 100644
index 000000000..858f327d3
--- /dev/null
+++ b/drivers/char/ftape/Config.in
@@ -0,0 +1,38 @@
+#
+# Ftape configuration
+#
+dep_tristate 'Zftape, the VFS interface' CONFIG_ZFTAPE $CONFIG_FTAPE
+if [ "$CONFIG_ZFTAPE" != "n" ]; then
+ int 'Default block size' CONFIG_ZFT_DFLT_BLK_SZ 10240
+ comment 'The compressor will be built as a module only!'
+ define_bool CONFIG_ZFT_COMPRESSOR m
+fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ int 'Number of ftape buffers (EXPERIMENTAL)' CONFIG_FT_NR_BUFFERS 3
+fi
+if [ "$CONFIG_PROC_FS" = "y" ]; then
+ bool 'Enable procfs status report (+2kb)' CONFIG_FT_PROC_FS y
+fi
+choice 'Debugging output' \
+ "Normal CONFIG_FT_NORMAL_DEBUG \
+ Excessive CONFIG_FT_FULL_DEBUG \
+ Reduced CONFIG_FT_NO_TRACE \
+ None CONIFG_FT_NO_TRACE_AT_ALL" Normal
+comment 'Hardware configuration'
+choice 'Floppy tape controllers' \
+ "Standard CONFIG_FT_STD_FDC \
+ MACH-2 CONFIG_FT_MACH2 \
+ FC-10/FC-20 CONFIG_FT_PROBE_FC10 \
+ Alt/82078 CONFIG_FT_ALT_FDC" Standard
+if [ "$CONFIG_FT_STD_FDC" != "y" ]; then
+ comment ' Consult the manuals of your tape drive for the correct settings!'
+ hex ' IO base of the floppy disk controller' CONFIG_FT_FDC_BASE 0
+ int ' IRQ channel of the floppy disk controller' CONFIG_FT_FDC_IRQ 0
+ int ' DMA channel of the floppy disk controller' CONFIG_FT_FDC_DMA 0
+fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ int 'Default FIFO threshold (EXPERIMENTAL)' CONFIG_FT_FDC_THR 8
+ int 'Maximal data rate to use (EXPERIMENTAL)' CONFIG_FT_FDC_MAX_RATE 2000
+fi
+comment 'ONLY for DEC Alpha architectures'
+int 'CPU clock frequency of your DEC Alpha' CONFIG_FT_ALPHA_CLOCK 0
diff --git a/drivers/char/ftape/Makefile b/drivers/char/ftape/Makefile
index 1da53beb1..5726e788e 100644
--- a/drivers/char/ftape/Makefile
+++ b/drivers/char/ftape/Makefile
@@ -1,65 +1,67 @@
#
-# Makefile for the ftape device driver.
+# Copyright (C) 1997 Claus Heine.
#
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
+# This program is free software; you can redistribute 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.
#
-# Note 2! The CFLAGS definitions are now inherited from the
-# parent makes..
+# $Source: /homes/cvs/ftape-stacked/ftape/Makefile,v $
+# $Revision: 1.4 $
+# $Date: 1997/10/05 19:17:56 $
#
-
-# Valid ftape options are:
-# NO_TRACE - if defined, only information and errors show up.
-# NO_TRACE_AT_ALL - if defined, no trace output shows up.
-# GCC_2_4_5_BUG - must be set if using gcc-2.4.5 to prevent
-# bad assembler-code for the dma handling.
-# NR_BUFFERS - Number of ftape DMA buffers (keep it at 3!)
-# VERIFY_HEADERS - if set the headers segments are verified after
-# being written.
-# PROBE_FC10 - if defined will look for a FC-10 card at specified
-# settings (FDC_BASE,FDC_IRQ,FDC_DMA) before using
-# the standard fd controller.
-# FDC_BASE - sets base address (only!) if using non-standard fdc
-# FDC_IRQ - sets interrupt if FDC_BASE is defined
-# FDC_DMA - sets dma channel if FDC_BASE is defined
-# MACH2 - Support for Mountain MACH-2 controller at either 1E0
-# or 3E0, don't forget the FDC_OPT's !
-# CLK_48MHZ - Set to 1. If you have a i82078-1 FDC and it does not
-# work, try setting it to 0. (only used for i82078-1's)
-# FDC_82078SL - If you have a 82078SL, define this.
-
-FTAPE_OPT = -DVERIFY_HEADERS -DNR_BUFFERS=3 -DCLK_48MHZ=1 \
- -DNO_TRACE -DFDC_82078SL
-
-# If you're using a non-standard floppy disk controller for the
-# tape drive, enable one (only!) of the following lines and set
-# the FDC_BASE, FDC_IRQ and FDC_DMA parameters to the actual values.
-#
-# Note1: A FC-10/FC-20 controller must use either of DMA 1, 2, or 3.
-# DMA 5 and 7 does NOT work!.
-#
-# Note2: IRQ 2 and IRQ 9 can be considered the same. When using IRQ 2
-# on a controller you must specify IRQ 9 here!
+# Makefile for the QIC-40/80/3010/3020 floppy-tape driver for
+# Linux.
#
-# For a Mountain MACH-2 controller, try
-#FDC_OPT = -DMACH2 -DFDC_BASE=0x1E0 -DFDC_IRQ=6 -DFDC_DMA=2
-#
-# For Colorado CMS FC-10 or FC-20 controllers:
-#FDC_OPT = -DPROBE_FC10 -DFDC_BASE=0x180 -DFDC_IRQ=9 -DFDC_DMA=3
+
#
-# Secondary floppy disk controller:
-#FDC_OPT = -DFDC_BASE=0x370 -DFDC_IRQ=9 -DFDC_DMA=3
+# This isn't used inside the kernel, only for my private development
+# version
#
-# This enables some (most?) 2Mbps controllers:
-#FDC_OPT = -DFDC_BASE=0x3E0 -DFDC_IRQ=6 -DFDC_DMA=2
+ifndef TOPDIR
+TOPDIR= ..
+include $(TOPDIR)/MCONFIG
+endif
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS) lowlevel zftape compressor
+
+ifeq ($(CONFIG_FTAPE),y)
+ O_TARGET := ftape.o
+ SUB_DIRS += lowlevel
+ O_OBJS += lowlevel/ftape.o
+else
+ ifeq ($(CONFIG_FTAPE),m)
+ MOD_SUB_DIRS += lowlevel
+ endif
+endif
-EXTRA_CFLAGS := $(FTAPE_OPT) $(FDC_OPT)
+ifeq ($(CONFIG_ZFTAPE),y)
+ SUB_DIRS += zftape
+ O_OBJS += zftape/zftape.o
+else
+ ifeq ($(CONFIG_ZFTAPE),m)
+ MOD_SUB_DIRS += zftape
+ endif
+endif
-O_TARGET := ftape.o
-O_OBJS = kernel-interface.o tracing.o fdc-io.o fdc-isr.o \
- ftape-bsm.o ftape-ctl.o ftape-eof.o ftape-read.o ftape-rw.o \
- ftape-write.o ftape-io.o calibr.o ecc.o fc-10.o
-M_OBJS = $(O_TARGET)
+ifeq ($(CONFIG_ZFT_COMPRESSOR),y)
+ SUB_DIRS += compressor
+ O_OBJS += compressor/zft-compressor.o
+else
+ ifeq ($(CONFIG_ZFT_COMPRESSOR),m)
+ MOD_SUB_DIRS += compressor
+ endif
+endif
include $(TOPDIR)/Rules.make
diff --git a/drivers/char/ftape/README.PCI b/drivers/char/ftape/README.PCI
index 05842f071..18de159d3 100644
--- a/drivers/char/ftape/README.PCI
+++ b/drivers/char/ftape/README.PCI
@@ -30,7 +30,7 @@ bus timing.
I judge this a hardware problem and not a limitation of ftape ;-)
My DOS backup software seems to be suffering from the same problems
and even refuses to run at 1 Mbps !
-Ftape will reduce the datarate from 1 Mbps to 500 Kbps if the number
+Ftape will reduce the data-rate from 1 Mbps to 500 Kbps if the number
of overrun errors on a track exceeds a threshold.
@@ -77,3 +77,5 @@ under DOS. If it's very slow and often repositions you're
probably having this problem.
--//--
+ LocalWords: ftape PCI bios GAT ISA DMA chipset Mbps Kbps FDC isa AF ok ASUS
+ LocalWords: SP linebuffer masterbuffer XPS http www com
diff --git a/drivers/char/ftape/RELEASE-NOTES b/drivers/char/ftape/RELEASE-NOTES
index fc82f9900..dcc250808 100644
--- a/drivers/char/ftape/RELEASE-NOTES
+++ b/drivers/char/ftape/RELEASE-NOTES
@@ -1,3 +1,392 @@
+Hey, Emacs, we're -*-Text-*- mode!
+
+===== Release notes for ftape-3.04d 25/11/97 =====
+- The correct pre-processor statement for "else if" is "#elif" not
+ "elsif".
+- Need to call zft_reset_position() when overwriting cartridges
+ previously written with ftape-2.x, sftape, or ancient
+ (pre-ftape-3.x) versions of zftape.
+
+===== Release notes for ftape-3.04c 16/11/97 =====
+- fdc_probe() was calling DUMPREGS with a result length of "1" which
+ was just fine. Undo previous change.
+
+===== Release notes for ftape-3.04b 14/11/97 =====
+
+- patches/2.x.x/floppy.c.diff was somewhat broken, releasing i/o
+ regions it never had allocated.
+- fdc_probe() was calling DUMPREGS with a result length of "1" instead
+ of "10"
+- Writing deleted data marks if the first segents on track zero are
+ should work now.
+- ftformat should now be able to handle those cases where the tape
+ drive sets the read only status bit (QIC-40/80 cartridges with
+ QIC-3010/3020 tape drives) because the header segment is damaged.
+- the MTIOCFTCMD ioctl may now be issued by the superuser ONLY.
+
+===== Release notes for ftape-3.04a 12/11/97 =====
+- Fix an "infinite loop can't be killed by signal" bug in
+ ftape_get_drive_status(). Only relevant when trying to access
+ buggy/misconfigured hardware
+- Try to compensate a bug in the HP Colorado T3000's firmware: it
+ doesn't set the write protect bit for QIC80/QIC40 cartridges.
+
+===== Release notes for ftape-3.04 06/11/97 =====
+- If positioning with fast seeking fails fall back to a slow seek
+ before giving up.
+- (nearly) no retries on "no data errors" when verifying after
+ formatting. Improved tuning of the bad sector map after formatting.
+- the directory layout has changed again to allow for easier kernel
+ integration
+- Module parameter "ftape_tracing" now is called "ft_tracing" because
+ the "ftape_tracing" variable has the version checksum attached to it.
+- `/proc/ftape' interface for 2.0.* kernels. `/proc/ftape' no longer
+ is a directory but a file that contains all the information formerly
+ provided in separate files under the `/proc/ftape/' directory.
+- Most of the configuration options have been prefixed by "CONFIG_FT_"
+ in preparation of the kernel inclusion. The Makefiles under
+ "./ftape/" should be directly usable by the kernel.
+- The MODVERSIONS stuff is now auto-detected.
+- Broke backslashed multi line options in MCONFIG into separate lines
+ using GNU-make's "+=" feature.
+- The html and dvi version of the manual is now installed under
+ '/usr/doc/ftape` with 'make install`
+- New SMP define in MCONFIG. ftape works with SMP if this is defined.
+- attempt to cope with "excessive overrun errors" by gradually
+ increasing FDC FIFO threshold. But this doesn't seem to have too
+ much an effect.
+- New load time configuration parameter "ft_fdc_rate_limit". If you
+ encounter too many overrun errors with a 2Mb controller then you
+ might want to set this to 1000.
+- overrun errors on the last sector in a segment sometimes result in
+ a zero DMA residue. Dunno why, but compensate for it.
+- there were still fdc_read() timeout errors. I think I have fixed it
+ now, please FIXME.
+- Sometimes ftape_write() failed to re-start the tape drive when a
+ segment without a good sector was reached ("wait for empty segment
+ failed"). This is fixed. Especially important for > QIC-3010.
+- sftape (aka ftape-2.x) has vanished. I didn't work on it for
+ ages. It is probably still possible to use the old code with
+ ftape-3.04, if one really needs it (BUT RECOMPILE IT)
+- zftape no longer alters the contents of already existing volume
+ table entries, which makes it possible to fill in missing fields,
+ like time stamps using some user space program.
+- ./contrib/vtblc/ contains such a program.
+- new perl script ./contrib/scripts/listtape that list the contents of a
+ floppy tape cartridge parsing the output of "mt volinfo" + "mt fsf"
+- the MTWEOF implementation has changed a little bit (after I had a
+ look at amanda). Calling MTWEOF while the tape is still held open
+ after writing something to the tape now will terminate the current
+ volume, and start a new one at the current position.
+- the volume table maintained by zftape now is a doubly linked list
+ that grows dynamically as needed.
+
+ formatting floppy tape cartridges
+ ---------------------------------
+ * there is a new user space formatting program that does most of the
+ dirty work in user space (auto-detect, computing the sector
+ coordinates, adjusting time stamps and statistics). It has a
+ simple command line interface.
+ * ftape-format.o has vanished, it has been folded into the low level
+ ftape.o module, and the ioctl interface into zftape.o. Most of the
+ complicated stuff has been moved to user space, so there was no
+ need for a separate module anymore.
+ * there is a new ioctl MTIOCFTCMD that sends a bare QIC-117 command
+ to the tape drive.
+ * there is a new mmap() feature to map the dma buffers into user
+ space to be used by the user level formatting program.
+ * Formatting of yet unformatted or totally degaussed cartridges
+ should be possible now. FIXME.
+
+===== Release notes for ftape-3.03b, <forgot the exact date> ====
+
+ftape-3.03b was released as a beta release only. Its main new feature
+was support of the DITTO-2GB drive. This was made possible by reverse
+engineering done by <fill in his name> after Iomega failed to support
+ftape. Although they had promised to do so (this makes me feel a bit
+sad and uncomfortable about Iomega).
+
+===== Release notes for ftape-3.03a, 22/05/97 ====
+
+- Finally fixed auto-un-loading of modules for kernels > 2.1.18
+- Add an "uninstall" target to the Makefile
+- removed the kdtime hack
+- texi2www didn't properly set the back-reference from a footnote back
+ to the regular text.
+
+ zftape specific
+ ---------------
+ * hide the old compression map volume. Taper doesn't accept the
+ presence of non-Taper volumes and Taper-written volume on the same
+ tape.
+ * EOD (End Of Data) handling was still broken: the expected behavior
+ is to return a zero byte count at the first attempt to read past
+ EOD, return a zero byte count at the second attempt to read past
+ EOD and THEN return -EIO.
+
+ ftape-format specific
+ ---------------------
+ * Detection of QIC-40 cartridges in select_tape_format() was broken
+ and made it impossible to format QIC-3010/3020 cartridges.
+ * There are strange "TR-1 Extra" cartridges out there which weren't
+ detected properly because the don't strictly conform to the
+ QIC-80, Rev. N, spec.
+
+===== Release notes for ftape-3.03, 30/04/97 =====
+
+- Removed kernel integration code from the package. I plan to provide
+ a package that can be integrated into the stock kernel separately
+ (hopefully soon).
+ As a result, a simple `make' command now will build everything.
+- ALL compile time configuration options have been moved to the file
+ `MCONFIG'.
+- Quite a few `low level' changes to allow formatting of cartridges.
+- formatting is implemented as a separate module `ftape-format.o'. The
+ modified `mt' program contains sample code that shows how to use it.
+- The VFS interface has been moved from the `ftape.o' module to the
+ high level modules `zftape.o' resp. `sftape.o'. `ftape.o' contains
+ the hardware support only.
+- A bit of /proc support for kernels > 2.1.28
+- Moved documentation to Doc subdir. INSTALL now contains some real
+ installation notes.
+- `install' target in Makefile.
+
+zftape specific:
+----------------
+
+- zftape works for large cartridges now ( > 2^31 bytes)
+- MTIOCVOLINFO and MTIOCGETSIZE now return the size in KILOBYTES,
+ NO LONGER in bytes.
+
+- permissions for write access to a cartridge have changed:
+ * zftape now also takes the file access mode into account
+ * zftape no longer allows writing in the middle of the recorded
+ media. The tape has to be positioned at BOT or EOD for write
+ access.
+
+- MTBSF has changed. It used to position at the beginning of the
+ previous file when called with count 1. This was different from the
+ expected behavior for other Un*x tape drivers (i.e. SCSI). MTBSF
+ with count 1 should merely position at the beginning of the current
+ volume. Fixed. As a result, `tar --verify' now produces the desired
+ result: it verifies the last written volume, not the pre-last
+ written volume.
+
+- The compression map has vanished --> no need for `mt erase' any
+ more. Fast seeking in a compressed volume is still be possible, but
+ takes slightly longer. As a side effect, you may experience an
+ additional volume showing up in front of all others for old
+ cartridges. This is the tape volume that holds the compression map.
+
+- The compression support for zftape has been moved to a separate
+ module `zft-compressor'. DON'T forget to load it before trying to
+ read back compressed volumes. The stock `zftape.o' module probes for
+ the module `zft-compressor' using the kerneld message channel; you
+ have to install `zft-compressor.o' in a place where modprobe can
+ find it if you want to use this.
+
+- New experimental feature that tries to get the broken down GMT time
+ from user space via a kernel daemon message channel. You need to
+ compile and start the `kdtime' daemon contained in the contrib
+ directory to use it. Needed (?) for time stamps in the header
+ segments and the volume table.
+
+- variable block size mode via MTSETBLK 0
+
+- keep modules locked in memory after the block size has been changed
+
+sftape specific:
+----------------
+
+- end of tape handling should be fixed, i.e. multi volume archives
+ written with `afio' can be read back now.
+
+
+===== Release notes for ftape-3.02a, 09/01/97 =====
+
+No big news:
+- call zft_init() resp. sft_init() when compiling the entire stuff
+ into the kernel image.
+- fix bug in ftape-setup.c when NO_TRACE_AT_ALL was defined.
+- fix bug in sftape-eof.c/zftape-eof.c for old kernels (1.2.*)
+- add support for new module interface for recent kernels
+
+===== Release notes for ftape-3.02, 16/12/96 =====
+- Fixed the `FDC unlock command failed' bug in fdc-io.c. When the FIFO
+ was already locked when ftape was loaded, ftape failed to unlock it.
+- Fixed compilation of `contrib/gnumt'. It now finds `mtio.h' even if
+ ftape is NOT included into the kernel source tree.
+- fc-10.c: include <asm/io.h> for inb() and outb().
+- ftape/sftape/zftape: all global variable now have either a `ftape_',
+ a `ft_', `sft_', `zft_' or `qic_' prefix to prevent name clashes
+ with other parts of the kernel when including ftape into the kernel
+ source tree.
+- Kerneld support has changed. `ftape' now searches for a module
+ `ftape-frontend' when none of the frontend (`sftape' or `zftape') is
+ loaded. Please refer to the `Installation/Loading ftape' section of
+ the TeXinfo manual.
+- Add load resp. boot-time configuration of ftape. There are now
+ variables ft_fdc_base, ft_fdc_dma and ft_fdc_irq corresponding to
+ the former FDC_BASE etc. compile time definitions. One can also use
+ the kernel command line parameters to configure the driver if it is
+ compiled into the kernel. Also, the FC-10/FC-20 support is load-time
+ configurable now as well as the MACH-II hack (ft_probe_fc10,
+ resp. ft_mach2). Please refer to the section `Installation/Configure
+ ftape' of the TeXinfo manual.
+- I removed the MODVERSIONS option from `Makefile.module'. Let me alone
+ with ftape and MODVERSIONS unless you include the ftape sources into
+ the kernel source tree.
+- new vendors in `vendors.h':
+ * HP Colorado T3000
+ * ComByte DoublePlay (including a bug fix for their broken
+ formatting software, thanks to whraven@njackn.com)
+ * Iomega DITTO 2GIG. NOTE: this drive cannot work with ftape because
+ the logical data layout of the cartridges used by this drive does
+ NOT conform to the QIC standards, it is a special Iomega specific
+ format. I've sent mail to Iomega but didn't receive an answer
+ yet. If you want this drive to be supported by ftape, ask Iomega
+ to give me information about it.
+- zftape:
+ * re-introduced the MTIOC_ZFTAPE_GETBLKSZ ioctl for compatibility
+ with zftape 1.06a and earlier. Please don't use it when writing
+ new software, use the MTIOCVOLINFO ioctl instead.
+ * Major overhaul of the code that updates the header segments. Never
+ change the tape label unless erasing the tape. Thus we almost
+ never need to write the header segments, unless we would modify
+ the bad sector map which isn't done yet. Updating of volume table
+ and compression map more secure now although it takes a bit
+ longer.
+ * Fixed bug when aborting a write operation with a signal: zftape
+ now finishes the current volume (i.e. writes an eof marker) at the
+ current position. It didn't before which led to somehow *strange*
+ behavior in this cases.
+ * Keep module locked in memory when using it with the non-rewinding
+ devices and the tape is not logical at BOT. Needed for kerneld
+ support.
+- sftape:
+ * Keep module locked in memory when using it with the non-rewinding
+ devices and the tape is not logical at BOT. Needed for kerneld
+ support.
+
+===== Release notes for ftape-3.01, 14/11/96 =====
+
+- Fixed silly bugs in ftape-3.00:
+ * MAKEDEV.ftape: major device number must be 27, not 23
+ * sftape/sftape-read.c: sftape_read_header_segments() called
+ itself recursively instead of calling ftape_read_header_segment()
+ * zftape/qic-vtbl.h: conversion of ftape's file marks to zftape's
+ internal volume table was broken.
+ * patches/2.x.x/linux-2.0.21.dif: my RCS (resp. CVS) system replaced
+ the `$Revison:' etc. macros in the `ftape.h' concerning part of the
+ patch :-( Fixed.
+ * info/ftape.info: Fixed misspellings (`cp' <-> `cp -r' etc.)
+ * when ftape/sftape or ftape/zftape was compiled into the kernel the
+ variable ftape_status was declared twice. Fixed.
+ * removed reference to undeclared variable kernel_version when not
+ compiling as module
+ * fixed a bug introduced by the use of bit-fields for some flags
+ (i.e. write_protected, no_cartridge, formatted)
+ * flag `header_read' is now reset correctly to zero when tape is
+ removed.
+- fixed a bug in sftape/sftape-eof.c that was already in the original
+ ftape code. MTFSF/BSF was not handled correctly when positioned
+ right before the file mark (think of tar)
+- Changed TRACE macros (following a suggestion of Marcin Dalecki) to use
+ the predefined __FUNCTION__ macro of GCC. Spares about 4k of code.
+- added new vendor id for Iomega DITTO 2GIG
+- fixed a bug already present in zftape-1.06 when aborting a write
+ with a signal: we now finish the current volume at that
+ position. Header segments remain NOT up to date until an explicit call
+ to MTREW or MTOFFL is done.
+
+===== Release notes for ftape-3.00, 14/10/96 =====
+
+- Merged ftape with zftape. There are three modules now:
+ ftape for the hardware support, sftape for the implementation of the
+ original ftape eof mark stuff and zftape that implements zftape's way
+ of handling things (compression, volume table, tape blocks of
+ constant length)
+- Documentation in TeXinfo format in the `info' subdirectory.
+- New ioctls for zftape. See zftape/zftape.h
+- Dummy formatting ioctl for ftape. See ftape.h
+- Kernel patch files for the 2.*.* series to include ftape-3.00 in the
+ kernel source tree. These includes a kernel compatible Config.in
+ script and fairly large online information for the kernel configure
+ script.
+- Support for compiling with Linux-1.2.13.
+- Modified GNU mt from their cpio package that can handle the new
+ ioctls.
+- ftape/sftape/zftape is kerneld save now!
+
+Notes on sftape:
+- sftape implements the eof handling code of the original ftape. If
+ you like to stick with the original ftape stuff, you have to use
+ this module, not zftape.
+- sftape is kerneld save, unlike the original ftape.
+- we keep the entire header segment now in memory, so no need to read
+ it before updating the header segments. Additional memory
+ consumption: 256 bytes.
+
+Notes for zftape:
+- zftape has support for tapes with format code 6 now, which use a
+ slightly different volume table format compared with other floppy
+ tapes.
+- new ioctls for zftape. Have a look at zftape/zftape.h
+- The internal volume table representation has changed for zftape. Old
+ cartridges are converted automatically.
+- zftape no longer uses compression map segments, which have vanished
+ from the QIC specs, but creates volume table entry that reserves
+ enough space for the compression map.
+- zftape is kerneld save now.
+- we keep the entire header segment now in memory, so no need to read
+ it before updating the header segments. Additional memory
+ consumption: 256 bytes.
+
+Notes for contrib/gnumt:
+- modified mt from the GNU cpio package that supports all the new
+ ioctls of zftape.
+Notes for contrib/swapout:
+- This contains the swapout.c program that was written by Kai
+ Harrekilde-Pederson. I simply added a Makefile.
+
+===== Release notes for ftape-2.10, 14/10/96 =====
+
+The ftape maintainer has changed.
+Kai Harrekilde-Petersen <khp@dolphinics.no>
+has resigned from maintaining ftape, and I,
+Claus-Justus Heine <claus@momo.math.rwth-aachen.de>,
+have taken over.
+
+- Added support for tapes with `format code 6', i.e. QIC-3020 tapes
+ with more than 2^16 segments.
+- merged changes made by Bas Laarhoven with ftape-2.09. Refer
+ to his release notes below. I've included them into this
+ file unchanged for your reference.
+- disabled call stack back trace for now. This new feature
+ introduced by the interim release 2.0.x still seems to
+ be buggy.
+- Tried to minimize differences between the ftape version
+ to be included into the kernel source tree and the standalone
+ module version.
+- Reintroduced support for Linux-1.2.13. Please refer to the
+ Install-guide.
+
+===== Release notes for ftape-2.09, 16/06/96 =====
+
+There aren't any really big news in this release, mostly just that I
+(the maintainer) have changed my email address (due to a new job). My
+new address is <khp@dolphinics.no>
+
+- The CLK_48MHZ and FDC_82078SL options has gone (all 2Mbps cards seem
+ to use a 48MHz oscillator anyway and I haven't heard of an 'SL
+ chip out there).
+- The S82078B has been `downgraded' to i82077AA compability.
+- TESTING option revived. Right now, it'll enable the (seriously broken)
+ 2Mbps code. If you enable it, you'll experience a tape drive that's
+ *really* out to lunch!
+- Some (bold) changes in the init code. Please notify me if they
+ break things for you.
+
===== Release notes for ftape-2.08, 14/03/96 =====
If you correct a problem with ftape, please send your patch to
@@ -567,3 +956,13 @@ Have fun,
Bas.
----
+ LocalWords: ftape MCONFIG mt VFS zftape resp sftape proc subdir MTIOCVOLINFO
+ LocalWords: MTIOCGETSIZE BOT EOD MTBSF zft kerneld modprobe kdtime contrib TR
+ LocalWords: MTSETBLK afio uninstall texi www EIO QIC init sft eof aka dma GB
+ LocalWords: SIGKILL MTIOCFTCMD mmap Iomega FDC fdc io gnumt mtio fc asm inb
+ LocalWords: outb ft qic frontend TeXinfo irq mach MODVERSIONS CONFIG html dvi
+ LocalWords: usr doc SMP Mb Dunno FIXME vtblc perl listtape volinfo fsf MTWEOF
+ LocalWords: amanda degaussed ComByte DoublePlay whraven njackn com MTIOC vtbl
+ LocalWords: GETBLKSZ MAKEDEV zftape's linux dif CVS Revison cp MTREW MTOFFL
+ LocalWords: MTFSF BSF Marcin Dalecki GCC Config cpio swapout Kai Harrekilde
+ LocalWords: Pederson khp dolphinics Justus claus momo rwth aachen Laarhoven
diff --git a/drivers/char/ftape/calibr.c b/drivers/char/ftape/calibr.c
deleted file mode 100644
index bd41bddb8..000000000
--- a/drivers/char/ftape/calibr.c
+++ /dev/null
@@ -1,183 +0,0 @@
-/* Yo, Emacs! we're -*- Linux-C -*-
- *
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute 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.
-
- * GP calibration routine for processor speed dependent
- * functions.
- */
-
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/ftape.h>
-#include <asm/system.h>
-#include <asm/io.h>
-
-#include "tracing.h"
-#include "calibr.h"
-#include "fdc-io.h"
-
-#undef DEBUG
-
-unsigned timestamp(void)
-{
- unsigned count;
- unsigned long flags;
-
- save_flags(flags);
- cli();
- outb_p(0x00, 0x43); /* latch the count ASAP */
- count = inb_p(0x40); /* read the latched count */
- count |= inb(0x40) << 8;
- restore_flags(flags);
- return (LATCH - count); /* normal: downcounter */
-}
-
-int timediff(int t0, int t1)
-{
- /* Calculate difference in usec for timestamp results t0 & t1.
- * Note that the maximum timespan allowed is 1/HZ or we'll lose ticks!
- */
- if (t1 < t0) {
- t1 += LATCH;
- }
- return (1000 * (t1 - t0)) / ((CLOCK_TICK_RATE + 500) / 1000);
-}
-
-/* To get an indication of the I/O performance,
- * measure the duration of the inb() function.
- */
-void time_inb(void)
-{
- TRACE_FUN(8, "time_inb");
- int i;
- int t0, t1;
- unsigned long flags;
- int status;
-
- save_flags(flags);
- cli();
- t0 = timestamp();
- for (i = 0; i < 1000; ++i) {
- status = inb(fdc.msr);
- }
- t1 = timestamp();
- restore_flags(flags);
- if (t1 - t0 <= 0) {
- t1 += LATCH;
- }
- TRACEx1(4, "inb() duration: %d nsec", timediff(t0, t1));
- TRACE_EXIT;
-}
-
-/* Haven't studied on why, but there sometimes is a problem
- * with the tick timer readout. The two bytes get swapped.
- * This hack solves that problem by doing one extra input.
- */
-void fix_clock(void)
-{
- TRACE_FUN(8, "fix_clock");
- int t;
- int i;
-
- for (i = 0; i < 1000; ++i) {
- t = timestamp();
- if (t < 0) {
- inb_p(0x40); /* get in sync again */
- TRACE(2, "clock counter fixed");
- break;
- }
- }
- TRACE_EXIT;
-}
-
-/*
- * Input: function taking int count as parameter.
- * pointers to calculated calibration variables.
- */
-int calibrate(char *name, void (*fun) (int), int *calibr_count, int *calibr_time)
-{
- TRACE_FUN(5, "calibrate");
- static int first_time = 1;
- int i;
- int old_tc = 0;
- int old_count = 1;
- int old_time = 1;
-
- if (first_time) { /* get idea of I/O performance */
- fix_clock();
- time_inb();
- first_time = 0;
- }
- /* value of timeout must be set so that on very slow systems
- * it will give a time less than one jiffy, and on
- * very fast systems it'll give reasonable precision.
- */
-
- *calibr_count = 10;
- for (i = 0; i < 15; ++i) {
- int t0, t1;
- unsigned long flags;
- int once;
- int multiple;
- int tc;
-
- *calibr_time = *calibr_count; /* set TC to 1 */
- fun(0); /* dummy, get code into cache */
- save_flags(flags);
- cli();
- t0 = timestamp();
- fun(0); /* overhead + one test */
- t1 = timestamp();
- if (t1 < t0) {
- t1 += LATCH;
- }
- once = t1 - t0;
- t0 = timestamp();
- fun(*calibr_count); /* overhead + multiple tests */
- t1 = timestamp();
- if (t1 < t0) {
- t1 += LATCH;
- }
- multiple = t1 - t0;
- restore_flags(flags);
- *calibr_time = (10000 * (multiple - once)) / (CLOCK_TICK_RATE / 100);
- --*calibr_count; /* because delta corresponds to this count */
- tc = (1000 * *calibr_time) / *calibr_count;
- TRACEx4(8, "once:%4d us,%5d times:%6d us, TC:%5d ns",
- (10000 * once) / (CLOCK_TICK_RATE / 100),
- *calibr_count,
- (10000 * multiple) / (CLOCK_TICK_RATE / 100),
- tc);
- /*
- * increase the count until the resulting time nears 2/HZ,
- * then the tc will drop sharply because we lose LATCH counts.
- */
- if (tc <= old_tc / 2) {
- *calibr_time = old_time;
- *calibr_count = old_count;
- break;
- }
- old_tc = tc;
- old_count = *calibr_count;
- old_time = *calibr_time;
- *calibr_count *= 2;
- }
- TRACEx3(4, "TC for `%s()' = %d nsec (at %d counts)",
- name, (1000 * *calibr_time) / *calibr_count, *calibr_count);
- TRACE_EXIT;
- return 0;
-}
diff --git a/drivers/char/ftape/compressor/Makefile b/drivers/char/ftape/compressor/Makefile
new file mode 100644
index 000000000..fde3fd10b
--- /dev/null
+++ b/drivers/char/ftape/compressor/Makefile
@@ -0,0 +1,48 @@
+#
+# Copyright (C) 1997 Claus-Justus Heine.
+#
+# This program is free software; you can redistribute 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.
+#
+# $Source: /homes/cvs/ftape-stacked/ftape/compressor/Makefile,v $
+# $Revision: 1.1 $
+# $Date: 1997/10/05 19:12:28 $
+#
+# Makefile for the optional compressor for th zftape VFS
+# interface to the QIC-40/80/3010/3020 floppy-tape driver for
+# Linux.
+#
+
+#
+# This isn't used inside the kernel, only for my private development
+# version
+#
+ifndef TOPDIR
+TOPDIR=../..
+include $(TOPDIR)/MCONFIG
+endif
+
+O_TARGET := zft-compressor.o
+O_OBJS = zftape-compress.o lzrw3.o
+
+M_OBJS = $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
+
+#
+# sorry, a special rule.
+#
+lzrw3.o: lzrw3.c
+ $(CC) $(CFLAGS) -O6 -funroll-all-loops -c $<
+
diff --git a/drivers/char/ftape/compressor/lzrw3.c b/drivers/char/ftape/compressor/lzrw3.c
new file mode 100644
index 000000000..64ad32556
--- /dev/null
+++ b/drivers/char/ftape/compressor/lzrw3.c
@@ -0,0 +1,750 @@
+/*
+ * $Source: /homes/cvs/ftape-stacked/ftape/compressor/lzrw3.c,v $
+ * $Revision: 1.1 $
+ * $Date: 1997/10/05 19:12:29 $
+ *
+ * Implementation of Ross Williams lzrw3 algorithm. Adaption for zftape.
+ *
+ */
+
+#include "../compressor/lzrw3.h" /* Defines single exported function "compress". */
+
+/******************************************************************************/
+/* */
+/* LZRW3.C */
+/* */
+/******************************************************************************/
+/* */
+/* Author : Ross Williams. */
+/* Date : 30-Jun-1991. */
+/* Release : 1. */
+/* */
+/******************************************************************************/
+/* */
+/* This file contains an implementation of the LZRW3 data compression */
+/* algorithm in C. */
+/* */
+/* The algorithm is a general purpose compression algorithm that runs fast */
+/* and gives reasonable compression. The algorithm is a member of the Lempel */
+/* Ziv family of algorithms and bases its compression on the presence in the */
+/* data of repeated substrings. */
+/* */
+/* This algorithm is unpatented and the code is public domain. As the */
+/* algorithm is based on the LZ77 class of algorithms, it is unlikely to be */
+/* the subject of a patent challenge. */
+/* */
+/* Unlike the LZRW1 and LZRW1-A algorithms, the LZRW3 algorithm is */
+/* deterministic and is guaranteed to yield the same compressed */
+/* representation for a given file each time it is run. */
+/* */
+/* The LZRW3 algorithm was originally designed and implemented */
+/* by Ross Williams on 31-Dec-1990. */
+/* */
+/* Here are the results of applying this code, compiled under THINK C 4.0 */
+/* and running on a Mac-SE (8MHz 68000), to the standard calgary corpus. */
+/* */
+/* +----------------------------------------------------------------+ */
+/* | DATA COMPRESSION TEST | */
+/* | ===================== | */
+/* | Time of run : Sun 30-Jun-1991 09:31PM | */
+/* | Timing accuracy : One part in 100 | */
+/* | Context length : 262144 bytes (= 256.0000K) | */
+/* | Test suite : Calgary Corpus Suite | */
+/* | Files in suite : 14 | */
+/* | Algorithm : LZRW3 | */
+/* | Note: All averages are calculated from the un-rounded values. | */
+/* +----------------------------------------------------------------+ */
+/* | File Name Length CxB ComLen %Remn Bits Com K/s Dec K/s | */
+/* | ---------- ------ --- ------ ----- ---- ------- ------- | */
+/* | rpus:Bib.D 111261 1 55033 49.5 3.96 19.46 32.27 | */
+/* | us:Book1.D 768771 3 467962 60.9 4.87 17.03 31.07 | */
+/* | us:Book2.D 610856 3 317102 51.9 4.15 19.39 34.15 | */
+/* | rpus:Geo.D 102400 1 82424 80.5 6.44 11.65 18.18 | */
+/* | pus:News.D 377109 2 205670 54.5 4.36 17.14 27.47 | */
+/* | pus:Obj1.D 21504 1 13027 60.6 4.85 13.40 18.95 | */
+/* | pus:Obj2.D 246814 1 116286 47.1 3.77 19.31 30.10 | */
+/* | s:Paper1.D 53161 1 27522 51.8 4.14 18.60 31.15 | */
+/* | s:Paper2.D 82199 1 45160 54.9 4.40 18.45 32.84 | */
+/* | rpus:Pic.D 513216 2 122388 23.8 1.91 35.29 51.05 | */
+/* | us:Progc.D 39611 1 19669 49.7 3.97 18.87 30.64 | */
+/* | us:Progl.D 71646 1 28247 39.4 3.15 24.34 40.66 | */
+/* | us:Progp.D 49379 1 19377 39.2 3.14 23.91 39.23 | */
+/* | us:Trans.D 93695 1 33481 35.7 2.86 25.48 40.37 | */
+/* +----------------------------------------------------------------+ */
+/* | Average 224401 1 110953 50.0 4.00 20.17 32.72 | */
+/* +----------------------------------------------------------------+ */
+/* */
+/******************************************************************************/
+
+/******************************************************************************/
+
+/* The following structure is returned by the "compress" function below when */
+/* the user asks the function to return identifying information. */
+/* The most important field in the record is the working memory field which */
+/* tells the calling program how much working memory should be passed to */
+/* "compress" when it is called to perform a compression or decompression. */
+/* LZRW3 uses the same amount of memory during compression and decompression. */
+/* For more information on this structure see "compress.h". */
+
+#define U(X) ((ULONG) X)
+#define SIZE_P_BYTE (U(sizeof(UBYTE *)))
+#define SIZE_WORD (U(sizeof(UWORD )))
+#define ALIGNMENT_FUDGE (U(16))
+#define MEM_REQ ( U(4096)*(SIZE_P_BYTE) + ALIGNMENT_FUDGE )
+
+static struct compress_identity identity =
+{
+ U(0x032DDEA8), /* Algorithm identification number. */
+ MEM_REQ, /* Working memory (bytes) required. */
+ "LZRW3", /* Name of algorithm. */
+ "1.0", /* Version number of algorithm. */
+ "31-Dec-1990", /* Date of algorithm. */
+ "Public Domain", /* Copyright notice. */
+ "Ross N. Williams", /* Author of algorithm. */
+ "Renaissance Software", /* Affiliation of author. */
+ "Public Domain" /* Vendor of algorithm. */
+};
+
+LOCAL void compress_compress (UBYTE *,UBYTE *,ULONG,UBYTE *, LONG *);
+LOCAL void compress_decompress(UBYTE *,UBYTE *,LONG, UBYTE *, ULONG *);
+
+/******************************************************************************/
+
+/* This function is the only function exported by this module. */
+/* Depending on its first parameter, the function can be requested to */
+/* compress a block of memory, decompress a block of memory, or to identify */
+/* itself. For more information, see the specification file "compress.h". */
+
+EXPORT void lzrw3_compress(action,wrk_mem,src_adr,src_len,dst_adr,p_dst_len)
+UWORD action; /* Action to be performed. */
+UBYTE *wrk_mem; /* Address of working memory we can use. */
+UBYTE *src_adr; /* Address of input data. */
+LONG src_len; /* Length of input data. */
+UBYTE *dst_adr; /* Address to put output data. */
+void *p_dst_len; /* Address of longword for length of output data. */
+{
+ switch (action)
+ {
+ case COMPRESS_ACTION_IDENTITY:
+ *((struct compress_identity **)p_dst_len)= &identity;
+ break;
+ case COMPRESS_ACTION_COMPRESS:
+ compress_compress(wrk_mem,src_adr,src_len,dst_adr,(LONG *)p_dst_len);
+ break;
+ case COMPRESS_ACTION_DECOMPRESS:
+ compress_decompress(wrk_mem,src_adr,src_len,dst_adr,(LONG *)p_dst_len);
+ break;
+ }
+}
+
+/******************************************************************************/
+/* */
+/* BRIEF DESCRIPTION OF THE LZRW3 ALGORITHM */
+/* ======================================== */
+/* The LZRW3 algorithm is identical to the LZRW1-A algorithm except that */
+/* instead of transmitting history offsets, it transmits hash table indexes. */
+/* In order to decode the indexes, the decompressor must maintain an */
+/* identical hash table. Copy items are straightforward:when the decompressor */
+/* receives a copy item, it simply looks up the hash table to translate the */
+/* index into a pointer into the data already decompressed. To update the */
+/* hash table, it replaces the same table entry with a pointer to the start */
+/* of the newly decoded phrase. The tricky part is with literal items, for at */
+/* the time that the decompressor receives a literal item the decompressor */
+/* does not have the three bytes in the Ziv (that the compressor has) to */
+/* perform the three-byte hash. To solve this problem, in LZRW3, both the */
+/* compressor and decompressor are wired up so that they "buffer" these */
+/* literals and update their hash tables only when three bytes are available. */
+/* This makes the maximum buffering 2 bytes. */
+/* */
+/* Replacement of offsets by hash table indexes yields a few percent extra */
+/* compression at the cost of some speed. LZRW3 is slower than LZRW1, LZRW1-A */
+/* and LZRW2, but yields better compression. */
+/* */
+/* Extra compression could be obtained by using a hash table of depth two. */
+/* However, increasing the depth above one incurs a significant decrease in */
+/* compression speed which was not considered worthwhile. Another reason for */
+/* keeping the depth down to one was to allow easy comparison with the */
+/* LZRW1-A and LZRW2 algorithms so as to demonstrate the exact effect of the */
+/* use of direct hash indexes. */
+/* */
+/* +---+ */
+/* |___|4095 */
+/* |___| */
+/* +---------------------*_|<---+ /----+---\ */
+/* | |___| +---|Hash | */
+/* | |___| |Function| */
+/* | |___| \--------/ */
+/* | |___|0 ^ */
+/* | +---+ | */
+/* | Hash +-----+ */
+/* | Table | */
+/* | --- */
+/* v ^^^ */
+/* +-------------------------------------|----------------+ */
+/* |||||||||||||||||||||||||||||||||||||||||||||||||||||||| */
+/* +-------------------------------------|----------------+ */
+/* | |1......18| | */
+/* |<------- Lempel=History ------------>|<--Ziv-->| | */
+/* | (=bytes already processed) |<-Still to go-->| */
+/* |<-------------------- INPUT BLOCK ------------------->| */
+/* */
+/* The diagram above for LZRW3 looks almost identical to the diagram for */
+/* LZRW1. The difference is that in LZRW3, the compressor transmits hash */
+/* table indices instead of Lempel offsets. For this to work, the */
+/* decompressor must maintain a hash table as well as the compressor and both */
+/* compressor and decompressor must "buffer" literals, as the decompressor */
+/* cannot hash phrases commencing with a literal until another two bytes have */
+/* arrived. */
+/* */
+/* LZRW3 Algorithm Execution Summary */
+/* --------------------------------- */
+/* 1. Hash the first three bytes of the Ziv to yield a hash table index h. */
+/* 2. Look up the hash table yielding history pointer p. */
+/* 3. Match where p points with the Ziv. If there is a match of three or */
+/* more bytes, code those bytes (in the Ziv) as a copy item, otherwise */
+/* code the next byte in the Ziv as a literal item. */
+/* 4. Update the hash table as possible subject to the constraint that only */
+/* phrases commencing three bytes back from the Ziv can be hashed and */
+/* entered into the hash table. (This enables the decompressor to keep */
+/* pace). See the description and code for more details. */
+/* */
+/******************************************************************************/
+/* */
+/* DEFINITION OF COMPRESSED FILE FORMAT */
+/* ==================================== */
+/* * A compressed file consists of a COPY FLAG followed by a REMAINDER. */
+/* * The copy flag CF uses up four bytes with the first byte being the */
+/* least significant. */
+/* * If CF=1, then the compressed file represents the remainder of the file */
+/* exactly. Otherwise CF=0 and the remainder of the file consists of zero */
+/* or more GROUPS, each of which represents one or more bytes. */
+/* * Each group consists of two bytes of CONTROL information followed by */
+/* sixteen ITEMs except for the last group which can contain from one */
+/* to sixteen items. */
+/* * An item can be either a LITERAL item or a COPY item. */
+/* * Each item corresponds to a bit in the control bytes. */
+/* * The first control byte corresponds to the first 8 items in the group */
+/* with bit 0 corresponding to the first item in the group and bit 7 to */
+/* the eighth item in the group. */
+/* * The second control byte corresponds to the second 8 items in the group */
+/* with bit 0 corresponding to the ninth item in the group and bit 7 to */
+/* the sixteenth item in the group. */
+/* * A zero bit in a control word means that the corresponding item is a */
+/* literal item. A one bit corresponds to a copy item. */
+/* * A literal item consists of a single byte which represents itself. */
+/* * A copy item consists of two bytes that represent from 3 to 18 bytes. */
+/* * The first byte in a copy item will be denoted C1. */
+/* * The second byte in a copy item will be denoted C2. */
+/* * Bits will be selected using square brackets. */
+/* For example: C1[0..3] is the low nibble of the first control byte. */
+/* of copy item C1. */
+/* * The LENGTH of a copy item is defined to be C1[0..3]+3 which is a number */
+/* in the range [3,18]. */
+/* * The INDEX of a copy item is defined to be C1[4..7]*256+C2[0..8] which */
+/* is a number in the range [0,4095]. */
+/* * A copy item represents the sequence of bytes */
+/* text[POS-OFFSET..POS-OFFSET+LENGTH-1] where */
+/* text is the entire text of the uncompressed string. */
+/* POS is the index in the text of the character following the */
+/* string represented by all the items preceeding the item */
+/* being defined. */
+/* OFFSET is obtained from INDEX by looking up the hash table. */
+/* */
+/******************************************************************************/
+
+/* The following #define defines the length of the copy flag that appears at */
+/* the start of the compressed file. The value of four bytes was chosen */
+/* because the fast_copy routine on my Macintosh runs faster if the source */
+/* and destination blocks are relatively longword aligned. */
+/* The actual flag data appears in the first byte. The rest are zeroed so as */
+/* to normalize the compressed representation (i.e. not non-deterministic). */
+#define FLAG_BYTES 4
+
+/* The following #defines define the meaning of the values of the copy */
+/* flag at the start of the compressed file. */
+#define FLAG_COMPRESS 0 /* Signals that output was result of compression. */
+#define FLAG_COPY 1 /* Signals that output was simply copied over. */
+
+/* The 68000 microprocessor (on which this algorithm was originally developed */
+/* is fussy about non-aligned arrays of words. To avoid these problems the */
+/* following macro can be used to "waste" from 0 to 3 bytes so as to align */
+/* the argument pointer. */
+#define ULONG_ALIGN_UP(X) ((((ULONG)X)+sizeof(ULONG)-1)&~(sizeof(ULONG)-1))
+
+
+/* The following constant defines the maximum length of an uncompressed item. */
+/* This definition must not be changed; its value is hardwired into the code. */
+/* The longest number of bytes that can be spanned by a single item is 18 */
+/* for the longest copy item. */
+#define MAX_RAW_ITEM (18)
+
+/* The following constant defines the maximum length of an uncompressed group.*/
+/* This definition must not be changed; its value is hardwired into the code. */
+/* A group contains at most 16 items which explains this definition. */
+#define MAX_RAW_GROUP (16*MAX_RAW_ITEM)
+
+/* The following constant defines the maximum length of a compressed group. */
+/* This definition must not be changed; its value is hardwired into the code. */
+/* A compressed group consists of two control bytes followed by up to 16 */
+/* compressed items each of which can have a maximum length of two bytes. */
+#define MAX_CMP_GROUP (2+16*2)
+
+/* The following constant defines the number of entries in the hash table. */
+/* This definition must not be changed; its value is hardwired into the code. */
+#define HASH_TABLE_LENGTH (4096)
+
+/* LZRW3, unlike LZRW1(-A), must initialize its hash table so as to enable */
+/* the compressor and decompressor to stay in step maintaining identical hash */
+/* tables. In an early version of the algorithm, the tables were simply */
+/* initialized to zero and a check for zero was included just before the */
+/* matching code. However, this test costs time. A better solution is to */
+/* initialize all the entries in the hash table to point to a constant */
+/* string. The decompressor does the same. This solution requires no extra */
+/* test. The contents of the string do not matter so long as the string is */
+/* the same for the compressor and decompressor and contains at least */
+/* MAX_RAW_ITEM bytes. I chose consecutive decimal digits because they do not */
+/* have white space problems (e.g. there is no chance that the compiler will */
+/* replace more than one space by a TAB) and because they make the length of */
+/* the string obvious by inspection. */
+#define START_STRING_18 ((UBYTE *) "123456789012345678")
+
+/* In this algorithm, hash values have to be calculated at more than one */
+/* point. The following macro neatens the code up for this. */
+#define HASH(PTR) \
+ (((40543*(((*(PTR))<<8)^((*((PTR)+1))<<4)^(*((PTR)+2))))>>4) & 0xFFF)
+
+/******************************************************************************/
+
+LOCAL void compress_compress
+ (p_wrk_mem,p_src_first,src_len,p_dst_first,p_dst_len)
+/* Input : Hand over the required amount of working memory in p_wrk_mem. */
+/* Input : Specify input block using p_src_first and src_len. */
+/* Input : Point p_dst_first to the start of the output zone (OZ). */
+/* Input : Point p_dst_len to a ULONG to receive the output length. */
+/* Input : Input block and output zone must not overlap. */
+/* Output : Length of output block written to *p_dst_len. */
+/* Output : Output block in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. May */
+/* Output : write in OZ=Mem[p_dst_first..p_dst_first+src_len+MAX_CMP_GROUP-1].*/
+/* Output : Upon completion guaranteed *p_dst_len<=src_len+FLAG_BYTES. */
+UBYTE *p_wrk_mem;
+UBYTE *p_src_first;
+ULONG src_len;
+UBYTE *p_dst_first;
+LONG *p_dst_len;
+{
+ /* p_src and p_dst step through the source and destination blocks. */
+ register UBYTE *p_src = p_src_first;
+ register UBYTE *p_dst = p_dst_first;
+
+ /* The following variables are never modified and are used in the */
+ /* calculations that determine when the main loop terminates. */
+ UBYTE *p_src_post = p_src_first+src_len;
+ UBYTE *p_dst_post = p_dst_first+src_len;
+ UBYTE *p_src_max1 = p_src_first+src_len-MAX_RAW_ITEM;
+ UBYTE *p_src_max16 = p_src_first+src_len-MAX_RAW_ITEM*16;
+
+ /* The variables 'p_control' and 'control' are used to buffer control bits. */
+ /* Before each group is processed, the next two bytes of the output block */
+ /* are set aside for the control word for the group about to be processed. */
+ /* 'p_control' is set to point to the first byte of that word. Meanwhile, */
+ /* 'control' buffers the control bits being generated during the processing */
+ /* of the group. Instead of having a counter to keep track of how many items */
+ /* have been processed (=the number of bits in the control word), at the */
+ /* start of each group, the top word of 'control' is filled with 1 bits. */
+ /* As 'control' is shifted for each item, the 1 bits in the top word are */
+ /* absorbed or destroyed. When they all run out (i.e. when the top word is */
+ /* all zero bits, we know that we are at the end of a group. */
+# define TOPWORD 0xFFFF0000
+ UBYTE *p_control;
+ register ULONG control=TOPWORD;
+
+ /* THe variable 'hash' always points to the first element of the hash table. */
+ UBYTE **hash= (UBYTE **) ULONG_ALIGN_UP(p_wrk_mem);
+
+ /* The following two variables represent the literal buffer. p_h1 points to */
+ /* the hash table entry corresponding to the youngest literal. p_h2 points */
+ /* to the hash table entry corresponding to the second youngest literal. */
+ /* Note: p_h1=0=>p_h2=0 because zero values denote absence of a pending */
+ /* literal. The variables are initialized to zero meaning an empty "buffer". */
+ UBYTE **p_h1=0;
+ UBYTE **p_h2=0;
+
+ /* To start, we write the flag bytes. Being optimistic, we set the flag to */
+ /* FLAG_COMPRESS. The remaining flag bytes are zeroed so as to keep the */
+ /* algorithm deterministic. */
+ *p_dst++=FLAG_COMPRESS;
+ {UWORD i; for (i=2;i<=FLAG_BYTES;i++) *p_dst++=0;}
+
+ /* Reserve the first word of output as the control word for the first group. */
+ /* Note: This is undone at the end if the input block is empty. */
+ p_control=p_dst; p_dst+=2;
+
+ /* Initialize all elements of the hash table to point to a constant string. */
+ /* Use of an unrolled loop speeds this up considerably. */
+ {UWORD i; UBYTE **p_h=hash;
+# define ZH *p_h++=START_STRING_18
+ for (i=0;i<256;i++) /* 256=HASH_TABLE_LENGTH/16. */
+ {ZH;ZH;ZH;ZH;
+ ZH;ZH;ZH;ZH;
+ ZH;ZH;ZH;ZH;
+ ZH;ZH;ZH;ZH;}
+ }
+
+ /* The main loop processes either 1 or 16 items per iteration. As its */
+ /* termination logic is complicated, I have opted for an infinite loop */
+ /* structure containing 'break' and 'goto' statements. */
+ while (TRUE)
+ {/* Begin main processing loop. */
+
+ /* Note: All the variables here except unroll should be defined within */
+ /* the inner loop. Unfortunately the loop hasn't got a block. */
+ register UBYTE *p; /* Scans through targ phrase during matching. */
+ register UBYTE *p_ziv= NULL ; /* Points to first byte of current Ziv. */
+ register UWORD unroll; /* Loop counter for unrolled inner loop. */
+ register UWORD index; /* Index of current hash table entry. */
+ register UBYTE **p_h0 = NULL ; /* Pointer to current hash table entry. */
+
+ /* Test for overrun and jump to overrun code if necessary. */
+ if (p_dst>p_dst_post)
+ goto overrun;
+
+ /* The following cascade of if statements efficiently catches and deals */
+ /* with varying degrees of closeness to the end of the input block. */
+ /* When we get very close to the end, we stop updating the table and */
+ /* code the remaining bytes as literals. This makes the code simpler. */
+ unroll=16;
+ if (p_src>p_src_max16)
+ {
+ unroll=1;
+ if (p_src>p_src_max1)
+ {
+ if (p_src==p_src_post)
+ break;
+ else
+ goto literal;
+ }
+ }
+
+ /* This inner unrolled loop processes 'unroll' (whose value is either 1 */
+ /* or 16) items. I have chosen to implement this loop with labels and */
+ /* gotos to heighten the ease with which the loop may be implemented with */
+ /* a single decrement and branch instruction in assembly language and */
+ /* also because the labels act as highly readable place markers. */
+ /* (Also because we jump into the loop for endgame literals (see above)). */
+
+ begin_unrolled_loop:
+
+ /* To process the next phrase, we hash the next three bytes and use */
+ /* the resultant hash table index to look up the hash table. A pointer */
+ /* to the entry is stored in p_h0 so as to avoid an array lookup. The */
+ /* hash table entry *p_h0 is looked up yielding a pointer p to a */
+ /* potential match of the Ziv in the history. */
+ index=HASH(p_src);
+ p_h0=&hash[index];
+ p=*p_h0;
+
+ /* Having looked up the candidate position, we are in a position to */
+ /* attempt a match. The match loop has been unrolled using the PS */
+ /* macro so that failure within the first three bytes automatically */
+ /* results in the literal branch being taken. The coding is simple. */
+ /* p_ziv saves p_src so we can let p_src wander. */
+# define PS *p++!=*p_src++
+ p_ziv=p_src;
+ if (PS || PS || PS)
+ {
+ /* Literal. */
+
+ /* Code the literal byte as itself and a zero control bit. */
+ p_src=p_ziv; literal: *p_dst++=*p_src++; control&=0xFFFEFFFF;
+
+ /* We have just coded a literal. If we had two pending ones, that */
+ /* makes three and we can update the hash table. */
+ if (p_h2!=0)
+ {*p_h2=p_ziv-2;}
+
+ /* In any case, rotate the hash table pointers for next time. */
+ p_h2=p_h1; p_h1=p_h0;
+
+ }
+ else
+ {
+ /* Copy */
+
+ /* Match up to 15 remaining bytes using an unrolled loop and code. */
+#if 0
+ PS || PS || PS || PS || PS || PS || PS || PS ||
+ PS || PS || PS || PS || PS || PS || PS || p_src++;
+#else
+ if (
+ !( PS || PS || PS || PS || PS || PS || PS || PS ||
+ PS || PS || PS || PS || PS || PS || PS )
+ ) p_src++;
+#endif
+ *p_dst++=((index&0xF00)>>4)|(--p_src-p_ziv-3);
+ *p_dst++=index&0xFF;
+
+ /* As we have just coded three bytes, we are now in a position to */
+ /* update the hash table with the literal bytes that were pending */
+ /* upon the arrival of extra context bytes. */
+ if (p_h1!=0)
+ {
+ if (p_h2!=0)
+ {*p_h2=p_ziv-2; p_h2=0;}
+ *p_h1=p_ziv-1; p_h1=0;
+ }
+
+ /* In any case, we can update the hash table based on the current */
+ /* position as we just coded at least three bytes in a copy items. */
+ *p_h0=p_ziv;
+
+ }
+ control>>=1;
+
+ /* This loop is all set up for a decrement and jump instruction! */
+#ifndef linux
+` end_unrolled_loop: if (--unroll) goto begin_unrolled_loop;
+#else
+ /* end_unrolled_loop: */ if (--unroll) goto begin_unrolled_loop;
+#endif
+
+ /* At this point it will nearly always be the end of a group in which */
+ /* case, we have to do some control-word processing. However, near the */
+ /* end of the input block, the inner unrolled loop is only executed once. */
+ /* This necessitates the 'if' test. */
+ if ((control&TOPWORD)==0)
+ {
+ /* Write the control word to the place we saved for it in the output. */
+ *p_control++= control &0xFF;
+ *p_control = (control>>8) &0xFF;
+
+ /* Reserve the next word in the output block for the control word */
+ /* for the group about to be processed. */
+ p_control=p_dst; p_dst+=2;
+
+ /* Reset the control bits buffer. */
+ control=TOPWORD;
+ }
+
+ } /* End main processing loop. */
+
+ /* After the main processing loop has executed, all the input bytes have */
+ /* been processed. However, the control word has still to be written to the */
+ /* word reserved for it in the output at the start of the most recent group. */
+ /* Before writing, the control word has to be shifted so that all the bits */
+ /* are in the right place. The "empty" bit positions are filled with 1s */
+ /* which partially fill the top word. */
+ while(control&TOPWORD) control>>=1;
+ *p_control++= control &0xFF;
+ *p_control++=(control>>8) &0xFF;
+
+ /* If the last group contained no items, delete the control word too. */
+ if (p_control==p_dst) p_dst-=2;
+
+ /* Write the length of the output block to the dst_len parameter and return. */
+ *p_dst_len=p_dst-p_dst_first;
+ return;
+
+ /* Jump here as soon as an overrun is detected. An overrun is defined to */
+ /* have occurred if p_dst>p_dst_first+src_len. That is, the moment the */
+ /* length of the output written so far exceeds the length of the input block.*/
+ /* The algorithm checks for overruns at least at the end of each group */
+ /* which means that the maximum overrun is MAX_CMP_GROUP bytes. */
+ /* Once an overrun occurs, the only thing to do is to set the copy flag and */
+ /* copy the input over. */
+ overrun:
+#if 0
+ *p_dst_first=FLAG_COPY;
+ fast_copy(p_src_first,p_dst_first+FLAG_BYTES,src_len);
+ *p_dst_len=src_len+FLAG_BYTES;
+#else
+ fast_copy(p_src_first,p_dst_first,src_len);
+ *p_dst_len= -src_len; /* return a negative number to indicate uncompressed data */
+#endif
+}
+
+/******************************************************************************/
+
+LOCAL void compress_decompress
+ (p_wrk_mem,p_src_first,src_len,p_dst_first,p_dst_len)
+/* Input : Hand over the required amount of working memory in p_wrk_mem. */
+/* Input : Specify input block using p_src_first and src_len. */
+/* Input : Point p_dst_first to the start of the output zone. */
+/* Input : Point p_dst_len to a ULONG to receive the output length. */
+/* Input : Input block and output zone must not overlap. User knows */
+/* Input : upperbound on output block length from earlier compression. */
+/* Input : In any case, maximum expansion possible is nine times. */
+/* Output : Length of output block written to *p_dst_len. */
+/* Output : Output block in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. */
+/* Output : Writes only in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. */
+UBYTE *p_wrk_mem;
+UBYTE *p_src_first;
+LONG src_len;
+UBYTE *p_dst_first;
+ULONG *p_dst_len;
+{
+ /* Byte pointers p_src and p_dst scan through the input and output blocks. */
+ register UBYTE *p_src = p_src_first+FLAG_BYTES;
+ register UBYTE *p_dst = p_dst_first;
+ /* we need to avoid a SEGV when trying to uncompress corrupt data */
+ register UBYTE *p_dst_post = p_dst_first + *p_dst_len;
+
+ /* The following two variables are never modified and are used to control */
+ /* the main loop. */
+ UBYTE *p_src_post = p_src_first+src_len;
+ UBYTE *p_src_max16 = p_src_first+src_len-(MAX_CMP_GROUP-2);
+
+ /* The hash table is the only resident of the working memory. The hash table */
+ /* contains HASH_TABLE_LENGTH=4096 pointers to positions in the history. To */
+ /* keep Macintoshes happy, it is longword aligned. */
+ UBYTE **hash = (UBYTE **) ULONG_ALIGN_UP(p_wrk_mem);
+
+ /* The variable 'control' is used to buffer the control bits which appear in */
+ /* groups of 16 bits (control words) at the start of each compressed group. */
+ /* When each group is read, bit 16 of the register is set to one. Whenever */
+ /* a new bit is needed, the register is shifted right. When the value of the */
+ /* register becomes 1, we know that we have reached the end of a group. */
+ /* Initializing the register to 1 thus instructs the code to follow that it */
+ /* should read a new control word immediately. */
+ register ULONG control=1;
+
+ /* The value of 'literals' is always in the range 0..3. It is the number of */
+ /* consecutive literal items just seen. We have to record this number so as */
+ /* to know when to update the hash table. When literals gets to 3, there */
+ /* have been three consecutive literals and we can update at the position of */
+ /* the oldest of the three. */
+ register UWORD literals=0;
+
+ /* Check the leading copy flag to see if the compressor chose to use a copy */
+ /* operation instead of a compression operation. If a copy operation was */
+ /* used, then all we need to do is copy the data over, set the output length */
+ /* and return. */
+#if 0
+ if (*p_src_first==FLAG_COPY)
+ {
+ fast_copy(p_src_first+FLAG_BYTES,p_dst_first,src_len-FLAG_BYTES);
+ *p_dst_len=src_len-FLAG_BYTES;
+ return;
+ }
+#else
+ if ( src_len < 0 )
+ {
+ fast_copy(p_src_first,p_dst_first,-src_len );
+ *p_dst_len = (ULONG)-src_len;
+ return;
+ }
+#endif
+
+ /* Initialize all elements of the hash table to point to a constant string. */
+ /* Use of an unrolled loop speeds this up considerably. */
+ {UWORD i; UBYTE **p_h=hash;
+# define ZJ *p_h++=START_STRING_18
+ for (i=0;i<256;i++) /* 256=HASH_TABLE_LENGTH/16. */
+ {ZJ;ZJ;ZJ;ZJ;
+ ZJ;ZJ;ZJ;ZJ;
+ ZJ;ZJ;ZJ;ZJ;
+ ZJ;ZJ;ZJ;ZJ;}
+ }
+
+ /* The outer loop processes either 1 or 16 items per iteration depending on */
+ /* how close p_src is to the end of the input block. */
+ while (p_src!=p_src_post)
+ {/* Start of outer loop */
+
+ register UWORD unroll; /* Counts unrolled loop executions. */
+
+ /* When 'control' has the value 1, it means that the 16 buffered control */
+ /* bits that were read in at the start of the current group have all been */
+ /* shifted out and that all that is left is the 1 bit that was injected */
+ /* into bit 16 at the start of the current group. When we reach the end */
+ /* of a group, we have to load a new control word and inject a new 1 bit. */
+ if (control==1)
+ {
+ control=0x10000|*p_src++;
+ control|=(*p_src++)<<8;
+ }
+
+ /* If it is possible that we are within 16 groups from the end of the */
+ /* input, execute the unrolled loop only once, else process a whole group */
+ /* of 16 items by looping 16 times. */
+ unroll= p_src<=p_src_max16 ? 16 : 1;
+
+ /* This inner loop processes one phrase (item) per iteration. */
+ while (unroll--)
+ { /* Begin unrolled inner loop. */
+
+ /* Process a literal or copy item depending on the next control bit. */
+ if (control&1)
+ {
+ /* Copy item. */
+
+ register UBYTE *p; /* Points to place from which to copy. */
+ register UWORD lenmt; /* Length of copy item minus three. */
+ register UBYTE **p_hte; /* Pointer to current hash table entry.*/
+ register UBYTE *p_ziv=p_dst; /* Pointer to start of current Ziv. */
+
+ /* Read and dismantle the copy word. Work out from where to copy. */
+ lenmt=*p_src++;
+ p_hte=&hash[((lenmt&0xF0)<<4)|*p_src++];
+ p=*p_hte;
+ lenmt&=0xF;
+
+ /* Now perform the copy using a half unrolled loop. */
+ *p_dst++=*p++;
+ *p_dst++=*p++;
+ *p_dst++=*p++;
+ while (lenmt--)
+ *p_dst++=*p++;
+
+ /* Because we have just received 3 or more bytes in a copy item */
+ /* (whose bytes we have just installed in the output), we are now */
+ /* in a position to flush all the pending literal hashings that had */
+ /* been postponed for lack of bytes. */
+ if (literals>0)
+ {
+ register UBYTE *r=p_ziv-literals;;
+ hash[HASH(r)]=r;
+ if (literals==2)
+ {r++; hash[HASH(r)]=r;}
+ literals=0;
+ }
+
+ /* In any case, we can immediately update the hash table with the */
+ /* current position. We don't need to do a HASH(...) to work out */
+ /* where to put the pointer, as the compressor just told us!!! */
+ *p_hte=p_ziv;
+
+ }
+ else
+ {
+ /* Literal item. */
+
+ /* Copy over the literal byte. */
+ *p_dst++=*p_src++;
+
+ /* If we now have three literals waiting to be hashed into the hash */
+ /* table, we can do one of them now (because there are three). */
+ if (++literals == 3)
+ {register UBYTE *p=p_dst-3; hash[HASH(p)]=p; literals=2;}
+ }
+
+ /* Shift the control buffer so the next control bit is in bit 0. */
+ control>>=1;
+#if 1
+ if (p_dst > p_dst_post)
+ {
+ /* Shit: we tried to decompress corrupt data */
+ *p_dst_len = 0;
+ return;
+ }
+#endif
+ } /* End unrolled inner loop. */
+
+ } /* End of outer loop */
+
+ /* Write the length of the decompressed data before returning. */
+ *p_dst_len=p_dst-p_dst_first;
+}
+
+/******************************************************************************/
+/* End of LZRW3.C */
+/******************************************************************************/
diff --git a/drivers/char/ftape/compressor/lzrw3.h b/drivers/char/ftape/compressor/lzrw3.h
new file mode 100644
index 000000000..533feba47
--- /dev/null
+++ b/drivers/char/ftape/compressor/lzrw3.h
@@ -0,0 +1,253 @@
+#ifndef _LZRW3_H
+#define _LZRW3_H
+/*
+ * $Source: /homes/cvs/ftape-stacked/ftape/compressor/lzrw3.h,v $
+ * $Revision: 1.1 $
+ * $Date: 1997/10/05 19:12:30 $
+ *
+ * include files for lzrw3. Only slighty modified from the original
+ * version. Assembles the three include files compress.h, port.h and
+ * fastcopy.h from the original lzrw3 package.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/string.h>
+
+/******************************************************************************/
+/* */
+/* COMPRESS.H */
+/* */
+/******************************************************************************/
+/* */
+/* Author : Ross Williams. */
+/* Date : December 1989. */
+/* */
+/* This header file defines the interface to a set of functions called */
+/* 'compress', each member of which implements a particular data compression */
+/* algorithm. */
+/* */
+/* Normally in C programming, for each .H file, there is a corresponding .C */
+/* file that implements the functions promised in the .H file. */
+/* Here, there are many .C files corresponding to this header file. */
+/* Each comforming implementation file contains a single function */
+/* called 'compress' that implements a single data compression */
+/* algorithm that conforms with the interface specified in this header file. */
+/* Only one algorithm can be linked in at a time in this organization. */
+/* */
+/******************************************************************************/
+/* */
+/* DEFINITION OF FUNCTION COMPRESS */
+/* =============================== */
+/* */
+/* Summary of Function Compress */
+/* ---------------------------- */
+/* The action that 'compress' takes depends on its first argument called */
+/* 'action'. The function provides three actions: */
+/* */
+/* - Return information about the algorithm. */
+/* - Compress a block of memory. */
+/* - Decompress a block of memory. */
+/* */
+/* Parameters */
+/* ---------- */
+/* See the formal C definition later for a description of the parameters. */
+/* */
+/* Constants */
+/* --------- */
+/* COMPRESS_OVERRUN: The constant COMPRESS_OVERRUN defines by how many bytes */
+/* an algorithm is allowed to expand a block during a compression operation. */
+/* */
+/* Although compression algorithms usually compress data, there will always */
+/* be data that a given compressor will expand (this can be proven). */
+/* Fortunately, the degree of expansion can be limited to a single bit, by */
+/* copying over the input data if the data gets bigger during compression. */
+/* To allow for this possibility, the first bit of a compressed */
+/* representation can be used as a flag indicating whether the */
+/* input data was copied over, or truly compressed. In practice, the first */
+/* byte would be used to store this bit so as to maintain byte alignment. */
+/* */
+/* Unfortunately, in general, the only way to tell if an algorithm will */
+/* expand a particular block of data is to run the algorithm on the data. */
+/* If the algorithm does not continuously monitor how many output bytes it */
+/* has written, it might write an output block far larger than the input */
+/* block before realizing that it has done so. */
+/* On the other hand, continuous checks on output length are inefficient. */
+/* */
+/* To cater for all these problems, this interface definition: */
+/* > Allows a compression algorithm to return an output block that is up to */
+/* COMPRESS_OVERRUN bytes longer than the input block. */
+/* > Allows a compression algorithm to write up to COMPRESS_OVERRUN bytes */
+/* more than the length of the input block to the memory of the output */
+/* block regardless of the length of the output block eventually returned. */
+/* This allows an algorithm to overrun the length of the input block in the */
+/* output block by up to COMPRESS_OVERRUN bytes between expansion checks. */
+/* */
+/* The problem does not arise for decompression. */
+/* */
+/* Identity Action */
+/* --------------- */
+/* > action must be COMPRESS_ACTION_IDENTITY. */
+/* > p_dst_len must point to a longword to receive a longword address. */
+/* > The value of the other parameters does not matter. */
+/* > After execution, the longword that p_dst_len points to will be a pointer */
+/* to a structure of type compress_identity. */
+/* Thus, for example, after the call, (*p_dst_len)->memory will return the */
+/* number of bytes of working memory that the algorithm requires to run. */
+/* > The values of the identity structure returned are fixed constant */
+/* attributes of the algorithm and must not vary from call to call. */
+/* */
+/* Common Requirements for Compression and Decompression Actions */
+/* ------------------------------------------------------------- */
+/* > wrk_mem must point to an unused block of memory of a length specified in */
+/* the algorithm's identity block. The identity block can be obtained by */
+/* making a separate call to compress, specifying the identity action. */
+/* > The INPUT BLOCK is defined to be Memory[src_addr,src_addr+src_len-1]. */
+/* > dst_len will be used to denote *p_dst_len. */
+/* > dst_len is not read by compress, only written. */
+/* > The value of dst_len is defined only upon termination. */
+/* > The OUTPUT BLOCK is defined to be Memory[dst_addr,dst_addr+dst_len-1]. */
+/* */
+/* Compression Action */
+/* ------------------ */
+/* > action must be COMPRESS_ACTION_COMPRESS. */
+/* > src_len must be in the range [0,COMPRESS_MAX_ORG]. */
+/* > The OUTPUT ZONE is defined to be */
+/* Memory[dst_addr,dst_addr+src_len-1+COMPRESS_OVERRUN]. */
+/* > The function can modify any part of the output zone regardless of the */
+/* final length of the output block. */
+/* > The input block and the output zone must not overlap. */
+/* > dst_len will be in the range [0,src_len+COMPRESS_OVERRUN]. */
+/* > dst_len will be in the range [0,COMPRESS_MAX_COM] (from prev fact). */
+/* > The output block will consist of a representation of the input block. */
+/* */
+/* Decompression Action */
+/* -------------------- */
+/* > action must be COMPRESS_ACTION_DECOMPRESS. */
+/* > The input block must be the result of an earlier compression operation. */
+/* > If the previous fact is true, the following facts must also be true: */
+/* > src_len will be in the range [0,COMPRESS_MAX_COM]. */
+/* > dst_len will be in the range [0,COMPRESS_MAX_ORG]. */
+/* > The input and output blocks must not overlap. */
+/* > Only the output block is modified. */
+/* > Upon termination, the output block will consist of the bytes contained */
+/* in the input block passed to the earlier compression operation. */
+/* */
+/******************************************************************************/
+
+/******************************************************************************/
+/* */
+/* PORT.H */
+/* */
+/******************************************************************************/
+/* */
+/* This module contains macro definitions and types that are likely to */
+/* change between computers. */
+/* */
+/******************************************************************************/
+
+#ifndef DONE_PORT /* Only do this if not previously done. */
+
+ #ifdef THINK_C
+ #define UBYTE unsigned char /* Unsigned byte */
+ #define UWORD unsigned int /* Unsigned word (2 bytes) */
+ #define ULONG unsigned long /* Unsigned word (4 bytes) */
+ #define BOOL unsigned char /* Boolean */
+ #define FOPEN_BINARY_READ "rb" /* Mode string for binary reading. */
+ #define FOPEN_BINARY_WRITE "wb" /* Mode string for binary writing. */
+ #define FOPEN_TEXT_APPEND "a" /* Mode string for text appending. */
+ #define REAL double /* USed for floating point stuff. */
+ #endif
+ #if defined(LINUX) || defined(linux)
+ #define UBYTE __u8 /* Unsigned byte */
+ #define UWORD __u16 /* Unsigned word (2 bytes) */
+ #define ULONG __u32 /* Unsigned word (4 bytes) */
+ #define LONG __s32 /* Signed word (4 bytes) */
+ #define BOOL is not used here /* Boolean */
+ #define FOPEN_BINARY_READ not used /* Mode string for binary reading. */
+ #define FOPEN_BINARY_WRITE not used /* Mode string for binary writing. */
+ #define FOPEN_TEXT_APPEND not used /* Mode string for text appending. */
+ #define REAL not used /* USed for floating point stuff. */
+ #ifndef TRUE
+ #define TRUE 1
+ #endif
+ #endif
+
+ #define DONE_PORT /* Don't do all this again. */
+ #define MALLOC_FAIL NULL /* Failure status from malloc() */
+ #define LOCAL static /* For non-exported routines. */
+ #define EXPORT /* Signals exported function. */
+ #define then /* Useful for aligning ifs. */
+
+#endif
+
+/******************************************************************************/
+/* End of PORT.H */
+/******************************************************************************/
+
+#define COMPRESS_ACTION_IDENTITY 0
+#define COMPRESS_ACTION_COMPRESS 1
+#define COMPRESS_ACTION_DECOMPRESS 2
+
+#define COMPRESS_OVERRUN 1024
+#define COMPRESS_MAX_COM 0x70000000
+#define COMPRESS_MAX_ORG (COMPRESS_MAX_COM-COMPRESS_OVERRUN)
+
+#define COMPRESS_MAX_STRLEN 255
+
+/* The following structure provides information about the algorithm. */
+/* > The top bit of id must be zero. The remaining bits must be chosen by */
+/* the author of the algorithm by tossing a coin 31 times. */
+/* > The amount of memory requested by the algorithm is specified in bytes */
+/* and must be in the range [0,0x70000000]. */
+/* > All strings s must be such that strlen(s)<=COMPRESS_MAX_STRLEN. */
+struct compress_identity
+ {
+ ULONG id; /* Identifying number of algorithm. */
+ ULONG memory; /* Number of bytes of working memory required. */
+
+ char *name; /* Name of algorithm. */
+ char *version; /* Version number. */
+ char *date; /* Date of release of this version. */
+ char *copyright; /* Copyright message. */
+
+ char *author; /* Author of algorithm. */
+ char *affiliation; /* Affiliation of author. */
+ char *vendor; /* Where the algorithm can be obtained. */
+ };
+
+void lzrw3_compress( /* Single function interface to compression algorithm. */
+UWORD action, /* Action to be performed. */
+UBYTE *wrk_mem, /* Working memory temporarily given to routine to use. */
+UBYTE *src_adr, /* Address of input data. */
+LONG src_len, /* Length of input data. */
+UBYTE *dst_adr, /* Address of output data. */
+void *p_dst_len /* Pointer to a longword where routine will write: */
+ /* If action=..IDENTITY => Adr of id structure. */
+ /* If action=..COMPRESS => Length of output data. */
+ /* If action=..DECOMPRESS => Length of output data. */
+);
+
+/******************************************************************************/
+/* End of COMPRESS.H */
+/******************************************************************************/
+
+
+/******************************************************************************/
+/* fast_copy.h */
+/******************************************************************************/
+
+/* This function copies a block of memory very quickly. */
+/* The exact speed depends on the relative alignment of the blocks of memory. */
+/* PRE : 0<=src_len<=(2^32)-1 . */
+/* PRE : Source and destination blocks must not overlap. */
+/* POST : MEM[dst_adr,dst_adr+src_len-1]=MEM[src_adr,src_adr+src_len-1]. */
+/* POST : MEM[dst_adr,dst_adr+src_len-1] is the only memory changed. */
+
+#define fast_copy(src,dst,len) memcpy(dst,src,len)
+
+/******************************************************************************/
+/* End of fast_copy.h */
+/******************************************************************************/
+
+#endif
diff --git a/drivers/char/ftape/compressor/zftape-compress.c b/drivers/char/ftape/compressor/zftape-compress.c
new file mode 100644
index 000000000..382b58abe
--- /dev/null
+++ b/drivers/char/ftape/compressor/zftape-compress.c
@@ -0,0 +1,1317 @@
+/*
+ * Copyright (C) 1994-1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * This file implements a "generic" interface between the *
+ * zftape-driver and a compression-algorithm. The *
+ * compression-algorithm currently used is a LZ77. I use the *
+ * implementation lzrw3 by Ross N. Williams (Renaissance *
+ * Software). The compression program itself is in the file
+ * lzrw3.c * and lzrw3.h. To adopt another compression algorithm
+ * the functions * zft_compress() and zft_uncompress() must be
+ * changed * appropriately. See below.
+ */
+
+ char zftc_src[] ="$Source: /homes/cvs/ftape-stacked/ftape/compressor/zftape-compress.c,v $";
+ char zftc_rev[] = "$Revision: 1.1.6.1 $";
+ char zftc_dat[] = "$Date: 1997/11/16 15:15:56 $";
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+
+#include <linux/zftape.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6)
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#endif
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../compressor/zftape-compress.h"
+#include "../zftape/zftape-vtbl.h"
+#include "../compressor/lzrw3.h"
+
+/*
+ * global variables
+ */
+
+/* I handle the allocation of this buffer as a special case, because
+ * it's size varies depending on the tape length inserted.
+ */
+
+/* local variables
+ */
+static int keep_module_locked = 1;
+
+static void *zftc_wrk_mem = NULL;
+static __u8 *zftc_buf = NULL;
+static void *zftc_scratch_buf = NULL;
+
+/* compression statistics
+ */
+static unsigned int zftc_wr_uncompressed = 0;
+static unsigned int zftc_wr_compressed = 0;
+static unsigned int zftc_rd_uncompressed = 0;
+static unsigned int zftc_rd_compressed = 0;
+
+/* forward */
+static int zftc_write(int *write_cnt,
+ __u8 *dst_buf, const int seg_sz,
+ const __u8 *src_buf, const int req_len,
+ const zft_position *pos, const zft_volinfo *volume);
+static int zftc_read(int *read_cnt,
+ __u8 *dst_buf, const int to_do,
+ const __u8 *src_buf, const int seg_sz,
+ const zft_position *pos, const zft_volinfo *volume);
+static int zftc_seek(unsigned int new_block_pos,
+ zft_position *pos, const zft_volinfo *volume,
+ __u8 *buffer);
+static void zftc_lock (void);
+static void zftc_reset (void);
+static void zftc_cleanup(void);
+static void zftc_stats (void);
+
+/* compressed segment. This conforms to QIC-80-MC, Revision K.
+ *
+ * Rev. K applies to tapes with `fixed length format' which is
+ * indicated by format code 2,3 and 5. See below for format code 4 and 6
+ *
+ * 2 bytes: offset of compression segment structure
+ * 29k > offset >= 29k-18: data from previous segment ens in this
+ * segment and no compressed block starts
+ * in this segment
+ * offset == 0: data from previous segment occupies entire
+ * segment and continues in next segment
+ * n bytes: remainder from previous segment
+ *
+ * Rev. K:
+ * 4 bytes: 4 bytes: files set byte offset
+ * Post Rev. K and QIC-3020/3020:
+ * 8 bytes: 8 bytes: files set byte offset
+ * 2 bytes: byte count N (amount of data following)
+ * bit 15 is set if data is compressed, bit 15 is not
+ * set if data is uncompressed
+ * N bytes: data (as much as specified in the byte count)
+ * 2 bytes: byte count N_1 of next cluster
+ * N_1 bytes: data of next cluset
+ * 2 bytes: byte count N_2 of next cluster
+ * N_2 bytes: ...
+ *
+ * Note that the `N' byte count accounts only for the bytes that in the
+ * current segment if the cluster spans to the next segment.
+ */
+
+typedef struct
+{
+ int cmpr_pos; /* actual position in compression buffer */
+ int cmpr_sz; /* what is left in the compression buffer
+ * when copying the compressed data to the
+ * deblock buffer
+ */
+ unsigned int first_block; /* location of header information in
+ * this segment
+ */
+ unsigned int count; /* amount of data of current block
+ * contained in current segment
+ */
+ unsigned int offset; /* offset in current segment */
+ unsigned int spans:1; /* might continue in next segment */
+ unsigned int uncmpr; /* 0x8000 if this block contains
+ * uncompressed data
+ */
+ __s64 foffs; /* file set byte offset, same as in
+ * compression map segment
+ */
+} cmpr_info;
+
+static cmpr_info cseg; /* static data. Must be kept uptodate and shared by
+ * read, write and seek functions
+ */
+
+#define DUMP_CMPR_INFO(level, msg, info) \
+ TRACE(level, msg "\n" \
+ KERN_INFO "cmpr_pos : %d\n" \
+ KERN_INFO "cmpr_sz : %d\n" \
+ KERN_INFO "first_block: %d\n" \
+ KERN_INFO "count : %d\n" \
+ KERN_INFO "offset : %d\n" \
+ KERN_INFO "spans : %d\n" \
+ KERN_INFO "uncmpr : 0x%04x\n" \
+ KERN_INFO "foffs : " LL_X, \
+ (info)->cmpr_pos, (info)->cmpr_sz, (info)->first_block, \
+ (info)->count, (info)->offset, (info)->spans == 1, \
+ (info)->uncmpr, LL((info)->foffs))
+
+/* dispatch compression segment info, return error code
+ *
+ * afterwards, cseg->offset points to start of data of the NEXT
+ * compressed block, and cseg->count contains the amount of data
+ * left in the actual compressed block. cseg->spans is set to 1 if
+ * the block is continued in the following segment. Otherwise it is
+ * set to 0.
+ */
+static int get_cseg (cmpr_info *cinfo, const __u8 *buff,
+ const unsigned int seg_sz,
+ const zft_volinfo *volume)
+{
+ TRACE_FUN(ft_t_flow);
+
+ cinfo->first_block = GET2(buff, 0);
+ if (cinfo->first_block == 0) { /* data spans to next segment */
+ cinfo->count = seg_sz - sizeof(__u16);
+ cinfo->offset = seg_sz;
+ cinfo->spans = 1;
+ } else { /* cluster definetely ends in this segment */
+ if (cinfo->first_block > seg_sz) {
+ /* data corrupted */
+ TRACE_ABORT(-EIO, ft_t_err, "corrupted data:\n"
+ KERN_INFO "segment size: %d\n"
+ KERN_INFO "first block : %d",
+ seg_sz, cinfo->first_block);
+ }
+ cinfo->count = cinfo->first_block - sizeof(__u16);
+ cinfo->offset = cinfo->first_block;
+ cinfo->spans = 0;
+ }
+ /* now get the offset the first block should have in the
+ * uncompressed data stream.
+ *
+ * For this magic `18' refer to CRF-3 standard or QIC-80MC,
+ * Rev. K.
+ */
+ if ((seg_sz - cinfo->offset) > 18) {
+ if (volume->qic113) { /* > revision K */
+ TRACE(ft_t_data_flow, "New QIC-113 compliance");
+ cinfo->foffs = GET8(buff, cinfo->offset);
+ cinfo->offset += sizeof(__s64);
+ } else {
+ TRACE(/* ft_t_data_flow */ ft_t_noise, "pre QIC-113 version");
+ cinfo->foffs = (__s64)GET4(buff, cinfo->offset);
+ cinfo->offset += sizeof(__u32);
+ }
+ }
+ if (cinfo->foffs > volume->size) {
+ TRACE_ABORT(-EIO, ft_t_err, "Inconsistency:\n"
+ KERN_INFO "offset in current volume: %d\n"
+ KERN_INFO "size of current volume : %d",
+ (int)(cinfo->foffs>>10), (int)(volume->size>>10));
+ }
+ if (cinfo->cmpr_pos + cinfo->count > volume->blk_sz) {
+ TRACE_ABORT(-EIO, ft_t_err, "Inconsistency:\n"
+ KERN_INFO "block size : %d\n"
+ KERN_INFO "data record: %d",
+ volume->blk_sz, cinfo->cmpr_pos + cinfo->count);
+ }
+ DUMP_CMPR_INFO(ft_t_noise /* ft_t_any */, "", cinfo);
+ TRACE_EXIT 0;
+}
+
+/* This one is called, when a new cluster starts in same segment.
+ *
+ * Note: if this is the first cluster in the current segment, we must
+ * not check whether there are more than 18 bytes available because
+ * this have already been done in get_cseg() and there may be less
+ * than 18 bytes available due to header information.
+ *
+ */
+static void get_next_cluster(cmpr_info *cluster, const __u8 *buff,
+ const int seg_sz, const int finish)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (seg_sz - cluster->offset > 18 || cluster->foffs != 0) {
+ cluster->count = GET2(buff, cluster->offset);
+ cluster->uncmpr = cluster->count & 0x8000;
+ cluster->count -= cluster->uncmpr;
+ cluster->offset += sizeof(__u16);
+ cluster->foffs = 0;
+ if ((cluster->offset + cluster->count) < seg_sz) {
+ cluster->spans = 0;
+ } else if (cluster->offset + cluster->count == seg_sz) {
+ cluster->spans = !finish;
+ } else {
+ /* either an error or a volume written by an
+ * old version. If this is a data error, then we'll
+ * catch it later.
+ */
+ TRACE(ft_t_data_flow, "Either error or old volume");
+ cluster->spans = 1;
+ cluster->count = seg_sz - cluster->offset;
+ }
+ } else {
+ cluster->count = 0;
+ cluster->spans = 0;
+ cluster->foffs = 0;
+ }
+ DUMP_CMPR_INFO(ft_t_noise /* ft_t_any */ , "", cluster);
+ TRACE_EXIT;
+}
+
+static void zftc_lock(void)
+{
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+ if (!MOD_IN_USE) {
+ MOD_INC_USE_COUNT;
+ }
+#else
+ MOD_INC_USE_COUNT; /* sets MOD_VISITED and MOD_USED_ONCE,
+ * locking is done with can_unload()
+ */
+#endif
+ keep_module_locked = 1;
+}
+
+/* this function is needed for zftape_reset_position in zftape-io.c
+ */
+static void zftc_reset(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ memset((void *)&cseg, '\0', sizeof(cseg));
+ zftc_stats();
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+ if (MOD_IN_USE) {
+ MOD_DEC_USE_COUNT;
+ }
+#endif
+ keep_module_locked = 0;
+ TRACE_EXIT;
+}
+
+static int cmpr_mem_initialized = 0;
+static unsigned int alloc_blksz = 0;
+
+static int zft_allocate_cmpr_mem(unsigned int blksz)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (cmpr_mem_initialized && blksz == alloc_blksz) {
+ TRACE_EXIT 0;
+ }
+ TRACE_CATCH(zft_vmalloc_once(&zftc_wrk_mem, CMPR_WRK_MEM_SIZE),
+ zftc_cleanup());
+ TRACE_CATCH(zft_vmalloc_always(&zftc_buf, blksz + CMPR_OVERRUN),
+ zftc_cleanup());
+ alloc_blksz = blksz;
+ TRACE_CATCH(zft_vmalloc_always(&zftc_scratch_buf, blksz+CMPR_OVERRUN),
+ zftc_cleanup());
+ cmpr_mem_initialized = 1;
+ TRACE_EXIT 0;
+}
+
+static void zftc_cleanup(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ zft_vfree(&zftc_wrk_mem, CMPR_WRK_MEM_SIZE);
+ zft_vfree(&zftc_buf, alloc_blksz + CMPR_OVERRUN);
+ zft_vfree(&zftc_scratch_buf, alloc_blksz + CMPR_OVERRUN);
+ cmpr_mem_initialized = alloc_blksz = 0;
+ TRACE_EXIT;
+}
+
+/*****************************************************************************
+ * *
+ * The following two functions "ftape_compress()" and *
+ * "ftape_uncompress()" are the interface to the actual compression *
+ * algorithm (i.e. they are calling the "compress()" function from *
+ * the lzrw3 package for now). These routines could quite easily be *
+ * changed to adopt another compression algorithm instead of lzrw3, *
+ * which currently is used. *
+ * *
+ *****************************************************************************/
+
+/* called by zft_compress_write() to perform the compression. Must
+ * return the size of the compressed data.
+ *
+ * NOTE: The size of the compressed data should not exceed the size of
+ * the uncompressed data. Most compression algorithms have means
+ * to store data unchanged if the "compressed" data amount would
+ * exceed the original one. Mostly this is done by storing some
+ * flag-bytes in front of the compressed data to indicate if it
+ * is compressed or not. Thus the worst compression result
+ * length is the original length plus those flag-bytes.
+ *
+ * We don't want that, as the QIC-80 standard provides a means
+ * of marking uncompressed blocks by simply setting bit 15 of
+ * the compressed block's length. Thus a compessed block can
+ * have at most a length of 2^15-1 bytes. The QIC-80 standard
+ * restricts the block-length even further, allowing only 29k -
+ * 6 bytes.
+ *
+ * Currently, the maximum blocksize used by zftape is 28k.
+ *
+ * In short: don't exceed the length of the input-package, set
+ * bit 15 of the compressed size to 1 if you have copied data
+ * instead of compressing it.
+ */
+static int zft_compress(__u8 *in_buffer, unsigned int in_sz, __u8 *out_buffer)
+{
+ __s32 compressed_sz;
+ TRACE_FUN(ft_t_flow);
+
+
+ lzrw3_compress(COMPRESS_ACTION_COMPRESS, zftc_wrk_mem,
+ in_buffer, in_sz, out_buffer, &compressed_sz);
+ if (TRACE_LEVEL >= ft_t_info) {
+ /* the compiler will optimize this away when
+ * compiled with NO_TRACE_AT_ALL option
+ */
+ TRACE(ft_t_data_flow, "\n"
+ KERN_INFO "before compression: %d bytes\n"
+ KERN_INFO "after compresison : %d bytes",
+ in_sz,
+ (int)(compressed_sz < 0
+ ? -compressed_sz : compressed_sz));
+ /* for statistical purposes
+ */
+ zftc_wr_compressed += (compressed_sz < 0
+ ? -compressed_sz : compressed_sz);
+ zftc_wr_uncompressed += in_sz;
+ }
+ TRACE_EXIT (int)compressed_sz;
+}
+
+/* called by zft_compress_read() to decompress the data. Must
+ * return the size of the decompressed data for sanity checks
+ * (compared with zft_blk_sz)
+ *
+ * NOTE: Read the note for zft_compress() above! If bit 15 of the
+ * parameter in_sz is set, then the data in in_buffer isn't
+ * compressed, which must be handled by the un-compression
+ * algorithm. (I changed lzrw3 to handle this.)
+ *
+ * The parameter max_out_sz is needed to prevent buffer overruns when
+ * uncompressing corrupt data.
+ */
+static unsigned int zft_uncompress(__u8 *in_buffer,
+ int in_sz,
+ __u8 *out_buffer,
+ unsigned int max_out_sz)
+{
+ TRACE_FUN(ft_t_flow);
+
+ lzrw3_compress(COMPRESS_ACTION_DECOMPRESS, zftc_wrk_mem,
+ in_buffer, (__s32)in_sz,
+ out_buffer, (__u32 *)&max_out_sz);
+
+ if (TRACE_LEVEL >= ft_t_info) {
+ TRACE(ft_t_data_flow, "\n"
+ KERN_INFO "before decompression: %d bytes\n"
+ KERN_INFO "after decompression : %d bytes",
+ in_sz < 0 ? -in_sz : in_sz,(int)max_out_sz);
+ /* for statistical purposes
+ */
+ zftc_rd_compressed += in_sz < 0 ? -in_sz : in_sz;
+ zftc_rd_uncompressed += max_out_sz;
+ }
+ TRACE_EXIT (unsigned int)max_out_sz;
+}
+
+/* print some statistics about the efficiency of the compression to
+ * the kernel log
+ */
+static void zftc_stats(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (TRACE_LEVEL < ft_t_info) {
+ TRACE_EXIT;
+ }
+ if (zftc_wr_uncompressed != 0) {
+ if (zftc_wr_compressed > (1<<14)) {
+ TRACE(ft_t_info, "compression statistics (writing):\n"
+ KERN_INFO " compr./uncmpr. : %3d %%",
+ (((zftc_wr_compressed>>10) * 100)
+ / (zftc_wr_uncompressed>>10)));
+ } else {
+ TRACE(ft_t_info, "compression statistics (writing):\n"
+ KERN_INFO " compr./uncmpr. : %3d %%",
+ ((zftc_wr_compressed * 100)
+ / zftc_wr_uncompressed));
+ }
+ }
+ if (zftc_rd_uncompressed != 0) {
+ if (zftc_rd_compressed > (1<<14)) {
+ TRACE(ft_t_info, "compression statistics (reading):\n"
+ KERN_INFO " compr./uncmpr. : %3d %%",
+ (((zftc_rd_compressed>>10) * 100)
+ / (zftc_rd_uncompressed>>10)));
+ } else {
+ TRACE(ft_t_info, "compression statistics (reading):\n"
+ KERN_INFO " compr./uncmpr. : %3d %%",
+ ((zftc_rd_compressed * 100)
+ / zftc_rd_uncompressed));
+ }
+ }
+ /* only print it once: */
+ zftc_wr_uncompressed =
+ zftc_wr_compressed =
+ zftc_rd_uncompressed =
+ zftc_rd_compressed = 0;
+ TRACE_EXIT;
+}
+
+/* start new compressed block
+ */
+static int start_new_cseg(cmpr_info *cluster,
+ char *dst_buf,
+ const zft_position *pos,
+ const unsigned int blk_sz,
+ const char *src_buf,
+ const int this_segs_sz,
+ const int qic113)
+{
+ int size_left;
+ int cp_cnt;
+ int buf_pos;
+ TRACE_FUN(ft_t_flow);
+
+ size_left = this_segs_sz - sizeof(__u16) - cluster->cmpr_sz;
+ TRACE(ft_t_data_flow,"\n"
+ KERN_INFO "segment size : %d\n"
+ KERN_INFO "compressed_sz: %d\n"
+ KERN_INFO "size_left : %d",
+ this_segs_sz, cluster->cmpr_sz, size_left);
+ if (size_left > 18) { /* start a new cluseter */
+ cp_cnt = cluster->cmpr_sz;
+ cluster->cmpr_sz = 0;
+ buf_pos = cp_cnt + sizeof(__u16);
+ PUT2(dst_buf, 0, buf_pos);
+
+ if (qic113) {
+ __s64 foffs = pos->volume_pos;
+ if (cp_cnt) foffs += (__s64)blk_sz;
+
+ TRACE(ft_t_data_flow, "new style QIC-113 header");
+ PUT8(dst_buf, buf_pos, foffs);
+ buf_pos += sizeof(__s64);
+ } else {
+ __u32 foffs = (__u32)pos->volume_pos;
+ if (cp_cnt) foffs += (__u32)blk_sz;
+
+ TRACE(ft_t_data_flow, "old style QIC-80MC header");
+ PUT4(dst_buf, buf_pos, foffs);
+ buf_pos += sizeof(__u32);
+ }
+ } else if (size_left >= 0) {
+ cp_cnt = cluster->cmpr_sz;
+ cluster->cmpr_sz = 0;
+ buf_pos = cp_cnt + sizeof(__u16);
+ PUT2(dst_buf, 0, buf_pos);
+ /* zero unused part of segment. */
+ memset(dst_buf + buf_pos, '\0', size_left);
+ buf_pos = this_segs_sz;
+ } else { /* need entire segment and more space */
+ PUT2(dst_buf, 0, 0);
+ cp_cnt = this_segs_sz - sizeof(__u16);
+ cluster->cmpr_sz -= cp_cnt;
+ buf_pos = this_segs_sz;
+ }
+ memcpy(dst_buf + sizeof(__u16), src_buf + cluster->cmpr_pos, cp_cnt);
+ cluster->cmpr_pos += cp_cnt;
+ TRACE_EXIT buf_pos;
+}
+
+/* return-value: the number of bytes removed from the user-buffer
+ * `src_buf' or error code
+ *
+ * int *write_cnt : how much actually has been moved to the
+ * dst_buf. Need not be initialized when
+ * function returns with an error code
+ * (negativ return value)
+ * __u8 *dst_buf : kernel space buffer where the has to be
+ * copied to. The contents of this buffers
+ * goes to a specific segment.
+ * const int seg_sz : the size of the segment dst_buf will be
+ * copied to.
+ * const zft_position *pos : struct containing the coordinates in
+ * the current volume (byte position,
+ * segment id of current segment etc)
+ * const zft_volinfo *volume: information about the current volume,
+ * size etc.
+ * const __u8 *src_buf : user space buffer that contains the
+ * data the user wants to be written to
+ * tape.
+ * const int req_len : the amount of data the user wants to be
+ * written to tape.
+ */
+static int zftc_write(int *write_cnt,
+ __u8 *dst_buf, const int seg_sz,
+ const __u8 *src_buf, const int req_len,
+ const zft_position *pos, const zft_volinfo *volume)
+{
+ int req_len_left = req_len;
+ int result;
+ int len_left;
+ int buf_pos_write = pos->seg_byte_pos;
+ TRACE_FUN(ft_t_flow);
+
+ keep_module_locked = 1;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+ MOD_INC_USE_COUNT; /* sets MOD_VISITED and MOD_USED_ONCE,
+ * locking is done with can_unload()
+ */
+#else
+ if (!MOD_IN_USE) {
+ MOD_INC_USE_COUNT;
+ }
+#endif
+ /* Note: we do not unlock the module because
+ * there are some values cached in that `cseg' variable. We
+ * don't don't want to use this information when being
+ * unloaded by kerneld even when the tape is full or when we
+ * cannot allocate enough memory.
+ */
+ if (pos->tape_pos > (volume->size-volume->blk_sz-ZFT_CMPR_OVERHEAD)) {
+ TRACE_EXIT -ENOSPC;
+ }
+ if (zft_allocate_cmpr_mem(volume->blk_sz) < 0) {
+ /* should we unlock the module? But it shouldn't
+ * be locked anyway ...
+ */
+ TRACE_EXIT -ENOMEM;
+ }
+ if (buf_pos_write == 0) { /* fill a new segment */
+ *write_cnt = buf_pos_write = start_new_cseg(&cseg,
+ dst_buf,
+ pos,
+ volume->blk_sz,
+ zftc_buf,
+ seg_sz,
+ volume->qic113);
+ if (cseg.cmpr_sz == 0 && cseg.cmpr_pos != 0) {
+ req_len_left -= result = volume->blk_sz;
+ cseg.cmpr_pos = 0;
+ } else {
+ result = 0;
+ }
+ } else {
+ *write_cnt = result = 0;
+ }
+
+ len_left = seg_sz - buf_pos_write;
+ while ((req_len_left > 0) && (len_left > 18)) {
+ /* now we have some size left for a new compressed
+ * block. We know, that the compression buffer is
+ * empty (else there wouldn't be any space left).
+ */
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+ if (copy_from_user(zftc_scratch_buf, src_buf + result,
+ volume->blk_sz) != 0) {
+ TRACE_EXIT -EFAULT;
+ }
+#else
+ TRACE_CATCH(verify_area(VERIFY_READ, src_buf + result,
+ volume->blk_sz),);
+ memcpy_fromfs(zftc_scratch_buf, src_buf + result,
+ volume->blk_sz);
+#endif
+ req_len_left -= volume->blk_sz;
+ cseg.cmpr_sz = zft_compress(zftc_scratch_buf, volume->blk_sz,
+ zftc_buf);
+ if (cseg.cmpr_sz < 0) {
+ cseg.uncmpr = 0x8000;
+ cseg.cmpr_sz = -cseg.cmpr_sz;
+ } else {
+ cseg.uncmpr = 0;
+ }
+ /* increment "result" iff we copied the entire
+ * compressed block to the zft_deblock_buf
+ */
+ len_left -= sizeof(__u16);
+ if (len_left >= cseg.cmpr_sz) {
+ len_left -= cseg.count = cseg.cmpr_sz;
+ cseg.cmpr_pos = cseg.cmpr_sz = 0;
+ result += volume->blk_sz;
+ } else {
+ cseg.cmpr_sz -=
+ cseg.cmpr_pos =
+ cseg.count = len_left;
+ len_left = 0;
+ }
+ PUT2(dst_buf, buf_pos_write, cseg.uncmpr | cseg.count);
+ buf_pos_write += sizeof(__u16);
+ memcpy(dst_buf + buf_pos_write, zftc_buf, cseg.count);
+ buf_pos_write += cseg.count;
+ *write_cnt += cseg.count + sizeof(__u16);
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ }
+ /* erase the remainder of the segment if less than 18 bytes
+ * left (18 bytes is due to the QIC-80 standard)
+ */
+ if (len_left <= 18) {
+ memset(dst_buf + buf_pos_write, '\0', len_left);
+ (*write_cnt) += len_left;
+ }
+ TRACE(ft_t_data_flow, "returning %d", result);
+ TRACE_EXIT result;
+}
+
+/* out:
+ *
+ * int *read_cnt: the number of bytes we removed from the zft_deblock_buf
+ * (result)
+ * int *to_do : the remaining size of the read-request.
+ *
+ * in:
+ *
+ * char *buff : buff is the address of the upper part of the user
+ * buffer, that hasn't been filled with data yet.
+
+ * int buf_pos_read : copy of from _ftape_read()
+ * int buf_len_read : copy of buf_len_rd from _ftape_read()
+ * char *zft_deblock_buf: zft_deblock_buf
+ * unsigned short blk_sz: the block size valid for this volume, may differ
+ * from zft_blk_sz.
+ * int finish: if != 0 means that this is the last segment belonging
+ * to this volume
+ * returns the amount of data actually copied to the user-buffer
+ *
+ * to_do MUST NOT SHRINK except to indicate an EOF. In this case *to_do has to
+ * be set to 0
+ */
+static int zftc_read (int *read_cnt,
+ __u8 *dst_buf, const int to_do,
+ const __u8 *src_buf, const int seg_sz,
+ const zft_position *pos, const zft_volinfo *volume)
+{
+ int uncompressed_sz;
+ int result = 0;
+ int remaining = to_do;
+ TRACE_FUN(ft_t_flow);
+
+ keep_module_locked = 1;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+ MOD_INC_USE_COUNT; /* sets MOD_VISITED and MOD_USED_ONCE,
+ * locking is done with can_unload()
+ */
+#else
+ if (!MOD_IN_USE) {
+ MOD_INC_USE_COUNT;
+ }
+#endif
+ TRACE_CATCH(zft_allocate_cmpr_mem(volume->blk_sz),);
+ if (pos->seg_byte_pos == 0) {
+ /* new segment just read
+ */
+ TRACE_CATCH(get_cseg(&cseg, src_buf, seg_sz, volume),
+ *read_cnt = 0);
+ memcpy(zftc_buf + cseg.cmpr_pos, src_buf + sizeof(__u16),
+ cseg.count);
+ cseg.cmpr_pos += cseg.count;
+ *read_cnt = cseg.offset;
+ DUMP_CMPR_INFO(ft_t_noise /* ft_t_any */, "", &cseg);
+ } else {
+ *read_cnt = 0;
+ }
+ /* loop and uncompress until user buffer full or
+ * deblock-buffer empty
+ */
+ TRACE(ft_t_data_flow, "compressed_sz: %d, compos : %d, *read_cnt: %d",
+ cseg.cmpr_sz, cseg.cmpr_pos, *read_cnt);
+ while ((cseg.spans == 0) && (remaining > 0)) {
+ if (cseg.cmpr_pos != 0) { /* cmpr buf is not empty */
+ uncompressed_sz =
+ zft_uncompress(zftc_buf,
+ cseg.uncmpr == 0x8000 ?
+ -cseg.cmpr_pos : cseg.cmpr_pos,
+ zftc_scratch_buf,
+ volume->blk_sz);
+ if (uncompressed_sz != volume->blk_sz) {
+ *read_cnt = 0;
+ TRACE_ABORT(-EIO, ft_t_warn,
+ "Uncompressed blk (%d) != blk size (%d)",
+ uncompressed_sz, volume->blk_sz);
+ }
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+ if (copy_to_user(dst_buf + result,
+ zftc_scratch_buf,
+ uncompressed_sz) != 0 ) {
+ TRACE_EXIT -EFAULT;
+ }
+#else
+ memcpy_tofs(dst_buf + result, zftc_scratch_buf,
+ uncompressed_sz);
+#endif
+ remaining -= uncompressed_sz;
+ result += uncompressed_sz;
+ cseg.cmpr_pos = 0;
+ }
+ if (remaining > 0) {
+ get_next_cluster(&cseg, src_buf, seg_sz,
+ volume->end_seg == pos->seg_pos);
+ if (cseg.count != 0) {
+ memcpy(zftc_buf, src_buf + cseg.offset,
+ cseg.count);
+ cseg.cmpr_pos = cseg.count;
+ cseg.offset += cseg.count;
+ *read_cnt += cseg.count + sizeof(__u16);
+ } else {
+ remaining = 0;
+ }
+ }
+ TRACE(ft_t_data_flow, "\n"
+ KERN_INFO "compressed_sz: %d\n"
+ KERN_INFO "compos : %d\n"
+ KERN_INFO "*read_cnt : %d",
+ cseg.cmpr_sz, cseg.cmpr_pos, *read_cnt);
+ }
+ if (seg_sz - cseg.offset <= 18) {
+ *read_cnt += seg_sz - cseg.offset;
+ TRACE(ft_t_data_flow, "expanding read cnt to: %d", *read_cnt);
+ }
+ TRACE(ft_t_data_flow, "\n"
+ KERN_INFO "segment size : %d\n"
+ KERN_INFO "read count : %d\n"
+ KERN_INFO "buf_pos_read : %d\n"
+ KERN_INFO "remaining : %d",
+ seg_sz, *read_cnt, pos->seg_byte_pos,
+ seg_sz - *read_cnt - pos->seg_byte_pos);
+ TRACE(ft_t_data_flow, "returning: %d", result);
+ TRACE_EXIT result;
+}
+
+/* seeks to the new data-position. Reads sometimes a segment.
+ *
+ * start_seg and end_seg give the boundaries of the current volume
+ * blk_sz is the blk_sz of the current volume as stored in the
+ * volume label
+ *
+ * We don't allow blocksizes less than 1024 bytes, therefore we don't need
+ * a 64 bit argument for new_block_pos.
+ */
+
+static int seek_in_segment(const unsigned int to_do, cmpr_info *c_info,
+ const char *src_buf, const int seg_sz,
+ const int seg_pos, const zft_volinfo *volume);
+static int slow_seek_forward_until_error(const unsigned int distance,
+ cmpr_info *c_info, zft_position *pos,
+ const zft_volinfo *volume, __u8 *buf);
+static int search_valid_segment(unsigned int segment,
+ const unsigned int end_seg,
+ const unsigned int max_foffs,
+ zft_position *pos, cmpr_info *c_info,
+ const zft_volinfo *volume, __u8 *buf);
+static int slow_seek_forward(unsigned int dest, cmpr_info *c_info,
+ zft_position *pos, const zft_volinfo *volume,
+ __u8 *buf);
+static int compute_seg_pos(unsigned int dest, zft_position *pos,
+ const zft_volinfo *volume);
+
+#define ZFT_SLOW_SEEK_THRESHOLD 10 /* segments */
+#define ZFT_FAST_SEEK_MAX_TRIALS 10 /* times */
+#define ZFT_FAST_SEEK_BACKUP 10 /* segments */
+
+static int zftc_seek(unsigned int new_block_pos,
+ zft_position *pos, const zft_volinfo *volume, __u8 *buf)
+{
+ unsigned int dest;
+ int limit;
+ int distance;
+ int result = 0;
+ int seg_dist;
+ int new_seg;
+ int old_seg = 0;
+ int fast_seek_trials = 0;
+ TRACE_FUN(ft_t_flow);
+
+ keep_module_locked = 1;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+ MOD_INC_USE_COUNT; /* sets MOD_VISITED and MOD_USED_ONCE,
+ * locking is done with can_unload()
+ */
+#else
+ if (!MOD_IN_USE) {
+ MOD_INC_USE_COUNT;
+ }
+#endif
+ if (new_block_pos == 0) {
+ pos->seg_pos = volume->start_seg;
+ pos->seg_byte_pos = 0;
+ pos->volume_pos = 0;
+ zftc_reset();
+ TRACE_EXIT 0;
+ }
+ dest = new_block_pos * (volume->blk_sz >> 10);
+ distance = dest - (pos->volume_pos >> 10);
+ while (distance != 0) {
+ seg_dist = compute_seg_pos(dest, pos, volume);
+ TRACE(ft_t_noise, "\n"
+ KERN_INFO "seg_dist: %d\n"
+ KERN_INFO "distance: %d\n"
+ KERN_INFO "dest : %d\n"
+ KERN_INFO "vpos : %d\n"
+ KERN_INFO "seg_pos : %d\n"
+ KERN_INFO "trials : %d",
+ seg_dist, distance, dest,
+ (unsigned int)(pos->volume_pos>>10), pos->seg_pos,
+ fast_seek_trials);
+ if (distance > 0) {
+ if (seg_dist < 0) {
+ TRACE(ft_t_bug, "BUG: distance %d > 0, "
+ "segment difference %d < 0",
+ distance, seg_dist);
+ result = -EIO;
+ break;
+ }
+ new_seg = pos->seg_pos + seg_dist;
+ if (new_seg > volume->end_seg) {
+ new_seg = volume->end_seg;
+ }
+ if (old_seg == new_seg || /* loop */
+ seg_dist <= ZFT_SLOW_SEEK_THRESHOLD ||
+ fast_seek_trials >= ZFT_FAST_SEEK_MAX_TRIALS) {
+ TRACE(ft_t_noise, "starting slow seek:\n"
+ KERN_INFO "fast seek failed too often: %s\n"
+ KERN_INFO "near target position : %s\n"
+ KERN_INFO "looping between two segs : %s",
+ (fast_seek_trials >=
+ ZFT_FAST_SEEK_MAX_TRIALS)
+ ? "yes" : "no",
+ (seg_dist <= ZFT_SLOW_SEEK_THRESHOLD)
+ ? "yes" : "no",
+ (old_seg == new_seg)
+ ? "yes" : "no");
+ result = slow_seek_forward(dest, &cseg,
+ pos, volume, buf);
+ break;
+ }
+ old_seg = new_seg;
+ limit = volume->end_seg;
+ fast_seek_trials ++;
+ for (;;) {
+ result = search_valid_segment(new_seg, limit,
+ volume->size,
+ pos, &cseg,
+ volume, buf);
+ if (result == 0 || result == -EINTR) {
+ break;
+ }
+ if (new_seg == volume->start_seg) {
+ result = -EIO; /* set errror
+ * condition
+ */
+ break;
+ }
+ limit = new_seg;
+ new_seg -= ZFT_FAST_SEEK_BACKUP;
+ if (new_seg < volume->start_seg) {
+ new_seg = volume->start_seg;
+ }
+ }
+ if (result < 0) {
+ TRACE(ft_t_warn,
+ "Couldn't find a readable segment");
+ break;
+ }
+ } else /* if (distance < 0) */ {
+ if (seg_dist > 0) {
+ TRACE(ft_t_bug, "BUG: distance %d < 0, "
+ "segment difference %d >0",
+ distance, seg_dist);
+ result = -EIO;
+ break;
+ }
+ new_seg = pos->seg_pos + seg_dist;
+ if (fast_seek_trials > 0 && seg_dist == 0) {
+ /* this avoids sticking to the same
+ * segment all the time. On the other hand:
+ * if we got here for the first time, and the
+ * deblock_buffer still contains a valid
+ * segment, then there is no need to skip to
+ * the previous segment if the desired position
+ * is inside this segment.
+ */
+ new_seg --;
+ }
+ if (new_seg < volume->start_seg) {
+ new_seg = volume->start_seg;
+ }
+ limit = pos->seg_pos;
+ fast_seek_trials ++;
+ for (;;) {
+ result = search_valid_segment(new_seg, limit,
+ pos->volume_pos,
+ pos, &cseg,
+ volume, buf);
+ if (result == 0 || result == -EINTR) {
+ break;
+ }
+ if (new_seg == volume->start_seg) {
+ result = -EIO; /* set errror
+ * condition
+ */
+ break;
+ }
+ limit = new_seg;
+ new_seg -= ZFT_FAST_SEEK_BACKUP;
+ if (new_seg < volume->start_seg) {
+ new_seg = volume->start_seg;
+ }
+ }
+ if (result < 0) {
+ TRACE(ft_t_warn,
+ "Couldn't find a readable segment");
+ break;
+ }
+ }
+ distance = dest - (pos->volume_pos >> 10);
+ }
+ TRACE_EXIT result;
+}
+
+
+/* advance inside the given segment at most to_do bytes.
+ * of kilobytes moved
+ */
+
+static int seek_in_segment(const unsigned int to_do,
+ cmpr_info *c_info,
+ const char *src_buf,
+ const int seg_sz,
+ const int seg_pos,
+ const zft_volinfo *volume)
+{
+ int result = 0;
+ int blk_sz = volume->blk_sz >> 10;
+ int remaining = to_do;
+ TRACE_FUN(ft_t_flow);
+
+ if (c_info->offset == 0) {
+ /* new segment just read
+ */
+ TRACE_CATCH(get_cseg(c_info, src_buf, seg_sz, volume),);
+ c_info->cmpr_pos += c_info->count;
+ DUMP_CMPR_INFO(ft_t_noise, "", c_info);
+ }
+ /* loop and uncompress until user buffer full or
+ * deblock-buffer empty
+ */
+ TRACE(ft_t_noise, "compressed_sz: %d, compos : %d",
+ c_info->cmpr_sz, c_info->cmpr_pos);
+ while (c_info->spans == 0 && remaining > 0) {
+ if (c_info->cmpr_pos != 0) { /* cmpr buf is not empty */
+ result += blk_sz;
+ remaining -= blk_sz;
+ c_info->cmpr_pos = 0;
+ }
+ if (remaining > 0) {
+ get_next_cluster(c_info, src_buf, seg_sz,
+ volume->end_seg == seg_pos);
+ if (c_info->count != 0) {
+ c_info->cmpr_pos = c_info->count;
+ c_info->offset += c_info->count;
+ } else {
+ break;
+ }
+ }
+ /* Allow escape from this loop on signal!
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ DUMP_CMPR_INFO(ft_t_noise, "", c_info);
+ TRACE(ft_t_noise, "to_do: %d", remaining);
+ }
+ if (seg_sz - c_info->offset <= 18) {
+ c_info->offset = seg_sz;
+ }
+ TRACE(ft_t_noise, "\n"
+ KERN_INFO "segment size : %d\n"
+ KERN_INFO "buf_pos_read : %d\n"
+ KERN_INFO "remaining : %d",
+ seg_sz, c_info->offset,
+ seg_sz - c_info->offset);
+ TRACE_EXIT result;
+}
+
+static int slow_seek_forward_until_error(const unsigned int distance,
+ cmpr_info *c_info,
+ zft_position *pos,
+ const zft_volinfo *volume,
+ __u8 *buf)
+{
+ unsigned int remaining = distance;
+ int seg_sz;
+ int seg_pos;
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ seg_pos = pos->seg_pos;
+ do {
+ TRACE_CATCH(seg_sz = zft_fetch_segment(seg_pos, buf,
+ FT_RD_AHEAD),);
+ /* now we have the contents of the actual segment in
+ * the deblock buffer
+ */
+ TRACE_CATCH(result = seek_in_segment(remaining, c_info, buf,
+ seg_sz, seg_pos,volume),);
+ remaining -= result;
+ pos->volume_pos += result<<10;
+ pos->seg_pos = seg_pos;
+ pos->seg_byte_pos = c_info->offset;
+ seg_pos ++;
+ if (seg_pos <= volume->end_seg && c_info->offset == seg_sz) {
+ pos->seg_pos ++;
+ pos->seg_byte_pos = 0;
+ c_info->offset = 0;
+ }
+ /* Allow escape from this loop on signal!
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ TRACE(ft_t_noise, "\n"
+ KERN_INFO "remaining: %d\n"
+ KERN_INFO "seg_pos: %d\n"
+ KERN_INFO "end_seg: %d\n"
+ KERN_INFO "result: %d",
+ remaining, seg_pos, volume->end_seg, result);
+ } while (remaining > 0 && seg_pos <= volume->end_seg);
+ TRACE_EXIT 0;
+}
+
+/* return segment id of next segment containing valid data, -EIO otherwise
+ */
+static int search_valid_segment(unsigned int segment,
+ const unsigned int end_seg,
+ const unsigned int max_foffs,
+ zft_position *pos,
+ cmpr_info *c_info,
+ const zft_volinfo *volume,
+ __u8 *buf)
+{
+ cmpr_info tmp_info;
+ int seg_sz;
+ TRACE_FUN(ft_t_flow);
+
+ memset(&tmp_info, 0, sizeof(cmpr_info));
+ while (segment <= end_seg) {
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ TRACE(ft_t_noise,
+ "Searching readable segment between %d and %d",
+ segment, end_seg);
+ seg_sz = zft_fetch_segment(segment, buf, FT_RD_AHEAD);
+ if ((seg_sz > 0) &&
+ (get_cseg (&tmp_info, buf, seg_sz, volume) >= 0) &&
+ (tmp_info.foffs != 0 || segment == volume->start_seg)) {
+ if ((tmp_info.foffs>>10) > max_foffs) {
+ TRACE_ABORT(-EIO, ft_t_noise, "\n"
+ KERN_INFO "cseg.foff: %d\n"
+ KERN_INFO "dest : %d",
+ (int)(tmp_info.foffs >> 10),
+ max_foffs);
+ }
+ DUMP_CMPR_INFO(ft_t_noise, "", &tmp_info);
+ *c_info = tmp_info;
+ pos->seg_pos = segment;
+ pos->volume_pos = c_info->foffs;
+ pos->seg_byte_pos = c_info->offset;
+ TRACE(ft_t_noise, "found segment at %d", segment);
+ TRACE_EXIT 0;
+ }
+ segment++;
+ }
+ TRACE_EXIT -EIO;
+}
+
+static int slow_seek_forward(unsigned int dest,
+ cmpr_info *c_info,
+ zft_position *pos,
+ const zft_volinfo *volume,
+ __u8 *buf)
+{
+ unsigned int distance;
+ int result = 0;
+ TRACE_FUN(ft_t_flow);
+
+ distance = dest - (pos->volume_pos >> 10);
+ while ((distance > 0) &&
+ (result = slow_seek_forward_until_error(distance,
+ c_info,
+ pos,
+ volume,
+ buf)) < 0) {
+ if (result == -EINTR) {
+ break;
+ }
+ TRACE(ft_t_noise, "seg_pos: %d", pos->seg_pos);
+ /* the failing segment is either pos->seg_pos or
+ * pos->seg_pos + 1. There is no need to further try
+ * that segment, because ftape_read_segment() already
+ * has tried very much to read it. So we start with
+ * following segment, which is pos->seg_pos + 1
+ */
+ if(search_valid_segment(pos->seg_pos+1, volume->end_seg, dest,
+ pos, c_info,
+ volume, buf) < 0) {
+ TRACE(ft_t_noise, "search_valid_segment() failed");
+ result = -EIO;
+ break;
+ }
+ distance = dest - (pos->volume_pos >> 10);
+ result = 0;
+ TRACE(ft_t_noise, "segment: %d", pos->seg_pos);
+ /* found valid segment, retry the seek */
+ }
+ TRACE_EXIT result;
+}
+
+static int compute_seg_pos(const unsigned int dest,
+ zft_position *pos,
+ const zft_volinfo *volume)
+{
+ int segment;
+ int distance = dest - (pos->volume_pos >> 10);
+ unsigned int raw_size;
+ unsigned int virt_size;
+ unsigned int factor;
+ TRACE_FUN(ft_t_flow);
+
+ if (distance >= 0) {
+ raw_size = volume->end_seg - pos->seg_pos + 1;
+ virt_size = ((unsigned int)(volume->size>>10)
+ - (unsigned int)(pos->volume_pos>>10)
+ + FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS - 1);
+ virt_size /= FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS;
+ if (virt_size == 0 || raw_size == 0) {
+ TRACE_EXIT 0;
+ }
+ if (raw_size >= (1<<25)) {
+ factor = raw_size/(virt_size>>7);
+ } else {
+ factor = (raw_size<<7)/virt_size;
+ }
+ segment = distance/(FT_SECTORS_PER_SEGMENT-FT_ECC_SECTORS);
+ segment = (segment * factor)>>7;
+ } else {
+ raw_size = pos->seg_pos - volume->start_seg + 1;
+ virt_size = ((unsigned int)(pos->volume_pos>>10)
+ + FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS - 1);
+ virt_size /= FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS;
+ if (virt_size == 0 || raw_size == 0) {
+ TRACE_EXIT 0;
+ }
+ if (raw_size >= (1<<25)) {
+ factor = raw_size/(virt_size>>7);
+ } else {
+ factor = (raw_size<<7)/virt_size;
+ }
+ segment = distance/(FT_SECTORS_PER_SEGMENT-FT_ECC_SECTORS);
+ }
+ TRACE(ft_t_noise, "factor: %d/%d", factor, 1<<7);
+ TRACE_EXIT segment;
+}
+
+static struct zft_cmpr_ops cmpr_ops = {
+ zftc_write,
+ zftc_read,
+ zftc_seek,
+ zftc_lock,
+ zftc_reset,
+ zftc_cleanup
+};
+
+int zft_compressor_init(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+#ifdef MODULE
+ printk(KERN_INFO "zftape compressor v1.00a 970514 for " FTAPE_VERSION "\n");
+ if (TRACE_LEVEL >= ft_t_info) {
+ printk(
+KERN_INFO "(c) 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)\n"
+KERN_INFO "Compressor for zftape (lzrw3 algorithm)\n"
+KERN_INFO "Compiled for kernel version %s"
+#ifdef MODVERSIONS
+ " with versioned symbols"
+#endif
+ "\n", UTS_RELEASE);
+ }
+#else /* !MODULE */
+ /* print a short no-nonsense boot message */
+ printk("zftape compressor v1.00a 970514 for Linux " UTS_RELEASE "\n");
+ printk("For use with " FTAPE_VERSION "\n");
+#endif /* MODULE */
+ TRACE(ft_t_info, "zft_compressor_init @ 0x%p", zft_compressor_init);
+ TRACE(ft_t_info, "installing compressor for zftape ...");
+ TRACE_CATCH(zft_cmpr_register(&cmpr_ops),);
+ TRACE_EXIT 0;
+}
+
+
+#ifdef MODULE
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+MODULE_AUTHOR(
+ "(c) 1996, 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de");
+MODULE_DESCRIPTION(
+"Compression routines for zftape. Uses the lzrw3 algorithm by Ross Williams");
+#endif
+
+#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13)
+char kernel_version[] = UTS_RELEASE;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+static int can_unload(void)
+{
+ return keep_module_locked ? -EBUSY : 0;
+}
+#endif
+
+/* Called by modules package when installing the driver
+ */
+int init_module(void)
+{
+ int result;
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,1,85)
+# if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+ register_symtab(0); /* remove global ftape symbols */
+# else
+ if (!mod_member_present(&__this_module, can_unload))
+ return -EBUSY;
+ __this_module.can_unload = can_unload;
+ EXPORT_NO_SYMBOLS;
+# endif
+#endif
+ result = zft_compressor_init();
+ keep_module_locked = 0;
+ return result;
+}
+
+/* Called by modules package when removing the driver
+ */
+void cleanup_module(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (zft_cmpr_unregister() != &cmpr_ops) {
+ TRACE(ft_t_info, "failed");
+ } else {
+ TRACE(ft_t_info, "successful");
+ }
+ zftc_cleanup();
+ printk(KERN_INFO "zft-compressor successfully unloaded.\n");
+ TRACE_EXIT;
+}
+#endif /* MODULE */
diff --git a/drivers/char/ftape/compressor/zftape-compress.h b/drivers/char/ftape/compressor/zftape-compress.h
new file mode 100644
index 000000000..f200741e3
--- /dev/null
+++ b/drivers/char/ftape/compressor/zftape-compress.h
@@ -0,0 +1,83 @@
+#ifndef _ZFTAPE_COMPRESS_H
+#define _ZFTAPE_COMPRESS_H
+/*
+ * Copyright (c) 1994-1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/compressor/zftape-compress.h,v $
+ * $Revision: 1.1 $
+ * $Date: 1997/10/05 19:12:32 $
+ *
+ * This file contains macros and definitions for zftape's
+ * builtin compression code.
+ *
+ */
+
+#include "../zftape/zftape-buffers.h"
+#include "../zftape/zftape-vtbl.h"
+#include "../compressor/lzrw3.h"
+
+/* CMPR_WRK_MEM_SIZE gives the size of the compression wrk_mem */
+/* I got these out of lzrw3.c */
+#define U(X) ((__u32) X)
+#define SIZE_P_BYTE (U(sizeof(__u8 *)))
+#define ALIGNMENT_FUDGE (U(16))
+
+#define CMPR_WRK_MEM_SIZE (U(4096)*(SIZE_P_BYTE) + ALIGNMENT_FUDGE)
+
+/* the maximum number of bytes the size of the "compressed" data can
+ * exceed the uncompressed data. As it is quite useless to compress
+ * data twice it is sometimes the case that it is more efficient to
+ * copy a block of data but to feed it to the "compression"
+ * algorithm. In this case there are some flag bytes or the like
+ * proceding the "compressed" data. THAT MUST NOT BE THE CASE for the
+ * algorithm we use for this driver. Instead, the high bit 15 of
+ * compressed_size:
+ *
+ * compressed_size = ftape_compress()
+ *
+ * must be set in such a case.
+ *
+ * Nevertheless, it might also be as for lzrw3 that there is an
+ * "intermediate" overrun that exceeds the amount of the compressed
+ * data that is actually produced. During the algorithm we need in the
+ * worst case MAX_CMP_GROUP bytes more than the input-size.
+ */
+#define MAX_CMP_GROUP (2+16*2) /* from lzrw3.c */
+
+#define CMPR_OVERRUN MAX_CMP_GROUP /* during compression */
+
+/****************************************************/
+
+#define CMPR_BUFFER_SIZE (MAX_BLOCK_SIZE + CMPR_OVERRUN)
+
+/* the compression map stores the byte offset compressed blocks within
+ * the current volume for catridges with format code 2,3 and 5
+ * (and old versions of zftape) and the offset measured in kilobytes for
+ * format code 4 and 6. This gives us a possible max. size of a
+ * compressed volume of 1024*4GIG which should be enough.
+ */
+typedef __u32 CmprMap;
+
+/* globals
+ */
+
+/* exported functions
+ */
+
+#endif /* _ZFTAPE_COMPRESS_H */
diff --git a/drivers/char/ftape/fdc-io.c b/drivers/char/ftape/fdc-io.c
deleted file mode 100644
index 51fc128d8..000000000
--- a/drivers/char/ftape/fdc-io.c
+++ /dev/null
@@ -1,1300 +0,0 @@
-/* Yo, Emacs! we're -*- Linux-C -*-
- *
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute 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.
-
- *
- * This file contains the low-level floppy disk interface code
- * for the QIC-40/80 tape streamer device driver.
- */
-
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/ioport.h>
-#include <linux/ftape.h>
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/irq.h>
-
-#include "tracing.h"
-#include "fdc-io.h"
-#include "fdc-isr.h"
-#include "ftape-io.h"
-#include "ftape-rw.h"
-#include "calibr.h"
-#include "fc-10.h"
-#include "qic117.h"
-
-
-/* Global vars.
- */
-int ftape_unit = -1;
-int ftape_motor = 0;
-int current_cylinder = -1;
-fdc_mode_enum fdc_mode = fdc_idle;
-fdc_config_info fdc = {0};
-
-/* Local vars.
- */
-static int fdc_calibr_count;
-static int fdc_calibr_time;
-static int fdc_confused = 0;
-static int fdc_status;
-volatile byte fdc_head; /* FDC head */
-volatile byte fdc_cyl; /* FDC track */
-volatile byte fdc_sect; /* FDC sector */
-static int fdc_data_rate = 0; /* default rate = 500 Kbps */
-static int fdc_seek_rate = 14; /* default rate = 2 msec @ 500 Kbps */
-static void (*do_ftape) (void);
-static int fdc_fifo_state; /* original fifo setting - fifo enabled */
-static int fdc_fifo_thr; /* original fifo setting - threshold */
-static int fdc_lock_state; /* original lock setting - locked */
-static int fdc_fifo_locked = 0; /* has fifo && lock set ? */
-static byte fdc_precomp = 0; /* sets fdc to default precomp. value */
-static byte fdc_drv_spec[4]; /* drive specification bytes for i82078 */
-static int perpend_mode; /* true if fdc is in perpendicular mode */
-
-static char ftape_id[] = "ftape"; /* used by request irq and free irq */
-
-void fdc_catch_stray_interrupts(unsigned count)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
- if (count == 0) {
- expected_stray_interrupts = 0;
- } else {
- expected_stray_interrupts += count;
- }
- restore_flags(flags);
-}
-
-/* Wait during a timeout period for a given FDC status.
- * If usecs == 0 then just test status, else wait at least for usecs.
- * Returns -ETIME on timeout. Function must be calibrated first !
- */
-int fdc_wait(int usecs, byte mask, byte state)
-{
- int count_1 = (fdc_calibr_count * usecs - 1) / fdc_calibr_time;
-
- do {
- fdc_status = inb_p(fdc.msr);
- if ((fdc_status & mask) == state) {
- return 0;
- }
- } while (count_1-- >= 0);
- return -ETIME;
-}
-
-int fdc_ready_wait(int usecs)
-{
- return fdc_wait(usecs, FDC_DATA_READY, FDC_DATA_READY);
-}
-
-static void fdc_usec_wait(int usecs)
-{
- fdc_wait(usecs, 0, 1); /* will always timeout ! */
-}
-
-int fdc_ready_out_wait(int usecs)
-{
- fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */
- return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_OUT_READY);
-}
-
-int fdc_ready_in_wait(int usecs)
-{
- fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */
- return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_IN_READY);
-}
-
-int fdc_wait_calibrate(void)
-{
- return calibrate("fdc_wait",
- fdc_usec_wait, &fdc_calibr_count, &fdc_calibr_time);
-}
-
-/* Wait for a (short) while for the FDC to become ready
- * and transfer the next command byte.
- * Return -ETIME on timeout on getting ready (depends on hardware!).
- */
-int fdc_write(byte data)
-{
- fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */
- if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_IN_READY) < 0) {
- return -ETIME;
- } else {
- outb(data, fdc.fifo);
- return 0;
- }
-}
-
-/* Wait for a (short) while for the FDC to become ready
- * and transfer the next result byte.
- * Return -ETIME if timeout on getting ready (depends on hardware!).
- */
-int fdc_read(byte * data)
-{
- fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */
- if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_OUT_READY) < 0) {
- return -ETIME;
- } else {
- *data = inb(fdc.fifo);
- return 0;
- }
-}
-
-/* Output a cmd_len long command string to the FDC.
- * The FDC should be ready to receive a new command or
- * an error (EBUSY) will occur.
- */
-int fdc_command(byte * cmd_data, int cmd_len)
-{
- TRACE_FUN(8, "fdc_command");
- int result = 0;
- unsigned long flags;
- int count = cmd_len;
-
- fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */
- save_flags(flags);
- cli();
- fdc_status = inb(fdc.msr);
- if ((fdc_status & FDC_DATA_READY_MASK) == FDC_DATA_IN_READY) {
- int retry = 0;
- fdc_mode = *cmd_data; /* used by isr */
- interrupt_seen = 0;
- while (count) {
- result = fdc_write(*cmd_data);
- if (result < 0) {
- TRACEx3(6, "fdc_mode = %02x, status = %02x at index %d",
- (int) fdc_mode, (int) fdc_status, cmd_len - count);
- if (++retry <= 3) {
- TRACE(2, "fdc_write timeout, retry");
- } else {
- TRACE(1, "fdc_write timeout, fatal");
- fdc_confused = 1;
- /* recover ??? */
- break;
- }
- } else {
- --count;
- ++cmd_data;
- }
- }
- } else {
- TRACE(1, "fdc not ready");
- result = -EBUSY;
- }
- restore_flags(flags);
- TRACE_EXIT;
- return result;
-}
-
-/* Input a res_len long result string from the FDC.
- * The FDC should be ready to send the result or an error
- * (EBUSY) will occur.
- */
-int fdc_result(byte * res_data, int res_len)
-{
- TRACE_FUN(8, "fdc_result");
- int result = 0;
- unsigned long flags;
- int count = res_len;
-
- save_flags(flags);
- cli();
- fdc_status = inb(fdc.msr);
- if ((fdc_status & FDC_DATA_READY_MASK) == FDC_DATA_OUT_READY) {
- int retry = 0;
- while (count) {
- if (!(fdc_status & FDC_BUSY)) {
- TRACE(1, "premature end of result phase");
- }
- result = fdc_read(res_data);
- if (result < 0) {
- TRACEx3(6, "fdc_mode = %02x, status = %02x at index %d",
- (int) fdc_mode, (int) fdc_status, res_len - count);
- if (++retry <= 3) {
- TRACE(2, "fdc_read timeout, retry");
- } else {
- TRACE(1, "fdc_read timeout, fatal");
- fdc_confused = 1;
- /* recover ??? */
- break;
- }
- } else {
- --count;
- ++res_data;
- }
- }
- } else {
- TRACE(1, "fdc not ready");
- result = -EBUSY;
- }
- restore_flags(flags);
- fdc_usec_wait(RQM_DELAY); /* allow FDC to negate BSY */
- TRACE_EXIT;
- return result;
-}
-
-/* Handle command and result phases for
- * commands without data phase.
- */
-int fdc_issue_command(byte * out_data, int out_count,
- byte * in_data, int in_count)
-{
- TRACE_FUN(8, "fdc_issue_command");
- int result;
- int t0, t1;
-
- if (out_count > 0) {
- result = fdc_command(out_data, out_count);
- if (result < 0) {
- TRACE(1, "fdc_command failed");
- TRACE_EXIT;
- return result;
- }
- }
- /* will take 24 - 30 usec for fdc_sense_drive_status and
- * fdc_sense_interrupt_status commands.
- * 35 fails sometimes (5/9/93 SJL)
- * On a loaded system it incidentally takes longer than
- * this for the fdc to get ready ! ?????? WHY ??????
- * So until we know what's going on use a very long timeout.
- */
- t0 = timestamp();
- result = fdc_ready_out_wait(500 /* usec */ );
- t1 = timestamp();
- if (result < 0) {
- TRACEi(1, "fdc_ready_out_wait failed after:", timediff(t0, t1));
- TRACE_EXIT;
- return result;
- }
- if (in_count > 0) {
- result = fdc_result(in_data, in_count);
- if (result < 0) {
- TRACE(1, "result phase aborted");
- TRACE_EXIT;
- return result;
- }
- }
- TRACE_EXIT;
- return 0;
-}
-
-/* Wait for FDC interrupt with timeout.
- * Signals are blocked so the wait will not be aborted.
- * Note: interrupts must be enabled ! (23/05/93 SJL)
- */
-int fdc_interrupt_wait(int time)
-{
- TRACE_FUN(8, "fdc_interrupt_wait");
- struct wait_queue wait =
- {current, NULL};
- int result = -ETIME;
- int need_cleanup = 0;
- int current_blocked = current->blocked;
- static int resetting = 0;
-
- if (waitqueue_active(&wait_intr)) {
- TRACE(1, "error: nested call");
- return -EIO; /* return error... */
- }
- if (interrupt_seen == 0) {
- /* timeout time will be between 0 and MSPT milliseconds too long !
- */
- current->timeout = jiffies + 1 + (time + MSPT - 1) / MSPT;
- current->state = TASK_INTERRUPTIBLE;
- current->blocked = _BLOCK_ALL;
- add_wait_queue(&wait_intr, &wait);
- do {
- schedule(); /* sets TASK_RUNNING on timeout */
- } while (!interrupt_seen && current->state != TASK_RUNNING);
- current->blocked = current_blocked; /* restore */
- remove_wait_queue(&wait_intr, &wait);
- if (interrupt_seen) {
- current->timeout = 0; /* interrupt hasn't cleared this */
- result = 0;
- } else {
-#if 1
-/*** remove me when sure this doesn't happen ***/
- if (current->timeout > 0) {
- TRACE(-1, "*** BUG: unexpected schedule exit ***");
- if (current->signal & ~current->blocked) {
- TRACE(4, "caused by signal ?");
- }
- }
-#endif
- if (current->signal & ~current->blocked) {
- result = -EINTR;
- } else {
- result = -ETIME;
- }
- need_cleanup = 1; /* missing interrupt, reset fdc. */
- }
- } else {
- result = 0;
- }
- /* In first instance, next statement seems unnecessary since
- * it will be cleared in fdc_command. However, a small part of
- * the software seems to rely on this being cleared here
- * (ftape_close might fail) so stick to it until things get fixed !
- */
- interrupt_seen = 0; /* clear for next call */
-
- if (need_cleanup & !resetting) {
- resetting = 1; /* break infinite recursion if reset fails */
- TRACE(8, "cleanup reset");
- fdc_reset();
- resetting = 0;
- }
- TRACE_EXIT;
- return result;
-}
-
-/* Start/stop drive motor. Enable DMA mode.
- */
-void fdc_motor(int motor)
-{
- TRACE_FUN(8, "fdc_motor");
- int unit = FTAPE_UNIT;
- int data = unit | FDC_RESET_NOT | FDC_DMA_MODE;
-
- ftape_motor = motor;
- if (ftape_motor) {
- data |= FDC_MOTOR_0 << unit;
- TRACEx1(4, "turning motor %d on", unit);
- } else {
- TRACEx1(4, "turning motor %d off", unit);
- }
-#ifdef MACH2
- outb_p(data, fdc.dor2);
-#else
- outb_p(data, fdc.dor);
-#endif
- ftape_sleep(10 * MILLISECOND);
- TRACE_EXIT;
-}
-
-static void fdc_update_dsr(void)
-{
- TRACE_FUN(8, "fdc_update_dsr");
-
- TRACEx2(5, "rate = %d, precomp = %d", fdc_data_rate, fdc_precomp);
- if (fdc.type >= i82077) {
- outb_p((fdc_data_rate & 0x03) | fdc_precomp, fdc.dsr);
- } else {
- outb_p(fdc_data_rate, fdc.ccr);
- }
- TRACE_EXIT;
-}
-
-void fdc_set_write_precomp(int precomp)
-{
- /* write precompensation can be set in multiples of 41.67 nsec.
- * round the parameter to the nearest multiple and convert it
- * into a fdc setting. Note that 0 means default to the fdc,
- * 7 is used instead of that.
- */
- fdc_precomp = ((precomp + 21) / 42) << 2;
- if (fdc_precomp == 0) {
- fdc_precomp = 7 << 2;
- }
- fdc_update_dsr();
-}
-
-/* Read back the Drive Specification regs on an i82078, so that we
- * are able to restore them later
- */
-void fdc_save_drive_specs(void)
-{
- byte cmd1[] =
- {FDC_DRIVE_SPEC, 0x80};
- byte cmd2[] =
- {FDC_DRIVE_SPEC, 0x00, 0x00, 0x00, 0x00, 0xc0};
- int result;
-
- TRACE_FUN(8, "fdc_save_drive_specs");
- if (fdc.type >= i82078_1) {
- result = fdc_issue_command(cmd1, NR_ITEMS(cmd1), fdc_drv_spec, 4);
- if (result >= 0) {
- cmd2[1] = (fdc_drv_spec[0] & 0x03) | 0x04;
- cmd2[2] = (fdc_drv_spec[1] & 0x03) | 0x24;
- cmd2[3] = (fdc_drv_spec[2] & 0x03) | 0x44;
- cmd2[4] = (fdc_drv_spec[3] & 0x03) | 0x64;
- fdc_command(cmd2, NR_ITEMS(cmd2));
- if (result < 0) {
- TRACE(1, "Setting of drive specs failed");
- return;
- }
- } else {
- TRACE(2, "Save of drive specs failed");
- }
- }
- TRACE_EXIT;
-}
-
-/* Restore the previously saved Drive Specification values */
-void fdc_restore_drive_specs(void)
-{
- byte cmd[] =
- {FDC_DRIVE_SPEC, 0x00, 0x00, 0x00, 0x00, 0xc0};
- int result;
-
- TRACE_FUN(8, "fdc_restore_drive_specs");
- if (fdc.type > i82078_1) {
- cmd[1] = (fdc_drv_spec[0] & 0x1f) | 0x00;
- cmd[2] = (fdc_drv_spec[1] & 0x1f) | 0x20;
- cmd[3] = (fdc_drv_spec[2] & 0x1f) | 0x40;
- cmd[4] = (fdc_drv_spec[3] & 0x1f) | 0x60;
- result = fdc_command(cmd, NR_ITEMS(cmd));
- if (result < 0) {
- TRACE(2, "Restoration of drive specs failed");
- }
- }
- TRACE_EXIT;
-}
-
-/* Select clock for fdc, must correspond with tape drive setting !
- * This also influences the fdc timing so we must adjust some values.
- */
-void fdc_set_data_rate(int rate)
-{
- /* Select clock for fdc, must correspond with tape drive setting !
- * This also influences the fdc timing so we must adjust some values.
- */
- fdc_data_rate = rate;
- fdc_update_dsr();
- fdc_set_seek_rate(fdc_seek_rate); /* re-adjust for changed clock */
-}
-
-/* Reset the floppy disk controller. Leave the ftape_unit selected.
- */
-void fdc_reset(void)
-{
- TRACE_FUN(8, "fdc_reset");
- int unit = FTAPE_UNIT;
- byte fdc_ctl = unit | FDC_DMA_MODE;
- int st0;
- int i;
- int result;
- int dummy;
-
- if (ftape_motor) {
- fdc_ctl |= FDC_MOTOR_0 << unit;
- }
-#ifdef MACH2
- outb_p(fdc_ctl & 0x0f, fdc.dor);
- outb_p(fdc_ctl, fdc.dor2);
-#else
- outb_p(fdc_ctl, fdc.dor); /* assert reset, keep unit selected */
-#endif
- fdc_usec_wait(10 /* usec */ ); /* delay >= 14 fdc clocks */
- fdc_ctl |= FDC_RESET_NOT;
- fdc_mode = fdc_idle;
-#ifdef MACH2
- outb_p(fdc_ctl & 0x0f, fdc.dor);
- outb_p(fdc_ctl, fdc.dor2);
-#else
- outb_p(fdc_ctl, fdc.dor); /* release reset */
-#endif
- result = fdc_interrupt_wait(1 * SECOND);
- if (result < 0) {
- TRACE(1, "missing interrupt after reset");
- }
- fdc_set_data_rate(fdc_data_rate); /* keep original setting */
- fdc_usec_wait(1000 /* usec */ ); /* don't know why, but needed */
- for (i = 0; i < 4; ++i) { /* clear disk-change status */
- fdc_sense_interrupt_status(&st0, &dummy);
- if (i == unit) {
- current_cylinder = dummy;
- }
- }
- fdc_set_seek_rate(2);
- TRACE_EXIT;
-}
-
-/* When we're done, put the fdc into reset mode so that the regular
- floppy disk driver will figure out that something is wrong and
- initialize the controller the way it wants. */
-void fdc_disable(void)
-{
- TRACE_FUN(8, "fdc_disable");
- int result;
- byte cmd1[] = {FDC_CONFIGURE, 0x00, 0x00, 0x00};
- byte cmd2[] = {FDC_LOCK};
- byte cmd3[] = {FDC_UNLOCK};
- byte stat[1];
-
- if (CLK_48MHZ && fdc.type >= i82078)
- cmd1[0] |= FDC_CLK48_BIT;
- if (fdc_fifo_locked) {
- result = fdc_issue_command(cmd3, 1, stat, 1);
- if (result < 0 || stat[0] != 0x00) {
- TRACE(-1, "couldn't unlock fifo, configuration remains changed");
- } else {
- cmd1[2] = ((fdc_fifo_state) ? 0 : 0x20) + (fdc_fifo_thr - 1);
- result = fdc_command(cmd1, NR_ITEMS(cmd1));
- if (result < 0) {
- TRACE(-1, "couldn't reconfigure fifo to old state");
- } else if (fdc_lock_state) {
- result = fdc_issue_command(cmd2, 1, stat, 1);
- if (result < 0) {
- TRACE(-1, "couldn't lock old state again");
- }
- }
- TRACEx3(5, "fifo restored: %sabled, thr. %d, %slocked",
- fdc_fifo_state ? "en" : "dis",
- fdc_fifo_thr, (fdc_lock_state) ? "" : "not ");
- }
- fdc_fifo_locked = 0;
- }
-#ifdef MACH2
- outb_p(FTAPE_UNIT & 0x0f, fdc.dor);
- outb_p(FTAPE_UNIT, fdc.dor2);
- udelay(10);
- outb_p(FDC_RESET_NOT & 0x0f, fdc.dor);
- outb_p(FDC_RESET_NOT, fdc.dor2);
-#else
- outb_p(FTAPE_UNIT, fdc.dor);
- udelay(10);
- outb_p(FDC_RESET_NOT, fdc.dor);
-#endif
- TRACE_EXIT;
-}
-
-/* Specify FDC seek-rate
- */
-int fdc_set_seek_rate(int seek_rate)
-{
- byte in[3];
- const int hut = 1; /* minimize head unload time */
- const int hlt = 1; /* minimize head load time */
- const int rates[] = {250, 2000, 500, 1000};
-
- in[0] = FDC_SPECIFY;
- in[1] = (((16 - (rates[fdc_data_rate & 0x03] * seek_rate) / 500) << 4) |
- hut);
- in[2] = (hlt << 1) | 0;
- fdc_seek_rate = seek_rate;
-
- return fdc_command(in, 3);
-}
-
-/* Sense drive status: get unit's drive status (ST3)
- */
-int fdc_sense_drive_status(int *st3)
-{
- TRACE_FUN(8, "fdc_sense_drive_status");
- int result;
- byte out[2];
- byte in[1];
-
- out[0] = FDC_SENSED;
- out[1] = FTAPE_UNIT;
- result = fdc_issue_command(out, 2, in, 1);
- if (result < 0) {
- TRACE(1, "issue_command failed");
- } else {
- *st3 = in[0];
- result = 0;
- }
- TRACE_EXIT;
- return result;
-}
-
-/* Sense Interrupt Status command:
- * should be issued at the end of each seek.
- * get ST0 and current cylinder.
- */
-int fdc_sense_interrupt_status(int *st0, int *current_cylinder)
-{
- TRACE_FUN(8, "fdc_sense_interrupt_status");
- int result;
- byte out[1];
- byte in[2];
-
- out[0] = FDC_SENSEI;
- result = fdc_issue_command(out, 1, in, 2);
- if (result) {
- TRACE(1, "issue_command failed");
- } else {
- *st0 = in[0];
- *current_cylinder = in[1];
- result = 0;
- }
- TRACE_EXIT;
- return result;
-}
-
-/* step to track
- */
-int fdc_seek(int track)
-{
- TRACE_FUN(8, "fdc_seek");
- int result;
- byte out[3];
- int st0, pcn;
-
- out[0] = FDC_SEEK;
- out[1] = FTAPE_UNIT;
- out[2] = track;
- seek_completed = 0;
- result = fdc_command(out, 3);
- if (result != 0) {
- TRACEi(1, "failed, status =", result);
- TRACEx1(4, "destination was: %d, resetting FDC...", track);
- /* We really need this command to work !
- */
- fdc_reset();
- TRACE_EXIT;
- return result;
- }
- /* Handle interrupts until seek_completed or timeout.
- */
- for (;;) {
- result = fdc_interrupt_wait(2 * SECOND);
- if (result < 0) {
- TRACEi(2, "fdc_interrupt_wait timeout, status =", result);
- TRACE_EXIT;
- return result;
- } else if (seek_completed) {
- result = fdc_sense_interrupt_status(&st0, &pcn);
- if (result != 0) {
- TRACEi(1, "fdc_sense_interrupt_status failed, status =", result);
- TRACE_EXIT;
- return result;
- }
- if ((st0 & ST0_SEEK_END) == 0) {
- TRACE(1, "no seek-end after seek completion !??");
- TRACE_EXIT;
- return -EIO;
- }
- break;
- }
- }
- /* Verify whether we issued the right tape command.
- */
- /* Verify that we seek to the proper track. */
- if (pcn != track) {
- TRACE(1, "bad seek..");
- TRACE_EXIT;
- return -EIO;
- }
- current_cylinder = pcn;
- TRACE_EXIT;
- return 0;
-}
-
-/* Recalibrate and wait until home.
- */
-int fdc_recalibrate(void)
-{
- TRACE_FUN(8, "fdc_recalibrate");
- int result;
- byte out[2];
- int st0;
- int pcn;
- int retry;
-
- result = fdc_set_seek_rate(6);
- if (result) {
- TRACEi(1, "fdc_set_seek_rate failed, status =", result);
- TRACE_EXIT;
- return result;
- }
- out[0] = FDC_RECAL;
- out[1] = FTAPE_UNIT;
- seek_completed = 0;
- result = fdc_command(out, 2);
- if (result) {
- TRACEi(1, "fdc_command failed, status =", result);
- TRACE_EXIT;
- return result;
- }
- /* Handle interrupts until seek_completed or timeout.
- */
- for (retry = 0;; ++retry) {
- result = fdc_interrupt_wait(2 * SECOND);
- if (result < 0) {
- TRACE(1, "fdc_interrupt_wait failed");
- TRACE_EXIT;
- return result;
- } else if (result == 0 && seek_completed) {
- result = fdc_sense_interrupt_status(&st0, &pcn);
- if (result != 0) {
- TRACEi(1, "fdc_sense_interrupt_status failed, status =", result);
- TRACE_EXIT;
- return result;
- }
- if ((st0 & ST0_SEEK_END) == 0) {
- if (retry < 1) {
- continue; /* some drives/fdc's give an extra interrupt */
- } else {
- TRACE(1, "no seek-end after seek completion !??");
- TRACE_EXIT;
- return -EIO;
- }
- }
- break;
- }
- }
- current_cylinder = pcn;
- if (pcn != 0) {
- TRACEi(1, "failed: resulting track =", pcn);
- }
- result = fdc_set_seek_rate(2);
- if (result != 0) {
- TRACEi(1, "fdc_set_seek_rate failed, status =", result);
- TRACE_EXIT;
- return result;
- }
- TRACE_EXIT;
- return 0;
-}
-
-/* Setup Floppy Disk Controller and DMA to read or write the next cluster
- * of good sectors from or to the current segment.
- */
-int setup_fdc_and_dma(buffer_struct * buff, unsigned char operation)
-{
- TRACE_FUN(8, "setup_fdc_and_dma");
- unsigned long flags;
- byte perpend[] = {FDC_PERPEND, 0x00};
- unsigned char out[9];
- int result;
- int dma_mode;
-
- if (operation == FDC_READ || operation == FDC_READ_DELETED) {
- dma_mode = DMA_MODE_READ;
- if (qic_std == QIC_TAPE_QIC3020) {
- if (fdc.type < i82077AA) {
- /* fdc does not support perpendicular mode. complain */
- TRACE(0, "Your FDC does not support QIC-3020.");
- return -EIO;
- }
- /* enable perpendicular mode */
- perpend[1] = 0x83 + (0x04 << FTAPE_UNIT);
- result = fdc_command(perpend, 2);
- if (result < 0) {
- TRACE(1, "Perpendicular mode entry failed!");
- } else {
- TRACE(4, "Perpendicular mode entered");
- perpend_mode = 1;
- }
- } else if (perpend_mode) {
- /* Turn off perpendicular mode */
- perpend[1] = 0x80;
- result = fdc_command(perpend, 2);
- if (result < 0) {
- TRACE(1, "Perpendicular mode exit failed!");
- } else {
- TRACE(4, "Perpendicular mode exited");
- perpend_mode = 0;
- }
- }
- TRACEx2(5, "xfer %d sectors to 0x%p", buff->sector_count, buff->ptr);
- } else if (operation == FDC_WRITE || operation == FDC_WRITE_DELETED) {
- dma_mode = DMA_MODE_WRITE;
- /* When writing QIC-3020 tapes, turn on perpendicular mode.
- */
- if (qic_std == QIC_TAPE_QIC3020) {
- if (fdc.type < i82077AA) {
- /* fdc does not support perpendicular mode: complain */
- TRACE(0, "Your FDC does not support QIC-3020.");
- return -EIO;
- }
- perpend[1] = 0x83 + (0x4 << FTAPE_UNIT);
- result = fdc_command(perpend, 2);
- if (result < 0) {
- TRACE(1, "Perpendicular mode entry failed!");
- } else {
- TRACE(4, "Perpendicular mode entered");
- perpend_mode = 1;
- }
- } else if (perpend_mode) {
- perpend[1] = 0x80;
- result = fdc_command(perpend, 2);
- if (result < 0) {
- TRACE(1, "Perpendicular mode exit failed!");
- } else {
- TRACE(4, "Perpendicular mode exited");
- perpend_mode = 0;
- }
- }
- TRACEx2(5, "xfer %d sectors from 0x%p", buff->sector_count, buff->ptr);
- } else {
- TRACE(-1, "bug: illegal operation parameter");
- TRACE_EXIT;
- return -EIO;
- }
- /* Program the DMA controller.
- */
- save_flags(flags);
- cli(); /* could be called from ISR ! */
- disable_dma(fdc.dma);
- clear_dma_ff(fdc.dma);
- set_dma_mode(fdc.dma, dma_mode);
- set_dma_addr(fdc.dma, (unsigned) buff->ptr);
- set_dma_count(fdc.dma, SECTOR_SIZE * buff->sector_count);
-#ifdef GCC_2_4_5_BUG
- /* This seemingly stupid construction confuses the gcc-2.4.5
- * code generator enough to create correct code.
- */
- if (1) {
- int i;
-
- for (i = 0; i < 1; ++i) {
- udelay(1);
- }
- }
-#endif
- enable_dma(fdc.dma);
- /* Issue FDC command to start reading/writing.
- */
- out[0] = operation;
- out[1] = FTAPE_UNIT;
- out[2] = buff->cyl;
- out[3] = buff->head;
- out[4] = buff->sect + buff->sector_offset;
- out[5] = 3; /* Sector size of 1K. */
- out[6] = out[4] + buff->sector_count - 1; /* last sector */
- out[7] = 109; /* Gap length. */
- out[8] = 0xff; /* No limit to transfer size. */
- restore_flags(flags);
- TRACEx4(6, "C: 0x%02x, H: 0x%02x, R: 0x%02x, cnt: 0x%02x",
- out[2], out[3], out[4], out[6] - out[4] + 1);
- result = fdc_command(out, 9);
- if (result != 0) {
- fdc_mode = fdc_idle;
- TRACE(1, "fdc_command failed");
- }
- fdc_setup_error = result;
- TRACE_EXIT;
- return result;
-}
-
-int fdc_fifo_enable(void)
-{
- TRACE_FUN(8, "fdc_fifo_enable");
- int result = 0;
- byte cmd0[] = {FDC_DUMPREGS};
- byte cmd1[] = {FDC_CONFIGURE, 0, 0x07, 0}; /* enable fifo, thr = 8 */
- byte cmd2[] = {FDC_LOCK};
- byte cmd3[] = {FDC_UNLOCK};
- byte stat;
- byte reg[10];
- int i;
-
- if (CLK_48MHZ && fdc.type >= i82078)
- cmd1[0] |= FDC_CLK48_BIT;
- if (!fdc_fifo_locked) {
- /* Dump fdc internal registers for examination
- */
- result = fdc_command(cmd0, NR_ITEMS(cmd0));
- if (result < 0) {
- TRACE(2, "FDC dumpreg command failed, fifo unchanged");
- result = -EIO;
- } else {
- /* Now read fdc internal registers from fifo
- */
- for (i = 0; i < NR_ITEMS(reg); ++i) {
- fdc_read(&reg[i]);
- TRACEx2(6, "Register %d = 0x%02x", i, reg[i]);
- }
- fdc_fifo_state = (reg[8] & 0x20) == 0;
- fdc_lock_state = reg[7] & 0x80;
- fdc_fifo_thr = 1 + (reg[8] & 0x0f);
- TRACEx3(5, "original fifo state: %sabled, threshold %d, %slocked",
- (fdc_fifo_state) ? "en" : "dis",
- fdc_fifo_thr, (fdc_lock_state) ? "" : "not ");
- /* If fdc is already locked, unlock it first !
- */
- if (fdc_lock_state) {
- fdc_ready_wait(100);
- result = fdc_command(cmd3, NR_ITEMS(cmd3));
- if (result < 0) {
- TRACE(-1, "FDC unlock command failed, configuration unchanged");
- result = -EIO;
- }
- }
- /* Enable fifo and set threshold at xx bytes to allow a
- * reasonably large latency and reduce number of dma bursts.
- */
- fdc_ready_wait(100);
- result = fdc_command(cmd1, NR_ITEMS(cmd1));
- if (result < 0) {
- TRACE(-1, "FDC configure command failed, fifo unchanged");
- result = -EIO;
- } else {
- /* Now lock configuration so reset will not change it
- */
- result = fdc_issue_command(cmd2, NR_ITEMS(cmd2), &stat, 1);
- if (result < 0 || stat != 0x10) {
- TRACEx1(-1, "FDC lock command failed, stat = 0x%02x", stat);
- result = -EIO;
- } else {
- fdc_fifo_locked = 1;
- result = 0;
- }
- }
- }
- } else {
- TRACE(2, "Fifo not enabled because locked");
- }
- TRACE_EXIT;
- return result;
-}
-
-/* Determine fd controller type
- */
-static byte fdc_save_state[2] = {0, 0};
-
-int fdc_probe(void)
-{
- TRACE_FUN(8, "fdc_probe");
- byte cmd[1];
- byte stat[16]; /* must be able to hold dumpregs & save results */
- int result;
-
- /* Try to find out what kind of fd controller we have to deal with
- * Scheme borrowed from floppy driver:
- * first try if FDC_DUMPREGS command works
- * (this indicates that we have a 82072 or better)
- * then try the FDC_VERSION command (82072 doesn't support this)
- * then try the FDC_UNLOCK command (some older 82077's don't support this)
- * then try the FDC_PARTID command (82078's support this)
- */
- cmd[0] = FDC_DUMPREGS;
- result = fdc_issue_command(cmd, 1, stat, 1);
- if (result == 0) {
- if (stat[0] == 0x80) {
- /* invalid command: must be pre 82072
- */
- TRACE(2, "Type 8272A/765A compatible FDC found");
- result = i8272;
- } else {
- fdc_result(&stat[1], 9);
- fdc_save_state[0] = stat[7];
- fdc_save_state[1] = stat[8];
- cmd[0] = FDC_VERSION;
- result = fdc_issue_command(cmd, 1, stat, 1);
- if (result < 0 || stat[0] == 0x80) {
- TRACE(2, "Type 82072 FDC found");
- result = i8272;
- } else if (*stat == 0x90) {
- cmd[0] = FDC_UNLOCK;
- result = fdc_issue_command(cmd, 1, stat, 1);
- if (result < 0 || stat[0] != 0x00) {
- TRACE(2, "Type pre-1991 82077 FDC found, treating it like a 82072");
- result = i8272;
- } else {
- int i;
-
- if (fdc_save_state[0] & 0x80) { /* was locked */
- cmd[0] = FDC_LOCK; /* restore lock */
- result = fdc_issue_command(cmd, 1, stat, 1);
- TRACE(2, "FDC is already locked");
- }
- /* Test for an i82078 FDC */
- cmd[0] = FDC_PARTID;
- result = fdc_issue_command(cmd, 1, stat, 1);
- if (result < 0 || stat[0] == 0x80) {
- /* invalid command: not an i82078xx type FDC */
- result = no_fdc;
- for (i = 0; i < 4; ++i) {
- outb_p(i, fdc.tdr);
- if ((inb_p(fdc.tdr) & 0x03) != i) {
- result = i82077;
- break;
- }
- }
- if (result == no_fdc) {
- result = i82077AA;
- TRACE(2, "Type 82077AA FDC found");
- } else {
- TRACE(2, "Type 82077 FDC found");
- }
- } else {
- /* FDC_PARTID cmd succeeded */
- switch (stat[0] >> 5) {
- case 0x0:
- /* i82078SL or i82078-1. The SL part cannot run at 2Mbps (the
- * SL and -1 dies are identical; they are speed graded after
- * production, according to Intel). Some SL's can be detected
- * by doing a SAVE cmd and look at bit 7 of the first byte (the
- * SEL3V# bit). If it is 0, the part runs off 3Volts, and hence
- * it is a SL.
- */
- cmd[0] = FDC_SAVE;
- result = fdc_issue_command(cmd, 1, stat, 16);
- if (result < 0) {
- TRACE(1, "FDC_SAVE failed. Dunno why");
- /* guess we better claim the fdc to be an i82078 */
- result = i82078;
- TRACE(2, "Type i82078 FDC (i suppose) found");
- } else {
- if ((stat[0] & FDC_SEL3V_BIT)) {
- /* fdc running off 5Volts; Pray that it's an i82078-1
- */
- TRACE(2, "Type i82078-1 or 5Volt i82078SL FDC found");
- TRACE(2, "Treating it as an i82078-1 (2Mbps) FDC");
- result = i82078_1;
- } else {
- TRACE(2, "Type 3Volt i82078SL FDC (1Mbps) found");
- result = i82078;
- }
- }
- break;
- case 0x1:
- case 0x2: /* S82078B (?!) */
- /* 44pin i82078 found */
- result = i82078;
- TRACE(2, "Type i82078 FDC found");
- break;
- case 0x3: /* NSC PC8744 core; used in several super-IO chips */
- result = i82077AA;
- TRACE(2, "Type 82077AA compatible FDC found");
- break;
- default:
- TRACE(2, "A previously undetected FDC found");
- TRACEi(2, "Treating it as a 82077AA. Please report partid=",
- stat[0]);
- result = i82077AA;
- } /* switch(stat[ 0] >> 5) */
- } /* if (result < 0 || stat[ 0] == 0x80) */
- }
- } else {
- TRACE(2, "Unknown FDC found");
- result = i8272;
- }
- }
- } else {
- TRACE(-1, "No FDC found");
- result = no_fdc;
- }
- TRACE_EXIT;
- return result;
-}
-
-void fdc_config_regs(unsigned fdc_base, unsigned fdc_irq, unsigned fdc_dma)
-{
- fdc.irq = fdc_irq;
- fdc.dma = fdc_dma;
- fdc.sra = fdc_base;
- fdc.srb = fdc_base + 1;
- fdc.dor = fdc_base + 2;
- fdc.tdr = fdc_base + 3;
- fdc.msr = fdc.dsr = fdc_base + 4;
- fdc.fifo = fdc_base + 5;
-#if defined MACH2 || defined PROBE_FC10
- fdc.dor2 = fdc_base + 6;
-#endif
- fdc.dir = fdc.ccr = fdc_base + 7;
-}
-
-/* If probing for a FC-10/20 controller the fdc base address, interrupt
- * and dma channel must be specified.
- * If using an alternate fdc controller, base address, interrupt and
- * dma channel must be specified.
- */
-#if defined PROBE_FC10 && !defined FDC_BASE
-#error No FDC base address (FDC_BASE) specified in Makefile!
-#endif
-#if defined FDC_BASE && !defined FDC_IRQ
-#error No interrupt (FDC_IRQ) specified in Makefile!
-#endif
-#if defined FDC_BASE && !defined FDC_DMA
-#error No dma channel (FDC_DMA) specified in Makefile!
-#endif
-
-void fdc_config(void)
-{
- TRACE_FUN(8, "fdc_config");
- static int already_done = 0;
-
- if (!already_done) {
-#ifdef PROBE_FC10
- int fc_type;
-
- fdc_config_regs(FDC_BASE, FDC_IRQ, FDC_DMA);
- fc_type = fc10_enable();
- if (fc_type != 0) {
- TRACEx1(2, "FC-%c0 controller found", '0' + fc_type);
- fdc.type = fc10;
- fdc.hook = &do_ftape;
- } else {
- TRACE(2, "FC-10/20 controller not found");
- fdc.type = no_fdc;
- fdc.dor2 = 0; /* not used with std fdc */
- fdc_config_regs(0x3f0, 6, 2); /* back to std fdc again */
- fdc.hook = &do_ftape;
- }
-#else
-#ifdef FDC_BASE
- TRACE(2, "Using fdc controller at alternate address");
- fdc_config_regs(FDC_BASE, FDC_IRQ, FDC_DMA);
- fdc.hook = &do_ftape;
-#else
- TRACE(2, "Using the standard fdc controller");
- fdc_config_regs(0x3f0, 6, 2); /* std fdc */
- fdc.hook = &do_ftape;
-#endif /* !FDC_BASE */
-#endif /* !PROBE_FC10 */
- }
- *(fdc.hook) = fdc_isr; /* hook our handler in */
- already_done = 1;
- TRACE_EXIT;
-}
-
-static void ftape_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- TRACE_FUN(8, "ftape_interrupt");
- void (*handler) (void) = *fdc.hook;
-
- *fdc.hook = NULL;
- if (handler) {
- handler();
- } else {
- TRACE(-1, "Unexpected ftape interrupt");
- }
- TRACE_EXIT;
-}
-
-int fdc_grab_irq_and_dma(void)
-{
- TRACE_FUN(8, "fdc_grab_irq_and_dma");
- int result = 0;
-
- if (fdc.hook == &do_ftape) {
- /* Get fast interrupt handler.
- */
- result = request_irq(fdc.irq, ftape_interrupt, SA_INTERRUPT,
- "ftape", ftape_id);
- if (result) {
- TRACEx1(-1, "Unable to grab IRQ%d for ftape driver", fdc.irq);
- result = -EIO;
- } else {
- result = request_dma(fdc.dma, ftape_id);
- if (result) {
- TRACEx1(-1, "Unable to grab DMA%d for ftape driver", fdc.dma);
- free_irq(fdc.irq, ftape_id);
- result = -EIO;
- } else {
- enable_irq(fdc.irq);
- }
- }
- }
-#ifdef FDC_DMA
- if (result == 0 && FDC_DMA == 2) {
- /* Using same dma channel as standard fdc, need to disable the
- * dma-gate on the std fdc. This couldn't be done in the floppy
- * driver as some laptops are using the dma-gate to enter a
- * low power or even suspended state :-(
- */
- outb_p(FDC_RESET_NOT, 0x3f2);
- TRACE(2, "DMA-gate on standard fdc disabled");
- }
-#endif
- TRACE_EXIT;
- return result;
-}
-
-int fdc_release_irq_and_dma(void)
-{
- TRACE_FUN(8, "fdc_grab_irq_and_dma");
- int result = 0;
-
- if (fdc.hook == &do_ftape) {
- disable_dma(fdc.dma); /* just in case... */
- free_dma(fdc.dma);
- disable_irq(fdc.irq);
- free_irq(fdc.irq, ftape_id);
- }
-#ifdef FDC_DMA
- if (result == 0 && FDC_DMA == 2) {
- /* Using same dma channel as standard fdc, need to disable the
- * dma-gate on the std fdc. This couldn't be done in the floppy
- * driver as some laptops are using the dma-gate to enter a
- * low power or even suspended state :-(
- */
- outb_p(FDC_RESET_NOT | FDC_DMA_MODE, 0x3f2);
- TRACE(2, "DMA-gate on standard fdc enabled again");
- }
-#endif
- TRACE_EXIT;
- return result;
-}
-
-int fdc_uninit(void)
-{
- TRACE_FUN(8, "fdc_uninit");
- int result = 0;
-
- if (fdc.sra != 0) {
- if (fdc.dor2 == 0) {
- release_region(fdc.sra, 6);
- release_region(fdc.sra + 7, 1);
- } else {
- release_region(fdc.sra, 8);
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-int fdc_init(void)
-{
- TRACE_FUN(8, "fdc_init");
- int result = 0;
-
- fdc_config();
- if (fdc_grab_irq_and_dma() < 0) {
- result = -EBUSY;
- } else {
- ftape_motor = 0;
- fdc_catch_stray_interrupts(1); /* one always comes */
- TRACE(5, "resetting fdc");
- fdc_reset(); /* init fdc & clear track counters */
- if (fdc.type == no_fdc) { /* default, means no FC-10 or 20 found */
- fdc.type = fdc_probe();
- }
- if (fdc.type != no_fdc) {
- if (fdc.type >= i82077) {
- if (fdc_fifo_enable() < 0) {
- TRACE(2, "couldn't enable fdc fifo !");
- } else {
- TRACE(5, "fdc fifo enabled and locked");
- }
- }
- } else {
- fdc_release_irq_and_dma();
- result = -EIO;
- }
- }
- if (result >= 0) {
- if (fdc.dor2 == 0) {
- request_region(fdc.sra, 6, "fdc (ftape)");
- request_region(fdc.sra + 7, 1, "fdc (ftape)");
- } else {
- request_region(fdc.sra, 8, "fdc (ftape)");
- }
- }
- TRACE_EXIT;
- return result;
-}
diff --git a/drivers/char/ftape/fdc-io.h b/drivers/char/ftape/fdc-io.h
deleted file mode 100644
index 083eab7be..000000000
--- a/drivers/char/ftape/fdc-io.h
+++ /dev/null
@@ -1,182 +0,0 @@
-#ifndef _FDC_IO_H
-#define _FDC_IO_H
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute 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.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/fdc-io.h,v $
- $Author: bas $
- *
- $Revision: 1.38 $
- $Date: 1995/05/10 16:09:36 $
- $State: Beta $
- *
- * This file contains the low level functions
- * that communicate with the floppy disk controller,
- * for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/fdreg.h>
-
-#define FDC_SK_BIT (0x20)
-#define FDC_MT_BIT (0x80)
-
-#define FDC_READ (FD_READ & ~(FDC_SK_BIT | FDC_MT_BIT))
-#define FDC_WRITE (FD_WRITE & ~FDC_MT_BIT)
-#define FDC_READ_DELETED (0x4c)
-#define FDC_WRITE_DELETED (0x49)
-#define FDC_READID (0x4a)
-#define FDC_SENSED (0x04)
-#define FDC_SENSEI (FD_SENSEI)
-#define FDC_RECAL (FD_RECALIBRATE)
-#define FDC_SEEK (FD_SEEK)
-#define FDC_SPECIFY (FD_SPECIFY)
-#define FDC_RECALIBR (FD_RECALIBRATE)
-#define FDC_VERSION (FD_VERSION)
-#define FDC_PERPEND (FD_PERPENDICULAR)
-#define FDC_DUMPREGS (FD_DUMPREGS)
-#define FDC_LOCK (FD_LOCK)
-#define FDC_UNLOCK (FD_UNLOCK)
-#define FDC_CONFIGURE (FD_CONFIGURE)
-#define FDC_DRIVE_SPEC (0x8e) /* i82078 has this (any others?) */
-#define FDC_PARTID (0x18) /* i82078 has this */
-#define FDC_SAVE (0x2e) /* i82078 has this (any others?) */
-#define FDC_RESTORE (0x4e) /* i82078 has this (any others?) */
-
-#define FDC_STATUS_MASK (STATUS_BUSY | STATUS_DMA | STATUS_DIR | STATUS_READY)
-#define FDC_DATA_READY (STATUS_READY)
-#define FDC_DATA_OUTPUT (STATUS_DIR)
-#define FDC_DATA_READY_MASK (STATUS_READY | STATUS_DIR)
-#define FDC_DATA_OUT_READY (STATUS_READY | STATUS_DIR)
-#define FDC_DATA_IN_READY (STATUS_READY)
-#define FDC_BUSY (STATUS_BUSY)
-#define FDC_CLK48_BIT (0x80)
-#define FDC_SEL3V_BIT (0x40)
-
-#define ST0_INT_MASK (ST0_INTR)
-#define FDC_INT_NORMAL (ST0_INTR & 0x00)
-#define FDC_INT_ABNORMAL (ST0_INTR & 0x40)
-#define FDC_INT_INVALID (ST0_INTR & 0x80)
-#define FDC_INT_READYCH (ST0_INTR & 0xC0)
-#define ST0_SEEK_END (ST0_SE)
-#define ST3_TRACK_0 (ST3_TZ)
-
-#define FDC_RESET_NOT (0x04)
-#define FDC_DMA_MODE (0x08)
-#define FDC_MOTOR_0 (0x10)
-#define FDC_MOTOR_1 (0x20)
-
-typedef struct {
- void (**hook) (void); /* our wedge into the isr */
- enum {
- no_fdc, i8272, i82077, i82077AA, fc10,
- i82078, i82078_1
- } type; /* FDC type */
- unsigned char irq; /* FDC irq nr */
- unsigned char dma; /* FDC dma channel nr */
- unsigned short sra; /* Status register A (PS/2 only) */
- unsigned short srb; /* Status register B (PS/2 only) */
- unsigned short dor; /* Digital output register */
- unsigned short tdr; /* Tape Drive Register (82077SL-1 &
- 82078 only) */
- unsigned short msr; /* Main Status Register */
- unsigned short dsr; /* Datarate Select Register (8207x only) */
- unsigned short fifo; /* Data register / Fifo on 8207x */
- unsigned short dir; /* Digital Input Register */
- unsigned short ccr; /* Configuration Control Register */
- unsigned short dor2; /* Alternate dor on MACH-2 controller,
- also used with FC-10, meaning unknown */
-} fdc_config_info;
-
-typedef enum {
- fdc_data_rate_250 = 2,
- fdc_data_rate_500 = 0,
- fdc_data_rate_1000 = 3,
- fdc_data_rate_2000 = 1, /* i82078-1: remember to use Data Rate Table #2 */
-} fdc_data_rate_type;
-
-typedef enum {
- waiting = 0,
- reading,
- writing,
- done,
- error,
-} buffer_state_enum;
-
-typedef volatile enum {
- fdc_idle = 0,
- fdc_reading_data = FDC_READ,
- fdc_seeking = FDC_SEEK,
- fdc_writing_data = FDC_WRITE,
- fdc_reading_id = FDC_READID,
- fdc_recalibrating = FDC_RECAL,
-} fdc_mode_enum;
-
-/*
- * fdc-io.c defined public variables
- */
-extern fdc_mode_enum fdc_mode;
-extern volatile enum runner_status_enum runner_status;
-extern int old_vfo;
-extern volatile int head;
-extern volatile int tail;
-extern int fdc_setup_error; /* outdated ??? */
-extern struct wait_queue *wait_intr;
-extern volatile unsigned int next_segment; /* next segment for read ahead */
-extern int ftape_unit; /* fdc unit specified at ftape_open() */
-extern int ftape_motor; /* fdc motor line state */
-extern int current_cylinder; /* track nr the FDC thinks we're on */
-extern volatile byte fdc_head; /* FDC head */
-extern volatile byte fdc_cyl; /* FDC track */
-extern volatile byte fdc_sect; /* FDC sector */
-extern fdc_config_info fdc; /* FDC hardware configuration */
-
-/*
- * fdc-io.c defined public functions
- */
-extern void fdc_catch_stray_interrupts(unsigned count);
-extern int fdc_ready_wait(int timeout);
-extern int fdc_write(byte data);
-extern int fdc_read(byte * data);
-extern int fdc_command(byte * cmd_data, int cmd_len);
-extern int fdc_result(byte * res_data, int res_len);
-extern int fdc_issue_command(byte * out_data, int out_count, \
- byte * in_data, int in_count);
-extern void fdc_isr(void);
-extern int fdc_interrupt_wait(int time);
-extern void fdt_sleep(unsigned int time);
-extern int fdc_specify(int head_unload_time, int seek_rate,
- int head_load_time, int non_dma);
-extern int fdc_set_seek_rate(int seek_rate);
-extern int fdc_seek(int track);
-extern int fdc_sense_drive_status(int *st3);
-extern void fdc_motor(int motor);
-extern void fdc_reset(void);
-extern int fdc_recalibrate(void);
-extern void fdc_disable(void);
-extern int fdc_wait_calibrate(void);
-extern int fdc_sense_interrupt_status(int *st0, int *current_cylinder);
-extern void fdc_save_drive_specs(void);
-extern void fdc_restore_drive_specs(void);
-extern void fdc_set_data_rate(int rate);
-extern int fdc_release_irq_and_dma(void);
-extern int fdc_init(void);
-extern int fdc_uninit(void);
-extern void fdc_set_write_precomp(int precomp);
-
-#endif
diff --git a/drivers/char/ftape/fdc-isr.c b/drivers/char/ftape/fdc-isr.c
deleted file mode 100644
index 0fda85914..000000000
--- a/drivers/char/ftape/fdc-isr.c
+++ /dev/null
@@ -1,813 +0,0 @@
-/*
- * Copyright (C) 1994-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute 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.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/fdc-isr.c,v $
- $Author: bas $
- *
- $Revision: 1.36 $
- $Date: 1995/05/27 08:54:21 $
- $State: Beta $
- *
- * This file contains the interrupt service routine and associated
- * code for the QIC-40/80 tape streamer device driver.
- */
-
-#include <linux/ftape.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-
-#define volatile /* */
-
-#include "tracing.h"
-#include "fdc-isr.h"
-#include "qic117.h"
-#include "fdc-io.h"
-#include "ftape-ctl.h"
-#include "ftape-rw.h"
-#include "ftape-io.h"
-#include "calibr.h"
-#include "ftape-bsm.h"
-
-/* Global vars.
- */
-volatile int expected_stray_interrupts = 0;
-volatile int seek_completed = 0;
-volatile int interrupt_seen = 0;
-volatile int expect_stray_interrupt = 0;
-int random_rw = 0;
-
-/* Local vars.
- */
-typedef enum {
- no_error = 0, id_am_error = 0x01, id_crc_error = 0x02,
- data_am_error = 0x04, data_crc_error = 0x08,
- no_data_error = 0x10, overrun_error = 0x20,
-} error_cause;
-static int hide_interrupt;
-static int stop_read_ahead = 0;
-
-
-static void print_error_cause(int cause)
-{
- TRACE_FUN(8, "print_error_cause");
-
- switch (cause) {
- case no_data_error:
- TRACE(4, "no data error");
- break;
- case id_am_error:
- TRACE(4, "id am error");
- break;
- case id_crc_error:
- TRACE(4, "id crc error");
- break;
- case data_am_error:
- TRACE(4, "data am error");
- break;
- case data_crc_error:
- TRACE(4, "data crc error");
- break;
- case overrun_error:
- TRACE(4, "overrun error");
- break;
- default:
- }
- TRACE_EXIT;
-}
-
-static char *
-get_fdc_mode_text(fdc_mode_enum fdc_mode)
-{
- switch (fdc_mode) {
- case fdc_idle:
- return "fdc_idle";
- case fdc_reading_data:
- return "fdc_reading_data";
- case fdc_seeking:
- return "fdc_seeking";
- case fdc_writing_data:
- return "fdc_writing_data";
- case fdc_reading_id:
- return "fdc_reading_id";
- case fdc_recalibrating:
- return "fdc_recalibrating";
- default:
- return "unknown";
- }
-}
-
-static void
-decode_irq_cause(fdc_mode_enum fdc_mode, byte st[],
- char **fdc_mode_txt, error_cause * cause)
-{
- TRACE_FUN(8, "decode_irq_cause");
-
- /* Valid st[], decode cause of interrupt.
- */
- *fdc_mode_txt = get_fdc_mode_text(fdc_mode);
- switch (st[0] & ST0_INT_MASK) {
- case FDC_INT_NORMAL:
- TRACEx1(fdc_mode == fdc_reading_id ? 6 : 5,
- "normal completion: %s", *fdc_mode_txt);
- *cause = no_error;
- break;
- case FDC_INT_ABNORMAL:
- TRACEx1(5, "abnormal completion %s", *fdc_mode_txt);
- TRACEx3(6, "ST0: 0x%02x, ST1: 0x%02x, ST2: 0x%02x",
- st[0], st[1], st[2]);
- TRACEx4(6, "C: 0x%02x, H: 0x%02x, R: 0x%02x, N: 0x%02x",
- st[3], st[4], st[5], st[6]);
- if (st[1] & 0x01) {
- if (st[2] & 0x01) {
- *cause = data_am_error;
- } else {
- *cause = id_am_error;
- }
- } else if (st[1] & 0x20) {
- if (st[2] & 0x20) {
- *cause = data_crc_error;
- } else {
- *cause = id_crc_error;
- }
- } else if (st[1] & 0x04) {
- *cause = no_data_error;
- } else if (st[1] & 0x10) {
- *cause = overrun_error;
- }
- print_error_cause(*cause);
- break;
- case FDC_INT_INVALID:
- TRACEx1(5, "invalid completion %s", *fdc_mode_txt);
- *cause = no_error;
- break;
- case FDC_INT_READYCH:
- TRACEx1(5, "ready change %s", *fdc_mode_txt);
- *cause = no_error;
- break;
- default:
- }
- TRACE_EXIT;
-}
-
-static void update_history(error_cause cause)
-{
- switch (cause) {
- case id_am_error:
- history.id_am_errors++;
- break;
- case id_crc_error:
- history.id_crc_errors++;
- break;
- case data_am_error:
- history.data_am_errors++;
- break;
- case data_crc_error:
- history.data_crc_errors++;
- break;
- case overrun_error:
- history.overrun_errors++;
- break;
- case no_data_error:
- history.no_data_errors++;
- break;
- default:
- }
-}
-
-static void skip_bad_sector(buffer_struct * buff)
-{
- TRACE_FUN(8, "skip_bad_sector");
-
- /* Mark sector as soft error and skip it
- */
- if (buff->remaining > 0) {
- ++buff->sector_offset;
- ++buff->data_offset;
- --buff->remaining;
- buff->ptr += SECTOR_SIZE;
- buff->bad_sector_map >>= 1;
- } else {
- ++buff->sector_offset; /* hack for error maps */
- TRACE(1, "skipping last sector in segment");
- }
- TRACE_EXIT;
-}
-
-static void update_error_maps(buffer_struct * buff, unsigned error_offset)
-{
- TRACE_FUN(8, "update_error_maps");
- int hard = 0;
-
- /* error_offset is a sector offset !
- */
- if (buff->retry < SOFT_RETRIES) {
- buff->soft_error_map |= (1 << error_offset);
- } else {
- buff->hard_error_map |= (1 << error_offset);
- buff->soft_error_map &= ~buff->hard_error_map;
- buff->retry = -1; /* will be set to 0 in setup_segment */
- hard = 1;
- }
- TRACEx2(4, "sector %d : %s error", SECTOR(error_offset),
- hard ? "hard" : "soft");
- TRACEx2(5, "hard map: 0x%08lx, soft map: 0x%08lx",
- buff->hard_error_map, buff->soft_error_map);
- TRACE_EXIT;
-}
-
-/*
- * Error cause: Amount xferred: Action:
- *
- * id_am_error 0 mark bad and skip
- * id_crc_error 0 mark bad and skip
- * data_am_error 0 mark bad and skip
- * data_crc_error % 1024 mark bad and skip
- * no_data_error 0 retry on write
- * mark bad and skip on read
- * overrun_error [ 0..all-1 ] mark bad and skip
- * no_error all continue
- */
-static void determine_progress(buffer_struct * buff, error_cause cause, int mode)
-{
- TRACE_FUN(8, "determine_progress");
- unsigned nr_not_xferred;
- unsigned nr_xferred;
- unsigned dma_residue;
-
- /* Using less preferred order of disable_dma and get_dma_residue
- * because this seems to fail on at least one system if reversed!
- */
- dma_residue = get_dma_residue(fdc.dma);
- disable_dma(fdc.dma);
- nr_xferred = buff->sector_count * SECTOR_SIZE - dma_residue;
- if (cause == no_error && dma_residue == 0) {
- nr_not_xferred = 0;
- } else {
- if (cause == no_error) {
- TRACEx1(4, "unexpected DMA residue: 0x%04x", dma_residue);
- } else {
- TRACEx1(6, "DMA residue = 0x%04x", dma_residue);
- }
- nr_not_xferred = ((dma_residue + (SECTOR_SIZE - 1)) / SECTOR_SIZE);
- buff->sector_count -= nr_not_xferred; /* adjust to actual value */
- }
- /* Update var's influenced by the DMA operation.
- */
- if (buff->sector_count > 0) {
- buff->sector_offset += buff->sector_count;
- buff->data_offset += buff->sector_count;
- buff->ptr += buff->sector_count * SECTOR_SIZE;
- buff->remaining -= buff->sector_count;
- buff->bad_sector_map >>= buff->sector_count;
- }
- if (cause == no_error) {
- TRACEx1(5, "%d Sector(s) transferred", buff->sector_count);
- } else if (cause == no_data_error) {
- TRACEx1(5, "Sector %d not found", SECTOR(buff->sector_offset));
- } else if (nr_xferred > 0 || cause == id_crc_error ||
- cause == id_am_error || cause == data_am_error) {
- TRACEx1(5, "Error in sector %d", SECTOR(buff->sector_offset));
- } else if (cause == overrun_error) {
- /* got an overrun error on the first byte, must be a hardware problem
- */
- TRACE(-1, "Unexpected error: failing DMA controller ?");
- } else {
- TRACEx1(4, "Unexpected error at sector %d", SECTOR(buff->sector_offset));
- }
- /* Sector_offset points to the problem area, except if we got
- * a data_crc_error. In that case it points one past the failing
- * sector.
- * Now adjust sector_offset so it always points one past he
- * failing sector. I.e. skip the bad sector.
- */
- if (cause != no_error) {
- if (cause != data_crc_error) {
- skip_bad_sector(buff);
- }
- update_error_maps(buff, buff->sector_offset - 1);
- }
- TRACE_EXIT;
-}
-
-static int calc_steps(int cmd)
-{
- if (current_cylinder > cmd) {
- return current_cylinder - cmd;
- } else {
- return current_cylinder + cmd;
- }
-}
-
-static void pause_tape(unsigned segment, int retry, int fdc_mode)
-{
- TRACE_FUN(8, "pause_tape");
- int result;
- /* The 3rd initializer needs to be explicit or else gcc will
- * generate a reference to memset :-(
- */
- byte out[3] =
- {FDC_SEEK, FTAPE_UNIT, 0};
-
- /* We'll use a raw seek command to get the tape to rewind
- * and stop for a retry.
- */
- ++history.rewinds;
- if (qic117_cmds[current_command].non_intr) {
- TRACE(2, "motion command may be issued too soon");
- }
- if (retry && (fdc_mode == fdc_reading_data || fdc_mode == fdc_reading_id)) {
- current_command = QIC_MICRO_STEP_PAUSE;
- might_be_off_track = 1;
- } else {
- current_command = QIC_PAUSE;
- }
- out[2] = calc_steps(current_command);
- result = fdc_command(out, 3); /* issue QIC_117 command */
- if (result < 0) {
- TRACEx1(4, "qic-pause failed, status = %d", result);
- } else {
- location.known = 0;
- runner_status = idle;
- hide_interrupt = 1;
- tape_running = 0;
- }
- TRACE_EXIT;
-}
-
-static void stop_tape(unsigned segment)
-{
- TRACE_FUN(8, "stop_tape");
- int result;
- byte out[3] =
- {FDC_SEEK, FTAPE_UNIT, calc_steps(QIC_STOP_TAPE)};
-
- if (qic117_cmds[current_command].non_intr) {
- TRACE(2, "motion command may be issued too soon");
- }
- current_command = QIC_STOP_TAPE;
- /* We'll use a raw seek command to get the tape to stop
- */
- result = fdc_command(out, 3); /* issue QIC_117 command */
- if (result < 0) {
- TRACEx1(4, "qic-stop failed, status = %d", result);
- } else {
- runner_status = idle;
- hide_interrupt = 1;
- tape_running = 0;
- }
- TRACE_EXIT;
-}
-
-static void continue_xfer(buffer_struct ** p_buff, error_cause cause,
- int fdc_mode, unsigned skip)
-{
- TRACE_FUN(8, "continue_xfer");
- buffer_struct *buff = *p_buff;
- int write = (fdc_mode == fdc_writing_data);
- byte fdc_op = (write) ? FDC_WRITE : FDC_READ;
-
- if (skip > 0) {
- /* This part can be removed if it never happens
- */
- if (runner_status != running ||
- (buff->status != (write ? writing : reading))) {
- TRACEx2(1, "unexpected runner/buffer state %d/%d",
- runner_status, buff->status);
- buff->status = error;
- *p_buff = next_buffer(&head); /* finish this buffer */
- runner_status = aborting;
- fdc_mode = fdc_idle;
- }
- }
- if (buff->remaining > 0 && calc_next_cluster(&buffer[head]) > 0) {
- /* still sectors left in current segment, continue with this segment
- */
- if (setup_fdc_and_dma(&buffer[head], fdc_op) < 0) {
- /* failed, abort operation
- */
- buff->bytes = buff->ptr - buff->address;
- buff->status = error;
- buff = *p_buff = next_buffer(&head); /* finish this buffer */
- runner_status = aborting;
- fdc_mode = fdc_idle;
- }
- } else {
- /* current segment completed
- */
- unsigned last_segment = buff->segment_id;
- int eot = ((last_segment + 1) % segments_per_track) == 0;
- int next = buff->next_segment; /* 0 means stop ! */
-
- buff->bytes = buff->ptr - buff->address;
- buff->status = done;
- buff = *p_buff = next_buffer(&head);
- if (eot) {
- /* finished last segment on current track, can't continue
- */
- runner_status = logical_eot;
- fdc_mode = fdc_idle;
- } else if (next > 0) {
- /* continue with next segment
- */
- if (buff->status == waiting) {
- if (write && next != buff->segment_id) {
- TRACE(5, "segments out of order, aborting write");
- runner_status = do_abort;
- fdc_mode = fdc_idle;
- } else {
- setup_new_segment(&buffer[head], next, 0);
- if (stop_read_ahead) {
- buff->next_segment = 0;
- stop_read_ahead = 0;
- }
- if (calc_next_cluster(&buffer[head]) == 0 ||
- setup_fdc_and_dma(&buffer[head], fdc_op) != 0) {
- TRACEx1(1, "couldn't start %s-ahead", (write) ? "write" : "read");
- runner_status = do_abort;
- fdc_mode = fdc_idle;
- } else {
- buff->status = (write) ? writing : reading; /* keep on going */
- }
- }
- } else {
- TRACEx1(5, "all input buffers %s, pausing tape",
- (write) ? "empty" : "full");
- pause_tape(last_segment, 0, fdc_mode);
- runner_status = idle; /* not quite true until next irq */
- }
- } else {
- /* don't continue with next segment
- */
- TRACEx1(5, "no %s allowed, stopping tape",
- (write) ? "write next" : "read ahead");
- if (random_rw) {
- stop_tape(last_segment);
- } else {
- pause_tape(last_segment, 0, fdc_mode);
- }
- runner_status = idle; /* not quite true until next irq */
- }
- }
- TRACE_EXIT;
- return;
-}
-
-static void
-retry_sector(buffer_struct ** p_buff, error_cause cause, int fdc_mode,
- unsigned skip)
-{
- TRACE_FUN(8, "retry_sector");
- buffer_struct *buff = *p_buff;
-
- TRACEx1(4, "%s error, will retry",
- (fdc_mode == fdc_writing_data) ? "write" : "read");
- pause_tape(buff->segment_id, 1, fdc_mode);
- runner_status = aborting;
- buff->status = error;
- buff->skip = skip;
- TRACE_EXIT;
-}
-
-static unsigned
-find_resume_point(buffer_struct * buff)
-{
- TRACE_FUN(8, "find_resume_point");
- int i = 0;
- unsigned long mask;
- unsigned long map;
-
- /* This function is to be called after all variables have been
- * updated to point past the failing sector.
- * If there are any soft errors before the failing sector,
- * find the first soft error and return the sector offset.
- * Otherwise find the last hard error.
- * Note: there should always be at least one hard or soft error !
- */
- if (buff->sector_offset < 1 || buff->sector_offset > 32) {
- TRACEx1(1, "bug: sector_offset = %d", buff->sector_offset);
- } else {
- if (buff->sector_offset >= 32) { /* C-limitation on shift ! */
- mask = 0xffffffff;
- } else {
- mask = (1 << buff->sector_offset) - 1;
- }
- map = buff->soft_error_map & mask;
- if (map) {
- while ((map & (1 << i)) == 0) {
- ++i;
- }
- TRACEx1(4, "at sector %d", SECTOR(i));
- } else {
- map = buff->hard_error_map & mask;
- i = buff->sector_offset - 1;
- if (map) {
- while ((map & (1 << i)) == 0) {
- --i;
- }
- TRACEx1(4, "after sector %d", SECTOR(i));
- ++i; /* first sector after last hard error */
- } else {
- TRACE(1, "bug: no soft or hard errors");
- }
- }
- }
- TRACE_EXIT;
- return i;
-}
-
-/* FDC interrupt service routine.
- */
-void
-fdc_isr(void)
-{
- TRACE_FUN(8, "fdc_isr");
- int result;
- int status;
- error_cause cause = no_error;
- byte in[7];
- static int isr_active = 0;
- int t0;
- buffer_struct *buff = &buffer[head];
- int skip;
-
- t0 = timestamp();
- if (isr_active) {
- TRACE(-1, "nested interrupt, not good !");
- *fdc.hook = fdc_isr; /* hook our handler into the fdc code again */
- TRACE_EXIT;
- return;
- }
- ++isr_active;
- sti(); /* enables interrupts again */
- status = inb_p(fdc.msr);
- if (status & FDC_BUSY) { /* Entering Result Phase */
- hide_interrupt = 0;
- result = fdc_result(in, 7); /* better get it fast ! */
- if (result < 0) {
- /* Entered unknown state...
- */
- TRACE(1, "probably fatal error during FDC Result Phase");
- TRACE(1, "drive may hang until (power) reset :-(");
- /* what to do next ????
- */
- } else {
- int i;
- char *fdc_mode_txt;
-
- decode_irq_cause(fdc_mode, in, &fdc_mode_txt, &cause);
- for (i = 0; i < NR_BUFFERS; ++i) {
- TRACEx3(8, "buffer[%d] status: %d, segment_id: %d",
- i, buffer[i].status, buffer[i].segment_id);
- }
- switch (fdc_mode) {
-
- case fdc_reading_data:{
-
- if (cause == no_error) {
- TRACEi(5, "reading segment", buff->segment_id);
- } else {
- TRACEi(4, "error reading segment", buff->segment_id);
- }
- if (runner_status == aborting || runner_status == do_abort) {
- TRACEx1(4, "aborting %s", fdc_mode_txt);
- break;
- }
- if (buff->retry > 0) {
- TRACEx1(5, "this is retry nr %d", buff->retry);
- }
- if (buff->bad_sector_map == FAKE_SEGMENT) {
- /* This condition occurs when reading a `fake' sector that's
- * not accessible. Doesn't really matter as we would have
- * ignored it anyway !
- * Chance is that we're past the next segment now, so the
- * next operation may fail and result in a retry.
- */
- TRACE(4, "skipping empty segment (read)");
- buff->remaining = 0; /* skip failing sector */
- continue_xfer(&buff, no_error, fdc_mode, 1); /* fake success */
- } else {
- switch (cause) {
- case no_error:{
- determine_progress(buff, cause, fdc_reading_data);
- if (in[2] & 0x40) {
- /* Handle deleted data in header segments.
- * Skip segment and force read-ahead.
- */
- TRACEx1(2, "deleted data in sector %d",
- SECTOR(buff->sector_offset - 1));
- buff->deleted = 1;
- buff->remaining = 0; /* abort transfer */
- buff->soft_error_map |= (-1L << buff->sector_offset);
- if (buff->segment_id == 0) {
- stop_read_ahead = 1; /* stop on next segment */
- }
- buff->next_segment = buff->segment_id + 1; /* force read-ahead */
- skip = (SECTORS_PER_SEGMENT - buff->sector_offset);
- } else {
- skip = 0;
- }
- continue_xfer(&buff, cause, fdc_mode, skip);
- break;
- }
- case no_data_error:
- /* Tape started too far ahead of or behind the right sector.
- * This may also happen in the middle of a segment !
- * Handle no-data as soft error. If next sector fails too,
- * a retry (with needed reposition) will follow.
- */
- case id_am_error:
- case id_crc_error:
- case data_am_error:
- case data_crc_error:
- case overrun_error:{
- int first_error = (buff->soft_error_map == 0 &&
- buff->hard_error_map == 0);
-
- update_history(cause);
- determine_progress(buff, cause, fdc_reading_data);
- if (first_error) {
- skip = buff->sector_offset;
- } else {
- skip = find_resume_point(buff);
- }
- /* Try to resume with next sector on single errors (let ecc
- * correct it), but retry on no_data (we'll be past the
- * target when we get here so we cannot retry) or on multiple
- * errors (reduce chance on ecc failure).
- */
- if (first_error && cause != no_data_error) {
- continue_xfer(&buff, cause, fdc_mode, skip);
- } else {
- retry_sector(&buff, cause, fdc_mode, skip);
- }
- break;
- }
- default:{
- /* Don't know why this could happen but find out.
- */
- TRACE(1, "unexpected error");
- determine_progress(buff, cause, fdc_reading_data);
- retry_sector(&buff, cause, fdc_mode, 0);
- break;
- }
- }
- }
- break;
- }
-
- case fdc_reading_id:{
-
- if (cause == no_error) {
- fdc_cyl = in[3];
- fdc_head = in[4];
- fdc_sect = in[5];
- TRACEx3(6, "id read: C: 0x%02x, H: 0x%02x, R: 0x%02x",
- fdc_cyl, fdc_head, fdc_sect);
- } else { /* no valid information, use invalid sector */
- fdc_cyl =
- fdc_head =
- fdc_sect = 0;
- TRACE(5, "Didn't find valid sector Id");
- }
- fdc_mode = fdc_idle;
- break;
- }
-
- case fdc_writing_data:{
-
- if (cause == no_error) {
- TRACEi(5, "writing segment", buff->segment_id);
- } else {
- TRACEi(4, "error writing segment", buff->segment_id);
- }
- if (runner_status == aborting || runner_status == do_abort) {
- TRACEx1(5, "aborting %s", fdc_mode_txt);
- break;
- }
- if (buff->retry > 0) {
- TRACEx1(5, "this is retry nr %d", buff->retry);
- }
- if (buff->bad_sector_map == FAKE_SEGMENT) {
- /* This condition occurs when trying to write to a `fake'
- * sector that's not accessible. Doesn't really matter as
- * it isn't used anyway ! Might be located at wrong segment,
- * then we'll fail on the next segment.
- */
- TRACE(4, "skipping empty segment (write)");
- buff->remaining = 0; /* skip failing sector */
- continue_xfer(&buff, no_error, fdc_mode, 1); /* fake success */
- } else {
- switch (cause) {
- case no_error:{
- determine_progress(buff, cause, fdc_writing_data);
- continue_xfer(&buff, cause, fdc_mode, 0);
- break;
- }
- case no_data_error:
- case id_am_error:
- case id_crc_error:
- case data_am_error:
- case overrun_error:{
- update_history(cause);
- determine_progress(buff, cause, fdc_writing_data);
- skip = find_resume_point(buff);
- retry_sector(&buff, cause, fdc_mode, skip);
- break;
- }
- default:{
- if (in[1] & 0x02) {
- TRACE(1, "media not writable");
- } else {
- TRACE(-1, "unforeseen write error");
- }
- fdc_mode = fdc_idle;
- break;
- }
- }
- }
- break;
- }
- default:
-
- TRACEx1(1, "Warning: unexpected irq during: %s",
- fdc_mode_txt);
- fdc_mode = fdc_idle;
- break;
- }
- }
- if (runner_status == do_abort) {
- /* cease operation, remember tape position
- */
- TRACE(5, "runner aborting");
- runner_status = aborting;
- ++expected_stray_interrupts;
- }
- } else { /* !FDC_BUSY */
- /* clear interrupt, cause should be gotten by issuing
- * a Sense Interrupt Status command.
- */
- if (fdc_mode == fdc_recalibrating || fdc_mode == fdc_seeking) {
- if (hide_interrupt) {
- int st0;
- int pcn;
-
- result = fdc_sense_interrupt_status(&st0, &pcn);
- current_cylinder = pcn;
- TRACE(5, "handled hidden interrupt");
- }
- seek_completed = 1;
- fdc_mode = fdc_idle;
- } else if (!waitqueue_active(&wait_intr)) {
- if (expected_stray_interrupts == 0) {
- TRACE(2, "unexpected stray interrupt");
- } else {
- TRACE(5, "expected stray interrupt");
- --expected_stray_interrupts;
- }
- } else {
- if (fdc_mode == fdc_reading_data || fdc_mode == fdc_writing_data ||
- fdc_mode == fdc_reading_id) {
- byte status = inb_p(fdc.msr);
- if (status & FDC_BUSY) {
- TRACE(-1, "***** FDC failure, busy too late");
- } else {
- TRACE(-1, "***** FDC failure, no busy");
- }
- } else {
- TRACE(6, "awaited stray interrupt");
- }
- }
- hide_interrupt = 0;
- }
- /* Handle sleep code.
- */
- if (!hide_interrupt) {
- ++interrupt_seen;
- if (wait_intr) {
- wake_up_interruptible(&wait_intr);
- }
- } else {
- TRACEx1(5, "hiding interrupt while %s", wait_intr ? "waiting" : "active");
- }
- t0 = timediff(t0, timestamp());
- if (t0 >= 1000) { /* only tell us about long calls */
- TRACEx1(7, "isr() duration: %5d usec", t0);
- }
- *fdc.hook = fdc_isr; /* hook our handler into the fdc code again */
- TRACE_EXIT;
- --isr_active;
-}
diff --git a/drivers/char/ftape/ftape-bsm.c b/drivers/char/ftape/ftape-bsm.c
deleted file mode 100644
index 6e86b065e..000000000
--- a/drivers/char/ftape/ftape-bsm.c
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- * Copyright (C) 1994-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute 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.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-bsm.c,v $
- $Author: bas $
- *
- $Revision: 1.7 $
- $Date: 1995/04/30 13:15:14 $
- $State: Beta $
- *
- * This file contains the bad-sector map handling code for
- * the QIC-117 floppy tape driver for Linux.
- * QIC-40, QIC-80, QIC-3010 and QIC-3020 maps are implemented.
- */
-
-#include <linux/ftape.h>
-#include <linux/string.h>
-
-#include "tracing.h"
-#include "ftape-bsm.h"
-#include "ftape-ctl.h"
-#include "ftape-rw.h"
-
-
-/* Global vars.
- */
-int bad_sector_map_changed = 0;
-
-/* Local vars.
- */
-static byte bad_sector_map[BAD_SECTOR_MAP_SIZE];
-typedef enum {
- forward, backward
-} mode_type;
-
-#if 0
-/* fix_tape converts a normal QIC-80 tape into a 'wide' tape.
- * For testing purposes only !
- */
-void fix_tape(byte * buffer)
-{
- static byte list[BAD_SECTOR_MAP_SIZE];
- unsigned long *src_ptr = (unsigned long *) list;
- byte *dst_ptr = bad_sector_map;
- unsigned long map;
- unsigned sector = 1;
- int i;
-
- memcpy(list, bad_sector_map, sizeof(list));
- memset(bad_sector_map, 0, sizeof(bad_sector_map));
- while ((byte *) src_ptr - list < sizeof(list)) {
- map = *src_ptr++;
- if (map == EMPTY_SEGMENT) {
- *(unsigned long *) dst_ptr = 0x800000 + sector;
- dst_ptr += 3;
- sector += SECTORS_PER_SEGMENT;
- } else {
- for (i = 0; i < SECTORS_PER_SEGMENT; ++i) {
- if (map & 1) {
- *(unsigned long *) dst_ptr = sector;
- dst_ptr += 3;
- }
- map >>= 1;
- ++sector;
- }
- }
- }
- bad_sector_map_changed = 1;
- *(buffer + 4) = 4; /* put new format code */
- format_code = 4;
-}
-
-#endif
-
-byte *
- find_end_of_bsm_list(byte * ptr, byte * limit)
-{
- while (ptr + 2 < limit) {
- if (ptr[0] || ptr[1] || ptr[2]) {
- ptr += 3;
- } else {
- return ptr;
- }
- }
- return NULL;
-}
-
-void store_bad_sector_map(byte * buffer)
-{
- TRACE_FUN(8, "store_bad_sector_map");
- size_t count;
- size_t offset;
-
- /* Store the bad sector map in buffer.
- */
- if (format_code == 4) {
- offset = 256;
- count = sizeof(bad_sector_map);
- } else {
- offset = 2 * SECTOR_SIZE; /* skip failed sector log */
- count = sizeof(bad_sector_map) - (offset - 256);
- }
- memcpy(buffer + offset, bad_sector_map, count);
- TRACE_EXIT;
-}
-
-void put_sector(byte ** ptr, unsigned long sector)
-{
- *(*ptr)++ = sector & 0xff;
- sector >>= 8;
- *(*ptr)++ = sector & 0xff;
- sector >>= 8;
- *(*ptr)++ = sector & 0xff;
-}
-
-unsigned long get_sector(byte ** ptr, mode_type mode)
-{
- unsigned long sector;
-
- if (mode == forward) {
- sector = *(*ptr)++;
- sector += *(*ptr)++ << 8;
- sector += *(*ptr)++ << 16;
- } else {
- sector = *--(*ptr) << 16;
- sector += *--(*ptr) << 8;
- sector += *--(*ptr);
- }
- return sector;
-}
-
-void extract_bad_sector_map(byte * buffer)
-{
- TRACE_FUN(8, "extract_bad_sector_map");
-
- /* Fill the bad sector map with the contents of buffer.
- */
- if (format_code == 4) {
- /* QIC-3010/3020 and wide QIC-80 tapes no longer have a failed
- * sector log but use this area to extend the bad sector map.
- */
- memcpy(bad_sector_map, buffer + 256, sizeof(bad_sector_map));
- } else {
- /* non-wide QIC-80 tapes have a failed sector log area that
- * mustn't be included in the bad sector map.
- */
- memcpy(bad_sector_map, buffer + 256 + FAILED_SECTOR_LOG_SIZE,
- sizeof(bad_sector_map) - FAILED_SECTOR_LOG_SIZE);
- }
-#if 0
- /* for testing of bad sector handling at end of tape
- */
- ((unsigned long *) bad_sector_map)[segments_per_track * tracks_per_tape - 3] = 0x000003e0;
- ((unsigned long *) bad_sector_map)[segments_per_track * tracks_per_tape - 2] = 0xff3fffff;
- ((unsigned long *) bad_sector_map)[segments_per_track * tracks_per_tape - 1] = 0xffffe000;
-#endif
-#if 0
- /* Enable to test bad sector handling
- */
- ((unsigned long *) bad_sector_map)[30] = 0xfffffffe;
- ((unsigned long *) bad_sector_map)[32] = 0x7fffffff;
- ((unsigned long *) bad_sector_map)[34] = 0xfffeffff;
- ((unsigned long *) bad_sector_map)[36] = 0x55555555;
- ((unsigned long *) bad_sector_map)[38] = 0xffffffff;
- ((unsigned long *) bad_sector_map)[50] = 0xffff0000;
- ((unsigned long *) bad_sector_map)[51] = 0xffffffff;
- ((unsigned long *) bad_sector_map)[52] = 0xffffffff;
- ((unsigned long *) bad_sector_map)[53] = 0x0000ffff;
-#endif
-#if 0
- /* Enable when testing multiple volume tar dumps.
- */
- for (i = first_data_segment; i <= ftape_last_segment.id - 7; ++i) {
- ((unsigned long *) bad_sector_map)[i] = EMPTY_SEGMENT;
- }
-#endif
-#if 0
- /* Enable when testing bit positions in *_error_map
- */
- for (i = first_data_segment; i <= ftape_last_segment.id; ++i) {
- ((unsigned long *) bad_sector_map)[i] |= 0x00ff00ff;
- }
-#endif
- if (tracing > 2) {
- unsigned int map;
- int good_sectors = 0;
- int bad_sectors;
- unsigned int total_bad = 0;
- int i;
-
- if (format_code == 4 || format_code == 3) {
- byte *ptr = bad_sector_map;
- unsigned sector;
-
- do {
- sector = get_sector(&ptr, forward);
- if (sector != 0) {
- if (format_code == 4 && sector & 0x800000) {
- total_bad += SECTORS_PER_SEGMENT - 3;
- TRACEx1(6, "bad segment at sector: %6d", sector & 0x7fffff);
- } else {
- ++total_bad;
- TRACEx1(6, "bad sector: %6d", sector);
- }
- }
- } while (sector != 0);
- /* Display end-of-file marks
- */
- do {
- sector = *((unsigned short *) ptr)++;
- if (sector) {
- TRACEx2(4, "eof mark: %4d/%2d", sector,
- *((unsigned short *) ptr)++);
- }
- } while (sector);
- } else {
- for (i = first_data_segment;
- i < segments_per_track * tracks_per_tape; ++i) {
- map = ((unsigned long *) bad_sector_map)[i];
- bad_sectors = count_ones(map);
- if (bad_sectors > 0) {
- TRACEx2(6, "bsm for segment %4d: 0x%08x", i, map);
- if (bad_sectors > SECTORS_PER_SEGMENT - 3) {
- bad_sectors = SECTORS_PER_SEGMENT - 3;
- }
- total_bad += bad_sectors;
- }
- }
- }
- good_sectors = ((segments_per_track * tracks_per_tape - first_data_segment)
- * (SECTORS_PER_SEGMENT - 3)) - total_bad;
- TRACEx1(3, "%d Kb usable on this tape",
- good_sectors - ftape_last_segment.free);
- if (total_bad == 0) {
- TRACE(1, "WARNING: this tape has no bad blocks registered !");
- } else {
- TRACEx1(2, "%d bad sectors", total_bad);
- }
- }
- TRACE_EXIT;
-}
-
-unsigned long cvt2map(int sector)
-{
- return 1 << (((sector & 0x7fffff) - 1) % SECTORS_PER_SEGMENT);
-}
-
-int cvt2segment(int sector)
-{
- return ((sector & 0x7fffff) - 1) / SECTORS_PER_SEGMENT;
-}
-
-int forward_seek_entry(int segment_id, byte ** ptr, unsigned long *map)
-{
- byte *tmp_ptr;
- unsigned long sector;
- int segment;
- int count;
-
- do {
- sector = get_sector(ptr, forward);
- segment = cvt2segment(sector);
- } while (sector != 0 && segment < segment_id);
- tmp_ptr = *ptr - 3; /* point to first sector >= segment_id */
- /* Get all sectors in segment_id
- */
- if (format_code == 4 && (sector & 0x800000) && segment == segment_id) {
- *map = EMPTY_SEGMENT;
- count = 32;
- } else {
- *map = 0;
- count = 0;
- while (sector != 0 && segment == segment_id) {
- *map |= cvt2map(sector);
- sector = get_sector(ptr, forward);
- segment = cvt2segment(sector);
- ++count;
- }
- }
- *ptr = tmp_ptr;
- return count;
-}
-
-int backwards_seek_entry(int segment_id, byte ** ptr, unsigned long *map)
-{
- unsigned long sector;
- int segment;
- int count;
-
- *map = 0;
- if (*ptr > bad_sector_map) {
- do {
- sector = get_sector(ptr, backward);
- segment = cvt2segment(sector);
- } while (*ptr > bad_sector_map && segment > segment_id);
- count = 0;
- if (segment > segment_id) {
- /* at start of list, no entry found */
- } else if (segment < segment_id) {
- /* before smaller entry, adjust for overshoot */
- *ptr += 3;
- } else {
- /* get all sectors in segment_id */
- if (format_code == 4 && (sector & 0x800000)) {
- *map = EMPTY_SEGMENT;
- count = 32;
- } else {
- do {
- *map |= cvt2map(sector);
- ++count;
- if (*ptr <= bad_sector_map) {
- break;
- }
- sector = get_sector(ptr, backward);
- segment = cvt2segment(sector);
- } while (segment == segment_id);
- if (segment < segment_id) {
- *ptr += 3;
- }
- }
- }
- } else {
- count = 0;
- }
- return count;
-}
-
-void put_bad_sector_entry(int segment_id, unsigned long new_map)
-{
- byte *ptr = bad_sector_map;
- int count;
- int new_count;
- unsigned long map;
-
- if (format_code == 3 || format_code == 4) {
- count = forward_seek_entry(segment_id, &ptr, &map);
- new_count = count_ones(new_map);
- /* If format code == 4 put empty segment instead of 32 bad sectors.
- */
- if (new_count == 32 && format_code == 4) {
- new_count = 1;
- }
- if (count != new_count) {
- /* insert (or delete if < 0) new_count - count entries.
- * Move trailing part of list including terminating 0.
- */
- byte *hi_ptr = ptr;
-
- do {
- } while (get_sector(&hi_ptr, forward) != 0);
- memmove(ptr + new_count, ptr + count, hi_ptr - (ptr + count));
- }
- if (new_count == 1 && new_map == EMPTY_SEGMENT) {
- put_sector(&ptr, 0x800001 + segment_id * SECTORS_PER_SEGMENT);
- } else {
- int i = 0;
-
- while (new_map) {
- if (new_map & 1) {
- put_sector(&ptr, 1 + segment_id * SECTORS_PER_SEGMENT + i);
- }
- ++i;
- new_map >>= 1;
- }
- }
- } else {
- ((unsigned long *) bad_sector_map)[segment_id] = new_map;
- }
- bad_sector_map_changed = 1;
-}
-
-unsigned long get_bad_sector_entry(int segment_id)
-{
- TRACE_FUN(8, "get_bad_sector_entry");
- static unsigned long map = 0;
-
- if (used_header_segment == -1) {
- /* When reading header segment we'll need a blank map.
- */
- map = 0;
- } else if (format_code == 3 || format_code == 4) {
- /* Invariants:
- * map - mask value returned on last call.
- * ptr - points to first sector greater or equal to
- * first sector in last_referenced segment.
- * last_referenced - segment id used in the last call,
- * sector and map belong to this id.
- * This code is designed for sequential access and retries.
- * For true random access it may have to be redesigned.
- */
- static int last_reference = -1;
- static byte *ptr = bad_sector_map;
-
- if (segment_id > last_reference) {
- /* Skip all sectors before segment_id
- */
- forward_seek_entry(segment_id, &ptr, &map);
- } else if (segment_id < last_reference) {
- /* Skip backwards until begin of buffer or first sector in segment_id
- */
- backwards_seek_entry(segment_id, &ptr, &map);
- } /* segment_id == last_reference : keep map */
- last_reference = segment_id;
- } else {
- map = ((unsigned long *) bad_sector_map)[segment_id];
- }
- TRACE_EXIT;
- return map;
-}
-
-void ftape_init_bsm(void)
-{
- memset(bad_sector_map, 0, sizeof(bad_sector_map));
-}
diff --git a/drivers/char/ftape/ftape-ctl.c b/drivers/char/ftape/ftape-ctl.c
deleted file mode 100644
index e8c3ae1a1..000000000
--- a/drivers/char/ftape/ftape-ctl.c
+++ /dev/null
@@ -1,883 +0,0 @@
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute 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.
-
- *
- * This file contains the non-read/write ftape functions
- * for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/ftape.h>
-#include <asm/uaccess.h>
-
-#include "tracing.h"
-#include "ftape-eof.h"
-#include "ftape-io.h"
-#include "ftape-ctl.h"
-#include "ftape-write.h"
-#include "ftape-read.h"
-#include "ftape-rw.h"
-#include "qic117.h"
-#include "ftape-bsm.h"
-
-
-/* Global vars.
- */
-int segments_per_track = 102;
-int segments_per_head = 1020;
-int segments_per_cylinder = 4;
-int tracks_per_tape = 20;
-int ftape_failure = 1;
-int ftape_seg_pos = 0;
-int first_data_segment = -1;
-int ftape_state = idle; /* use buffer_state_enum */
-history_record history;
-int write_protected;
-int ftape_offline = 0;
-int no_tape = 1;
-int formatted = 0;
-int ftape_data_rate = 0;
-int going_offline = 0;
-int read_only = 0;
-
-/* Local vars.
- */
-static int ftape_last_error = 0;
-static const vendor_struct vendors[] = QIC117_VENDORS;
-static const wakeup_method methods[] = WAKEUP_METHODS;
-static int init_drive_needed = 1;
-
-
-static int ftape_not_operational(int status)
-{
- /* return true if status indicates tape can not be used.
- */
- return ((status ^ QIC_STATUS_CARTRIDGE_PRESENT) &
- (QIC_STATUS_ERROR |
- QIC_STATUS_CARTRIDGE_PRESENT |
- QIC_STATUS_NEW_CARTRIDGE));
-}
-
-int ftape_seek_to_eot(void)
-{
- TRACE_FUN(8, "ftape_seek_to_eot");
- int result;
- int status;
-
- result = ftape_ready_wait(timeout.pause, &status);
- while ((status & QIC_STATUS_AT_EOT) == 0) {
- if (result < 0) {
- TRACE(1, "failed");
- TRACE_EXIT;
- return result;
- }
- if (ftape_not_operational(status)) {
- TRACE_EXIT;
- return -EIO;
- }
- result = ftape_command_wait(QIC_PHYSICAL_FORWARD,
- timeout.rewind, &status);
- }
- TRACE_EXIT;
- return 0;
-}
-
-int ftape_seek_to_bot(void)
-{
- TRACE_FUN(8, "ftape_seek_to_bot");
- int result;
- int status;
-
- result = ftape_ready_wait(timeout.pause, &status);
- while ((status & QIC_STATUS_AT_BOT) == 0) {
- if (result < 0) {
- TRACE(1, "failed");
- TRACE_EXIT;
- return result;
- }
- if (ftape_not_operational(status)) {
- TRACE_EXIT;
- return -EIO;
- }
- result = ftape_command_wait(QIC_PHYSICAL_REVERSE,
- timeout.rewind, &status);
- }
- TRACE_EXIT;
- return 0;
-}
-
-void ftape_reset_position(void)
-{
- ftape_seg_pos = first_data_segment;
- reset_eof_list();
-}
-
-int ftape_new_cartridge(void)
-{
- location.track = -1; /* force seek on first access */
- first_data_segment = -1; /* unknown */
- ftape_zap_read_buffers();
- ftape_zap_write_buffers();
- ftape_reset_position();
- return 0;
-}
-
-int ftape_abort_operation(void)
-{
- TRACE_FUN(5, "ftape_abort_operation");
- int result = 0;
- int i;
- int status;
-
- if (runner_status == running) {
- TRACE(5, "aborting runner, waiting");
- runner_status = do_abort;
- /* set timeout so that the tape will run to logical EOT
- * if we missed the last sector and there are no queue pulses.
- */
- result = ftape_dumb_stop();
- if (result == 0) {
- runner_status = idle;
- }
- }
- if (runner_status != idle) {
- if (runner_status == do_abort) {
- TRACE(5, "forcing runner abort");
- }
- TRACE(5, "stopping tape");
- result = ftape_command_wait(QIC_STOP_TAPE, timeout.stop, &status);
- location.known = 0;
- runner_status = idle;
- }
- for (i = 0; i < NR_BUFFERS; ++i) {
- buffer[i].status = waiting;
- }
- head = tail = 0;
- TRACE_EXIT;
- return result;
-}
-
-int lookup_vendor_id(int vendor_id)
-{
- int i = 0;
-
- while (vendors[i].vendor_id != vendor_id) {
- if (++i >= NR_ITEMS(vendors)) {
- return -1;
- }
- }
- return i;
-}
-
-void ftape_detach_drive(void)
-{
- TRACE_FUN(8, "ftape_detach_drive");
-
- TRACE(5, "disabling tape drive and fdc");
- ftape_put_drive_to_sleep(drive_type);
- fdc_catch_stray_interrupts(1); /* one always comes */
- fdc_disable();
- fdc_release_irq_and_dma();
- TRACE_EXIT;
-}
-
-static void clear_history(void)
-{
- history.used = 0;
- history.id_am_errors =
- history.id_crc_errors =
- history.data_am_errors =
- history.data_crc_errors =
- history.overrun_errors =
- history.no_data_errors =
- history.retries =
- history.crc_errors =
- history.crc_failures =
- history.ecc_failures =
- history.corrected =
- history.defects =
- history.rewinds = 0;
-}
-
-int ftape_activate_drive(vendor_struct * drive_type)
-{
- TRACE_FUN(5, "ftape_activate_drive");
- int result = 0;
-
- /* If we already know the drive type, wake it up.
- * Else try to find out what kind of drive is attached.
- */
- if (drive_type->wake_up != unknown_wake_up) {
- TRACE(5, "enabling tape drive and fdc");
- result = ftape_wakeup_drive(drive_type->wake_up);
- if (result < 0) {
- TRACE(1, "known wakeup method failed");
- }
- } else {
- int old_tracing = tracing;
- wake_up_types method;
-
- /* Try to awaken the drive using all known methods.
- * Lower tracing for a while.
- */
- if (tracing <= 4) {
- tracing = 0;
- }
- for (method = no_wake_up; method < NR_ITEMS(methods); ++method) {
- drive_type->wake_up = method;
-#if 0
- /* Test setup for dual drive configuration in dodo.
- * /dev/rft2 uses mountain wakeup only -> Archive QIC-80
- * /dev/rft3 uses colorado wakeup only -> Jumbo QIC-40
- * Other systems will use the normal scheme.
- */
- if ((FTAPE_UNIT < 2) ||
- (FTAPE_UNIT == 2 && method == wake_up_mountain) ||
- (FTAPE_UNIT == 3 && method == wake_up_colorado)) {
- result = ftape_wakeup_drive(drive_type->wake_up);
- } else {
- result = -EIO;
- }
-#else
- result = ftape_wakeup_drive(drive_type->wake_up);
-#endif
- if (result >= 0) {
- int tracing = old_tracing; /* fool TRACE */
- TRACEx1(2, "drive wakeup method: %s",
- methods[drive_type->wake_up].name);
- break;
- }
- }
- tracing = old_tracing;
- if (method >= NR_ITEMS(methods)) {
- /* no response at all, cannot open this drive */
- drive_type->wake_up = unknown_wake_up;
- TRACE(1, "no tape drive found !");
- tracing = old_tracing;
- result = -ENODEV;
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_get_drive_status(int *new_tape, int *no_tape, int *wp_tape)
-{
- TRACE_FUN(5, "ftape_get_drive_status");
- int result;
- int status;
-
- *no_tape =
- *wp_tape = 0;
- /* Tape drive is activated now.
- * First clear error status if present.
- */
- do {
- result = ftape_ready_wait(timeout.reset, &status);
- if (result < 0) {
- if (result == -ETIME) {
- TRACE(1, "ftape_ready_wait timeout");
- } else if (result == -EINTR) {
- TRACE(1, "ftape_ready_wait aborted");
- } else {
- TRACE(1, "ftape_ready_wait failed");
- }
- result = -EIO;
- break;
- }
- /* Clear error condition (drive is ready !)
- */
- if (status & QIC_STATUS_ERROR) {
- int error;
- int command;
-
- TRACE(1, "error status set");
- result = ftape_report_error(&error, &command, 1);
- if (result < 0) {
- TRACEi(1, "report_error_code failed:", result);
- ftape_reset_drive(); /* hope it's working next time */
- init_drive_needed = 1;
- result = -EIO;
- break;
- } else if (error != 0) {
- TRACEi(4, "error code :", error);
- TRACEi(4, "error command:", command);
- }
- }
- if (status & QIC_STATUS_NEW_CARTRIDGE) {
- int error;
- int command;
- int old_tracing = tracing;
-
- /* Undocumented feature: Must clear (not present!) error
- * here or we'll fail later.
- */
- tracing = 0;
- ftape_report_error(&error, &command, 1);
- tracing = old_tracing;
- TRACE(3, "status: new cartridge");
- *new_tape = 1;
- }
- } while (status & QIC_STATUS_ERROR);
-
- *no_tape = !(status & QIC_STATUS_CARTRIDGE_PRESENT);
- *wp_tape = (status & QIC_STATUS_WRITE_PROTECT);
- if (*no_tape) {
- TRACE(1, "no cartridge present");
- } else {
- if (*wp_tape) {
- TRACE(2, "Write protected cartridge");
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-void ftape_log_vendor_id(void)
-{
- TRACE_FUN(5, "ftape_log_vendor_id");
- int vendor_index;
-
- ftape_report_vendor_id(&drive_type.vendor_id);
- vendor_index = lookup_vendor_id(drive_type.vendor_id);
- if (drive_type.vendor_id == UNKNOWN_VENDOR &&
- drive_type.wake_up == wake_up_colorado) {
- vendor_index = 0;
- drive_type.vendor_id = 0; /* hack to get rid of all this mail */
- }
- if (vendor_index < 0) {
- /* Unknown vendor id, first time opening device.
- * The drive_type remains set to type found at wakeup time, this
- * will probably keep the driver operating for this new vendor.
- */
- TRACE(-1, "============ unknown vendor id ===========");
- TRACE(-1, "A new, yet unsupported tape drive is found");
- TRACE(-1, "Please report the following values:");
- TRACEx1(-1, " Vendor id : 0x%04x", drive_type.vendor_id);
- TRACEx1(-1, " Wakeup method : %s", methods[drive_type.wake_up].name);
- TRACE(-1, "And a description of your tape drive to:");
- TRACE(-1, "Kai Harrekilde-Petersen <khp@dolphinics.no>");
- TRACE(-1, "==========================================");
- drive_type.speed = 500; /* deci-ips: very safe value */
- } else {
- drive_type.name = vendors[vendor_index].name;
- drive_type.speed = vendors[vendor_index].speed;
- TRACEx1(3, "tape drive type: %s", drive_type.name);
- /* scan all methods for this vendor_id in table */
- while (drive_type.wake_up != vendors[vendor_index].wake_up) {
- if (vendor_index < NR_ITEMS(vendors) - 1 &&
- vendors[vendor_index + 1].vendor_id == drive_type.vendor_id) {
- ++vendor_index;
- } else {
- break;
- }
- }
- if (drive_type.wake_up != vendors[vendor_index].wake_up) {
- TRACE(-1, "==========================================");
- TRACE(-1, "wakeup type mismatch:");
- TRACEx2(-1, "found: %s, expected: %s",
- methods[drive_type.wake_up].name,
- methods[vendors[vendor_index].wake_up].name);
- TRACE(-1, "please report this to <khp@dolphinics.no>");
- TRACE(-1, "==========================================");
- }
- }
- TRACE_EXIT;
-}
-
-void ftape_calc_timeouts(void)
-{
- TRACE_FUN(8, "ftape_calc_timeouts");
- int speed; /* deci-ips ! */
- int length;
-
- /* tape transport speed
- * data rate: QIC-40 QIC-80 QIC-3010 QIC-3020
- *
- * 250 Kbps 25 ips n/a n/a n/a
- * 500 Kbps 50 ips 34 ips 22.6 ips n/a
- * 1 Mbps n/a 68 ips 45.2 ips 22.6 ips
- * 2 Mbps n/a n/a n/a 45.2 ips
- *
- * fast tape transport speed is at least 68 ips.
- */
- switch (qic_std) {
- case QIC_TAPE_QIC40:
- speed = (ftape_data_rate == 3) ? 250 : 500;
- break;
- case QIC_TAPE_QIC80:
- speed = (ftape_data_rate == 2) ? 340 : 680;
- break;
- case QIC_TAPE_QIC3010:
- speed = (ftape_data_rate == 2) ? 226 : 452;
- break;
- case QIC_TAPE_QIC3020:
- speed = (ftape_data_rate == 1) ? 226 : 452;
- break;
- default:
- TRACE(-1, "Unknown qic_std (bug) ?");
- speed = 500;
- break;
- }
- if (tape_len <= 0) {
- /* Handle unknown length tapes as 1100 ft ones (worst case)
- */
- TRACE(1, "Unknown tape length, using worst case timing values!");
- length = 1100;
- } else {
- length = tape_len;
- }
- if (drive_type.speed == 0) {
- unsigned long t0;
- int dt;
-
- ftape_seek_to_bot();
- t0 = jiffies;
- ftape_seek_to_eot();
- ftape_seek_to_bot();
- dt = (int) ((jiffies - t0) * MSPT);
- drive_type.speed = (2 * 12 * length * 1000) / dt;
- TRACE(-1, "==========================================");
- TRACEx1(-1, "drive : %s", drive_type.name);
- TRACEx2(-1, "delta time = %d, length = %d", dt, length);
- TRACEx1(-1, "has max tape speed of %d ips", drive_type.speed);
- TRACE(-1, "please report this to <khp@dolphinics.no>");
- TRACE(-1, "==========================================");
- }
- /* time to go from bot to eot at normal speed (data rate):
- * time = (1+delta) * length (ft) * 12 (inch/ft) / speed (ips)
- * delta = 10 % for seek speed, 20 % for rewind speed.
- */
- timeout.seek = (length * 132 * SECOND) / speed;
- timeout.rewind = (length * 144 * SECOND) / (10 * drive_type.speed);
- timeout.reset = 20 * SECOND + timeout.rewind;
- TRACEx2(4, "speed = %d, length = %d", speed, length);
- TRACEx1(4, "seek timeout: %d sec", (timeout.seek + 500) / 1000);
- TRACEx1(4, "rewind timeout: %d sec", (timeout.rewind + 500) / 1000);
- TRACE_EXIT;
-}
-
-int ftape_init_drive(int *formatted)
-{
- TRACE_FUN(5, "ftape_init_drive");
- int result = 0;
- int status;
-
- result = ftape_report_raw_drive_status(&status);
- if (result >= 0 && (status & QIC_STATUS_CARTRIDGE_PRESENT)) {
- if (!(status & QIC_STATUS_AT_BOT)) {
- /* Antique drives will get here after a soft reset,
- * modern ones only if the driver is loaded when the
- * tape wasn't rewound properly.
- */
- ftape_seek_to_bot();
- }
- if (!(status & QIC_STATUS_REFERENCED)) {
- TRACE(5, "starting seek_load_point");
- result = ftape_command_wait(QIC_SEEK_LOAD_POINT,
- timeout.reset, &status);
- if (result < 0) {
- TRACE(1, "seek_load_point failed (command)");
- }
- }
- }
- if (result >= 0) {
- int rate;
-
- *formatted = (status & QIC_STATUS_REFERENCED);
- if (!*formatted) {
- TRACE(1, "Warning: tape is not formatted !");
- }
- /* Select highest rate supported by both fdc and drive.
- * Start with highest rate supported by the fdc.
- */
- if (fdc.type >= i82078_1)
- rate = 0;
- else if (fdc.type >= i82077)
- rate = 1;
- else
- rate = 2;
- do {
- result = ftape_set_data_rate(rate);
- if (result >= 0) {
- ftape_calc_timeouts();
- break;
- }
- ++rate;
- } while (rate < 4);
- if (result < 0) {
- result = -EIO;
- }
- }
- if (result >= 0) {
- /* Tape should be at bot if new cartridge ! */
- ftape_new_cartridge();
- }
- init_drive_needed = 0;
- TRACE_EXIT;
- return result;
-}
-
-/* OPEN routine called by kernel-interface code
- */
-int _ftape_open(void)
-{
- TRACE_FUN(8, "_ftape_open");
- int result;
- static int new_tape = 1;
-
- result = fdc_init();
- if (result >= 0) {
- result = ftape_activate_drive(&drive_type);
- if (result < 0) {
- fdc_disable();
- fdc_release_irq_and_dma();
-
- } else {
- result = ftape_get_drive_status(&new_tape, &no_tape, &write_protected);
- if (result < 0) {
- ftape_detach_drive();
- } else {
- if (drive_type.vendor_id == UNKNOWN_VENDOR) {
- ftape_log_vendor_id();
- }
- if (no_tape) {
- ftape_offline = 1;
- } else if (new_tape) {
- ftape_offline = 0;
- init_drive_needed = 1;
- read_only = 0; /* enable writes again */
- }
- if (!ftape_offline && init_drive_needed) {
- result = ftape_init_drive(&formatted);
- if (result >= 0) {
- new_tape = 0;
- } else {
- ftape_detach_drive();
- }
- }
- if (result >= 0) {
- clear_history();
- }
- }
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-/* RELEASE routine called by kernel-interface code
- */
-int _ftape_close(void)
-{
- TRACE_FUN(8, "_ftape_close");
- int result = 0;
- int last_segment = 0;
-
- if (!ftape_offline) {
- result = ftape_flush_buffers();
- last_segment = ftape_seg_pos - 1;
- if (!(ftape_unit & FTAPE_NO_REWIND)) {
- if (result >= 0) {
- result = ftape_update_header_segments(NULL, 1);
- if (result < 0) {
- TRACE(1, "error: update of header segments failed");
- }
- } else {
- TRACE(1, "error: unable to update header segments");
- }
- }
- ftape_abort_operation();
- if (!(ftape_unit & FTAPE_NO_REWIND)) {
- if (!no_tape) {
- TRACE(5, "rewinding tape");
- result = ftape_seek_to_bot();
- }
- ftape_reset_position();
- ftape_zap_read_buffers();
- ftape_zap_write_buffers();
- }
- }
- ftape_detach_drive();
- fdc_uninit();
- if (history.used) {
- TRACE(3, "== Non-fatal errors this run: ==");
- TRACE(3, "fdc isr statistics:");
- TRACEi(3, " id_am_errors :", history.id_am_errors);
- TRACEi(3, " id_crc_errors :", history.id_crc_errors);
- TRACEi(3, " data_am_errors :", history.data_am_errors);
- TRACEi(3, " data_crc_errors :", history.data_crc_errors);
- TRACEi(3, " overrun_errors :", history.overrun_errors);
- TRACEi(3, " no_data_errors :", history.no_data_errors);
- TRACEi(3, " retries :", history.retries);
- if (history.used & 1) {
- TRACE(3, "ecc statistics:");
- TRACEi(3, " crc_errors :", history.crc_errors);
- TRACEi(3, " crc_failures :", history.crc_failures);
- TRACEi(3, " ecc_failures :", history.ecc_failures);
- TRACEi(3, " sectors corrected:", history.corrected);
- }
- TRACEx2(3, "media defects : %d%s", history.defects,
- history.defects ? " !!!" : "");
- TRACEi(3, "repositions :", history.rewinds);
- TRACEi(3, "last segment :", last_segment);
- }
- if (going_offline) {
- going_offline = 0;
- ftape_offline = 1;
- }
- TRACE_EXIT;
- return result;
-}
-
-/* IOCTL routine called by kernel-interface code
- */
-int _ftape_ioctl(unsigned int command, void *arg)
-{
- TRACE_FUN(8, "ftape_ioctl");
- int result = EINVAL;
- union {
- struct mtop mtop;
- struct mtget mtget;
- } krnl_arg;
- int arg_size = (command & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
-
- /* This check will only catch arguments that are too large !
- */
- if ((command & IOC_INOUT) && arg_size > sizeof(krnl_arg)) {
- TRACEi(1, "bad argument size:", arg_size);
- TRACE_EXIT;
- return -EINVAL;
- }
- if (command & IOC_IN) {
- int error = verify_area(VERIFY_READ, arg, arg_size);
- if (error) {
- TRACE_EXIT;
- return error;
- }
- copy_from_user(&krnl_arg.mtop, arg, arg_size);
- }
- TRACEx1(5, "called with ioctl command: 0x%08x", command);
- switch (command) {
- /* cpio compatibility
- * mtrasx and mtreset are mt extension by Hennus Bergman
- * mtseek and mttell are mt extension by eddy olk
- */
- case MTIOCTOP:
- TRACEx1(5, "calling MTIOCTOP command: 0x%08x", krnl_arg.mtop.mt_op);
- switch (krnl_arg.mtop.mt_op) {
- case MTNOP:
- /* gnu mt calls MTNOP before MTIOCGET to set status */
- result = 0;
- break;
- case MTRESET:
- result = ftape_reset_drive();
- init_drive_needed = 1;
- if (result < 0 || ftape_offline) {
- break;
- }
- result = ftape_seek_to_bot();
- ftape_reset_position();
- break;
- case MTREW:
- case MTOFFL:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- ftape_flush_buffers();
- ftape_update_header_segments(NULL, 1);
- result = ftape_seek_to_bot();
- ftape_reset_position();
- if (krnl_arg.mtop.mt_op == MTOFFL) {
- going_offline = 1;
- TRACE(4, "Putting tape drive offline");
- }
- result = 0;
- break;
- case MTRETEN:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- result = ftape_seek_to_eot();
- if (result >= 0) {
- result = ftape_seek_to_bot();
- }
- ftape_reset_position();
- break;
- case MTERASE:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- result = ftape_erase();
- break;
- case MTEOM:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- result = ftape_seek_eom();
- break;
- case MTFSFM:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- eof_mark = 1; /* position ready to extend */
- case MTFSF:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- result = ftape_seek_eof(krnl_arg.mtop.mt_count);
- break;
- case MTBSFM:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- eof_mark = 1; /* position ready to extend */
- case MTBSF:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- result = ftape_seek_eof(-krnl_arg.mtop.mt_count);
- break;
- case MTFSR:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- tracing = krnl_arg.mtop.mt_count;
- TRACEx1(2, "tracing set to %d", tracing);
- result = 0;
- break;
- case MTBSR:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
-#if 0
- result = ftape_fix();
-#else
- result = 0;
-#endif
- break;
- case MTWEOF:
- if (ftape_offline) {
- result = -EIO;
- break;
- }
- result = ftape_weof(krnl_arg.mtop.mt_count, ftape_seg_pos, 1);
- if (result >= 0) {
- ftape_seg_pos += krnl_arg.mtop.mt_count - 1;
- }
- break;
- /* MTRASx and MTRESET are mt extension by Hennus Bergman
- */
- case MTRAS1:
- case MTRAS2:
- case MTRAS3:
- case MTSEEK:
- case MTTELL:
- default:
- TRACEi(1, "MTIOCTOP sub-command not implemented:", krnl_arg.mtop.mt_op);
- result = -EIO;
- break;
- }
- break;
- case MTIOCGET:
- krnl_arg.mtget.mt_type = drive_type.vendor_id + 0x800000;
- krnl_arg.mtget.mt_resid = 0; /* not implemented */
- krnl_arg.mtget.mt_dsreg = 0; /* status register */
- krnl_arg.mtget.mt_gstat = /* device independent status */
- ((ftape_offline) ? 0 : GMT_ONLINE(-1L)) |
- ((write_protected) ? GMT_WR_PROT(-1L) : 0) |
- ((no_tape) ? GMT_DR_OPEN(-1L) : 0);
- krnl_arg.mtget.mt_erreg = ftape_last_error; /* error register */
- result = ftape_file_no(&krnl_arg.mtget.mt_fileno,
- &krnl_arg.mtget.mt_blkno);
- break;
- case MTIOCPOS:
- TRACE(5, "Mag tape ioctl command: MTIOCPOS");
- TRACE(1, "MTIOCPOS command not implemented");
- break;
- default:
- result = -EINVAL;
- break;
- }
- if (command & IOC_OUT) {
- int error = verify_area(VERIFY_WRITE, arg, arg_size);
- if (error) {
- TRACE_EXIT;
- return error;
- }
- copy_to_user(arg, &krnl_arg, arg_size);
- }
- TRACE_EXIT;
- return result;
-}
-
-void ftape_init_driver(void)
-{
- drive_type.vendor_id = UNKNOWN_VENDOR;
- drive_type.speed = 0;
- drive_type.wake_up = unknown_wake_up;
- drive_type.name = "Unknown";
-
- timeout.seek = 650 * SECOND;
- timeout.reset = 670 * SECOND;
- timeout.rewind = 650 * SECOND;
- timeout.head_seek = 15 * SECOND;
- timeout.stop = 5 * SECOND;
- timeout.pause = 16 * SECOND;
-
- qic_std = -1;
- tape_len = -1;
- current_command = 0;
- current_cylinder = -1;
-
- segments_per_track = 102;
- segments_per_head = 1020;
- segments_per_cylinder = 4;
- tracks_per_tape = 20;
- ftape_failure = 1;
- ftape_seg_pos = 0;
- first_data_segment = -1;
- ftape_state = idle;
- no_tape = 1;
- formatted = 0;
- ftape_data_rate = 0;
- going_offline = 0;
- read_only = 0;
-
- init_drive_needed = 1;
- header_segment_1 = -1;
- header_segment_2 = -1;
- used_header_segment = -1;
- location.track = -1;
- location.known = 0;
- tape_running = 0;
- might_be_off_track = 1;
-
- ftape_new_cartridge(); /* init some tape related variables */
- ftape_init_bsm();
-}
diff --git a/drivers/char/ftape/ftape-ctl.h b/drivers/char/ftape/ftape-ctl.h
deleted file mode 100644
index 59fcfdc76..000000000
--- a/drivers/char/ftape/ftape-ctl.h
+++ /dev/null
@@ -1,94 +0,0 @@
-#ifndef _FTAPE_CTL_H
-#define _FTAPE_CTL_H
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute 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.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-ctl.h,v $
- $Author: bas $
- *
- $Revision: 1.4 $
- $Date: 1995/05/03 18:04:03 $
- $State: Beta $
- *
- * This file contains the non-standard IOCTL related definitions
- * for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/ioctl.h>
-#include <linux/mtio.h>
-
-#include "vendors.h"
-
-
-typedef struct {
- int used; /* any reading or writing done */
- /* isr statistics */
- unsigned int id_am_errors; /* id address mark not found */
- unsigned int id_crc_errors; /* crc error in id address mark */
- unsigned int data_am_errors; /* data address mark not found */
- unsigned int data_crc_errors; /* crc error in data field */
- unsigned int overrun_errors; /* fdc access timing problem */
- unsigned int no_data_errors; /* sector not found */
- unsigned int retries; /* number of tape retries */
- /* ecc statistics */
- unsigned int crc_errors; /* crc error in data */
- unsigned int crc_failures; /* bad data without crc error */
- unsigned int ecc_failures; /* failed to correct */
- unsigned int corrected; /* total sectors corrected */
- /* general statistics */
- unsigned int rewinds; /* number of tape rewinds */
- unsigned int defects; /* bad sectors due to media defects */
-} history_record;
-
-/*
- * ftape-ctl.c defined global vars.
- */
-extern int ftape_failure;
-extern int write_protected;
-extern ftape_offline;
-extern int formatted;
-extern int no_tape;
-extern history_record history;
-extern int ftape_data_rate;
-extern int going_offline;
-extern vendor_struct drive_type;
-extern int segments_per_track;
-extern int segments_per_head;
-extern int segments_per_cylinder;
-extern int tracks_per_tape;
-extern int ftape_seg_pos;
-extern int first_data_segment;
-extern int ftape_state;
-extern int read_only;
-
-/*
- * ftape-ctl.c defined global functions.
- */
-extern int _ftape_open(void);
-extern int _ftape_close(void);
-extern int _ftape_ioctl(unsigned int command, void *arg);
-extern int ftape_seek_to_bot(void);
-extern int ftape_seek_to_eot(void);
-extern int ftape_new_cartridge(void);
-extern int ftape_abort_operation(void);
-extern void ftape_reset_position(void);
-extern void ftape_calc_timeouts(void);
-extern void ftape_init_driver(void);
-
-#endif
diff --git a/drivers/char/ftape/ftape-eof.c b/drivers/char/ftape/ftape-eof.c
deleted file mode 100644
index 75c4cbd76..000000000
--- a/drivers/char/ftape/ftape-eof.c
+++ /dev/null
@@ -1,554 +0,0 @@
-
-/*
- * Copyright (C) 1994-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute 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.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-eof.c,v $
- $Author: bas $
- *
- $Revision: 1.21 $
- $Date: 1995/05/27 08:54:21 $
- $State: Beta $
- *
- * This file contains the eof mark handling code
- * for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/ftape.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-
-#include "tracing.h"
-#include "ftape-eof.h"
-#include "ftape-write.h"
-#include "ftape-read.h"
-#include "ftape-rw.h"
-#include "ftape-ctl.h"
-#include "ftape-bsm.h"
-
-/* Global vars.
- */
-int failed_sector_log_changed = 0;
-int eof_mark = 0;
-
-/* Local vars.
- */
-static struct failed_sector_entry {
- unsigned short segment;
- unsigned short sector;
-} *eof_mark_ptr;
-
-typedef union {
- struct failed_sector_entry mark;
- unsigned long entry;
-} eof_mark_union;
-
-/* a copy of the failed sector log from the header segment.
- */
-static eof_mark_union eof_map[(2048 - 256) / 4];
-
-/* index into eof_map table pointing to last found eof mark.
- */
-static int eof_index;
-
-/* number of eof marks (entries in bad sector log) on tape.
- */
-static int nr_of_eof_marks = -1;
-
-static char linux_tape_label[] = "Linux raw format V";
-enum {
- min_fmt_version = 1, max_fmt_version = 2
-};
-static unsigned ftape_fmt_version = 0;
-
-
-/* Ftape (mis)uses the bad sector log to record end-of-file marks.
- * Initially (when the tape is erased) all entries in the bad sector
- * log are added to the tape's bad sector map. The bad sector log
- * then is cleared.
- *
- * The bad sector log normally contains entries of the form:
- * even 16-bit word: segment number of bad sector
- * odd 16-bit word: encoded date
- * There can be a total of 448 entries (1792 bytes).
- *
- * My guess is that no program is using this bad sector log (the
- * format seems useless as there is no indication of the bad sector
- * itself, only the segment)
- * However, if any program does use the bad sector log, the format
- * used by ftape will let the program think there are some bad sectors
- * and no harm is done.
- *
- * The eof mark entries that ftape stores in the bad sector log:
- * even 16-bit word: segment number of eof mark
- * odd 16-bit word: sector number of eof mark [1..32]
- *
- * The eof_map as maintained is a sorted list of eof mark entries.
- *
- *
- * The tape name field in the header segments is used to store a
- * linux tape identification string and a version number.
- * This way the tape can be recognized as a Linux raw format
- * tape when using tools under other OS's.
- *
- * 'Wide' QIC tapes (format code 4) don't have a failed sector list
- * anymore. That space is used for the (longer) bad sector map that
- * now is a variable length list too.
- * We now store our end-of-file marker list after the bad-sector-map
- * on tape. The list is delimited by a (long) 0 entry.
- */
-
-int ftape_validate_label(char *label)
-{
- TRACE_FUN(8, "ftape_validate_label");
- int result = 0;
-
- TRACEx1(4, "tape label = `%s'", label);
- ftape_fmt_version = 0;
- if (memcmp(label, linux_tape_label, strlen(linux_tape_label)) == 0) {
- int pos = strlen(linux_tape_label);
- while (label[pos] >= '0' && label[pos] <= '9') {
- ftape_fmt_version *= 10;
- ftape_fmt_version = label[pos++] - '0';
- }
- result = (ftape_fmt_version >= min_fmt_version &&
- ftape_fmt_version <= max_fmt_version);
- }
- TRACEx1(4, "format version = %d", ftape_fmt_version);
- TRACE_EXIT;
- return result;
-}
-
-static byte *
- find_end_of_eof_list(byte * ptr, byte * limit)
-{
- while (ptr + 3 < limit) {
- if (*(unsigned long *) ptr) {
- ++(unsigned long *) ptr;
- } else {
- return ptr;
- }
- }
- return NULL;
-}
-
-void reset_eof_list(void)
-{
- TRACE_FUN(8, "reset_eof_list");
-
- eof_mark_ptr = &eof_map[0].mark;
- eof_index = 0;
- eof_mark = 0;
- TRACE_EXIT;
-}
-
-/* Test if `segment' has an eof mark set (optimized for sequential access).
- * return 0 if not eof mark or sector number (> 0) if eof mark set.
- */
-int check_for_eof(unsigned segment)
-{
- TRACE_FUN(8, "check_for_eof");
- static unsigned last_reference = INT_MAX;
- int result;
-
- if (segment < last_reference) {
- reset_eof_list();
- }
- last_reference = segment;
- while (eof_index < nr_of_eof_marks && segment > eof_mark_ptr->segment) {
- ++eof_mark_ptr;
- ++eof_index;
- }
- if (eof_index < nr_of_eof_marks && segment == eof_mark_ptr->segment) {
- TRACEx3(5, "hit mark %d/%d at index %d",
- eof_map[eof_index].mark.segment, eof_map[eof_index].mark.sector,
- eof_index);
- if (eof_mark_ptr->sector >= SECTORS_PER_SEGMENT) {
- TRACEx2(-1, "Bad file mark detected: %d/%d",
- eof_mark_ptr->segment, eof_mark_ptr->sector);
- result = 0; /* return bogus (but valid) value */
- } else {
- result = eof_mark_ptr->sector;
- }
- } else {
- result = 0;
- }
- TRACE_EXIT;
- return result;
-}
-
-void clear_eof_mark_if_set(unsigned segment, unsigned byte_count)
-{
- TRACE_FUN(5, "clear_eof_mark_if_set");
- if (ftape_fmt_version != 0 &&
- check_for_eof(segment) > 0 &&
- byte_count >= eof_mark_ptr->sector * SECTOR_SIZE) {
- TRACEx3(5, "clearing mark %d/%d at index %d",
- eof_mark_ptr->segment, eof_mark_ptr->sector, eof_index);
- memmove(&eof_map[eof_index], &eof_map[eof_index + 1],
- (nr_of_eof_marks - eof_index) * sizeof(*eof_map));
- --nr_of_eof_marks;
- failed_sector_log_changed = 1;
- }
- TRACE_EXIT;
-}
-
-void put_file_mark_in_map(unsigned segment, unsigned sector)
-{
- TRACE_FUN(8, "put_file_mark_in_map");
- eof_mark_union new;
- int index;
- eof_mark_union *ptr;
-
- if (ftape_fmt_version != 0) {
- new.mark.segment = segment;
- new.mark.sector = sector;
- for (index = 0, ptr = &eof_map[0];
- index < nr_of_eof_marks && ptr->mark.segment < segment;
- ++index, ++ptr) {
- }
- if (index < nr_of_eof_marks) {
- if (ptr->mark.segment == segment) {
- /* overwrite */
- if (ptr->mark.sector == sector) {
- TRACEx2(5, "mark %d/%d already exists",
- new.mark.segment, new.mark.sector);
- } else {
- TRACEx5(5, "overwriting %d/%d at index %d with %d/%d",
- ptr->mark.segment, ptr->mark.sector, index,
- new.mark.segment, new.mark.sector);
- ptr->entry = new.entry;
- failed_sector_log_changed = 1;
- }
- } else {
- /* insert */
- TRACEx5(5, "inserting %d/%d at index %d before %d/%d",
- new.mark.segment, new.mark.sector, index,
- ptr->mark.segment, ptr->mark.sector);
- memmove(ptr + 1, ptr, (nr_of_eof_marks - index) * sizeof(*eof_map));
- ptr->entry = new.entry;
- ++nr_of_eof_marks;
- failed_sector_log_changed = 1;
- }
- } else {
- /* append */
- TRACEx3(5, "appending %d/%d at index %d",
- new.mark.segment, new.mark.sector, index);
- ptr->entry = new.entry;
- ++nr_of_eof_marks;
- failed_sector_log_changed = 1;
- }
- }
- TRACE_EXIT;
-}
-
-/* Write count file marks to tape starting at first non-bad
- * sector following the given segment and sector.
- * sector = base 1 !
- */
-int ftape_weof(unsigned count, unsigned segment, unsigned sector)
-{
- TRACE_FUN(5, "ftape_weof");
- int result = 0;
- unsigned long mask = get_bad_sector_entry(segment);
- unsigned sector_nr = 0;
-
- if (ftape_fmt_version != 0) {
- if (sector < 1 || sector > 29 ||
- segment + count >= ftape_last_segment.id) {
- TRACEx3(5, "parameter out of range: %d, %d, %d", count, segment, sector);
- result = -EIO;
- } else {
- while (count-- > 0) {
- do { /* count logical sectors */
- do { /* skip until good sector */
- while (mask & 1) { /* skip bad sectors */
- ++sector_nr;
- mask >>= 1;
- }
- if (sector_nr >= 29) {
- if (++segment >= ftape_last_segment.id) {
- TRACEx1(5, "segment out of range: %d", segment);
- result = -EIO;
- break;
- }
- mask = get_bad_sector_entry(segment);
- sector_nr = 0;
- }
- } while (mask & 1);
- ++sector_nr; /* point to good sector */
- mask >>= 1;
- } while (--sector);
- if (result >= 0) {
- TRACEx2(5, "writing filemark %d/%d", segment, sector_nr);
- put_file_mark_in_map(segment, sector_nr);
- ++segment; /* next segment */
- sector_nr = 0;
- sector = 1; /* first sector */
- }
- }
- }
- } else {
- result = -EPERM;
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_erase(void)
-{
- TRACE_FUN(5, "ftape_erase");
- int result = 0;
- int i;
- unsigned long now = 0;
- byte *buffer = deblock_buffer;
-
- if (write_protected) {
- result = -EROFS;
- } else {
- result = read_header_segment(buffer);
- if (result >= 0) {
- /* Copy entries from bad-sector-log into bad-sector-map
- */
- TRACEx1(5, "old label: `%s'", (char *) (buffer + 30));
- if (!ftape_validate_label((char *) &buffer[30])) {
- TRACE(5, "invalid label, overwriting with new");
- memset(buffer + 30, 0, 44);
- memcpy(buffer + 30, linux_tape_label, strlen(linux_tape_label));
- buffer[30 + strlen(linux_tape_label)] = '2';
- TRACEx1(5, "new label: `%s'", (char *) (buffer + 30));
- PUT4(buffer, 74, now);
- if (format_code != 4) {
- for (i = 0; i < nr_of_eof_marks; ++i) {
- unsigned failing_segment = eof_map[i].mark.segment;
-
- if (!valid_segment_no(failing_segment)) {
- TRACEi(4, "bad entry in failed sector log:", failing_segment);
- } else {
- put_bad_sector_entry(failing_segment, EMPTY_SEGMENT);
- TRACEx2(4, "moved entry %d from failed sector log (%d)",
- i, failing_segment);
- }
- }
- }
- }
- /* Clear failed sector log: remove all tape marks
- */
- failed_sector_log_changed = 1;
- memset(eof_map, 0, sizeof(eof_map));
- nr_of_eof_marks = 0;
- ftape_fmt_version = max_fmt_version;
-#if 0
- fix_tape(buffer); /* see ftape-bsm.c ! */
-#endif
- result = ftape_update_header_segments(buffer, 1);
- prevent_flush(); /* prevent flush_buffers writing file marks */
- reset_eof_list();
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-void extract_file_marks(byte * address)
-{
- TRACE_FUN(8, "extract_file_marks");
- int i;
-
- if (format_code == 4) {
- byte *end;
- byte *start = find_end_of_bsm_list(address + 256,
- address + 29 * SECTOR_SIZE);
-
- memset(eof_map, 0, sizeof(eof_map));
- nr_of_eof_marks = 0;
- if (start) {
- start += 3; /* skip end of list mark */
- end = find_end_of_eof_list(start, address + 29 * SECTOR_SIZE);
- if (end && end - start <= sizeof(eof_map)) {
- nr_of_eof_marks = (end - start) / sizeof(unsigned long);
- memcpy(eof_map, start, end - start);
- } else {
- TRACE(1, "File Mark List is too long or damaged !");
- }
- } else {
- TRACE(1, "Bad Sector List is too long or damaged !");
- }
- } else {
- memcpy(eof_map, address + 256, sizeof(eof_map));
- nr_of_eof_marks = GET2(address, 144);
- }
- TRACEi(4, "number of file marks:", nr_of_eof_marks);
- if (ftape_fmt_version == 1) {
- TRACE(-1, "swapping version 1 fields");
- /* version 1 format uses swapped sector and segment fields, correct that !
- */
- for (i = 0; i < nr_of_eof_marks; ++i) {
- unsigned short tmp = eof_map[i].mark.segment;
- eof_map[i].mark.segment = eof_map[i].mark.sector;
- eof_map[i].mark.sector = tmp;
- }
- }
- for (i = 0; i < nr_of_eof_marks; ++i) {
- TRACEx2(4, "eof mark: %5d/%2d",
- eof_map[i].mark.segment, eof_map[i].mark.sector);
- }
- reset_eof_list();
- TRACE_EXIT;
-}
-
-int update_failed_sector_log(byte * buffer)
-{
- TRACE_FUN(8, "update_failed_sector_log");
-
- if (ftape_fmt_version != 0 && failed_sector_log_changed) {
- if (ftape_fmt_version == 1) {
- TRACE(-1, "upgrading version 1 format to version 2");
- /* version 1 will be upgraded to version 2 when written.
- */
- buffer[30 + strlen(linux_tape_label)] = '2';
- ftape_fmt_version = 2;
- TRACEx1(-1, "new tape label = \"%s\"", &buffer[30]);
- }
- if (format_code == 4) {
- byte *dest = find_end_of_bsm_list(buffer + 256,
- buffer + 29 * SECTOR_SIZE) + 3;
-
- if (dest) {
- TRACEx2(4, "eof_map at byte offset %6d, size %d",
- dest - buffer - 256, nr_of_eof_marks * sizeof(unsigned long));
- memcpy(dest, eof_map, nr_of_eof_marks * sizeof(unsigned long));
- PUT4(dest, nr_of_eof_marks * sizeof(unsigned long), 0);
- }
- } else {
- memcpy(buffer + 256, eof_map, sizeof(eof_map));
- PUT2(buffer, 144, nr_of_eof_marks);
- }
- failed_sector_log_changed = 0;
- return 1;
- }
- TRACE_EXIT;
- return 0;
-}
-
-int ftape_seek_eom(void)
-{
- TRACE_FUN(5, "ftape_seek_eom");
- int result = 0;
- unsigned eom;
-
- if (first_data_segment == -1) {
- result = read_header_segment(deblock_buffer);
- }
- if (result >= 0 && ftape_fmt_version != 0) {
- eom = first_data_segment;
- eof_index = 0;
- eof_mark_ptr = &eof_map[0].mark;
- /* If fresh tape, count should be zero but we don't
- * want to worry about the case it's one.
- */
- for (eof_index = 1, eof_mark_ptr = &eof_map[1].mark;
- eof_index < nr_of_eof_marks; ++eof_index, ++eof_mark_ptr) {
- /* The eom is recorded as two eof marks in succeeding segments
- * where the second one is always at segment number 1.
- */
- if (eof_mark_ptr->sector == 1) {
- if (eof_mark_ptr->segment == (eof_mark_ptr - 1)->segment + 1) {
- eom = eof_mark_ptr->segment;
- break;
- }
- }
- }
- ftape_seg_pos = eom;
- TRACEx1(5, "eom found at segment %d", eom);
- } else {
- TRACE(5, "Couldn't get eof mark table");
- result = -EIO;
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_seek_eof(unsigned count)
-{
- TRACE_FUN(5, "ftape_seek_eof");
- int result = 0;
- enum {
- not = 0, begin, end
- } bad_seek = not;
-
- if (first_data_segment == -1) {
- result = read_header_segment(deblock_buffer);
- }
- TRACEx1(5, "tape positioned at segment %d", ftape_seg_pos);
- if (ftape_fmt_version == 0) {
- result = -1;
- }
- if (result >= 0 && count != 0) {
- for (eof_index = 0; eof_index <= nr_of_eof_marks; ++eof_index) {
- if (eof_index == nr_of_eof_marks || /* start seeking after last mark */
- ftape_seg_pos <= eof_map[eof_index].mark.segment) {
- eof_index += count;
- if (eof_index < 1) { /* begin of tape */
- ftape_seg_pos = first_data_segment;
- if (eof_index < 0) { /* `before' begin of tape */
- eof_index = 0;
- bad_seek = begin;
- }
- } else if (eof_index >= nr_of_eof_marks) { /* `after' end of tape */
- ftape_seg_pos = segments_per_track * tracks_per_tape;
- if (eof_index > nr_of_eof_marks) {
- eof_index = nr_of_eof_marks;
- bad_seek = end;
- }
- } else { /* after requested file mark */
- ftape_seg_pos = eof_map[eof_index - 1].mark.segment + 1;
- }
- eof_mark_ptr = &eof_map[eof_index].mark;
- break;
- }
- }
- }
- if (result < 0) {
- TRACE(5, "Couldn't get eof mark table");
- result = -EIO;
- } else if (bad_seek != not) {
- TRACEx1(1, "seek reached %s of tape",
- (bad_seek == begin) ? "begin" : "end");
- result = -EIO;
- } else {
- TRACEx1(5, "tape repositioned to segment %d", ftape_seg_pos);
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_file_no(daddr_t * f_no, daddr_t * b_no)
-{
- TRACE_FUN(5, "ftape_file_no");
- int result = 0;
- int i;
-
- *f_no = eof_index;
- *b_no = ftape_seg_pos;
- TRACEi(4, "number of file marks:", nr_of_eof_marks);
- for (i = 0; i < nr_of_eof_marks; ++i) {
- TRACEx2(4, "eof mark: %5d/%2d",
- eof_map[i].mark.segment, eof_map[i].mark.sector);
- }
- TRACE_EXIT;
- return result;
-}
diff --git a/drivers/char/ftape/ftape-io.c b/drivers/char/ftape/ftape-io.c
deleted file mode 100644
index 3957bc8ab..000000000
--- a/drivers/char/ftape/ftape-io.c
+++ /dev/null
@@ -1,1050 +0,0 @@
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute 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.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-io.c,v $
- $Author: bas $
- *
- $Revision: 1.58 $
- $Date: 1995/05/27 08:54:21 $
- $State: Beta $
- *
- * This file contains the general control functions
- * for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/ftape.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/ioctl.h>
-#include <linux/mtio.h>
-
-#include "tracing.h"
-#include "fdc-io.h"
-#include "qic117.h"
-#include "ftape-io.h"
-#include "ftape-ctl.h"
-#include "ftape-rw.h"
-#include "ftape-write.h"
-#include "ftape-read.h"
-#include "ftape-eof.h"
-#include "kernel-interface.h"
-#include "calibr.h"
-
-/* Global vars.
- */
-/* NOTE: sectors start numbering at 1, all others at 0 ! */
-timeout_table timeout;
-vendor_struct drive_type;
-int qic_std;
-int tape_len;
-volatile int current_command;
-const struct qic117_command_table qic117_cmds[] = QIC117_COMMANDS;
-int might_be_off_track;
-
-/* Local vars.
- */
-static int command_parameter = 0;
-/* command-restrictions is a table according
- * to the QIC-117 specs specifying the state
- * the drive status should be in at command execution.
- */
-static const ftape_error ftape_errors[] = QIC117_ERRORS;
-static int ftape_udelay_count;
-static int ftape_udelay_time;
-static const struct {
- char *text;
- int fdc_code;
- byte drive_code;
- int precomp;
-} rates[4] = {
-
-#if defined(FDC_82078SL)
- {
- "2 M", -1 /* unsupported */ , QIC_CONFIG_RATE_2000, 0
- },
-#else
- {
- "2 M", fdc_data_rate_2000, QIC_CONFIG_RATE_2000, 0
- },
-#endif
- {
- "1 M", fdc_data_rate_1000, QIC_CONFIG_RATE_1000, 42
- },
- {
- "500 K", fdc_data_rate_500, QIC_CONFIG_RATE_500, 125
- },
- {
- "250 K", fdc_data_rate_250, QIC_CONFIG_RATE_250, 250
- },
-};
-typedef enum {
- prehistoric, pre_qic117c, post_qic117b, post_qic117d
-} qic_model;
-
-
-void udelay(int usecs)
-{
- volatile int count = (1 + (usecs * ftape_udelay_count - 1) /
- ftape_udelay_time);
- volatile int i;
-
- while (count-- > 0) {
- for (i = 0; i < 20; ++i);
- }
-}
-
-int udelay_calibrate(void)
-{
- return calibrate("udelay", udelay, &ftape_udelay_count, &ftape_udelay_time);
-}
-
-/* Delay (msec) routine.
- */
-void ftape_sleep(unsigned int time)
-{
- TRACE_FUN(8, "ftape_sleep");
- unsigned long flags;
- int ticks = 1 + (time + MSPT - 1) / MSPT;
-
- /* error in range [0..1] MSPT
- */
- if (time < MSPT) {
- /* Time too small for scheduler, do a busy wait ! */
- udelay(1000 * time);
- } else {
- TRACEx2(8, "%d msec, %d ticks", time, ticks);
- current->timeout = jiffies + ticks;
- current->state = TASK_INTERRUPTIBLE;
- save_flags(flags);
- sti();
- do {
- while (current->state != TASK_RUNNING) {
- schedule();
- }
- if (current->signal & ~current->blocked) {
- TRACE(1, "awoken by non-blocked signal :-(");
- break; /* exit on signal */
- }
- } while (current->timeout > 0);
- restore_flags(flags);
- }
- TRACE_EXIT;
-}
-
-/* forward */ int ftape_report_raw_drive_status(int *status);
-
-/* Issue a tape command:
- * Generate command # of step pulses.
- */
-int ftape_command(int command)
-{
- TRACE_FUN(8, "ftape_command");
- int result = 0;
- int track;
- int old_tracing = tracing;
- static int level = 0;
- int status = -1;
-
- if (++level > 5) {
- /* This is a bug we'll want to know about.
- */
- TRACEx1(1, "bug - recursion for command: %d", command);
- result = -EIO;
- } else if (command_parameter) {
- /* Don't check restrictions for parameters.
- */
- TRACEx1(5, "called with parameter = %d", command - 2);
- } else if (command <= 0 || command > NR_ITEMS(qic117_cmds)) {
- /* This is a bug we'll want to know about too.
- */
- TRACEx1(-1, "bug - bad command: %d", command);
- result = -EIO;
- } else {
- /* disable logging and restriction check for some commands,
- * check all other commands that have a prescribed starting status.
- */
- if (command == QIC_REPORT_DRIVE_STATUS) {
- TRACE(8, "report drive status called");
- tracing = 0;
- } else if (command == QIC_REPORT_NEXT_BIT) {
- tracing = 0;
- } else {
- TRACEx1(5, "%s", qic117_cmds[command].name);
- /* A new motion command during an uninterruptible (motion)
- * command requires a ready status before the new command
- * can be issued. Otherwise a new motion command needs to
- * be checked against required status.
- */
- if (qic117_cmds[command].cmd_type == motion &&
- qic117_cmds[current_command].non_intr) {
- ftape_report_raw_drive_status(&status);
- if ((status & QIC_STATUS_READY) == 0) {
- TRACEx2(4, "motion cmd (%d) during non-intr cmd (%d)",
- command, current_command);
- TRACE(4, "waiting until drive gets ready");
- ftape_ready_wait(timeout.seek, &status);
- }
- }
- if (qic117_cmds[command].mask != 0) {
- byte difference;
-
- /* Some commands do require a certain status:
- */
- if (status == -1) { /* not yet set */
- ftape_report_raw_drive_status(&status);
- }
- difference = ((status ^ qic117_cmds[command].state) &
- qic117_cmds[command].mask);
- /* Wait until the drive gets ready. This may last forever
- * if the drive never gets ready...
- */
- while ((difference & QIC_STATUS_READY) != 0) {
- TRACEx1(4, "command %d issued while not ready", command);
- TRACE(4, "waiting until drive gets ready");
- ftape_ready_wait(timeout.seek, &status);
- difference = ((status ^ qic117_cmds[command].state) &
- qic117_cmds[command].mask);
- /* Bail out on signal !
- */
- if (current->signal & _DONT_BLOCK) {
- result = -EINTR;
- break;
- }
- }
- while (result == 0 && (difference & QIC_STATUS_ERROR) != 0) {
- int err;
- int cmd;
-
- TRACEx1(4, "command %d issued while error pending", command);
- TRACE(4, "clearing error status");
- ftape_report_error(&err, &cmd, 1);
- ftape_report_raw_drive_status(&status);
- difference = ((status ^ qic117_cmds[command].state) &
- qic117_cmds[command].mask);
- /* Bail out on fatal signal !
- */
- if (current->signal & _DONT_BLOCK) {
- result = -EINTR;
- break;
- }
- }
- if (result == 0 && difference) {
- /* Any remaining difference can't be solved here.
- */
- if (difference & (QIC_STATUS_CARTRIDGE_PRESENT |
- QIC_STATUS_NEW_CARTRIDGE |
- QIC_STATUS_REFERENCED)) {
- TRACE(1, "Fatal: tape removed or reinserted !");
- ftape_failure = 1;
- } else {
- TRACEx2(1, "wrong state: 0x%02x should be: 0x%02x",
- status & qic117_cmds[command].mask,
- qic117_cmds[command].state);
- }
- result = -EIO;
- }
- if (~status & QIC_STATUS_READY & qic117_cmds[command].mask) {
- TRACE(1, "Bad: still busy!");
- result = -EBUSY;
- }
- }
- }
- }
- tracing = old_tracing;
- /* Now all conditions are met or result is < 0.
- */
- if (result >= 0) {
- /* Always wait for a command_timeout period to separate
- * individuals commands and/or parameters.
- */
- ftape_sleep(3 * MILLISECOND);
- /* Keep cylinder nr within range, step towards home if possible.
- */
- if (current_cylinder >= command) {
- track = current_cylinder - command;
- } else {
- track = current_cylinder + command;
- }
- result = fdc_seek(track);
- /* position is no longer valid after any of these commands
- * have completed.
- */
- if (qic117_cmds[command].cmd_type == motion &&
- command != QIC_LOGICAL_FORWARD && command != QIC_STOP_TAPE) {
- location.known = 0;
- }
- command_parameter = 0; /* always turned off for next command */
- current_command = command;
- }
- --level;
- TRACE_EXIT;
- return result;
-}
-
-/* Send a tape command parameter:
- * Generates command # of step pulses.
- * Skips tape-status call !
- */
-int ftape_parameter(int command)
-{
- command_parameter = 1;
- return ftape_command(command + 2);
-}
-
-/* Wait for the drive to get ready.
- * timeout time in milli-seconds
- * Returned status is valid if result != -EIO
- */
-int ftape_ready_wait(int timeout, int *status)
-{
- TRACE_FUN(8, "ftape_ready_wait");
- int result;
- unsigned long t0;
- const int poll_delay = 100 * MILLISECOND;
-
- for (;;) {
- t0 = jiffies;
- result = ftape_report_raw_drive_status(status);
- if (result < 0) {
- TRACE(1, "ftape_report_raw_drive_status failed");
- result = -EIO;
- break;
- }
- if (*status & QIC_STATUS_READY) {
- result = 0;
- break;
- }
- if (timeout >= 0) {
- /* this will fail when jiffies wraps around about
- * once every year :-)
- */
- timeout -= ((jiffies - t0) * SECOND) / HZ;
- if (timeout <= 0) {
- TRACE(1, "timeout");
- result = -ETIME;
- break;
- }
- ftape_sleep(poll_delay);
- timeout -= poll_delay;
- } else {
- ftape_sleep(poll_delay);
- }
- if (current->signal & _NEVER_BLOCK) {
- TRACE(1, "interrupted by fatal signal");
- result = -EINTR;
- break; /* exit on signal */
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-/* Issue command and wait up to timeout seconds for drive ready
- */
-int ftape_command_wait(int command, int timeout, int *status)
-{
- TRACE_FUN(8, "ftape_command_wait");
- int result;
-
- /* Drive should be ready, issue command
- */
- result = ftape_command(command);
- if (result >= 0) {
- result = ftape_ready_wait(timeout, status);
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_parameter_wait(int command, int timeout, int *status)
-{
- TRACE_FUN(8, "ftape_parameter_wait");
- int result;
-
- /* Drive should be ready, issue command
- */
- result = ftape_parameter(command);
- if (result >= 0) {
- result = ftape_ready_wait(timeout, status);
- }
- TRACE_EXIT;
- return result;
-}
-
-/*--------------------------------------------------------------------------
- * Report operations
- */
-
-/* Query the drive about its status. The command is sent and
- result_length bits of status are returned (2 extra bits are read
- for start and stop). */
-
-static int ftape_report_operation(int *status, int command, int result_length)
-{
- TRACE_FUN(8, "ftape_report_operation");
- int i, st3;
- int result;
- unsigned int t0, t1, dt;
-
- result = ftape_command(command);
- if (result < 0) {
- TRACE(1, "ftape_command failed");
- TRACE_EXIT;
- return result;
- }
- t0 = timestamp();
- dt = 0;
- i = 0;
- do {
- ++i;
- ftape_sleep(3 * MILLISECOND); /* see remark below */
- result = fdc_sense_drive_status(&st3);
- if (result < 0) {
- TRACE(1, "fdc_sense_drive_status failed");
- TRACE_EXIT;
- return result;
- }
- /* Calculate time difference every iteration because timer may
- * wrap around (but only one !) and timediff will account for this.
- * Note that the sleep above must be < 1/HZ or we'll lose ticks !
- */
- t1 = timestamp();
- dt += timediff(t0, t1);
- t0 = t1;
- /* Ack should be asserted within Ttimout + Tack = 6 msec.
- * Looks like some drives fail to do this so extend this
- * period to 300 msec.
- */
- } while (!(st3 & ST3_TRACK_0) && dt < 300000);
- if (st3 & ST3_TRACK_0) {
- /* dt may be larger than expected because of other tasks
- * scheduled while we were sleeping.
- */
- if (i > 1 && dt > 6000) {
- TRACEx2(1, "Acknowledge after %u msec. (%i iter)", dt / 1000, i);
- }
- } else {
- TRACEx2(1, "No acknowledge after %u msec. (%i iter)", dt / 1000, i);
- TRACE(1, "timeout on Acknowledge");
- TRACE_EXIT;
- return -EIO;
- }
- *status = 0;
- for (i = 0; i < result_length + 1; i++) {
- result = ftape_command(QIC_REPORT_NEXT_BIT);
- if (result < 0) {
- TRACE(1, "report next bit failed");
- TRACE_EXIT;
- return result;
- }
-#if 1
- /* fdc_seek does interrupt wait, so why should we ?
- * (it will only fail causing fdc to be reset...)
- * It's only purpose may be the delay, we'll have to find out!
- */
-#else
- fdc_interrupt_wait(25 * MILLISECOND); /* fails only if hw fails */
-#endif
- result = fdc_sense_drive_status(&st3);
- if (result < 0) {
- TRACE(1, "fdc_sense_drive_status (2) failed");
- TRACE_EXIT;
- return result;
- }
- if (i < result_length) {
- *status |= ((st3 & ST3_TRACK_0) ? 1 : 0) << i;
- } else {
- if ((st3 & ST3_TRACK_0) == 0) {
- TRACE(1, "missing status stop bit");
- TRACE_EXIT;
- return -EIO;
- }
- }
- }
- /* this command will put track zero and index back into normal state */
- result = ftape_command(QIC_REPORT_NEXT_BIT);
- TRACE_EXIT;
- return 0;
-}
-
-/* Report the current drive status. */
-
-int ftape_report_raw_drive_status(int *status)
-{
- TRACE_FUN(8, "ftape_report_raw_drive_status");
- int result;
- int count = 0;
-
- do {
- result = ftape_report_operation(status, QIC_REPORT_DRIVE_STATUS, 8);
- } while (result < 0 && ++count <= 3);
- if (result < 0) {
- TRACE(1, "report_operation failed");
- result = -EIO;
- } else if (*status & QIC_STATUS_READY) {
- current_command = 0; /* completed */
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_report_drive_status(int *status)
-{
- TRACE_FUN(8, "ftape_report_drive_status");
- int result;
-
- result = ftape_report_raw_drive_status(status);
- if (result < 0) {
- TRACE(1, "ftape_report_raw_drive_status failed");
- TRACE_EXIT;
- return result;
- }
- if (*status & QIC_STATUS_NEW_CARTRIDGE ||
- !(*status & QIC_STATUS_CARTRIDGE_PRESENT)) {
- ftape_failure = 1; /* will inhibit further operations */
- TRACE_EXIT;
- return -EIO;
- }
- if (*status & QIC_STATUS_READY && *status & QIC_STATUS_ERROR) {
- /* Let caller handle all errors */
- TRACE(2, "warning: error status set!");
- result = 1;
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_report_error(int *error, int *command, int report)
-{
- TRACE_FUN(8, "ftape_report_error");
- int code;
- int result;
-
- result = ftape_report_operation(&code, QIC_REPORT_ERROR_CODE, 16);
- if (result < 0) {
- result = -EIO;
- } else {
- *error = code & 0xff;
- *command = (code >> 8) & 0xff;
- if (report) {
- if (*error != 0) {
- TRACEi(3, "errorcode:", *error);
- } else {
- TRACE(3, "No error");
- }
- }
- if (report && *error != 0 && tracing > 3) {
- if (*error >= 0 && *error < NR_ITEMS(ftape_errors)) {
- TRACEx1(-1, "%sFatal ERROR:",
- (ftape_errors[*error].fatal ? "" : "Non-"));
- TRACEx1(-1, "%s ...", ftape_errors[*error].message);
- } else {
- TRACE(-1, "Unknown ERROR !");
- }
- if (*command >= 0 && *command < NR_ITEMS(qic117_cmds) &&
- qic117_cmds[*command].name != NULL) {
- TRACEx1(-1, "... caused by command \'%s\'",
- qic117_cmds[*command].name);
- } else {
- TRACEi(-1, "... caused by unknown command", *command);
- }
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_in_error_state(int status)
-{
- TRACE_FUN(8, "ftape_in_error_state");
- int result = 0;
-
- if ((status & QIC_STATUS_READY) && (status & QIC_STATUS_ERROR)) {
- TRACE(2, "warning: error status set!");
- result = 1;
- }
- TRACE_EXIT;
- return result;
-}
-
-static int ftape_report_configuration(qic_model * model, int *rate,
- int *qic_std, int *tape_len)
-{
- int result;
- int config;
- int status;
-
- TRACE_FUN(8, "ftape_report_configuration");
- result = ftape_report_operation(&config, QIC_REPORT_DRIVE_CONFIGURATION, 8);
- if (result < 0) {
- *model = prehistoric;
- *rate = QIC_CONFIG_RATE_500;
- *qic_std = QIC_TAPE_QIC40;
- *tape_len = 205;
- result = 0;
- } else {
- *rate = (config & QIC_CONFIG_RATE_MASK) >> QIC_CONFIG_RATE_SHIFT;
- result = ftape_report_operation(&status, QIC_REPORT_TAPE_STATUS, 8);
- if (result < 0) {
- /* pre- QIC117 rev C spec. drive, QIC_CONFIG_80 bit is valid.
- */
- *qic_std = (config & QIC_CONFIG_80) ? QIC_TAPE_QIC80 : QIC_TAPE_QIC40;
- *tape_len = (config & QIC_CONFIG_LONG) ? 307 : 205;
- *model = pre_qic117c;
- result = 0;
- } else {
- *model = post_qic117b;
- TRACEx1(8, "report tape status result = %02x", status);
- /* post- QIC117 rev C spec. drive, QIC_CONFIG_80 bit is invalid.
- */
- switch (status & QIC_TAPE_STD_MASK) {
- case QIC_TAPE_QIC40:
- case QIC_TAPE_QIC80:
- case QIC_TAPE_QIC3020:
- case QIC_TAPE_QIC3010:
- *qic_std = status & QIC_TAPE_STD_MASK;
- break;
- default:
- *qic_std = -1;
- break;
- }
- switch (status & QIC_TAPE_LEN_MASK) {
- case QIC_TAPE_205FT:
- /* Unfortunately the new QIC-117 rev G standard shows
- * no way to discriminate between 205 and 425 ft tapes.
- * The obvious way seems not to be used: the QIC_CONFIG_LONG
- * bit isn't used for this (on all drives ?).
- */
- if (config & QIC_CONFIG_LONG) {
- *tape_len = 425; /* will this ever execute ??? */
- } else {
- *tape_len = 0; /* length unknown: 205 or 425 ft. */
- }
- break;
- case QIC_TAPE_307FT:
- *tape_len = 307;
- break;
- case QIC_TAPE_400FT:
- /*
- * Trouble! Iomega Ditto 800 and Conner TST800R drives reports
- * 400ft for 750ft tapes. Yuck, yuck, yuck. Since the value
- * is only used to compute a timeout value, the largest of the
- * two is used.
- */
- *tape_len = 750; /* either 400 or 750 ft. */
- break;
- case QIC_TAPE_1100FT:
- *tape_len = 1100;
- break;
- case QIC_TAPE_FLEX:
- *tape_len = 0;
- break;
- default:
- *tape_len = -1;
- break;
- }
- if (*qic_std == -1 || *tape_len == -1) {
- TRACE(2, "post qic-117b spec drive with unknown tape");
- result = -EIO;
- } else {
- result = 0;
- }
- }
- }
- TRACE_EXIT;
- return (result < 0) ? -EIO : 0;
-}
-
-int ftape_report_rom_version(int *version)
-{
- int result;
-
- result = ftape_report_operation(version, QIC_REPORT_ROM_VERSION, 8);
- return (result < 0) ? -EIO : 0;
-}
-
-int ftape_report_signature(int *signature)
-{
- int result;
-
- result = ftape_command(28);
- result = ftape_report_operation(signature, 9, 8);
- result = ftape_command(30);
- return (result < 0) ? -EIO : 0;
-}
-
-void ftape_report_vendor_id(unsigned int *id)
-{
- TRACE_FUN(8, "ftape_report_vendor_id");
- int result;
-
- /*
- * We'll try to get a vendor id from the drive.
- * First according to the QIC-117 spec, a 16-bit id is requested.
- * If that fails we'll try an 8-bit version, otherwise we'll try
- * an undocumented query.
- */
- result = ftape_report_operation((int *) id, QIC_REPORT_VENDOR_ID, 16);
- if (result < 0) {
- result = ftape_report_operation((int *) id, QIC_REPORT_VENDOR_ID, 8);
- if (result < 0) {
- /* The following is an undocumented call found in the CMS code.
- */
- result = ftape_report_operation((int *) id, 24, 8);
- if (result < 0) {
- *id = UNKNOWN_VENDOR;
- } else {
- TRACEx1(4, "got old 8 bit id: %04x", *id);
- *id |= 0x20000;
- }
- } else {
- TRACEx1(4, "got 8 bit id: %04x", *id);
- *id |= 0x10000;
- }
- } else {
- TRACEx1(4, "got 16 bit id: %04x", *id);
- }
- if (*id == 0x0047) {
- int version;
- int sign;
-
- result = ftape_report_rom_version(&version);
- if (result < 0) {
- TRACE(-1, "report rom version failed");
- TRACE_EXIT;
- return;
- }
- TRACEx1(4, "CMS rom version: %d", version);
- ftape_command(QIC_ENTER_DIAGNOSTIC_1);
- ftape_command(QIC_ENTER_DIAGNOSTIC_1);
- result = ftape_report_operation(&sign, 9, 8);
- if (result < 0) {
- int error, command;
-
- ftape_report_error(&error, &command, 1);
- ftape_command(QIC_ENTER_PRIMARY_MODE);
- TRACE_EXIT;
- return; /* faalt hier ! */
- } else {
- TRACEx1(4, "CMS signature: %02x", sign);
- }
- if (sign == 0xa5) {
- result = ftape_report_operation(&sign, 37, 8);
- if (result < 0) {
- if (version >= 63) {
- *id = 0x8880;
- TRACE(4, "This is an Iomega drive !");
- } else {
- *id = 0x0047;
- TRACE(4, "This is a real CMS drive !");
- }
- } else {
- *id = 0x0047;
- TRACEx1(4, "CMS status: %d", sign);
- }
- } else {
- *id = UNKNOWN_VENDOR;
- }
- ftape_command(QIC_ENTER_PRIMARY_MODE);
- }
- TRACE_EXIT;
-}
-
-void ftape_set_rate_test(int *supported)
-{
- TRACE_FUN(8, "ftape_set_rate_test");
- int error;
- int command;
- int i;
- int result;
- int status;
-
- /* Check if the drive does support the select rate command by testing
- * all different settings.
- * If any one is accepted we assume the command is supported, else not.
- */
- *supported = 0;
- for (i = 0; i < NR_ITEMS(rates); ++i) {
- result = ftape_command(QIC_SELECT_RATE);
- if (result >= 0) {
- result = ftape_parameter_wait(rates[i].drive_code,
- 1 * SECOND, &status);
- if (result >= 0) {
- if (status & QIC_STATUS_ERROR) {
- result = ftape_report_error(&error, &command, 0);
- } else {
- *supported = 1; /* did accept a request */
- }
- }
- }
- }
- TRACEx1(4, "Select Rate command is%s supported",
- *supported ? "" : " not");
- TRACE_EXIT;
-}
-
-int ftape_set_data_rate(int new_rate)
-{
- TRACE_FUN(8, "ftape_set_data_rate");
- int status;
- int result;
- int data_rate;
- qic_model model;
- int supported;
- static int first_time = 1;
-
- if (first_time) {
- ftape_set_rate_test(&supported);
- first_time = 0;
- }
- if (rates[new_rate].fdc_code == -1) {
- TRACEx1(4, "%sb/s data rate not supported by the fdc",
- rates[new_rate].text);
- result = -EINVAL;
- } else {
- int error = 0;
- int command;
-
- result = ftape_command(QIC_SELECT_RATE);
- if (result >= 0) {
- result = ftape_parameter_wait(rates[new_rate].drive_code,
- 1 * SECOND, &status);
- result = ftape_report_raw_drive_status(&status);
- if (result >= 0 && (status & QIC_STATUS_ERROR)) {
- result = ftape_report_error(&error, &command, 0);
- if (result >= 0 && supported &&
- error == 31 && command == QIC_SELECT_RATE) {
- result = -EINVAL;
- }
- }
- }
- if (result >= 0) {
- result = ftape_report_configuration(&model, &data_rate,
- &qic_std, &tape_len);
- if (result >= 0 && data_rate != rates[new_rate].drive_code) {
- result = -EINVAL;
- }
- }
- if (result < 0) {
- TRACEx1(4, "could not set %sb/s data rate", rates[new_rate].text);
- } else {
- TRACEx2(2, "%s drive @ %sb/s",
- (model == prehistoric) ? "prehistoric" :
- ((model == pre_qic117c) ? "pre QIC-117C" :
- ((model == post_qic117b) ? "post QIC-117B" : "post QIC-117D")),
- rates[new_rate].text);
- if (tape_len == 0) {
- TRACEx1(2, "unknown length QIC-%s tape",
- (qic_std == QIC_TAPE_QIC40) ? "40" :
- ((qic_std == QIC_TAPE_QIC80) ? "80" :
- ((qic_std == QIC_TAPE_QIC3010) ? "3010" : "3020")));
- } else {
- TRACEx2(2, "%d ft. QIC-%s tape",
- tape_len,
- (qic_std == QIC_TAPE_QIC40) ? "40" :
- ((qic_std == QIC_TAPE_QIC80) ? "80" :
- ((qic_std == QIC_TAPE_QIC3010) ? "3010" : "3020")));
- }
- /*
- * Set data rate and write precompensation as specified:
- *
- * | QIC-40/80 | QIC-3010/3020
- * rate | precomp | precomp
- * ----------+-------------+--------------
- * 250 Kbps. | 250 ns. | 0 ns.
- * 500 Kbps. | 125 ns. | 0 ns.
- * 1 Mbps. | 42 ns. | 0 ns.
- * 2 Mbps | N/A | 0 ns.
- */
- if (qic_std == QIC_TAPE_QIC40 || qic_std == QIC_TAPE_QIC80) {
- fdc_set_write_precomp(rates[new_rate].precomp);
- } else {
- fdc_set_write_precomp(0);
- }
- fdc_set_data_rate(rates[new_rate].fdc_code);
- ftape_data_rate = new_rate; /* store rate set */
- }
- }
- if (result < 0 && result != -EINVAL) {
- result = -EIO;
- }
- TRACE_EXIT;
- return result;
-}
-
-/* Seek the head to the specified track.
- */
-int ftape_seek_head_to_track(int track)
-{
- TRACE_FUN(8, "ftape_seek_head_to_track");
- int status;
- int result;
-
- location.track = -1; /* remains set in case of error */
- if (track < 0 || track >= tracks_per_tape) {
- TRACE(-1, "track out of bounds");
- result = -EINVAL;
- } else {
- TRACEx1(5, "seeking track %d", track);
- result = ftape_command(QIC_SEEK_HEAD_TO_TRACK);
- if (result < 0) {
- TRACE(1, "ftape_command failed");
- } else {
- result = ftape_parameter_wait(track, timeout.head_seek, &status);
- if (result < 0) {
- TRACE(1, "ftape_parameter_wait failed");
- } else {
- location.track = track;
- might_be_off_track = 0;
- }
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_wakeup_drive(wake_up_types method)
-{
- TRACE_FUN(8, "ftape_wakeup_drive");
- int result;
- int status;
- int motor_on = 0;
-
- switch (method) {
- case wake_up_colorado:
- result = ftape_command(QIC_PHANTOM_SELECT);
- if (result == 0) {
- result = ftape_parameter( /* unit */ 0);
- }
- break;
- case wake_up_mountain:
- result = ftape_command(QIC_SOFT_SELECT);
- if (result == 0) {
- ftape_sleep(MILLISECOND); /* NEEDED */
- result = ftape_parameter(18);
- }
- break;
- case wake_up_insight:
- ftape_sleep(100 * MILLISECOND);
- motor_on = 1;
- fdc_motor(motor_on); /* enable is done by motor-on */
- case no_wake_up:
- result = 0;
- break;
- default:
- result = -ENODEV; /* unknown wakeup method */
- }
- /* If wakeup succeeded we shouldn't get an error here..
- */
- if (result == 0) {
- result = ftape_report_raw_drive_status(&status);
- if (result < 0 && motor_on) {
- fdc_motor(0); /* motor off if failed */
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_put_drive_to_sleep(vendor_struct drive_type)
-{
- TRACE_FUN(8, "ftape_put_drive_to_sleep");
- int result;
-
- switch (drive_type.wake_up) {
- case wake_up_colorado:
- result = ftape_command(QIC_PHANTOM_DESELECT);
- break;
- case wake_up_mountain:
- result = ftape_command(QIC_SOFT_DESELECT);
- break;
- case wake_up_insight:
- fdc_motor(0); /* enable is done by motor-on */
- case no_wake_up: /* no wakeup / no sleep ! */
- result = 0;
- break;
- default:
- result = -ENODEV; /* unknown wakeup method */
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_reset_drive(void)
-{
- TRACE_FUN(8, "ftape_reset_drive");
- int result = 0;
- int status;
- int err_code;
- int err_command;
- int i;
-
- /* We want to re-establish contact with our drive.
- * Fire a number of reset commands (single step pulses)
- * and pray for success.
- */
- for (i = 0; i < 2; ++i) {
- TRACE(5, "Resetting fdc");
- fdc_reset();
- ftape_sleep(10 * MILLISECOND);
- TRACE(5, "Reset command to drive");
- result = ftape_command(QIC_RESET);
- if (result == 0) {
- ftape_sleep(1 * SECOND); /* drive not accessible during 1 second */
- TRACE(5, "Re-selecting drive");
- /* Strange, the QIC-117 specs don't mention this but the
- * drive gets deselected after a soft reset !
- * So we need to enable it again.
- */
- result = ftape_wakeup_drive(drive_type.wake_up);
- if (result < 0) {
- TRACE(1, "Wakeup failed !");
- }
- TRACE(5, "Waiting until drive gets ready");
- result = ftape_ready_wait(timeout.reset, &status);
- if (result == 0 && status & QIC_STATUS_ERROR) {
- result = ftape_report_error(&err_code, &err_command, 1);
- if (result == 0 && err_code == 27) {
- /* Okay, drive saw reset command and responded as it should
- */
- break;
- } else {
- result = -EIO;
- }
- } else {
- result = -EIO;
- }
- }
- if (current->signal & _DONT_BLOCK) {
- TRACE(1, "aborted by non-blockable signal");
- result = -EINTR;
- break; /* exit on signal */
- }
- }
- if (result != 0) {
- TRACE(1, "General failure to reset tape drive");
- } else {
- /* Restore correct settings
- */
- ftape_set_data_rate(ftape_data_rate); /* keep original rate */
- }
- TRACE_EXIT;
- return result;
-}
diff --git a/drivers/char/ftape/ftape-io.h b/drivers/char/ftape/ftape-io.h
deleted file mode 100644
index 5e105125d..000000000
--- a/drivers/char/ftape/ftape-io.h
+++ /dev/null
@@ -1,77 +0,0 @@
-#ifndef _FTAPE_IO_H
-#define _FTAPE_IO_H
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute 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.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-io.h,v $
- $Author: bas $
- *
- $Revision: 1.36 $
- $Date: 1995/05/06 16:11:53 $
- $State: Beta $
- *
- * This file contains definitions for the glue part
- * of the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include "vendors.h"
-
-typedef struct {
- unsigned seek;
- unsigned reset;
- unsigned rewind;
- unsigned head_seek;
- unsigned stop;
- unsigned pause;
-} timeout_table;
-
-/*
- * ftape-io.c defined global vars.
- */
-extern timeout_table timeout;
-extern int qic_std;
-extern int tape_len;
-extern volatile int current_command;
-extern const struct qic117_command_table qic117_cmds[];
-extern int might_be_off_track;
-
-/*
- * ftape-io.c defined global functions.
- */
-extern void udelay(int usecs);
-extern int udelay_calibrate(void);
-extern void ftape_sleep(unsigned int time);
-extern void ftape_report_vendor_id(unsigned int *id);
-extern int ftape_command(int command);
-extern int ftape_command_wait(int command, int timeout, int *status);
-extern int ftape_report_drive_status(int *status);
-extern int ftape_report_raw_drive_status(int *status);
-extern int ftape_report_status(int *status);
-extern int ftape_interrupt_wait(int time);
-extern int ftape_ready_wait(int timeout, int *status);
-extern int ftape_seek_head_to_track(int track);
-extern int ftape_parameter(int command);
-extern int ftape_in_error_state(int status);
-extern int ftape_set_data_rate(int rate);
-extern int ftape_report_error(int *error, int *command, int report);
-extern int ftape_reset_drive(void);
-extern int ftape_put_drive_to_sleep(vendor_struct drive_type);
-extern int ftape_wakeup_drive(wake_up_types method);
-
-#endif
diff --git a/drivers/char/ftape/ftape-read.c b/drivers/char/ftape/ftape-read.c
deleted file mode 100644
index 23fdab945..000000000
--- a/drivers/char/ftape/ftape-read.c
+++ /dev/null
@@ -1,677 +0,0 @@
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute 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.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-read.c,v $
- $Author: bas $
- *
- $Revision: 1.30 $
- $Date: 1995/05/27 08:54:21 $
- $State: Beta $
- *
- * This file contains the reading code
- * for the QIC-117 floppy-tape driver for Linux.
- */
-
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/ftape.h>
-#include <asm/uaccess.h>
-
-#include "tracing.h"
-#include "ftape-read.h"
-#include "qic117.h"
-#include "ftape-io.h"
-#include "ftape-ctl.h"
-#include "ftape-rw.h"
-#include "ftape-write.h"
-#include "ftape-eof.h"
-#include "ecc.h"
-#include "ftape-bsm.h"
-
-/* Global vars.
- */
-
-/* Local vars.
- */
-int buf_pos_rd = 0;
-int buf_len_rd = 0;
-
-void ftape_zap_read_buffers(void)
-{
- int i;
-
- for (i = 0; i < NR_BUFFERS; ++i) {
- /*
- * changed to "fit" with dynamic allocation of tape_buffer. --khp
- */
- buffer[i].address = tape_buffer[i];
- buffer[i].status = waiting;
- buffer[i].bytes = 0;
- buffer[i].skip = 0;
- buffer[i].retry = 0;
- }
- buf_len_rd = 0;
- buf_pos_rd = 0;
- eof_mark = 0;
- ftape_state = idle;
-}
-
-static unsigned long convert_sector_map(buffer_struct * buff)
-{
- TRACE_FUN(8, "convert_sector_map");
- int i = 0;
- unsigned long bad_map = get_bad_sector_entry(buff->segment_id);
- unsigned long src_map = buff->soft_error_map | buff->hard_error_map;
- unsigned long dst_map = 0;
-
- if (bad_map || src_map) {
- TRACEx1(5, "bad_map = 0x%08lx", bad_map);
- TRACEx1(5, "src_map = 0x%08lx", src_map);
- }
- while (bad_map) {
- while ((bad_map & 1) == 0) {
- if (src_map & 1) {
- dst_map |= (1 << i);
- }
- src_map >>= 1;
- bad_map >>= 1;
- ++i;
- }
- /* (bad_map & 1) == 1 */
- src_map >>= 1;
- bad_map >>= 1;
- }
- if (src_map) {
- dst_map |= (src_map << i);
- }
- if (dst_map) {
- TRACEx1(5, "dst_map = 0x%08lx", dst_map);
- }
- TRACE_EXIT;
- return dst_map;
-}
-
-int correct_and_copy(unsigned int tail, byte * destination)
-{
- TRACE_FUN(8, "correct_and_copy");
- struct memory_segment mseg;
- int result;
- BAD_SECTOR read_bad;
-
- mseg.read_bad = convert_sector_map(&buffer[tail]);
- mseg.marked_bad = 0; /* not used... */
- mseg.blocks = buffer[tail].bytes / SECTOR_SIZE;
- mseg.data = buffer[tail].address;
- /* If there are no data sectors we can skip this segment.
- */
- if (mseg.blocks <= 3) {
- TRACE(4, "empty segment");
- TRACE_EXIT;
- return 0;
- }
- read_bad = mseg.read_bad;
- history.crc_errors += count_ones(read_bad);
- result = ecc_correct_data(&mseg);
- if (read_bad != 0 || mseg.corrected != 0) {
- TRACElx(4, "crc error map:", read_bad);
- TRACElx(4, "corrected map:", mseg.corrected);
- history.corrected += count_ones(mseg.corrected);
- }
- if (result == ECC_CORRECTED || result == ECC_OK) {
- if (result == ECC_CORRECTED) {
- TRACEi(3, "ecc corrected segment:", buffer[tail].segment_id);
- }
- memcpy(destination, mseg.data, (mseg.blocks - 3) * SECTOR_SIZE);
- if ((read_bad ^ mseg.corrected) & mseg.corrected) {
- /* sectors corrected without crc errors set */
- history.crc_failures++;
- }
- TRACE_EXIT;
- return (mseg.blocks - 3) * SECTOR_SIZE;
- } else {
- TRACEi(1, "ecc failure on segment", buffer[tail].segment_id);
- history.ecc_failures++;
- TRACE_EXIT;
- return -EAGAIN; /* should retry */
- }
- TRACE_EXIT;
- return 0;
-}
-
-/* Read given segment into buffer at address.
- */
-int read_segment(unsigned segment_id, byte * address, int *eof_mark,
- int read_ahead)
-{
- TRACE_FUN(5, "read_segment");
- int read_done = 0;
- int result = 0;
- int bytes_read = 0;
- int retry = 0;
-
- TRACEi(5, "segment_id =", segment_id);
- if (ftape_state != reading) {
- if (ftape_state == writing) {
- ftape_flush_buffers(); /* flush write buffer */
- TRACE(5, "calling ftape_abort_operation");
- result = ftape_abort_operation();
- if (result < 0) {
- TRACE(1, "ftape_abort_operation failed");
- TRACE_EXIT;
- return -EIO;
- }
- } else {
- /* clear remaining read buffers */
- ftape_zap_read_buffers();
- }
- ftape_state = reading;
- }
- if (segment_id >= segments_per_track * tracks_per_tape) {
- TRACE(5, "reading past end of tape");
- TRACE_EXIT;
- return -ENOSPC;
- }
- for (;;) {
- /* Search all full buffers for the first matching the wanted segment.
- * Clear other buffers on the fly.
- */
- while (!read_done && buffer[tail].status == done) {
- if (buffer[tail].segment_id == segment_id) {
- unsigned eof_sector;
- unsigned sector_count = 0;
- unsigned long bsm = get_bad_sector_entry(segment_id);
- int i;
-
- /* If out buffer is already full, return its contents.
- */
- if (buffer[tail].deleted) {
- TRACEi(5, "found segment in cache :", segment_id);
- TRACE_EXIT;
- /* Return a value that read_header_segment understands.
- * As this should only occur when searching for the header
- * segments it shouldn't be misinterpreted elsewhere.
- */
- return 0;
- }
- TRACEi(5, "found segment in cache :", segment_id);
- eof_sector = check_for_eof(segment_id);
- if (eof_sector > 0) {
- TRACEi(5, "end of file mark in sector:", eof_sector);
- for (i = 1; i < eof_sector; ++i) {
- if ((bsm & 1) == 0) {
- ++sector_count;
- }
- bsm >>= 1;
- }
- *eof_mark = 1;
- }
- if (eof_sector != 1) { /* not found or gt 1 */
- result = correct_and_copy(tail, address);
- TRACEi(5, "segment contains (bytes) :", result);
- if (result < 0) {
- if (result != -EAGAIN) {
- TRACE_EXIT;
- return result;
- }
- /* keep read_done == 0, will trigger ftape_abort_operation
- * because reading wrong segment.
- */
- TRACE(1, "ecc failed, retry");
- ++retry;
- } else {
- read_done = 1;
- }
- } else {
- read_done = 1;
- }
- if (eof_sector > 0) {
- bytes_read = sector_count * SECTOR_SIZE;
- TRACEi(5, "partial read count:", bytes_read);
- } else {
- bytes_read = result;
- }
- } else {
- TRACEi(5, "zapping segment in cache :", buffer[tail].segment_id);
- }
- buffer[tail].status = waiting;
- next_buffer(&tail);
- }
- if (!read_done && buffer[tail].status == reading) {
- if (buffer[tail].segment_id == segment_id) {
- int result = wait_segment(reading);
- if (result < 0) {
- if (result == -EINTR) {
- TRACE_EXIT;
- return result;
- }
- TRACE(1, "wait_segment failed while reading");
- ftape_abort_operation();
- }
- } else {
- /* We're reading the wrong segment, stop runner.
- */
- ftape_abort_operation();
- }
- }
- /* if just passed the last segment on a track, wait for BOT or EOT mark.
- */
- if (runner_status == logical_eot) {
- int status;
- result = ftape_ready_wait(timeout.seek, &status);
- if (result < 0) {
- TRACE(1, "ftape_ready_wait waiting for eot/bot failed");
- }
- if ((status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) == 0) {
- TRACE(1, "eot/bot not reached");
- }
- runner_status = end_of_tape;
- }
- /* should runner stop ?
- */
- if (runner_status == aborting || runner_status == buffer_overrun ||
- runner_status == end_of_tape) {
- if (runner_status != end_of_tape &&
- !(runner_status == aborting && !tape_running)) {
- ftape_dumb_stop();
- }
- if (runner_status == aborting) {
- if (buffer[head].status == reading || buffer[head].status == error) {
- if (buffer[head].status == error) {
- history.defects += count_ones(buffer[head].hard_error_map);
- }
- buffer[head].status = waiting;
- }
- }
- runner_status = idle; /* aborted ? */
- }
- /* If segment to read is empty, do not start runner for it,
- * but wait for next read call.
- */
- if (get_bad_sector_entry(segment_id) == EMPTY_SEGMENT) {
- bytes_read = 0; /* flag empty segment */
- read_done = 1;
- }
- /* Allow escape from this loop on signal !
- */
- if (current->signal & _DONT_BLOCK) {
- TRACE(2, "interrupted by non-blockable signal");
- TRACE_EXIT;
- return -EINTR;
- }
- /* If we got a segment: quit, or else retry up to limit.
- */
- if (read_done) {
- break;
- }
- if (retry > RETRIES_ON_ECC_ERROR) {
- history.defects++;
- TRACE(1, "too many retries on ecc failure");
- TRACE_EXIT;
- return -ENODATA;
- }
- /* Now at least one buffer is empty !
- * Restart runner & tape if needed.
- */
- TRACEx3(8, "head: %d, tail: %d, runner_status: %d",
- head, tail, runner_status);
- TRACEx2(8, "buffer[].status, [head]: %d, [tail]: %d",
- buffer[head].status, buffer[tail].status);
- if (buffer[tail].status == waiting) {
- setup_new_segment(&buffer[head], segment_id, -1);
- if (!read_ahead) {
- buffer[head].next_segment = 0; /* disable read-ahead */
- }
- calc_next_cluster(&buffer[head]);
- if (runner_status == idle) {
- result = ftape_start_tape(segment_id,
- buffer[head].sector_offset);
- if (result < 0) {
- TRACEx1(1, "Error: segment %d unreachable", segment_id);
- TRACE_EXIT;
- return result;
- }
- runner_status = running;
- }
- buffer[head].status = reading;
- setup_fdc_and_dma(&buffer[head], FDC_READ);
- }
- }
- if (read_done) {
- TRACE_EXIT;
- return bytes_read;
- } else {
- TRACE(1, "too many retries");
- TRACE_EXIT;
- return -EIO;
- }
-}
-
-int read_header_segment(byte * address)
-{
- TRACE_FUN(5, "read_header_segment");
- int i;
- int result;
- int header_segment = -1;
- unsigned int max_floppy_side;
- unsigned int max_floppy_track;
- unsigned int max_floppy_sector;
- int first_failed = 0;
- int status;
- int new_tape_len;
-
- result = ftape_report_drive_status(&status);
- if (result < 0) {
- TRACE(1, "error: error_status or report failure");
- TRACE_EXIT;
- return -EIO;
- }
- TRACE(5, "reading...");
- ftape_last_segment.id = 68; /* will allow us to read the header ! */
- /* We're looking for the first header segment.
- * A header segment cannot contain bad sectors, therefor at the
- * tape start, segments with bad sectors are (according to QIC-40/80)
- * written with deleted data marks and must be skipped.
- */
- used_header_segment = -1;
- result = 0;
- for (header_segment = 0;
- header_segment < ftape_last_segment.id && result == 0;
- ++header_segment) {
- /* Set no read-ahead, the isr will force read-ahead whenever
- * it encounters deleted data !
- */
- result = read_segment(header_segment, address, &status, 0);
- if (result < 0 && !first_failed) {
- TRACE(1, "header segment damaged, trying backup");
- first_failed = 1;
- result = 0; /* force read of next (backup) segment */
- }
- }
- if (result < 0 || header_segment >= ftape_last_segment.id) {
- TRACE(1, "no readable header segment found");
- TRACE_EXIT;
- return -EIO;
- }
- result = ftape_abort_operation();
- if (result < 0) {
- TRACE(1, "ftape_abort_operation failed");
- TRACE_EXIT;
- return -EIO;
- }
- if (GET4(address, 0) != 0xaa55aa55) {
- TRACE(1, "wrong signature in header segment");
- TRACE_EXIT;
- return -EIO;
- }
- header_segment_1 = GET2(address, 6);
- header_segment_2 = GET2(address, 8);
- TRACEx2(2, "header segments are %d and %d",
- header_segment_1, header_segment_2);
- used_header_segment = (first_failed) ? header_segment_2 : header_segment_1;
-
- /* Verify tape parameters...
- * QIC-40/80 spec: tape_parameters:
- *
- * segments-per-track segments_per_track
- * tracks-per-cartridge tracks_per_tape
- * max-floppy-side (segments_per_track *
- * tracks_per_tape - 1) /
- * segments_per_head
- * max-floppy-track segments_per_head /
- * segments_per_cylinder - 1
- * max-floppy-sector segments_per_cylinder *
- * SECTORS_PER_SEGMENT
- */
- format_code = (format_type) * (address + 4);
- segments_per_track = GET2(address, 24);
- tracks_per_tape = *(address + 26);
- max_floppy_side = *(address + 27);
- max_floppy_track = *(address + 28);
- max_floppy_sector = *(address + 29);
- TRACEx6(4, "(fmt/spt/tpc/fhm/ftm/fsm) = %d/%d/%d/%d/%d/%d",
- format_code, segments_per_track, tracks_per_tape,
- max_floppy_side, max_floppy_track, max_floppy_sector);
- new_tape_len = tape_len;
- switch (format_code) {
- case fmt_425ft:
- new_tape_len = 425;
- break;
- case fmt_normal:
- if (tape_len == 0) { /* otherwise 307 ft */
- new_tape_len = 205;
- }
- break;
- case fmt_1100ft:
- new_tape_len = 1100;
- break;
- case fmt_wide:{
- int segments_per_1000_inch = 1; /* non-zero default for switch */
- switch (qic_std) {
- case QIC_TAPE_QIC40:
- segments_per_1000_inch = 332;
- break;
- case QIC_TAPE_QIC80:
- segments_per_1000_inch = 488;
- break;
- case QIC_TAPE_QIC3010:
- segments_per_1000_inch = 730;
- break;
- case QIC_TAPE_QIC3020:
- segments_per_1000_inch = 1430;
- break;
- }
- new_tape_len = (1000 * segments_per_track +
- (segments_per_1000_inch - 1)) / segments_per_1000_inch;
- break;
- }
- default:
- TRACE(1, "unknown tape format, please report !");
- TRACE_EXIT;
- return -EIO;
- }
- if (new_tape_len != tape_len) {
- tape_len = new_tape_len;
- TRACEx1(1, "calculated tape length is %d ft", tape_len);
- ftape_calc_timeouts();
- }
- if (segments_per_track == 0 && tracks_per_tape == 0 &&
- max_floppy_side == 0 && max_floppy_track == 0 &&
- max_floppy_sector == 0) {
- /* QIC-40 Rev E and earlier has no values in the header.
- */
- segments_per_track = 68;
- tracks_per_tape = 20;
- max_floppy_side = 1;
- max_floppy_track = 169;
- max_floppy_sector = 128;
- }
- /* This test will compensate for the wrong parameter on tapes
- * formatted by Conner software.
- */
- if (segments_per_track == 150 &&
- tracks_per_tape == 28 &&
- max_floppy_side == 7 &&
- max_floppy_track == 149 &&
- max_floppy_sector == 128) {
- TRACE(-1, "the famous CONNER bug: max_floppy_side off by one !");
- max_floppy_side = 6;
- }
- /* This test will compensate for the wrong parameter on tapes
- * formatted by Colorado Windows software.
- */
- if (segments_per_track == 150 &&
- tracks_per_tape == 28 &&
- max_floppy_side == 6 &&
- max_floppy_track == 150 &&
- max_floppy_sector == 128) {
- TRACE(-1, "the famous Colorado bug: max_floppy_track off by one !");
- max_floppy_track = 149;
- }
- segments_per_head = ((max_floppy_sector / SECTORS_PER_SEGMENT) *
- (max_floppy_track + 1));
- /*
- * Verify drive_configuration with tape parameters
- */
- if (segments_per_head == 0 || segments_per_cylinder == 0 ||
- ((segments_per_track * tracks_per_tape - 1) / segments_per_head
- != max_floppy_side) ||
- (segments_per_head / segments_per_cylinder - 1 != max_floppy_track) ||
- (segments_per_cylinder * SECTORS_PER_SEGMENT != max_floppy_sector)
-#ifdef TESTING
- || (format_code == 4 && (max_floppy_track != 254 || max_floppy_sector != 128))
-#endif
- ) {
- TRACE(1, "Tape parameters inconsistency, please report");
- TRACE_EXIT;
- return -EIO;
- }
- first_data_segment = GET2(address, 10); /* first data segment */
- TRACEi(4, "first data segment:", first_data_segment);
- extract_bad_sector_map(address);
- /* Find the highest segment id that allows still one full
- * deblock_buffer to be written to tape.
- */
- ftape_last_segment.size = 0;
- for (i = segments_per_track * tracks_per_tape - 1; i >= 0; --i) {
- int space = SECTORS_PER_SEGMENT - 3 - count_ones(get_bad_sector_entry(i));
- if (space > 0) {
- ftape_last_segment.size += space; /* sectors free */
- ftape_last_segment.free = (ftape_last_segment.size -
- sizeof(deblock_buffer) / SECTOR_SIZE);
- if (ftape_last_segment.free >= 0) {
- ftape_last_segment.id = i;
- TRACEx2(4, "`last' segment is %d, %d Kb",
- ftape_last_segment.id, ftape_last_segment.size);
- break;
- }
- }
- }
- /* Copy the failed sector log into our local buffer.
- */
- if (!ftape_validate_label(&deblock_buffer[30])) {
- TRACE(-1, "This tape has no `Linux raw format' label,\n"
- "***** Use `mt' to erase this tape if you want to use file marks !");
- } else {
- extract_file_marks(address);
- }
- ftape_reset_position();
- TRACE_EXIT;
- return 0;
-}
-
-int _ftape_read(char *buff, int req_len)
-{
- TRACE_FUN(5, "_ftape_read");
- int result = 0;
- int cnt;
- int to_do = req_len;
- static int remaining;
- int bytes_read = 0;
-
- if (ftape_offline || !formatted || no_tape) {
- TRACEx3(-1, "offline = %d, formatted = %d, no_tape = %d",
- ftape_offline, formatted, no_tape);
- result = -EIO;
- } else {
- history.used |= 1;
- if (first_data_segment == -1) {
- result = read_header_segment(deblock_buffer);
- }
- }
- if (result < 0) {
- TRACE_EXIT;
- return result;
- }
- /* As GNU tar doesn't accept partial read counts when the multiple
- * volume flag is set, we make sure to return the requested amount
- * of data. Except, of course, at the end of the tape or file mark.
- */
- while (to_do > 0) { /* don't return with a partial count ! */
- /* If we're reading the `last' segment(s) on tape, make sure we don't
- * get more than 29 Kb from it (As it only contains this much).
- * This works only for sequential access, so random access should
- * stay away from this `last' segment.
- * Note: ftape_seg_pos points to the next segment that will be
- * read, so it's one too high here!
- */
- if (!eof_mark && ftape_seg_pos - 1 >= ftape_last_segment.id) {
- TRACEi(5, "remaining of last segment:", remaining);
- if (to_do > remaining) {
- to_do = remaining; /* fake a smaller request */
- TRACE(5, "clipped request to remaining");
- }
- }
- while (!eof_mark && buf_len_rd == 0) {
- /* When starting to read the `last' segment, set remaining
- */
- if (ftape_seg_pos == ftape_last_segment.id) {
- remaining = sizeof(deblock_buffer);
- TRACEi(5, "remaining set to:", remaining);
- }
- result = read_segment(ftape_seg_pos, deblock_buffer, &eof_mark, 1);
- if (result < 0) {
- if (result == -ENODATA) {
- /* Unable to recover tape data, return error and skip bad spot.
- */
- ++ftape_seg_pos;
- }
- TRACEx1(4, "read_segment result: %d", result);
- TRACE_EXIT;
- return result;
- }
- /* Allow escape from this loop on signal !
- */
- if (current->signal & _DONT_BLOCK) {
- TRACE(2, "interrupted by non-blockable signal");
- TRACE_EXIT;
- return -EINTR;
- }
- buf_pos_rd = 0;
- buf_len_rd = result;
- ++ftape_seg_pos;
- }
- /* Take as much as we can use
- */
- cnt = (buf_len_rd < to_do) ? buf_len_rd : to_do;
- TRACEi(7, "nr bytes just read:", cnt);
- if (cnt > 0) {
- result = verify_area(VERIFY_WRITE, buff, cnt);
- if (result) {
- TRACEx1(1, "verify_area failed, exitcode = %d", result);
- TRACE_EXIT;
- return -EIO;
- }
- copy_to_user(buff, deblock_buffer + buf_pos_rd, cnt);
- buff += cnt;
- to_do -= cnt; /* what's left from req_len */
- remaining -= cnt; /* what remains on this tape */
- bytes_read += cnt; /* what we got so far */
- buf_pos_rd += cnt; /* index in buffer */
- buf_len_rd -= cnt; /* remaining bytes in buffer */
- }
- if (eof_mark && buf_len_rd == 0) { /* nothing left */
- TRACE(5, "partial count because of eof mark");
- if (bytes_read == 0) {
- eof_mark = 0; /* no need for mark next read */
- }
- break;
- }
- }
- TRACE_EXIT;
- return bytes_read;
-}
diff --git a/drivers/char/ftape/ftape-rw.c b/drivers/char/ftape/ftape-rw.c
deleted file mode 100644
index f1e0c10ef..000000000
--- a/drivers/char/ftape/ftape-rw.c
+++ /dev/null
@@ -1,953 +0,0 @@
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute 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.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-rw.c,v $
- $Author: bas $
- *
- $Revision: 1.54 $
- $Date: 1995/05/27 08:55:27 $
- $State: Beta $
- *
- * This file contains some common code for the segment read and segment
- * write routines for the QIC-117 floppy-tape driver for Linux.
- */
-
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/ftape.h>
-
-#include "tracing.h"
-#include "ftape-rw.h"
-#include "fdc-io.h"
-#include "kernel-interface.h"
-#include "qic117.h"
-#include "ftape-io.h"
-#include "ftape-ctl.h"
-#include "ftape-read.h"
-#include "ftape-eof.h"
-#include "ecc.h"
-#include "ftape-bsm.h"
-
-/* Global vars.
- */
-volatile enum runner_status_enum runner_status = idle;
-byte deblock_buffer[(SECTORS_PER_SEGMENT - 3) * SECTOR_SIZE];
-byte scratch_buffer[(SECTORS_PER_SEGMENT - 3) * SECTOR_SIZE];
-buffer_struct buffer[NR_BUFFERS];
-struct wait_queue *wait_intr = NULL;
-volatile int head;
-volatile int tail; /* not volatile but need same type as head */
-int fdc_setup_error;
-ftape_last_segment_struct ftape_last_segment;
-int header_segment_1 = -1;
-int header_segment_2 = -1;
-int used_header_segment = -1;
-location_record location =
-{-1, 0};
-volatile int tape_running = 0;
-format_type format_code;
-
-/* Local vars.
- */
-static int overrun_count_offset = 0;
-static int inhibit_correction = 0;
-
-
-/* Increment cyclic buffer nr.
- */
-buffer_struct *
- next_buffer(volatile int *x)
-{
- if (++*x >= NR_BUFFERS) {
- *x = 0;
- }
- return &buffer[*x];
-}
-
-int valid_segment_no(unsigned segment)
-{
- return (segment >= first_data_segment && segment <= ftape_last_segment.id);
-}
-
-/* Count nr of 1's in pattern.
- */
-int count_ones(unsigned long mask)
-{
- int bits;
-
- for (bits = 0; mask != 0; mask >>= 1) {
- if (mask & 1) {
- ++bits;
- }
- }
- return bits;
-}
-
-/* Calculate Floppy Disk Controller and DMA parameters for a segment.
- * head: selects buffer struct in array.
- * offset: number of physical sectors to skip (including bad ones).
- * count: number of physical sectors to handle (including bad ones).
- */
-static int setup_segment(buffer_struct * buff, unsigned int segment_id,
- unsigned int sector_offset, unsigned int sector_count, int retry)
-{
- TRACE_FUN(8, "setup_segment");
- unsigned long offset_mask;
- unsigned long mask;
-
- buff->segment_id = segment_id;
- buff->sector_offset = sector_offset;
- buff->remaining = sector_count;
- buff->head = segment_id / segments_per_head;
- buff->cyl = (segment_id % segments_per_head) / segments_per_cylinder;
- buff->sect = (segment_id % segments_per_cylinder) * SECTORS_PER_SEGMENT + 1;
- buff->deleted = 0;
- offset_mask = (1 << buff->sector_offset) - 1;
- mask = get_bad_sector_entry(segment_id) & offset_mask;
- while (mask) {
- if (mask & 1) {
- offset_mask >>= 1; /* don't count bad sector */
- }
- mask >>= 1;
- }
- buff->data_offset = count_ones(offset_mask); /* good sectors to skip */
- buff->ptr = buff->address + buff->data_offset * SECTOR_SIZE;
- TRACEx1(5, "data offset = %d sectors", buff->data_offset);
- if (retry) {
- buff->soft_error_map &= offset_mask; /* keep skipped part */
- } else {
- buff->hard_error_map = buff->soft_error_map = 0;
- }
- buff->bad_sector_map = get_bad_sector_entry(buff->segment_id);
- if (buff->bad_sector_map != 0) {
- TRACEx2(4, "segment: %d, bad sector map: %08lx",
- buff->segment_id, buff->bad_sector_map);
- } else {
- TRACEx1(5, "segment: %d", buff->segment_id);
- }
- if (buff->sector_offset > 0) {
- buff->bad_sector_map >>= buff->sector_offset;
- }
- if (buff->sector_offset != 0 || buff->remaining != SECTORS_PER_SEGMENT) {
- TRACEx2(5, "sector offset = %d, count = %d",
- buff->sector_offset, buff->remaining);
- }
- /*
- * Segments with 3 or less sectors are not written with
- * valid data because there is no space left for the ecc.
- * The data written is whatever happens to be in the buffer.
- * Reading such a segment will return a zero byte-count.
- * To allow us to read/write segments with all bad sectors
- * we fake one readable sector in the segment. This prevents
- * having to handle these segments in a very special way.
- * It is not important if the reading of this bad sector
- * fails or not (the data is ignored). It is only read to
- * keep the driver running.
- * The QIC-40/80 spec. has no information on how to handle
- * this case, so this is my interpretation.
- */
- if (buff->bad_sector_map == EMPTY_SEGMENT) {
- TRACE(5, "empty segment, fake first sector good");
- buff->bad_sector_map = FAKE_SEGMENT;
- }
- fdc_setup_error = 0;
- buff->next_segment = segment_id + 1;
- TRACE_EXIT;
- return 0;
-}
-
-/* Calculate Floppy Disk Controller and DMA parameters for a new segment.
- */
-int setup_new_segment(buffer_struct * buff, unsigned int segment_id, int skip)
-{
- TRACE_FUN(5, "setup_new_segment");
- int result = 0;
- static int old_segment_id = -1;
- static int old_ftape_state = idle;
- int retry = 0;
- unsigned offset = 0;
- int count = SECTORS_PER_SEGMENT;
-
- TRACEx3(5, "%s segment %d (old = %d)",
- (ftape_state == reading) ? "reading" : "writing",
- segment_id, old_segment_id);
- if (ftape_state != old_ftape_state) { /* when verifying */
- old_segment_id = -1;
- old_ftape_state = ftape_state;
- }
- if (segment_id == old_segment_id) {
- ++buff->retry;
- ++history.retries;
- TRACEx1(5, "setting up for retry nr %d", buff->retry);
- retry = 1;
- if (skip && buff->skip > 0) { /* allow skip on retry */
- offset = buff->skip;
- count -= offset;
- TRACEx1(5, "skipping %d sectors", offset);
- }
- } else {
- buff->retry = 0;
- buff->skip = 0;
- old_segment_id = segment_id;
- }
- result = setup_segment(buff, segment_id, offset, count, retry);
- TRACE_EXIT;
- return result;
-}
-
-/* Determine size of next cluster of good sectors.
- */
-int calc_next_cluster(buffer_struct * buff)
-{
- /* Skip bad sectors.
- */
- while (buff->remaining > 0 && (buff->bad_sector_map & 1) != 0) {
- buff->bad_sector_map >>= 1;
- ++buff->sector_offset;
- --buff->remaining;
- }
- /* Find next cluster of good sectors
- */
- if (buff->bad_sector_map == 0) { /* speed up */
- buff->sector_count = buff->remaining;
- } else {
- unsigned long map = buff->bad_sector_map;
-
- buff->sector_count = 0;
- while (buff->sector_count < buff->remaining && (map & 1) == 0) {
- ++buff->sector_count;
- map >>= 1;
- }
- }
- return buff->sector_count;
-}
-
-int check_bot_eot(int status)
-{
- TRACE_FUN(5, "check_bot_eot");
-
- if (status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) {
- location.bot = ((location.track & 1) == 0 ?
- (status & QIC_STATUS_AT_BOT) :
- (status & QIC_STATUS_AT_EOT));
- location.eot = !location.bot;
- location.segment = (location.track +
- (location.bot ? 0 : 1)) * segments_per_track - 1;
- location.sector = -1;
- location.known = 1;
- TRACEx1(5, "tape at logical %s", location.bot ? "bot" : "eot");
- TRACEx1(5, "segment = %d", location.segment);
- } else {
- location.known = 0;
- }
- TRACE_EXIT;
- return location.known;
-}
-
-/* Read Id of first sector passing tape head.
- */
-int ftape_read_id(void)
-{
- TRACE_FUN(8, "ftape_read_id");
- int result;
- int status;
- byte out[2];
-
- /* Assume tape is running on entry, be able to handle
- * situation where it stopped or is stopping.
- */
- location.known = 0; /* default is location not known */
- out[0] = FDC_READID;
- out[1] = FTAPE_UNIT;
- result = fdc_command(out, 2);
- if (result < 0) {
- TRACE(1, "fdc_command failed");
- } else {
- result = fdc_interrupt_wait(20 * SECOND);
- if (result == 0) {
- if (fdc_sect == 0) {
- result = ftape_report_drive_status(&status);
- if (result == 0) {
- if (status & QIC_STATUS_READY) {
- tape_running = 0;
- TRACE(5, "tape has stopped");
- check_bot_eot(status);
- if (!location.known) {
- result = -EIO;
- }
- } else {
- /* If read-id failed because of a hard or soft
- * error, return an error. Higher level must retry!
- */
- result = -EIO;
- }
- }
- } else {
- location.known = 1;
- location.segment = (segments_per_head * fdc_head
- + segments_per_cylinder * fdc_cyl
- + (fdc_sect - 1) / SECTORS_PER_SEGMENT);
- location.sector = (fdc_sect - 1) % SECTORS_PER_SEGMENT;
- location.eot =
- location.bot = 0;
- }
- } else if (result == -ETIME) {
- /* Didn't find id on tape, must be near end: Wait until stopped.
- */
- result = ftape_ready_wait(FOREVER, &status);
- if (result >= 0) {
- tape_running = 0;
- TRACE(5, "tape has stopped");
- check_bot_eot(status);
- if (!location.known) {
- result = -EIO;
- }
- }
- } else {
- /* Interrupted or otherwise failing fdc_interrupt_wait()
- */
- TRACE(1, "fdc_interrupt_wait failed :(");
- result = -EIO;
- }
- }
- if (!location.known) {
- TRACE(5, "no id found");
- } else {
- if (location.sector == 0) {
- TRACEx2(5, "passing segment %d/%d", location.segment, location.sector);
- } else {
- TRACEx2(6, "passing segment %d/%d", location.segment, location.sector);
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-static int logical_forward(void)
-{
- tape_running = 1;
- return ftape_command(QIC_LOGICAL_FORWARD);
-}
-
-static int stop_tape(int *pstatus)
-{
- TRACE_FUN(5, "stop_tape");
- int retry = 0;
- int result;
-
- do {
- result = ftape_command_wait(QIC_STOP_TAPE, timeout.stop, pstatus);
- if (result == 0) {
- if ((*pstatus & QIC_STATUS_READY) == 0) {
- result = -EIO;
- } else {
- tape_running = 0;
- }
- }
- } while (result < 0 && ++retry <= 3);
- if (result < 0) {
- TRACE(1, "failed ! (fatal)");
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_dumb_stop(void)
-{
- TRACE_FUN(5, "ftape_dumb_stop");
- int result;
- int status;
-
- /* Abort current fdc operation if it's busy (probably read
- * or write operation pending) with a reset.
- */
- result = fdc_ready_wait(100 /* usec */ );
- if (result < 0) {
- TRACE(1, "aborting fdc operation");
- fdc_reset();
- }
- /* Reading id's after the last segment on a track may fail
- * but eventually the drive will become ready (logical eot).
- */
- result = ftape_report_drive_status(&status);
- location.known = 0;
- do {
- if (result == 0 && status & QIC_STATUS_READY) {
- /* Tape is not running any more.
- */
- TRACE(5, "tape already halted");
- check_bot_eot(status);
- tape_running = 0;
- } else if (tape_running) {
- /* Tape is (was) still moving.
- */
-#ifdef TESTING
- ftape_read_id();
-#endif
- result = stop_tape(&status);
- } else {
- /* Tape not yet ready but stopped.
- */
- result = ftape_ready_wait(timeout.pause, &status);
- }
- } while (tape_running);
-#ifndef TESTING
- location.known = 0;
-#endif
- TRACE_EXIT;
- return result;
-}
-
-/* Wait until runner has finished tail buffer.
- */
-int wait_segment(buffer_state_enum state)
-{
- TRACE_FUN(5, "wait_segment");
- int result = 0;
-
- while (buffer[tail].status == state) {
- /* First buffer still being worked on, wait up to timeout.
- */
- result = fdc_interrupt_wait(50 * SECOND);
- if (result < 0) {
- if (result != -EINTR) {
- TRACE(1, "fdc_interrupt_wait failed");
- result = -ETIME;
- }
- break;
- }
- if (fdc_setup_error) {
- TRACE(1, "setup error");
- /* recover... */
- result = -EIO;
- break;
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-/* forward */ static int seek_forward(int segment_id);
-
-int fast_seek(int count, int reverse)
-{
- TRACE_FUN(5, "fast_seek");
- int result = 0;
- int status;
-
- if (count > 0) {
- /* If positioned at begin or end of tape, fast seeking needs
- * special treatment.
- * Starting from logical bot needs a (slow) seek to the first
- * segment before the high speed seek. Most drives do this
- * automatically but some older don't, so we treat them
- * all the same.
- * Starting from logical eot is even more difficult because
- * we cannot (slow) reverse seek to the last segment.
- * TO BE IMPLEMENTED.
- */
- inhibit_correction = 0;
- if (location.known &&
- ((location.bot && !reverse) ||
- (location.eot && reverse))) {
- if (!reverse) {
- /* (slow) skip to first segment on a track
- */
- seek_forward(location.track * segments_per_track);
- --count;
- } else {
- /* When seeking backwards from end-of-tape the number
- * of erased gaps found seems to be higher than expected.
- * Therefor the drive must skip some more segments than
- * calculated, but we don't know how many.
- * Thus we will prevent the re-calculation of offset
- * and overshoot when seeking backwards.
- */
- inhibit_correction = 1;
- count += 3; /* best guess */
- }
- }
- } else {
- TRACEx1(5, "warning: zero or negative count: %d", count);
- }
- if (count > 0) {
- int i;
- int nibbles = count > 255 ? 3 : 2;
-
- if (count > 4095) {
- TRACE(4, "skipping clipped at 4095 segment");
- count = 4095;
- }
- /* Issue this tape command first. */
- if (!reverse) {
- TRACEx1(4, "skipping %d segment(s)", count);
- result = ftape_command(nibbles == 3 ?
- QIC_SKIP_EXTENDED_FORWARD : QIC_SKIP_FORWARD);
- } else {
- TRACEx1(4, "backing up %d segment(s)", count);
- result = ftape_command(nibbles == 3 ?
- QIC_SKIP_EXTENDED_REVERSE : QIC_SKIP_REVERSE);
- }
- if (result < 0) {
- TRACE(4, "Skip command failed");
- } else {
- --count; /* 0 means one gap etc. */
- for (i = 0; i < nibbles; ++i) {
- if (result >= 0) {
- result = ftape_parameter(count & 15);
- count /= 16;
- }
- }
- result = ftape_ready_wait(timeout.rewind, &status);
- if (result >= 0) {
- tape_running = 0;
- }
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-static int validate(int id)
-{
- /* Check to see if position found is off-track as reported once.
- * Because all tracks in one direction lie next to each other,
- * if off-track the error will be approximately 2 * segments_per_track.
- */
- if (location.track == -1) {
- return 1; /* unforeseen situation, don't generate error */
- } else {
- /* Use margin of segments_per_track on both sides because ftape
- * needs some margin and the error we're looking for is much larger !
- */
- int lo = (location.track - 1) * segments_per_track;
- int hi = (location.track + 2) * segments_per_track;
-
- return (id >= lo && id < hi);
- }
-}
-
-static int seek_forward(int segment_id)
-{
- TRACE_FUN(5, "seek_forward");
- int failures = 0;
- int result = 0;
- int count;
- static int margin = 1; /* fixed: stop this before target */
- static int overshoot = 1;
- static int min_count = 8;
- int expected = -1;
- int target = segment_id - margin;
- int fast_seeking;
-
- if (!location.known) {
- TRACE(1, "fatal: cannot seek from unknown location");
- result = -EIO;
- } else if (!validate(segment_id)) {
- TRACE(1, "fatal: head off track (bad hardware?)");
- ftape_sleep(1 * SECOND);
- ftape_failure = 1;
- result = -EIO;
- } else {
- int prev_segment = location.segment;
-
- TRACEx4(4, "from %d/%d to %d/0 - %d", location.segment,
- location.sector, segment_id, margin);
- count = target - location.segment - overshoot;
- fast_seeking = (count > min_count + (location.bot ? 1 : 0));
- if (fast_seeking) {
- TRACEx1(5, "fast skipping %d segments", count);
- expected = segment_id - margin;
- fast_seek(count, 0);
- }
- if (!tape_running) {
- logical_forward();
- }
- while (location.segment < segment_id) {
- /* This requires at least one sector in a (bad) segment to
- * have a valid and readable sector id !
- * It looks like this is not guaranteed, so we must try
- * to find a way to skip an EMPTY_SEGMENT. !!! FIXME !!!
- */
- if (ftape_read_id() < 0 || !location.known) {
- location.known = 0;
- if (!tape_running || ++failures > SECTORS_PER_SEGMENT ||
- (current->signal & _DONT_BLOCK)) {
- TRACE(1, "read_id failed completely");
- result = -EIO;
- break;
- } else {
- TRACEx1(5, "read_id failed, retry (%d)", failures);
- }
- } else if (fast_seeking) {
- TRACEx4(4, "ended at %d/%d (%d,%d)", location.segment,
- location.sector, overshoot, inhibit_correction);
- if (!inhibit_correction &&
- (location.segment < expected ||
- location.segment > expected + margin)) {
- int error = location.segment - expected;
- TRACEx2(4, "adjusting overshoot from %d to %d",
- overshoot, overshoot + error);
- overshoot += error;
- /* All overshoots have the same direction, so it should
- * never become negative, but who knows.
- */
- if (overshoot < -5 || overshoot > 10) {
- if (overshoot < 0) {
- overshoot = -5; /* keep sane value */
- } else {
- overshoot = 10; /* keep sane value */
- }
- TRACEx1(4, "clipped overshoot to %d", overshoot);
- }
- }
- fast_seeking = 0;
- }
- if (location.known) {
- if (location.segment > prev_segment + 1) {
- TRACEx1(4, "missed segment %d while skipping", prev_segment + 1);
- }
- prev_segment = location.segment;
- }
- }
- if (location.segment > segment_id) {
- TRACEx2(4, "failed: skip ended at segment %d/%d",
- location.segment, location.sector);
- result = -EIO;
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-static int skip_reverse(int segment_id, int *pstatus)
-{
- TRACE_FUN(5, "skip_reverse");
- int result = 0;
- int failures = 0;
- static int overshoot = 1;
- static int min_rewind = 2; /* 1 + overshoot */
- static const int margin = 1; /* stop this before target */
- int expected = 0;
- int count;
- int short_seek;
- int target = segment_id - margin;
-
- if (location.known && !validate(segment_id)) {
- TRACE(1, "fatal: head off track (bad hardware?)");
- ftape_sleep(1 * SECOND);
- ftape_failure = 1;
- result = -EIO;
- } else
- do {
- if (!location.known) {
- TRACE(-1, "warning: location not known");
- }
- TRACEx4(4, "from %d/%d to %d/0 - %d",
- location.segment, location.sector, segment_id, margin);
- /* min_rewind == 1 + overshoot_when_doing_minimum_rewind
- * overshoot == overshoot_when_doing_larger_rewind
- * Initially min_rewind == 1 + overshoot, optimization
- * of both values will be done separately.
- * overshoot and min_rewind can be negative as both are
- * sums of three components:
- * any_overshoot == rewind_overshoot - stop_overshoot - start_overshoot
- */
- if (location.segment - target - (min_rewind - 1) < 1) {
- short_seek = 1;
- } else {
- count = location.segment - target - overshoot;
- short_seek = (count < 1);
- }
- if (short_seek) {
- count = 1; /* do shortest rewind */
- expected = location.segment - min_rewind;
- if (expected / segments_per_track != location.track) {
- expected = location.track * segments_per_track;
- }
- } else {
- expected = target;
- }
- fast_seek(count, 1);
- logical_forward();
- result = ftape_read_id();
- if (result == 0 && location.known) {
- TRACEx5(4, "ended at %d/%d (%d,%d,%d)", location.segment,
- location.sector, min_rewind, overshoot, inhibit_correction);
- if (!inhibit_correction &&
- (location.segment < expected ||
- location.segment > expected + margin)) {
- int error = expected - location.segment;
- if (short_seek) {
- TRACEx2(4, "adjusting min_rewind from %d to %d",
- min_rewind, min_rewind + error);
- min_rewind += error;
- if (min_rewind < -5) { /* is this right ? FIXME ! */
- min_rewind = -5; /* keep sane value */
- TRACEx1(4, "clipped min_rewind to %d", min_rewind);
- }
- } else {
- TRACEx2(4, "adjusting overshoot from %d to %d",
- overshoot, overshoot + error);
- overshoot += error;
- if (overshoot < -5 || overshoot > 10) {
- if (overshoot < 0) {
- overshoot = -5; /* keep sane value */
- } else {
- overshoot = 10; /* keep sane value */
- }
- TRACEx1(4, "clipped overshoot to %d", overshoot);
- }
- }
- }
- } else {
- if ((!tape_running && !location.known) ||
- ++failures > SECTORS_PER_SEGMENT) {
- TRACE(1, "read_id failed completely");
- result = -EIO;
- break;
- } else {
- TRACEx1(5, "ftape_read_id failed, retry (%d)", failures);
- }
- result = ftape_report_drive_status(pstatus);
- if (result < 0) {
- TRACEi(1, "ftape_report_drive_status failed with code", result);
- break;
- }
- }
- } while (location.segment > segment_id &&
- (current->signal & _DONT_BLOCK) == 0);
- if (location.known) {
- TRACEx2(4, "current location: %d/%d", location.segment, location.sector);
- }
- TRACE_EXIT;
- return result;
-}
-
-static int determine_position(void)
-{
- TRACE_FUN(5, "determine_position");
- int retry = 0;
- int fatal = 0;
- int status;
- int result;
-
- if (!tape_running) {
- /* This should only happen if tape is stopped by isr.
- */
- TRACE(5, "waiting for tape stop");
- result = ftape_ready_wait(timeout.pause, &status);
- if (result < 0) {
- TRACE(5, "drive still running (fatal)");
- tape_running = 1; /* ? */
- }
- } else {
- ftape_report_drive_status(&status);
- }
- if (status & QIC_STATUS_READY) {
- /* Drive must be ready to check error state !
- */
- TRACE(5, "drive is ready");
- if (status & QIC_STATUS_ERROR) {
- int error;
- int command;
-
- /* Report and clear error state, try to continue.
- */
- TRACE(5, "error status set");
- ftape_report_error(&error, &command, 1);
- ftape_ready_wait(timeout.reset, &status);
- tape_running = 0; /* ? */
- }
- if (check_bot_eot(status)) {
- if (location.bot) {
- if ((status & QIC_STATUS_READY) == 0) {
- /* tape moving away from bot/eot, let's see if we
- * can catch up with the first segment on this track.
- */
- } else {
- TRACE(5, "start tape from logical bot");
- logical_forward(); /* start moving */
- }
- } else {
- if ((status & QIC_STATUS_READY) == 0) {
- TRACE(4, "waiting for logical end of track");
- result = ftape_ready_wait(timeout.reset, &status);
- /* error handling needed ? */
- } else {
- TRACE(4, "tape at logical end of track");
- }
- }
- } else {
- TRACE(5, "start tape");
- logical_forward(); /* start moving */
- location.known = 0; /* not cleared by logical forward ! */
- }
- }
- if (!location.known) {
- /* tape should be moving now, start reading id's
- */
- TRACE(5, "location unknown");
- do {
- result = ftape_read_id();
- if (result < 0) {
- /* read-id somehow failed, tape may have reached end
- * or some other error happened.
- */
- TRACE(5, "read-id failed");
- ftape_report_drive_status(&status);
- if (status & QIC_STATUS_READY) {
- tape_running = 0;
- TRACEx1(4, "tape stopped for unknown reason ! status = 0x%02x",
- status);
- if (status & QIC_STATUS_ERROR) {
- fatal = 1;
- } else {
- if (check_bot_eot(status)) {
- result = 0;
- } else {
- fatal = 1; /* oops, tape stopped but not at end ! */
- }
- }
- }
- result = -EIO;
- }
- } while (result < 0 && !fatal && ++retry < SECTORS_PER_SEGMENT);
- } else {
- result = 0;
- }
- TRACEx1(5, "tape is positioned at segment %d", location.segment);
- TRACE_EXIT;
- return result;
-}
-
-/* Get the tape running and position it just before the
- * requested segment.
- * Seek tape-track and reposition as needed.
- */
-int ftape_start_tape(int segment_id, int sector_offset)
-{
- TRACE_FUN(5, "ftape_start_tape");
- int track = segment_id / segments_per_track;
- int result = -EIO;
- int status;
- static int last_segment = -1;
- static int bad_bus_timing = 0;
- /* number of segments passing the head between starting the tape
- * and being able to access the first sector.
- */
- static int start_offset = 1;
- int retry = 0;
-
- /* If sector_offset > 0, seek into wanted segment instead of
- * into previous.
- * This allows error recovery if a part of the segment is bad
- * (erased) causing the tape drive to generate an index pulse
- * thus causing a no-data error before the requested sector
- * is reached.
- */
- tape_running = 0;
- TRACEx3(4, "target segment: %d/%d%s", segment_id, sector_offset,
- buffer[head].retry > 0 ? " retry" : "");
- if (buffer[head].retry > 0) { /* this is a retry */
- if (!bad_bus_timing && ftape_data_rate == 1 &&
- history.overrun_errors - overrun_count_offset >= 8) {
- ftape_set_data_rate(ftape_data_rate + 1);
- bad_bus_timing = 1;
- TRACE(2, "reduced datarate because of excessive overrun errors");
- }
- }
- last_segment = segment_id;
- if (location.track != track || (might_be_off_track &&
- buffer[head].retry == 0)) {
- /* current track unknown or not equal to destination
- */
- ftape_ready_wait(timeout.seek, &status);
- ftape_seek_head_to_track(track);
- overrun_count_offset = history.overrun_errors;
- }
- do {
- if (!location.known) {
- determine_position();
- }
- /* Check if we are able to catch the requested segment in time.
- */
- if (location.known && location.segment >= segment_id -
- ((tape_running || location.bot) ? 0 : start_offset)) {
- /* Too far ahead (in or past target segment).
- */
- if (tape_running) {
- result = stop_tape(&status);
- if (result < 0) {
- TRACEi(1, "stop tape failed with code", result);
- break;
- }
- TRACE(5, "tape stopped");
- tape_running = 0;
- }
- TRACE(5, "repositioning");
- ++history.rewinds;
- if (segment_id % segments_per_track < start_offset) {
- /* If seeking to first segments on track better do a complete
- * rewind to logical begin of track to get a more steady tape
- * motion.
- */
- result = ftape_command_wait((location.track & 1) ?
- QIC_PHYSICAL_FORWARD :
- QIC_PHYSICAL_REVERSE,
- timeout.rewind, &status);
- check_bot_eot(status); /* update location */
- } else {
- result = skip_reverse(segment_id - start_offset, &status);
- }
- }
- if (!location.known) {
- TRACE(-1, "panic: location not known");
- result = -EIO;
- if ((current->signal & _DONT_BLOCK) || ftape_failure) {
- break;
- } else {
- continue;
- }
- }
- TRACEx2(4, "current segment: %d/%d", location.segment, location.sector);
- /* We're on the right track somewhere before the wanted segment.
- * Start tape movement if needed and skip to just before or inside
- * the requested segment. Keep tape running.
- */
- result = 0;
- if (location.segment < segment_id -
- ((tape_running || location.bot) ? 0 : start_offset)) {
- if (sector_offset > 0) {
- result = seek_forward(segment_id);
- } else {
- result = seek_forward(segment_id - 1);
- }
- }
- if (result == 0 &&
- location.segment != segment_id - (sector_offset > 0 ? 0 : 1)) {
- result = -EIO;
- }
- } while (result < 0 && !ftape_failure &&
- (current->signal & _DONT_BLOCK) == 0 &&
- ++retry <= 5);
- if (result < 0) {
- TRACE(1, "failed to reposition");
- }
- TRACE_EXIT;
- return result;
-}
diff --git a/drivers/char/ftape/ftape-rw.h b/drivers/char/ftape/ftape-rw.h
deleted file mode 100644
index 983a1edd0..000000000
--- a/drivers/char/ftape/ftape-rw.h
+++ /dev/null
@@ -1,173 +0,0 @@
-#ifndef _FTAPE_RW_H
-#define _FTAPE_RW_H
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute 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.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-rw.h,v $
- $Author: bas $
- *
- $Revision: 1.33 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
- *
- * This file contains the definitions for the read and write
- * functions for the QIC-117 floppy-tape driver for Linux.
- *
- */
-
-#include "fdc-io.h"
-#include "kernel-interface.h"
-
-#define GET2( address, offset) *(short*)(address + offset)
-#define GET4( address, offset) *(long*)(address + offset)
-#define PUT2( address, offset, value) *(short*)(address + offset) = value
-#define PUT4( address, offset, value) *(long*)(address + offset) = value
-
-enum runner_status_enum {
- idle = 0,
- running,
- do_abort,
- aborting,
- logical_eot,
- end_of_tape,
- buffer_overrun,
- buffer_underrun,
-};
-
-typedef struct {
- byte *address;
- volatile buffer_state_enum status;
- volatile byte *ptr;
- volatile unsigned bytes;
- volatile unsigned segment_id;
-
- /* bitmap for remainder of segment not yet handled.
- * one bit set for each bad sector that must be skipped.
- */
- volatile unsigned long bad_sector_map;
-
- /* bitmap with bad data blocks in data buffer.
- * the errors in this map may be retried.
- */
- volatile unsigned long soft_error_map;
-
- /* bitmap with bad data blocks in data buffer
- * the errors in this map may not be retried.
- */
- volatile unsigned long hard_error_map;
-
- /* retry counter for soft errors.
- */
- volatile int retry;
-
- /* sectors to skip on retry ???
- */
- volatile unsigned int skip;
-
- /* nr of data blocks in data buffer
- */
- volatile unsigned data_offset;
-
- /* offset in segment for first sector to be handled.
- */
- volatile unsigned sector_offset;
-
- /* size of cluster of good sectors to be handled.
- */
- volatile unsigned sector_count;
-
- /* size of remaining part of segment to be handled.
- */
- volatile unsigned remaining;
-
- /* points to next segment (contiguous) to be handled,
- * or is zero if no read-ahead is allowed.
- */
- volatile unsigned next_segment;
-
- /* flag being set if deleted data was read.
- */
- volatile int deleted;
-
- volatile byte head;
- volatile byte cyl;
- volatile byte sect;
-} buffer_struct;
-
-typedef struct {
- int active;
- int error;
- int offset;
-} ftape_fast_start_struct;
-
-typedef struct {
- int id;
- int size;
- int free;
-} ftape_last_segment_struct;
-
-typedef struct {
- int track; /* tape head position */
- volatile int known; /* validates bot, segment, sector */
- volatile int bot; /* logical begin of track */
- volatile int eot; /* logical end of track */
- volatile int segment; /* current segment */
- volatile int sector; /* sector offset within current segment */
-} location_record;
-
-typedef enum {
- fmt_normal = 2, fmt_1100ft = 3, fmt_wide = 4, fmt_425ft = 5
-} format_type;
-
-/* ftape-rw.c defined global vars.
- */
-extern int tracing;
-extern byte trace_id;
-extern buffer_struct buffer[];
-extern location_record location;
-extern volatile ftape_fast_start_struct ftape_fast_start;
-extern byte deblock_buffer[(SECTORS_PER_SEGMENT - 3) * SECTOR_SIZE];
-extern byte scratch_buffer[(SECTORS_PER_SEGMENT - 3) * SECTOR_SIZE];
-extern ftape_last_segment_struct ftape_last_segment;
-extern int header_segment_1;
-extern int header_segment_2;
-extern int used_header_segment;
-extern unsigned int fast_seek_segment_time;
-extern volatile int tape_running;
-extern format_type format_code;
-
-/* ftape-rw.c defined global functions.
- */
-extern int count_ones(unsigned long mask);
-extern int valid_segment_no(unsigned segment);
-extern int setup_new_segment(buffer_struct * buff, unsigned int segment_id,
- int offset);
-extern int calc_next_cluster(buffer_struct * buff);
-extern buffer_struct *next_buffer(volatile int *x);
-extern int ftape_read_id(void);
-extern void ftape_tape_parameters(byte drive_configuration);
-extern int wait_segment(buffer_state_enum state);
-extern int ftape_dumb_stop(void);
-extern int ftape_start_tape(int segment_id, int offset);
-
-/* fdc-io.c defined global functions.
- */
-extern int setup_fdc_and_dma(buffer_struct * buff, byte operation);
-
-#endif /* _FTAPE_RW_H */
diff --git a/drivers/char/ftape/ftape-write.c b/drivers/char/ftape/ftape-write.c
deleted file mode 100644
index 0ba24c978..000000000
--- a/drivers/char/ftape/ftape-write.c
+++ /dev/null
@@ -1,723 +0,0 @@
-
-
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute 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.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-write.c,v $
- $Author: bas $
- *
- $Revision: 1.26 $
- $Date: 1995/05/27 08:55:27 $
- $State: Beta $
- *
- * This file contains the writing code
- * for the QIC-117 floppy-tape driver for Linux.
- */
-
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/ftape.h>
-#include <asm/uaccess.h>
-
-#include "tracing.h"
-#include "ftape-write.h"
-#include "ftape-read.h"
-#include "qic117.h"
-#include "ftape-io.h"
-#include "ftape-ctl.h"
-#include "ftape-rw.h"
-#include "ftape-eof.h"
-#include "ecc.h"
-#include "ftape-bsm.h"
-
-
-/* Global vars.
- */
-
-/* Local vars.
- */
-static int buf_pos_wr = 0;
-static int last_write_failed = 0;
-static int need_flush = 0;
-
-#define WRITE_MULTI 0
-#define WRITE_SINGLE 1
-
-void ftape_zap_write_buffers(void)
-{
- int i;
-
- for (i = 0; i < NR_BUFFERS; ++i) {
- buffer[i].status = done;
- }
- need_flush = 0;
-}
-
-int copy_and_gen_ecc(char *destination, byte * source,
- unsigned int bad_sector_map)
-{
- TRACE_FUN(8, "copy_and_gen_ecc");
- int result;
- struct memory_segment mseg;
- int bads = count_ones(bad_sector_map);
-
- if (bads > 0) {
- TRACEi(4, "bad sectors in map:", bads);
- }
- if (bads + 3 >= SECTORS_PER_SEGMENT) {
- TRACE(4, "empty segment");
- mseg.blocks = 0; /* skip entire segment */
- result = 0; /* nothing written */
- } else {
- mseg.blocks = SECTORS_PER_SEGMENT - bads;
- mseg.data = destination;
- memcpy(mseg.data, source, (mseg.blocks - 3) * SECTOR_SIZE);
- result = ecc_set_segment_parity(&mseg);
- if (result < 0) {
- TRACE(1, "ecc_set_segment_parity failed");
- } else {
- result = (mseg.blocks - 3) * SECTOR_SIZE;
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-void prevent_flush(void)
-{
- need_flush = 0;
- ftape_state = idle;
-}
-
-int start_writing(int mode)
-{
- TRACE_FUN(5, "start_writing");
- int result = 0;
- buffer_struct *buff = &buffer[head];
- int segment_id = buff->segment_id;
-
- if (ftape_state == writing && buff->status == waiting) {
- setup_new_segment(buff, segment_id, 1);
- if (mode == WRITE_SINGLE) {
- buffer[head].next_segment = 0; /* stop tape instead of pause */
- }
- calc_next_cluster(buff); /* prepare */
- buff->status = writing;
- if (runner_status == idle) {
- TRACEi(5, "starting runner for segment", segment_id);
- result = ftape_start_tape(segment_id, buff->sector_offset);
- if (result >= 0) {
- runner_status = running;
- }
- }
- if (result >= 0) {
- result = setup_fdc_and_dma(buff, FDC_WRITE); /* go */
- }
- ftape_state = writing;
- }
- TRACE_EXIT;
- return result;
-}
-
-int loop_until_writes_done(void)
-{
- TRACE_FUN(5, "loop_until_writes_done");
- int i;
- int result = 0;
-
- /*
- * Wait until all data is actually written to tape.
- */
- while (ftape_state == writing && buffer[head].status != done) {
- TRACEx2(7, "tail: %d, head: %d", tail, head);
- for (i = 0; i < NR_BUFFERS; ++i) {
- TRACEx3(8, "buffer[ %d] segment_id: %d, status: %d",
- i, buffer[i].segment_id, buffer[i].status);
- }
- result = fdc_interrupt_wait(5 * SECOND);
- if (result < 0) {
- TRACE(1, "fdc_interrupt_wait failed");
- last_write_failed = 1;
- break;
- }
- if (buffer[head].status == error) {
- /* Allow escape from loop when signaled !
- */
- if (current->signal & _DONT_BLOCK) {
- TRACE(2, "interrupted by signal");
- TRACE_EXIT;
- result = -EINTR; /* is this the right return value ? */
- break;
- }
- if (buffer[head].hard_error_map != 0) {
- /* Implement hard write error recovery here
- */
- }
- buffer[head].status = waiting; /* retry this one */
- if (runner_status == aborting) {
- ftape_dumb_stop();
- runner_status = idle;
- }
- if (runner_status != idle) {
- TRACE(1, "unexpected state: runner_status != idle");
- result = -EIO;
- break;
- }
- start_writing(WRITE_MULTI);
- }
- TRACE(5, "looping until writes done");
- result = 0; /* normal exit status */
- }
- TRACE_EXIT;
- return result;
-}
-
-/* Write given segment from buffer at address onto tape.
- */
-int write_segment(unsigned segment_id, byte * address, int flushing)
-{
- TRACE_FUN(5, "write_segment");
- int result = 0;
- int bytes_written = 0;
-
- TRACEi(5, "segment_id =", segment_id);
- if (ftape_state != writing) {
- if (ftape_state == reading) {
- TRACE(5, "calling ftape_abort_operation");
- result = ftape_abort_operation();
- if (result < 0) {
- TRACE(1, "ftape_abort_operation failed");
- }
- }
- ftape_zap_read_buffers();
- ftape_zap_write_buffers();
- ftape_state = writing;
- }
- /* if all buffers full we'll have to wait...
- */
- wait_segment(writing);
- if (buffer[tail].status == error) {
- /* setup for a retry
- */
- buffer[tail].status = waiting;
- bytes_written = -EAGAIN; /* force retry */
- if (buffer[tail].hard_error_map != 0) {
- TRACEx1(1, "warning: %d hard error(s) in written segment",
- count_ones(buffer[tail].hard_error_map));
- TRACEx1(4, "hard_error_map = 0x%08lx", buffer[tail].hard_error_map);
- /* Implement hard write error recovery here
- */
- }
- } else if (buffer[tail].status == done) {
- history.defects += count_ones(buffer[tail].hard_error_map);
- } else {
- TRACE(1, "wait for empty segment failed");
- result = -EIO;
- }
- /* If just passed last segment on tape: wait for BOT or EOT mark.
- */
- if (result >= 0 && runner_status == logical_eot) {
- int status;
-
- result = ftape_ready_wait(timeout.seek, &status);
- if (result < 0 || (status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) == 0) {
- TRACE(1, "eot/bot not reached");
- } else {
- runner_status = end_of_tape;
- }
- }
- /* should runner stop ?
- */
- if (result >= 0 &&
- (runner_status == aborting || runner_status == buffer_underrun ||
- runner_status == end_of_tape)) {
- if (runner_status != end_of_tape) {
- result = ftape_dumb_stop();
- }
- if (result >= 0) {
- if (runner_status == aborting) {
- if (buffer[head].status == writing) {
- buffer[head].status = done; /* ????? */
- }
- }
- runner_status = idle; /* aborted ? */
- }
- }
- /* Don't start tape if runner idle and segment empty.
- */
- if (result >= 0 && !(runner_status == idle &&
- get_bad_sector_entry(segment_id) == EMPTY_SEGMENT)) {
- if (buffer[tail].status == done) {
- /* now at least one buffer is empty, fill it with our data.
- * skip bad sectors and generate ecc.
- * copy_and_gen_ecc return nr of bytes written,
- * range 0..29 Kb inclusive !
- */
- result = copy_and_gen_ecc(buffer[tail].address, address,
- get_bad_sector_entry(segment_id));
- if (result >= 0) {
- bytes_written = result;
- buffer[tail].segment_id = segment_id;
- buffer[tail].status = waiting;
- next_buffer(&tail);
- }
- }
- /* Start tape only if all buffers full or flush mode.
- * This will give higher probability of streaming.
- */
- if (result >= 0 && runner_status != running &&
- ((head == tail && buffer[tail].status == waiting) || flushing)) {
- result = start_writing(WRITE_MULTI);
- }
- }
- TRACE_EXIT;
- return (result < 0) ? result : bytes_written;
-}
-
-/* Write as much as fits from buffer to the given segment on tape
- * and handle retries.
- * Return the number of bytes written (>= 0), or:
- * -EIO write failed
- * -EINTR interrupted by signal
- * -ENOSPC device full
- */
-int _write_segment(unsigned int segment_id, byte * buffer, int flush)
-{
- TRACE_FUN(5, "_write_segment");
- int retry = 0;
- int result;
-
- history.used |= 2;
- for (;;) {
- if (segment_id > ftape_last_segment.id && !flush) {
- result = -ENOSPC; /* tape full */
- break;
- }
- result = write_segment(segment_id, buffer, flush);
- if (result < 0) {
- if (result == -EAGAIN) {
- if (++retry > 100) {
- TRACE(1, "write failed, >100 retries in segment");
- result = -EIO; /* give up */
- break;
- } else {
- TRACEx1(2, "write error, retry %d", retry);
- }
- } else {
- TRACEi(1, "write_segment failed, error:", -result);
- break;
- }
- } else { /* success */
- if (result == 0) { /* empty segment */
- TRACE(4, "empty segment, nothing written");
- }
- break;
- }
- /* Allow escape from loop when signaled !
- */
- if (current->signal & _DONT_BLOCK) {
- TRACE(2, "interrupted by signal");
- TRACE_EXIT;
- result = -EINTR; /* is this the right return value ? */
- break;
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-int update_header_segment(unsigned segment, byte * buffer)
-{
- TRACE_FUN(5, "update_header_segment");
- int result = 0;
- int status;
-
- if (buffer == NULL) {
- TRACE(5, "no input buffer specified");
- buffer = deblock_buffer;
- result = read_segment(used_header_segment, buffer, &status, 0);
- if (bad_sector_map_changed) {
- store_bad_sector_map(buffer);
- }
- if (failed_sector_log_changed) {
- update_failed_sector_log(buffer);
- }
- }
- if (result >= 0 && GET4(buffer, 0) != 0xaa55aa55) {
- TRACE(1, "wrong header signature found, aborting");
- result = -EIO;
- }
- if (result >= 0) {
- result = _write_segment(segment, buffer, 0);
- if (result >= 0 && runner_status == idle) {
- /* Force flush for single segment instead of relying on
- * flush in read_segment for multiple segments.
- */
- result = start_writing(WRITE_SINGLE);
- if (result >= 0 && ftape_state == writing) {
- result = loop_until_writes_done();
- prevent_flush();
- }
- }
-#ifdef VERIFY_HEADERS
- if (result >= 0) { /* read back and verify */
- result = read_segment(segment, scratch_buffer, &status, 0);
- /* Should retry if soft error during read !
- * TO BE IMPLEMENTED
- */
- if (result >= 0) {
- if (memcmp(buffer, scratch_buffer, sizeof(buffer)) == 0) {
- result = 0; /* verified */
- TRACE(5, "verified");
- } else {
- result = -EIO; /* verify failed */
- TRACE(5, "verify failed");
- }
- }
- }
-#endif
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_write_header_segments(byte * buffer)
-{
- TRACE_FUN(5, "ftape_write_header_segments");
- int result = 0;
- int retry = 0;
- int header_1_ok = 0;
- int header_2_ok = 0;
-
- do {
- if (!header_1_ok) {
- result = update_header_segment(header_segment_1, buffer);
- if (result < 0) {
- continue;
- }
- header_1_ok = 1;
- }
- if (!header_2_ok) {
- result = update_header_segment(header_segment_2, buffer);
- if (result < 0) {
- continue;
- }
- header_2_ok = 1;
- }
- } while (result < 0 && retry++ < 3);
- if (result < 0) {
- if (!header_1_ok) {
- TRACE(1, "update of first header segment failed");
- }
- if (!header_2_ok) {
- TRACE(1, "update of second header segment failed");
- }
- result = -EIO;
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_update_header_segments(byte * buffer, int update)
-{
- TRACE_FUN(5, "ftape_update_header_segments");
- int result = 0;
- int dummy;
- int header_changed = 1;
-
- if (ftape_state == writing) {
- result = loop_until_writes_done();
- }
- if (read_only) {
- result = 0; /* exit and fake success */
- TRACE(4, "Tape set read-only: no update");
- } else if (result >= 0) {
- result = ftape_abort_operation();
- if (result >= 0) {
- if (buffer == NULL) {
- if (bad_sector_map_changed || failed_sector_log_changed) {
- ftape_seek_to_bot(); /* prevents extra rewind */
- buffer = deblock_buffer;
- result = read_segment(used_header_segment, buffer, &dummy, 0);
- if (result < 0) {
- TRACE_EXIT;
- return result;
- }
- }
- header_changed = 0;
- }
- if (update) {
- if (bad_sector_map_changed) {
- store_bad_sector_map(buffer);
- header_changed = 1;
- }
- if (failed_sector_log_changed) {
- update_failed_sector_log(buffer);
- header_changed = 1;
- }
- }
- if (header_changed) {
- ftape_seek_to_bot(); /* prevents extra rewind */
- result = ftape_write_header_segments(buffer);
- }
- }
- }
- TRACE_EXIT;
- return result;
-}
-
-int ftape_flush_buffers(void)
-{
- TRACE_FUN(5, "ftape_flush_buffers");
- int result;
- int pad_count;
- int data_remaining;
- static int active = 0;
-
- if (active) {
- TRACE(5, "nested call, abort");
- TRACE_EXIT;
- return 0;
- }
- active = 1;
- TRACEi(5, "entered, ftape_state =", ftape_state);
- if (ftape_state != writing && !need_flush) {
- active = 0;
- TRACE(5, "no need for flush");
- TRACE_EXIT;
- return 0;
- }
- data_remaining = buf_pos_wr;
- buf_pos_wr = 0; /* prevent further writes if this fails */
- TRACE(5, "flushing write buffers");
- if (last_write_failed) {
- ftape_zap_write_buffers();
- active = 0;
- TRACE_EXIT;
- return write_protected ? -EROFS : -EIO;
- }
- /*
- * If there is any data not written to tape yet, append zero's
- * up to the end of the sector. Then write the segment(s) to tape.
- */
- if (data_remaining > 0) {
- int written;
-
- do {
- TRACEi(4, "remaining in buffer:", data_remaining);
- pad_count = sizeof(deblock_buffer) - data_remaining;
- TRACEi(7, "flush, padding count:", pad_count);
- memset(deblock_buffer + data_remaining, 0, pad_count); /* pad buffer */
- result = _write_segment(ftape_seg_pos, deblock_buffer, 1);
- if (result < 0) {
- if (result != -ENOSPC) {
- last_write_failed = 1;
- }
- active = 0;
- TRACE_EXIT;
- return result;
- }
- written = result;
- clear_eof_mark_if_set(ftape_seg_pos, written);
- TRACEi(7, "flush, moved out buffer:", written);
- if (written > 0) {
- data_remaining -= written;
- if (data_remaining > 0) {
- /* Need another segment for remaining data, move the remainder
- * to the beginning of the buffer
- */
- memmove(deblock_buffer, deblock_buffer + written, data_remaining);
- }
- }
- ++ftape_seg_pos;
- } while (data_remaining > 0);
- /* Data written to last segment == data_remaining + written
- * value is in range [1..29K].
- */
- TRACEx2(4, "last write: %d, netto pad-count: %d",
- data_remaining + written, -data_remaining);
- if (-1024 < data_remaining && data_remaining <= 0) {
- /* Last sector of segment was used for data, so put eof mark
- * in next segment and position at second file mark.
- */
- if (ftape_weof(2, ftape_seg_pos, 1) >= 0) {
- ++ftape_seg_pos; /* position between file marks */
- }
- } else {
- /* Put eof mark in previous segment after data and position
- * at second file mark.
- */
- ftape_weof(2, ftape_seg_pos - 1, 1 +
- ((SECTOR_SIZE - 1 + result + data_remaining) / SECTOR_SIZE));
- }
- } else {
- TRACE(7, "deblock_buffer empty");
- if (ftape_weof(2, ftape_seg_pos, 1) >= 0) {
- ++ftape_seg_pos; /* position between file marks */
- }
- start_writing(WRITE_MULTI);
- }
- TRACE(7, "waiting");
- result = loop_until_writes_done();
- if (result < 0) {
- TRACE(1, "flush buffers failed");
- }
- ftape_state = idle;
- last_write_failed = 0;
- need_flush = 0;
- active = 0;
- TRACE_EXIT;
- return result;
-}
-
-int _ftape_write(const char *buff, int req_len)
-{
- TRACE_FUN(5, "_ftape_write");
- int result = 0;
- int cnt;
- int written = 0;
-
- if (write_protected) {
- TRACE(1, "error: cartridge write protected");
- last_write_failed = 1;
- result = -EROFS;
- } else if (ftape_offline || !formatted || no_tape) {
- result = -EIO;
- } else if (first_data_segment == -1) {
- /*
- * If we haven't read the header segment yet, do it now.
- * This will verify the configuration, get the eof markers
- * and the bad sector table.
- * We'll use the deblock buffer for scratch.
- */
- result = read_header_segment(deblock_buffer);
- if (result >= 0 && ftape_seg_pos > ftape_last_segment.id) {
- result = -ENOSPC; /* full is full */
- }
- }
- if (result < 0) {
- TRACE_EXIT;
- return result;
- }
- /*
- * This part writes data blocks to tape until the
- * requested amount is written.
- * The data will go in a buffer until it's enough
- * for a segment without bad sectors. Then we'll write
- * that segment to tape.
- * The bytes written will be removed from the buffer
- * and the process is repeated until there is less
- * than one segment to write left in the buffer.
- */
- while (req_len > 0) {
- int space_left = sizeof(deblock_buffer) - buf_pos_wr;
-
- TRACEi(7, "remaining req_len:", req_len);
- TRACEi(7, " buf_pos:", buf_pos_wr);
- cnt = (req_len < space_left) ? req_len : space_left;
- if (cnt > 0) {
- result = verify_area(VERIFY_READ, buff, cnt);
- if (result) {
- TRACE(1, "verify_area failed");
- last_write_failed = 1;
- TRACE_EXIT;
- return result;
- }
- copy_from_user(deblock_buffer + buf_pos_wr, buff, cnt);
- buff += cnt;
- req_len -= cnt;
- buf_pos_wr += cnt;
- }
- TRACEi(7, "moved into blocking buffer:", cnt);
- while (buf_pos_wr >= sizeof(deblock_buffer)) {
- /* If this is the last buffer to be written, let flush handle it.
- */
- if (ftape_seg_pos >= ftape_last_segment.id) {
- TRACEi(7, "remaining in blocking buffer:", buf_pos_wr);
- TRACEi(7, "just written bytes:", written + cnt);
- TRACE_EXIT;
- return written + cnt;
- }
- /* Got one full buffer, write it to disk
- */
- result = _write_segment(ftape_seg_pos, deblock_buffer, 0);
- TRACEi(5, "_write_segment result =", result);
- if (result < 0) {
- if (result == -EAGAIN) {
- TRACE(5, "retry...");
- continue; /* failed, retry same segment */
- }
- last_write_failed = 1;
- TRACE_EXIT;
- return result;
- } else {
- clear_eof_mark_if_set(ftape_seg_pos, result);
- }
- if (result > 0 && result < buf_pos_wr) {
- /* Partial write: move remainder in lower part of buffer
- */
- memmove(deblock_buffer, deblock_buffer + result, buf_pos_wr - result);
- }
- TRACEi(7, "moved out of blocking buffer:", result);
- buf_pos_wr -= result; /* remainder */
- ++ftape_seg_pos;
- /* Allow us to escape from this loop with a signal !
- */
- if (current->signal & _DONT_BLOCK) {
- TRACE(2, "interrupted by signal");
- last_write_failed = 1;
- TRACE_EXIT;
- return -EINTR; /* is this the right return value ? */
- }
- }
- written += cnt;
- }
- TRACEi(7, "remaining in blocking buffer:", buf_pos_wr);
- TRACEi(7, "just written bytes:", written);
- last_write_failed = 0;
- if (!need_flush && written > 0) {
- need_flush = 1;
- }
- TRACE_EXIT;
- return written; /* bytes written */
-}
-
-int ftape_fix(void)
-{
- TRACE_FUN(5, "ftape_fix");
- int result = 0;
- int dummy;
- int status;
-
- if (write_protected) {
- result = -EROFS;
- } else {
- /* This will copy header segment 2 to header segment 1
- * Spares us a tape format operation if header 2 is still good.
- */
- header_segment_1 = 0;
- header_segment_2 = 1;
- first_data_segment = 2;
- result = read_segment(header_segment_2, scratch_buffer, &dummy, 0);
- result = ftape_ready_wait(timeout.pause, &status);
- result = ftape_write_header_segments(scratch_buffer);
- }
- TRACE_EXIT;
- return result;
-}
diff --git a/drivers/char/ftape/kernel-interface.c b/drivers/char/ftape/kernel-interface.c
deleted file mode 100644
index 9bcb35e46..000000000
--- a/drivers/char/ftape/kernel-interface.c
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute 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.
-
- *
- * This file contains the code that interfaces the kernel
- * for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <asm/uaccess.h>
-#include <linux/kernel.h>
-#include <linux/signal.h>
-#include <linux/major.h>
-#include <linux/malloc.h>
-#include <linux/init.h>
-#include <linux/ftape.h>
-#include <asm/dma.h>
-
-#include "tracing.h"
-#include "kernel-interface.h"
-#include "ftape-read.h"
-#include "ftape-write.h"
-#include "ftape-io.h"
-#include "ftape-ctl.h"
-#include "ftape-rw.h"
-#include "fdc-io.h"
-
-
-/* Global vars.
- */
-
-/* Allocating a 96Kb DMAable buffer in one chunk won't work due to
- * memory fragmentation. To avoid this, it is broken up into
- * NR_BUFFERS chunks of 32Kbyte. --khp
- */
-
-byte *tape_buffer[NR_BUFFERS] = {NULL};
-
-/* Local vars.
- */
-static int busy_flag = 0;
-static int old_sigmask;
-
-static int ftape_open(struct inode *ino, struct file *filep);
-static int ftape_close(struct inode *ino, struct file *filep);
-static int ftape_ioctl(struct inode *ino, struct file *filep,
- unsigned int command, unsigned long arg);
-static long ftape_read(struct inode *ino, struct file *fp,
- char *buff, unsigned long req_len);
-static long ftape_write(struct inode *ino, struct file *fp,
- const char *buff, unsigned long req_len);
-
-static struct file_operations ftape_cdev =
-{
- NULL, /* lseek */
- ftape_read, /* read */
- ftape_write, /* write */
- NULL, /* readdir */
- NULL, /* poll */
- ftape_ioctl, /* ioctl */
- NULL, /* mmap */
- ftape_open, /* open */
- ftape_close, /* release */
- NULL, /* fsync */
-};
-
-/*
- * DMA'able memory allocation stuff.
- */
-
-/* Pure 2^n version of get_order */
-static inline int __get_order(unsigned long size)
-{
- int order;
-
- size = (size-1) >> (PAGE_SHIFT-1);
- order = -1;
- do {
- size >>= 1;
- order++;
- } while (size);
- return order;
-}
-
-static inline
-void *dmaalloc(int order)
-{
- return (void *) __get_dma_pages(GFP_KERNEL, order);
-}
-
-static inline
-void dmafree(void *addr, int order)
-{
- free_pages((unsigned long) addr, order);
-}
-
-/*
- * Called by modules package when installing the driver
- * or by kernel during the initialization phase
- */
-
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-#define ftape_init init_module
-#endif
-
-__initfunc(int ftape_init(void))
-{
- int n;
- int order;
- TRACE_FUN(5, "ftape_init");
-#ifdef MODULE
- printk(KERN_INFO "ftape-2.08 960314\n"
- KERN_INFO " (c) 1993-1995 Bas Laarhoven (bas@vimec.nl)\n"
- KERN_INFO " (c) 1995-1996 Kai Harrekilde-Petersen (khp@dolphinics.no)\n"
- KERN_INFO " QIC-117 driver for QIC-40/80/3010/3020 tape drives\n"
- KERN_INFO " Compiled for kernel version " UTS_RELEASE
-#ifdef MODVERSIONS
- " with versioned symbols"
-#endif
- "\n");
-#else /* !MODULE */
- /* print a short no-nonsense boot message */
- printk("ftape-2.08 960314 for Linux 1.3.70\n");
-#endif /* MODULE */
- TRACE(3, "installing QIC-117 ftape driver...");
- if (register_chrdev(QIC117_TAPE_MAJOR, "ft", &ftape_cdev)) {
- TRACE(1, "register_chrdev failed");
- TRACE_EXIT;
- return -EIO;
- }
- TRACEx1(3, "ftape_init @ 0x%p", ftape_init);
- /*
- * Allocate the DMA buffers. They are deallocated at cleanup() time.
- */
- order = __get_order(BUFF_SIZE);
- for (n = 0; n < NR_BUFFERS; n++) {
- tape_buffer[n] = (byte *) dmaalloc(order);
- if (!tape_buffer[n]) {
- TRACE(1, "dmaalloc() failed");
- for (n = 0; n < NR_BUFFERS; n++) {
- if (tape_buffer[n]) {
- dmafree(tape_buffer[n], order);
- tape_buffer[n] = NULL;
- }
- }
- current->blocked = old_sigmask; /* restore mask */
- if (unregister_chrdev(QIC117_TAPE_MAJOR, "ft") != 0) {
- TRACE(3, "unregister_chrdev failed");
- }
- TRACE_EXIT;
- return -ENOMEM;
- } else {
- TRACEx2(3, "dma-buffer #%d @ %p", n, tape_buffer[n]);
- }
- }
- busy_flag = 0;
- ftape_unit = -1;
- ftape_failure = 1; /* inhibit any operation but open */
- udelay_calibrate(); /* must be before fdc_wait_calibrate ! */
- fdc_wait_calibrate();
- TRACE_EXIT;
-
- return 0;
-}
-
-
-#ifdef MODULE
-/* Called by modules package when removing the driver
- */
-void cleanup_module(void)
-{
- int n;
- int order;
- TRACE_FUN(5, "cleanup_module");
-
- if (unregister_chrdev(QIC117_TAPE_MAJOR, "ft") != 0) {
- TRACE(3, "failed");
- } else {
- TRACE(3, "successful");
- }
- order = __get_order(BUFF_SIZE);
- for (n = 0; n < NR_BUFFERS; n++) {
- if (tape_buffer[n]) {
- dmafree(tape_buffer[n], order);
- tape_buffer[n] = NULL;
- TRACEx1(3, "removed dma-buffer #%d", n);
- } else {
- TRACEx1(1, "dma-buffer #%d == NULL (bug?)", n);
- }
- }
- TRACE_EXIT;
-}
-#endif /* MODULE */
-
-/* Open ftape device
- */
-static int ftape_open(struct inode *ino, struct file *filep)
-{
- TRACE_FUN(4, "ftape_open");
- int result;
- MOD_INC_USE_COUNT; /* lock module in memory */
-
- TRACEi(5, "called for minor", MINOR(ino->i_rdev));
- if (busy_flag) {
- TRACE(1, "failed: already busy");
- MOD_DEC_USE_COUNT; /* unlock module in memory */
- TRACE_EXIT;
- return -EBUSY;
- }
- if ((MINOR(ino->i_rdev) & ~FTAPE_NO_REWIND) > 3) {
- TRACE(1, "failed: illegal unit nr");
- MOD_DEC_USE_COUNT; /* unlock module in memory */
- TRACE_EXIT;
- return -ENXIO;
- }
- if (ftape_unit == -1 || FTAPE_UNIT != (MINOR(ino->i_rdev) & 3)) {
- /* Other selection than last time
- */
- ftape_init_driver();
- }
- ftape_unit = MINOR(ino->i_rdev);
- ftape_failure = 0; /* allow tape operations */
- old_sigmask = current->blocked;
- current->blocked = _BLOCK_ALL;
- fdc_save_drive_specs(); /* save Drive Specification regs on i82078-1's */
- result = _ftape_open();
- if (result < 0) {
- TRACE(1, "_ftape_open failed");
- current->blocked = old_sigmask; /* restore mask */
- MOD_DEC_USE_COUNT; /* unlock module in memory */
- TRACE_EXIT;
- return result;
- } else {
- busy_flag = 1;
- /* Mask signals that will disturb proper operation of the
- * program that is calling.
- */
- current->blocked = old_sigmask | _DO_BLOCK;
- TRACE_EXIT;
- return 0;
- }
-}
-
-/* Close ftape device
- */
-static int ftape_close(struct inode *ino, struct file *filep)
-{
- TRACE_FUN(4, "ftape_close");
- int result;
-
- if (!busy_flag || MINOR(ino->i_rdev) != ftape_unit) {
- TRACE(1, "failed: not busy or wrong unit");
- TRACE_EXIT;
- return 0; /* keep busy_flag !(?) */
- }
- current->blocked = _BLOCK_ALL;
- result = _ftape_close();
- if (result < 0) {
- TRACE(1, "_ftape_close failed");
- }
- fdc_restore_drive_specs(); /* restore original values */
- ftape_failure = 1; /* inhibit any operation but open */
- busy_flag = 0;
- current->blocked = old_sigmask; /* restore before open state */
- TRACE_EXIT;
- MOD_DEC_USE_COUNT; /* unlock module in memory */
- return 0;
-}
-
-/* Ioctl for ftape device
- */
-static int ftape_ioctl(struct inode *ino, struct file *filep,
- unsigned int command, unsigned long arg)
-{
- TRACE_FUN(4, "ftape_ioctl");
- int result = -EIO;
- int old_sigmask;
-
- if (!busy_flag || MINOR(ino->i_rdev) != ftape_unit || ftape_failure) {
- TRACE(1, "failed: not busy, failure or wrong unit");
- TRACE_EXIT;
- return -EIO;
- }
- old_sigmask = current->blocked; /* save mask */
- current->blocked = _BLOCK_ALL;
- /* This will work as long as sizeof( void*) == sizeof( long)
- */
- result = _ftape_ioctl(command, (void *) arg);
- current->blocked = old_sigmask; /* restore mask */
- TRACE_EXIT;
- return result;
-}
-
-/* Read from tape device
- */
-static long ftape_read(struct inode *ino, struct file *fp,
- char *buff, unsigned long req_len)
-{
- TRACE_FUN(5, "ftape_read");
- int result = -EIO;
- int old_sigmask;
-
- TRACEi(5, "called with count:", (int) req_len);
- if (!busy_flag || MINOR(ino->i_rdev) != ftape_unit || ftape_failure) {
- TRACE(1, "failed: not busy, failure or wrong unit");
- TRACE_EXIT;
- return -EIO;
- }
- old_sigmask = current->blocked; /* save mask */
- current->blocked = _BLOCK_ALL;
- result = _ftape_read(buff, req_len);
- TRACEi(7, "return with count:", result);
- current->blocked = old_sigmask; /* restore mask */
- TRACE_EXIT;
- return result;
-}
-
-/* Write to tape device
- */
-static long ftape_write(struct inode *ino, struct file *fp,
- const char *buff, unsigned long req_len)
-{
- TRACE_FUN(8, "ftape_write");
- int result = -EIO;
- int old_sigmask;
-
- TRACEi(5, "called with count:", (int) req_len);
- if (!busy_flag || MINOR(ino->i_rdev) != ftape_unit || ftape_failure) {
- TRACE(1, "failed: not busy, failure or wrong unit");
- TRACE_EXIT;
- return -EIO;
- }
- old_sigmask = current->blocked; /* save mask */
- current->blocked = _BLOCK_ALL;
- result = _ftape_write(buff, req_len);
- TRACEi(7, "return with count:", result);
- current->blocked = old_sigmask; /* restore mask */
- TRACE_EXIT;
- return result;
-}
diff --git a/drivers/char/ftape/lowlevel/.cvsignore b/drivers/char/ftape/lowlevel/.cvsignore
new file mode 100644
index 000000000..4671378ae
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/.cvsignore
@@ -0,0 +1 @@
+.depend
diff --git a/drivers/char/ftape/lowlevel/Makefile b/drivers/char/ftape/lowlevel/Makefile
new file mode 100644
index 000000000..36834e85e
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/Makefile
@@ -0,0 +1,60 @@
+#
+# Copyright (C) 1996, 1997 Clau-Justus Heine.
+#
+# This program is free software; you can redistribute 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.
+#
+# $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/Makefile,v $
+# $Revision: 1.4 $
+# $Date: 1997/10/07 09:26:02 $
+#
+# Makefile for the lowlevel part QIC-40/80/3010/3020 floppy-tape
+# driver for Linux.
+#
+
+#
+# This isn't used inside the kernel, only for my private development
+# version
+#
+ifndef TOPDIR
+TOPDIR=../..
+include $(TOPDIR)/MCONFIG
+endif
+
+O_TARGET := ftape.o
+O_OBJS = ftape-init.o fdc-io.o fdc-isr.o \
+ ftape-bsm.o ftape-ctl.o ftape-read.o ftape-rw.o \
+ ftape-write.o ftape-io.o ftape-calibr.o ftape-ecc.o fc-10.o \
+ ftape-buffer.o ftape-format.o
+
+ifeq ($(CONFIG_FTAPE),y)
+O_OBJS += ftape-setup.o
+endif
+
+ifndef CONFIG_FT_NO_TRACE_AT_ALL
+O_OBJS += ftape-tracing.o
+endif
+
+ifeq ($(CONFIG_PROC_FS),y)
+ifeq ($(CONFIG_FT_PROC_FS),y)
+O_OBJS += ftape-proc.o
+endif
+endif
+
+OX_OBJS = ftape_syms.o
+
+M_OBJS = $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
+
diff --git a/drivers/char/ftape/fc-10.c b/drivers/char/ftape/lowlevel/fc-10.c
index d18d6501b..cbfd71f49 100644
--- a/drivers/char/ftape/fc-10.c
+++ b/drivers/char/ftape/lowlevel/fc-10.c
@@ -1,6 +1,6 @@
-/* Yo, Emacs! we're -*- Linux-C -*-
+/*
*
-
+
Copyright (C) 1993,1994 Jon Tombs.
This program is distributed in the hope that it will be useful,
@@ -37,81 +37,88 @@
Crider (gcrider@iclnet.org) for finding and locating this bug, as well as
testing the patch.
+ Modification on 11 December 96, by Claus Heine (claus@momo.math.rwth-aachen.de):
+ Modified a little to use variahle ft_fdc_base, ft_fdc_irq, ft_fdc_dma
+ instead of preprocessor symbols. Thus we can compile this into the module
+ or kernel and let the user specify the options as command line arguments.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fc-10.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:04 $
*
* This file contains code for the CMS FC-10/FC-20 card.
*/
-#include <linux/ftape.h>
#include <asm/io.h>
+#include <linux/ftape.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/fc-10.h"
-#include "tracing.h"
-#include "fdc-io.h"
-#include "fc-10.h"
-
-#ifdef PROBE_FC10
-
-/* This code will only work if the FC-10 (or FC-20) is set to
- * use DMA channels 1, 2, or 3. DMA channels 5 and 7 seem to be
- * initialized by the same command as channels 1 and 3, respectively.
- */
-#if (FDC_DMA > 3)
-#error : The FC-10/20 must be set to use DMA channels 1, 2, or 3!
-#endif
-
-/* Only allow the FC-10/20 to use IRQ 3-7, or 9. Note that CMS's program
- * only accepts IRQ's 2-7, but in linux, IRQ 2 is the same as IRQ 9.
- */
-#if (FDC_IRQ < 3 || FDC_IRQ == 8 || FDC_IRQ > 9)
-#error : The FC-10/20 must be set to use IRQ levels 3 - 7, or 9!
-#error : Note IRQ 9 is the same as IRQ 2
-#endif
-
-unsigned short inbs_magic[] = {
+__u16 inbs_magic[] = {
0x3, 0x3, 0x0, 0x4, 0x7, 0x2, 0x5, 0x3, 0x1, 0x4,
0x3, 0x5, 0x2, 0x0, 0x3, 0x7, 0x4, 0x2,
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7
};
-unsigned short fc10_ports[] = {
+__u16 fc10_ports[] = {
0x180, 0x210, 0x2A0, 0x300, 0x330, 0x340, 0x370
};
int fc10_enable(void)
{
int i;
- byte cardConfig = 0x00;
- byte x;
+ __u8 cardConfig = 0x00;
+ __u8 x;
+ TRACE_FUN(ft_t_flow);
+/* This code will only work if the FC-10 (or FC-20) is set to
+ * use DMA channels 1, 2, or 3. DMA channels 5 and 7 seem to be
+ * initialized by the same command as channels 1 and 3, respectively.
+ */
+ if (ft_fdc_dma > 3) {
+ TRACE_ABORT(0, ft_t_err,
+"Error: The FC-10/20 must be set to use DMA channels 1, 2, or 3!");
+ }
+/* Only allow the FC-10/20 to use IRQ 3-7, or 9. Note that CMS's program
+ * only accepts IRQ's 2-7, but in linux, IRQ 2 is the same as IRQ 9.
+ */
+ if (ft_fdc_irq < 3 || ft_fdc_irq == 8 || ft_fdc_irq > 9) {
+ TRACE_ABORT(0, ft_t_err,
+"Error: The FC-10/20 must be set to use IRQ levels 3 - 7, or 9!\n"
+KERN_INFO "Note: IRQ 9 is the same as IRQ 2");
+ }
/* Clear state machine ???
*/
for (i = 0; i < NR_ITEMS(inbs_magic); i++) {
- inb(FDC_BASE + inbs_magic[i]);
+ inb(ft_fdc_base + inbs_magic[i]);
}
- outb(0x0, FDC_BASE);
+ outb(0x0, ft_fdc_base);
- x = inb(FDC_BASE);
+ x = inb(ft_fdc_base);
if (x == 0x13 || x == 0x93) {
for (i = 1; i < 8; i++) {
- if (inb(FDC_BASE + i) != x) {
- return 0;
+ if (inb(ft_fdc_base + i) != x) {
+ TRACE_EXIT 0;
}
}
} else {
- return 0;
+ TRACE_EXIT 0;
}
- outb(0x8, FDC_BASE);
+ outb(0x8, ft_fdc_base);
for (i = 0; i < 8; i++) {
- if (inb(FDC_BASE + i) != 0x0) {
- return 0;
+ if (inb(ft_fdc_base + i) != 0x0) {
+ TRACE_EXIT 0;
}
}
- outb(0x10, FDC_BASE);
+ outb(0x10, ft_fdc_base);
for (i = 0; i < 8; i++) {
- if (inb(FDC_BASE + i) != 0xff) {
- return 0;
+ if (inb(ft_fdc_base + i) != 0xff) {
+ TRACE_EXIT 0;
}
}
@@ -122,39 +129,47 @@ int fc10_enable(void)
/* Clear state machine again ???
*/
for (i = 0; i < NR_ITEMS(inbs_magic); i++) {
- inb(FDC_BASE + inbs_magic[i]);
+ inb(ft_fdc_base + inbs_magic[i]);
}
/* Send io port */
for (i = 0; i < NR_ITEMS(fc10_ports); i++)
- if (FDC_BASE == fc10_ports[i])
+ if (ft_fdc_base == fc10_ports[i])
cardConfig = i + 1;
- if (cardConfig == 0)
- return 0; /* Invalid I/O Port */
+ if (cardConfig == 0) {
+ TRACE_EXIT 0; /* Invalid I/O Port */
+ }
/* and IRQ - If using IRQ 9, tell the FC card it is actually IRQ 2 */
- if (FDC_IRQ != 9)
- cardConfig |= FDC_IRQ << 3;
+ if (ft_fdc_irq != 9)
+ cardConfig |= ft_fdc_irq << 3;
else
cardConfig |= 2 << 3;
/* and finally DMA Channel */
- cardConfig |= FDC_DMA << 6;
- outb(cardConfig, FDC_BASE); /* DMA [2 bits]/IRQ [3 bits]/BASE [3 bits] */
+ cardConfig |= ft_fdc_dma << 6;
+ outb(cardConfig, ft_fdc_base); /* DMA [2 bits]/IRQ [3 bits]/BASE [3 bits] */
/* Enable FC-10 ???
*/
outb(0, fdc.ccr);
- outb(0, FDC_BASE + 0x6);
- outb(8, fdc.dor);
- outb(8, fdc.dor);
- outb(1, FDC_BASE + 0x6);
-
+ outb(0, fdc.dor2);
+ outb(FDC_DMA_MODE /* 8 */, fdc.dor);
+ outb(FDC_DMA_MODE /* 8 */, fdc.dor);
+ outb(1, fdc.dor2);
+
+ /*************************************
+ *
+ * cH: why the hell should this be necessary? This is done
+ * by fdc_reset()!!!
+ *
+ *************************************/
/* Initialize fdc, select drive B:
*/
- outb(0x08, fdc.dor); /* assert reset, dma & irq enabled */
- outb(0x0c, fdc.dor); /* release reset */
- outb(0x2d, fdc.dor); /* select drive 1 */
-
- return (x == 0x93) ? 2 : 1;
+ outb(FDC_DMA_MODE, fdc.dor); /* assert reset, dma & irq enabled */
+ /* 0x08 */
+ outb(FDC_DMA_MODE|FDC_RESET_NOT, fdc.dor); /* release reset */
+ /* 0x08 | 0x04 = 0x0c */
+ outb(FDC_DMA_MODE|FDC_RESET_NOT|FDC_MOTOR_1|FTAPE_SEL_B, fdc.dor);
+ /* 0x08 | 0x04 | 0x20 | 0x01 = 0x2d */
+ /* select drive 1 */ /* why not drive 0 ???? */
+ TRACE_EXIT (x == 0x93) ? 2 : 1;
}
-
-#endif /* CMS_FC10_CONTROLLER */
diff --git a/drivers/char/ftape/fc-10.h b/drivers/char/ftape/lowlevel/fc-10.h
index f38d4685e..da7b88bca 100644
--- a/drivers/char/ftape/fc-10.h
+++ b/drivers/char/ftape/lowlevel/fc-10.h
@@ -2,7 +2,7 @@
#define _FC_10_H
/*
- * Copyright (C) 1994 Bas Laarhoven.
+ * Copyright (C) 1994-1996 Bas Laarhoven.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,12 +19,9 @@
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
- $Source: /home/bas/distr/ftape-2.03b/RCS/fc-10.h,v $
- $Author: bas $
- *
- $Revision: 1.3 $
- $Date: 1995/01/08 14:16:21 $
- $State: Beta $
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fc-10.h,v $
+ * $Revision: 1.1 $
+ * $Date: 1997/09/19 09:05:22 $
*
* This file contains definitions for the FC-10 code
* of the QIC-40/80 floppy-tape driver for Linux.
diff --git a/drivers/char/ftape/lowlevel/fdc-io.c b/drivers/char/ftape/lowlevel/fdc-io.c
new file mode 100644
index 000000000..904a99ac5
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/fdc-io.c
@@ -0,0 +1,1439 @@
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-io.c,v $
+ * $Revision: 1.7.4.2 $
+ * $Date: 1997/11/16 14:48:17 $
+ *
+ * This file contains the low-level floppy disk interface code
+ * for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for
+ * Linux.
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/version.h>
+#include <linux/interrupt.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/irq.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/fdc-isr.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-calibr.h"
+#include "../lowlevel/fc-10.h"
+
+/* Global vars.
+ */
+int ftape_motor = 0;
+volatile int ftape_current_cylinder = -1;
+volatile fdc_mode_enum fdc_mode = fdc_idle;
+fdc_config_info fdc = {0};
+struct wait_queue *ftape_wait_intr = NULL;
+
+unsigned int ft_fdc_base = CONFIG_FT_FDC_BASE;
+unsigned int ft_fdc_irq = CONFIG_FT_FDC_IRQ;
+unsigned int ft_fdc_dma = CONFIG_FT_FDC_DMA;
+unsigned int ft_fdc_threshold = CONFIG_FT_FDC_THR; /* bytes */
+unsigned int ft_fdc_rate_limit = CONFIG_FT_FDC_MAX_RATE; /* bits/sec */
+int ft_probe_fc10 = CONFIG_FT_PROBE_FC10;
+int ft_mach2 = CONFIG_FT_MACH2;
+
+/* Local vars.
+ */
+static unsigned int fdc_calibr_count;
+static unsigned int fdc_calibr_time;
+static int fdc_status;
+volatile __u8 fdc_head; /* FDC head from sector id */
+volatile __u8 fdc_cyl; /* FDC track from sector id */
+volatile __u8 fdc_sect; /* FDC sector from sector id */
+static int fdc_data_rate = 500; /* data rate (Kbps) */
+static int fdc_rate_code = 0; /* data rate code (0 == 500 Kbps) */
+static int fdc_seek_rate = 2; /* step rate (msec) */
+static void (*do_ftape) (void);
+static int fdc_fifo_state; /* original fifo setting - fifo enabled */
+static int fdc_fifo_thr; /* original fifo setting - threshold */
+static int fdc_lock_state; /* original lock setting - locked */
+static int fdc_fifo_locked = 0; /* has fifo && lock set ? */
+static __u8 fdc_precomp = 0; /* default precomp. value (nsec) */
+static __u8 fdc_prec_code = 0; /* fdc precomp. select code */
+
+static char ftape_id[] = "ftape"; /* used by request irq and free irq */
+
+void fdc_catch_stray_interrupts(int count)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (count == 0) {
+ ft_expected_stray_interrupts = 0;
+ } else {
+ ft_expected_stray_interrupts += count;
+ }
+ restore_flags(flags);
+}
+
+/* Wait during a timeout period for a given FDC status.
+ * If usecs == 0 then just test status, else wait at least for usecs.
+ * Returns -ETIME on timeout. Function must be calibrated first !
+ */
+int fdc_wait(unsigned int usecs, __u8 mask, __u8 state)
+{
+ int count_1 = (fdc_calibr_count * usecs +
+ fdc_calibr_count - 1) / fdc_calibr_time;
+
+ do {
+ fdc_status = inb_p(fdc.msr);
+ if ((fdc_status & mask) == state) {
+ return 0;
+ }
+ } while (count_1-- >= 0);
+ return -ETIME;
+}
+
+int fdc_ready_wait(unsigned int usecs)
+{
+ return fdc_wait(usecs, FDC_DATA_READY | FDC_BUSY, FDC_DATA_READY);
+}
+
+/* Why can't we just use udelay()?
+ */
+static void fdc_usec_wait(unsigned int usecs)
+{
+ fdc_wait(usecs, 0, 1); /* will always timeout ! */
+}
+
+int fdc_ready_out_wait(unsigned int usecs)
+{
+ fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */
+ return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_OUT_READY);
+}
+
+int fdc_ready_in_wait(unsigned int usecs)
+{
+ fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */
+ return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_IN_READY);
+}
+
+void fdc_wait_calibrate(void)
+{
+ ftape_calibrate("fdc_wait",
+ fdc_usec_wait, &fdc_calibr_count, &fdc_calibr_time);
+}
+
+/* Wait for a (short) while for the FDC to become ready
+ * and transfer the next command byte.
+ * Return -ETIME on timeout on getting ready (depends on hardware!).
+ */
+static int fdc_write(const __u8 data)
+{
+ fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */
+ if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_IN_READY) < 0) {
+ return -ETIME;
+ } else {
+ outb(data, fdc.fifo);
+ return 0;
+ }
+}
+
+/* Wait for a (short) while for the FDC to become ready
+ * and transfer the next result byte.
+ * Return -ETIME if timeout on getting ready (depends on hardware!).
+ */
+static int fdc_read(__u8 * data)
+{
+ fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */
+ if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_OUT_READY) < 0) {
+ return -ETIME;
+ } else {
+ *data = inb(fdc.fifo);
+ return 0;
+ }
+}
+
+/* Output a cmd_len long command string to the FDC.
+ * The FDC should be ready to receive a new command or
+ * an error (EBUSY or ETIME) will occur.
+ */
+int fdc_command(const __u8 * cmd_data, int cmd_len)
+{
+ int result = 0;
+ unsigned long flags;
+ int count = cmd_len;
+ int retry = 0;
+#ifdef TESTING
+ static unsigned int last_time = 0;
+ unsigned int time;
+#endif
+ TRACE_FUN(ft_t_any);
+
+ fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,30)
+ if (!in_interrupt())
+#else
+ if (!intr_count)
+#endif
+ /* Yes, I know, too much comments inside this function
+ * ...
+ *
+ * Yet another bug in the original driver. All that
+ * havoc is caused by the fact that the isr() sends
+ * itself a command to the floppy tape driver (pause,
+ * micro step pause). Now, the problem is that
+ * commands are transmitted via the fdc_seek
+ * command. But: the fdc performs seeks in the
+ * background i.e. it doesn't signal busy while
+ * sending the step pulses to the drive. Therefore the
+ * non-interrupt level driver has no chance to tell
+ * whether the isr() just has issued a seek. Therefore
+ * we HAVE TO have a look at the the ft_hide_interrupt
+ * flag: it signals the non-interrupt level part of
+ * the driver that it has to wait for the fdc until it
+ * has completet seeking.
+ *
+ * THIS WAS PRESUMABLY THE REASON FOR ALL THAT
+ * "fdc_read timeout" errors, I HOPE :-)
+ */
+ if (ft_hide_interrupt) {
+ restore_flags(flags);
+ TRACE(ft_t_info,
+ "Waiting for the isr() completing fdc_seek()");
+ if (fdc_interrupt_wait(2 * FT_SECOND) < 0) {
+ TRACE(ft_t_warn,
+ "Warning: timeout waiting for isr() seek to complete");
+ }
+ if (ft_hide_interrupt || !ft_seek_completed) {
+ /* There cannot be another
+ * interrupt. The isr() only stops
+ * the tape and the next interrupt
+ * won't come until we have send our
+ * command to the drive.
+ */
+ TRACE_ABORT(-EIO, ft_t_bug,
+ "BUG? isr() is still seeking?\n"
+ KERN_INFO "hide: %d\n"
+ KERN_INFO "seek: %d",
+ ft_hide_interrupt,
+ ft_seek_completed);
+
+ }
+ fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */
+ save_flags(flags);
+ cli();
+ }
+ fdc_status = inb(fdc.msr);
+ if ((fdc_status & FDC_DATA_READY_MASK) != FDC_DATA_IN_READY) {
+ restore_flags(flags);
+ TRACE_ABORT(-EBUSY, ft_t_err, "fdc not ready");
+ }
+ fdc_mode = *cmd_data; /* used by isr */
+#ifdef TESTING
+ if (fdc_mode == FDC_SEEK) {
+ time = ftape_timediff(last_time, ftape_timestamp());
+ if (time < 6000) {
+ TRACE(ft_t_bug,"Warning: short timeout between seek commands: %d",
+ time);
+ }
+ }
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,30)
+ if (!in_interrupt()) {
+ /* shouldn't be cleared if called from isr
+ */
+ ft_interrupt_seen = 0;
+ }
+#else
+ if (!intr_count) {
+ /* shouldn't be cleared if called from isr
+ */
+ ft_interrupt_seen = 0;
+ }
+#endif
+ while (count) {
+ result = fdc_write(*cmd_data);
+ if (result < 0) {
+ TRACE(ft_t_fdc_dma,
+ "fdc_mode = %02x, status = %02x at index %d",
+ (int) fdc_mode, (int) fdc_status,
+ cmd_len - count);
+ if (++retry <= 3) {
+ TRACE(ft_t_warn, "fdc_write timeout, retry");
+ } else {
+ TRACE(ft_t_err, "fdc_write timeout, fatal");
+ /* recover ??? */
+ break;
+ }
+ } else {
+ --count;
+ ++cmd_data;
+ }
+ }
+#ifdef TESTING
+ if (fdc_mode == FDC_SEEK) {
+ last_time = ftape_timestamp();
+ }
+#endif
+ restore_flags(flags);
+ TRACE_EXIT result;
+}
+
+/* Input a res_len long result string from the FDC.
+ * The FDC should be ready to send the result or an error
+ * (EBUSY or ETIME) will occur.
+ */
+int fdc_result(__u8 * res_data, int res_len)
+{
+ int result = 0;
+ unsigned long flags;
+ int count = res_len;
+ int retry = 0;
+ TRACE_FUN(ft_t_any);
+
+ save_flags(flags);
+ cli();
+ fdc_status = inb(fdc.msr);
+ if ((fdc_status & FDC_DATA_READY_MASK) != FDC_DATA_OUT_READY) {
+ TRACE(ft_t_err, "fdc not ready");
+ result = -EBUSY;
+ } else while (count) {
+ if (!(fdc_status & FDC_BUSY)) {
+ restore_flags(flags);
+ TRACE_ABORT(-EIO, ft_t_err, "premature end of result phase");
+ }
+ result = fdc_read(res_data);
+ if (result < 0) {
+ TRACE(ft_t_fdc_dma,
+ "fdc_mode = %02x, status = %02x at index %d",
+ (int) fdc_mode,
+ (int) fdc_status,
+ res_len - count);
+ if (++retry <= 3) {
+ TRACE(ft_t_warn, "fdc_read timeout, retry");
+ } else {
+ TRACE(ft_t_err, "fdc_read timeout, fatal");
+ /* recover ??? */
+ break;
+ ++retry;
+ }
+ } else {
+ --count;
+ ++res_data;
+ }
+ }
+ restore_flags(flags);
+ fdc_usec_wait(FT_RQM_DELAY); /* allow FDC to negate BSY */
+ TRACE_EXIT result;
+}
+
+/* Handle command and result phases for
+ * commands without data phase.
+ */
+int fdc_issue_command(const __u8 * out_data, int out_count,
+ __u8 * in_data, int in_count)
+{
+ TRACE_FUN(ft_t_any);
+
+ if (out_count > 0) {
+ TRACE_CATCH(fdc_command(out_data, out_count),);
+ }
+ /* will take 24 - 30 usec for fdc_sense_drive_status and
+ * fdc_sense_interrupt_status commands.
+ * 35 fails sometimes (5/9/93 SJL)
+ * On a loaded system it incidentally takes longer than
+ * this for the fdc to get ready ! ?????? WHY ??????
+ * So until we know what's going on use a very long timeout.
+ */
+ TRACE_CATCH(fdc_ready_out_wait(500 /* usec */),);
+ if (in_count > 0) {
+ TRACE_CATCH(fdc_result(in_data, in_count),
+ TRACE(ft_t_err, "result phase aborted"));
+ }
+ TRACE_EXIT 0;
+}
+
+/* Wait for FDC interrupt with timeout (in milliseconds).
+ * Signals are blocked so the wait will not be aborted.
+ * Note: interrupts must be enabled ! (23/05/93 SJL)
+ */
+int fdc_interrupt_wait(unsigned int time)
+{
+ struct wait_queue wait = {current, NULL};
+ int current_blocked = current->blocked;
+ static int resetting = 0;
+ TRACE_FUN(ft_t_fdc_dma);
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,16)
+ if (waitqueue_active(&ftape_wait_intr)) {
+ TRACE_ABORT(-EIO, ft_t_err, "error: nested call");
+ }
+#else
+ if (ftape_wait_intr) {
+ TRACE_ABORT(-EIO, ft_t_err, "error: nested call");
+ }
+#endif
+ /* timeout time will be up to USPT microseconds too long ! */
+ current->timeout = jiffies + (1000 * time + FT_USPT - 1) / FT_USPT;
+ current->state = TASK_INTERRUPTIBLE;
+ current->blocked = _BLOCK_ALL; /* isn't this already set by the
+ * high level routines?
+ */
+ add_wait_queue(&ftape_wait_intr, &wait);
+ while (!ft_interrupt_seen && current->state != TASK_RUNNING) {
+ schedule(); /* sets TASK_RUNNING on timeout */
+ }
+ current->blocked = current_blocked; /* restore */
+ remove_wait_queue(&ftape_wait_intr, &wait);
+ /* the following IS necessary. True: as well
+ * wake_up_interruptible() as the schedule() set TASK_RUNNING
+ * when they wakeup a task, BUT: it may very well be that
+ * ft_interrupt_seen is already set to 1 when we enter here
+ * in which case schedule() gets never called, and
+ * TASK_RUNNING never set. This has the funny effect that we
+ * execute all the code until we leave kernel space, but then
+ * the task is stopped (a task CANNOT be preempted while in
+ * kernel mode. Sending a pair of SIGSTOP/SIGCONT to the
+ * tasks wakes it up again. Funny! :-)
+ */
+ current->state = TASK_RUNNING;
+ if (ft_interrupt_seen) { /* woken up by interrupt */
+ current->timeout = 0; /* interrupt hasn't cleared this */
+ ft_interrupt_seen = 0;
+ TRACE_EXIT 0;
+ }
+ /* Original comment:
+ * In first instance, next statement seems unnecessary since
+ * it will be cleared in fdc_command. However, a small part of
+ * the software seems to rely on this being cleared here
+ * (ftape_close might fail) so stick to it until things get fixed !
+ */
+ /* My deeply sought of knowledge:
+ * Behold NO! It is obvious. fdc_reset() doesn't call fdc_command()
+ * but nevertheless uses fdc_interrupt_wait(). OF COURSE this needs to
+ * be reset here.
+ */
+ ft_interrupt_seen = 0; /* clear for next call */
+ if (!resetting) {
+ resetting = 1; /* break infinite recursion if reset fails */
+ TRACE(ft_t_any, "cleanup reset");
+ fdc_reset();
+ resetting = 0;
+ }
+ TRACE_EXIT (current->signal & ~current->blocked) ? -EINTR : -ETIME;
+}
+
+/* Start/stop drive motor. Enable DMA mode.
+ */
+void fdc_motor(int motor)
+{
+ int unit = ft_drive_sel;
+ int data = unit | FDC_RESET_NOT | FDC_DMA_MODE;
+ TRACE_FUN(ft_t_any);
+
+ ftape_motor = motor;
+ if (ftape_motor) {
+ data |= FDC_MOTOR_0 << unit;
+ TRACE(ft_t_noise, "turning motor %d on", unit);
+ } else {
+ TRACE(ft_t_noise, "turning motor %d off", unit);
+ }
+ if (ft_mach2) {
+ outb_p(data, fdc.dor2);
+ } else {
+ outb_p(data, fdc.dor);
+ }
+ ftape_sleep(10 * FT_MILLISECOND);
+ TRACE_EXIT;
+}
+
+static void fdc_update_dsr(void)
+{
+ TRACE_FUN(ft_t_any);
+
+ TRACE(ft_t_flow, "rate = %d Kbps, precomp = %d ns",
+ fdc_data_rate, fdc_precomp);
+ if (fdc.type >= i82077) {
+ outb_p((fdc_rate_code & 0x03) | fdc_prec_code, fdc.dsr);
+ } else {
+ outb_p(fdc_rate_code & 0x03, fdc.ccr);
+ }
+ TRACE_EXIT;
+}
+
+void fdc_set_write_precomp(int precomp)
+{
+ TRACE_FUN(ft_t_any);
+
+ TRACE(ft_t_noise, "New precomp: %d nsec", precomp);
+ fdc_precomp = precomp;
+ /* write precompensation can be set in multiples of 41.67 nsec.
+ * round the parameter to the nearest multiple and convert it
+ * into a fdc setting. Note that 0 means default to the fdc,
+ * 7 is used instead of that.
+ */
+ fdc_prec_code = ((fdc_precomp + 21) / 42) << 2;
+ if (fdc_prec_code == 0 || fdc_prec_code > (6 << 2)) {
+ fdc_prec_code = 7 << 2;
+ }
+ fdc_update_dsr();
+ TRACE_EXIT;
+}
+
+/* Reprogram the 82078 registers to use Data Rate Table 1 on all drives.
+ */
+void fdc_set_drive_specs(void)
+{
+ __u8 cmd[] = { FDC_DRIVE_SPEC, 0x00, 0x00, 0x00, 0x00, 0xc0};
+ int result;
+ TRACE_FUN(ft_t_any);
+
+ TRACE(ft_t_flow, "Setting of drive specs called");
+ if (fdc.type >= i82078_1) {
+ cmd[1] = (0 << 5) | (2 << 2);
+ cmd[2] = (1 << 5) | (2 << 2);
+ cmd[3] = (2 << 5) | (2 << 2);
+ cmd[4] = (3 << 5) | (2 << 2);
+ result = fdc_command(cmd, NR_ITEMS(cmd));
+ if (result < 0) {
+ TRACE(ft_t_err, "Setting of drive specs failed");
+ }
+ }
+ TRACE_EXIT;
+}
+
+/* Select clock for fdc, must correspond with tape drive setting !
+ * This also influences the fdc timing so we must adjust some values.
+ */
+int fdc_set_data_rate(int rate)
+{
+ int bad_rate = 0;
+ TRACE_FUN(ft_t_any);
+
+ /* Select clock for fdc, must correspond with tape drive setting !
+ * This also influences the fdc timing so we must adjust some values.
+ */
+ TRACE(ft_t_fdc_dma, "new rate = %d", rate);
+ switch (rate) {
+ case 250:
+ fdc_rate_code = fdc_data_rate_250;
+ break;
+ case 500:
+ fdc_rate_code = fdc_data_rate_500;
+ break;
+ case 1000:
+ if (fdc.type < i82077) {
+ bad_rate = 1;
+ } else {
+ fdc_rate_code = fdc_data_rate_1000;
+ }
+ break;
+ case 2000:
+ if (fdc.type < i82078_1) {
+ bad_rate = 1;
+ } else {
+ fdc_rate_code = fdc_data_rate_2000;
+ }
+ break;
+ default:
+ bad_rate = 1;
+ }
+ if (bad_rate) {
+ TRACE_ABORT(-EIO,
+ ft_t_fdc_dma, "%d is not a valid data rate", rate);
+ }
+ fdc_data_rate = rate;
+ fdc_update_dsr();
+ fdc_set_seek_rate(fdc_seek_rate); /* clock changed! */
+ ftape_udelay(1000);
+ TRACE_EXIT 0;
+}
+
+/* keep the unit select if keep_select is != 0,
+ */
+static void fdc_dor_reset(int keep_select)
+{
+ __u8 fdc_ctl = ft_drive_sel;
+
+ if (keep_select != 0) {
+ fdc_ctl |= FDC_DMA_MODE;
+ if (ftape_motor) {
+ fdc_ctl |= FDC_MOTOR_0 << ft_drive_sel;
+ }
+ }
+ ftape_udelay(10); /* ??? but seems to be necessary */
+ if (ft_mach2) {
+ outb_p(fdc_ctl & 0x0f, fdc.dor);
+ outb_p(fdc_ctl, fdc.dor2);
+ } else {
+ outb_p(fdc_ctl, fdc.dor);
+ }
+ fdc_usec_wait(10); /* delay >= 14 fdc clocks */
+ if (keep_select == 0) {
+ fdc_ctl = 0;
+ }
+ fdc_ctl |= FDC_RESET_NOT;
+ if (ft_mach2) {
+ outb_p(fdc_ctl & 0x0f, fdc.dor);
+ outb_p(fdc_ctl, fdc.dor2);
+ } else {
+ outb_p(fdc_ctl, fdc.dor);
+ }
+}
+
+/* Reset the floppy disk controller. Leave the ftape_unit selected.
+ */
+void fdc_reset(void)
+{
+ int st0;
+ int i;
+ int dummy;
+ unsigned long flags;
+ TRACE_FUN(ft_t_any);
+
+ save_flags(flags);
+ cli();
+
+ fdc_dor_reset(1); /* keep unit selected */
+
+ fdc_mode = fdc_idle;
+
+ /* maybe the cli()/sti() pair is not necessary, BUT:
+ * the following line MUST be here. Otherwise fdc_interrupt_wait()
+ * won't wait. Note that fdc_reset() is called from
+ * ftape_dumb_stop() when the fdc is busy transferring data. In this
+ * case fdc_isr() MOST PROBABLY sets ft_interrupt_seen, and tries
+ * to get the result bytes from the fdc etc. CLASH.
+ */
+ ft_interrupt_seen = 0;
+
+ /* Program data rate
+ */
+ fdc_update_dsr(); /* restore data rate and precomp */
+
+ restore_flags(flags);
+
+ /*
+ * Wait for first polling cycle to complete
+ */
+ if (fdc_interrupt_wait(1 * FT_SECOND) < 0) {
+ TRACE(ft_t_err, "no drive polling interrupt!");
+ } else { /* clear all disk-changed statuses */
+ for (i = 0; i < 4; ++i) {
+ if(fdc_sense_interrupt_status(&st0, &dummy) != 0) {
+ TRACE(ft_t_err, "sense failed for %d", i);
+ }
+ if (i == ft_drive_sel) {
+ ftape_current_cylinder = dummy;
+ }
+ }
+ TRACE(ft_t_noise, "drive polling completed");
+ }
+ /*
+ * SPECIFY COMMAND
+ */
+ fdc_set_seek_rate(fdc_seek_rate);
+ /*
+ * DRIVE SPECIFICATION COMMAND (if fdc type known)
+ */
+ if (fdc.type >= i82078_1) {
+ fdc_set_drive_specs();
+ }
+ TRACE_EXIT;
+}
+
+#if !defined(CLK_48MHZ)
+# define CLK_48MHZ 1
+#endif
+
+/* When we're done, put the fdc into reset mode so that the regular
+ * floppy disk driver will figure out that something is wrong and
+ * initialize the controller the way it wants.
+ */
+void fdc_disable(void)
+{
+ __u8 cmd1[] = {FDC_CONFIGURE, 0x00, 0x00, 0x00};
+ __u8 cmd2[] = {FDC_LOCK};
+ __u8 cmd3[] = {FDC_UNLOCK};
+ __u8 stat[1];
+ TRACE_FUN(ft_t_flow);
+
+ if (!fdc_fifo_locked) {
+ fdc_reset();
+ TRACE_EXIT;
+ }
+ if (fdc_issue_command(cmd3, 1, stat, 1) < 0 || stat[0] != 0x00) {
+ fdc_dor_reset(0);
+ TRACE_ABORT(/**/, ft_t_bug,
+ "couldn't unlock fifo, configuration remains changed");
+ }
+ fdc_fifo_locked = 0;
+ if (CLK_48MHZ && fdc.type >= i82078) {
+ cmd1[0] |= FDC_CLK48_BIT;
+ }
+ cmd1[2] = ((fdc_fifo_state) ? 0 : 0x20) + (fdc_fifo_thr - 1);
+ if (fdc_command(cmd1, NR_ITEMS(cmd1)) < 0) {
+ fdc_dor_reset(0);
+ TRACE_ABORT(/**/, ft_t_bug,
+ "couldn't reconfigure fifo to old state");
+ }
+ if (fdc_lock_state &&
+ fdc_issue_command(cmd2, 1, stat, 1) < 0) {
+ fdc_dor_reset(0);
+ TRACE_ABORT(/**/, ft_t_bug, "couldn't lock old state again");
+ }
+ TRACE(ft_t_noise, "fifo restored: %sabled, thr. %d, %slocked",
+ fdc_fifo_state ? "en" : "dis",
+ fdc_fifo_thr, (fdc_lock_state) ? "" : "not ");
+ fdc_dor_reset(0);
+ TRACE_EXIT;
+}
+
+/* Specify FDC seek-rate (milliseconds)
+ */
+int fdc_set_seek_rate(int seek_rate)
+{
+ /* set step rate, dma mode, and minimal head load and unload times
+ */
+ __u8 in[3] = { FDC_SPECIFY, 1, (1 << 1)};
+
+ fdc_seek_rate = seek_rate;
+ in[1] |= (16 - (fdc_data_rate * fdc_seek_rate) / 500) << 4;
+
+ return fdc_command(in, 3);
+}
+
+/* Sense drive status: get unit's drive status (ST3)
+ */
+int fdc_sense_drive_status(int *st3)
+{
+ __u8 out[2];
+ __u8 in[1];
+ TRACE_FUN(ft_t_any);
+
+ out[0] = FDC_SENSED;
+ out[1] = ft_drive_sel;
+ TRACE_CATCH(fdc_issue_command(out, 2, in, 1),);
+ *st3 = in[0];
+ TRACE_EXIT 0;
+}
+
+/* Sense Interrupt Status command:
+ * should be issued at the end of each seek.
+ * get ST0 and current cylinder.
+ */
+int fdc_sense_interrupt_status(int *st0, int *current_cylinder)
+{
+ __u8 out[1];
+ __u8 in[2];
+ TRACE_FUN(ft_t_any);
+
+ out[0] = FDC_SENSEI;
+ TRACE_CATCH(fdc_issue_command(out, 1, in, 2),);
+ *st0 = in[0];
+ *current_cylinder = in[1];
+ TRACE_EXIT 0;
+}
+
+/* step to track
+ */
+int fdc_seek(int track)
+{
+ __u8 out[3];
+ int st0, pcn;
+#ifdef TESTING
+ unsigned int time;
+#endif
+ TRACE_FUN(ft_t_any);
+
+ out[0] = FDC_SEEK;
+ out[1] = ft_drive_sel;
+ out[2] = track;
+#ifdef TESTING
+ time = ftape_timestamp();
+#endif
+ /* We really need this command to work !
+ */
+ ft_seek_completed = 0;
+ TRACE_CATCH(fdc_command(out, 3),
+ fdc_reset();
+ TRACE(ft_t_noise, "destination was: %d, resetting FDC...",
+ track));
+ /* Handle interrupts until ft_seek_completed or timeout.
+ */
+ for (;;) {
+ TRACE_CATCH(fdc_interrupt_wait(2 * FT_SECOND),);
+ if (ft_seek_completed) {
+ TRACE_CATCH(fdc_sense_interrupt_status(&st0, &pcn),);
+ if ((st0 & ST0_SEEK_END) == 0) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "no seek-end after seek completion !??");
+ }
+ break;
+ }
+ }
+#ifdef TESTING
+ time = ftape_timediff(time, ftape_timestamp()) / ABS(track - ftape_current_cylinder);
+ if ((time < 900 || time > 3100) && ABS(track - ftape_current_cylinder) > 5) {
+ TRACE(ft_t_warn, "Wrong FDC STEP interval: %d usecs (%d)",
+ time, track - ftape_current_cylinder);
+ }
+#endif
+ /* Verify whether we issued the right tape command.
+ */
+ /* Verify that we seek to the proper track. */
+ if (pcn != track) {
+ TRACE_ABORT(-EIO, ft_t_err, "bad seek..");
+ }
+ ftape_current_cylinder = track;
+ TRACE_EXIT 0;
+}
+
+/* Recalibrate and wait until home.
+ */
+int fdc_recalibrate(void)
+{
+ __u8 out[2];
+ int st0;
+ int pcn;
+ int retry;
+ int old_seek_rate = fdc_seek_rate;
+ TRACE_FUN(ft_t_any);
+
+ TRACE_CATCH(fdc_set_seek_rate(6),);
+ out[0] = FDC_RECAL;
+ out[1] = ft_drive_sel;
+ ft_seek_completed = 0;
+ TRACE_CATCH(fdc_command(out, 2),);
+ /* Handle interrupts until ft_seek_completed or timeout.
+ */
+ for (retry = 0;; ++retry) {
+ TRACE_CATCH(fdc_interrupt_wait(2 * FT_SECOND),);
+ if (ft_seek_completed) {
+ TRACE_CATCH(fdc_sense_interrupt_status(&st0, &pcn),);
+ if ((st0 & ST0_SEEK_END) == 0) {
+ if (retry < 1) {
+ continue; /* some drives/fdc's
+ * give an extra interrupt
+ */
+ } else {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "no seek-end after seek completion !??");
+ }
+ }
+ break;
+ }
+ }
+ ftape_current_cylinder = pcn;
+ if (pcn != 0) {
+ TRACE(ft_t_err, "failed: resulting track = %d", pcn);
+ }
+ TRACE_CATCH(fdc_set_seek_rate(old_seek_rate),);
+ TRACE_EXIT 0;
+}
+
+static int perpend_mode = 0; /* set if fdc is in perpendicular mode */
+
+static int perpend_off(void)
+{
+ __u8 perpend[] = {FDC_PERPEND, 0x00};
+ TRACE_FUN(ft_t_any);
+
+ if (perpend_mode) {
+ /* Turn off perpendicular mode */
+ perpend[1] = 0x80;
+ TRACE_CATCH(fdc_command(perpend, 2),
+ TRACE(ft_t_err,"Perpendicular mode exit failed!"));
+ perpend_mode = 0;
+ }
+ TRACE_EXIT 0;
+}
+
+static int handle_perpend(int segment_id)
+{
+ __u8 perpend[] = {FDC_PERPEND, 0x00};
+ TRACE_FUN(ft_t_any);
+
+ /* When writing QIC-3020 tapes, turn on perpendicular mode
+ * if tape is moving in forward direction (even tracks).
+ */
+ if (ft_qic_std == QIC_TAPE_QIC3020 &&
+ ((segment_id / ft_segments_per_track) & 1) == 0) {
+/* FIXME: some i82077 seem to support perpendicular mode as
+ * well.
+ */
+#if 0
+ if (fdc.type < i82077AA) {}
+#else
+ if (fdc.type < i82077 && ft_data_rate < 1000) {
+#endif
+ /* fdc does not support perpendicular mode: complain
+ */
+ TRACE_ABORT(-EIO, ft_t_err,
+ "Your FDC does not support QIC-3020.");
+ }
+ perpend[1] = 0x03 /* 0x83 + (0x4 << ft_drive_sel) */ ;
+ TRACE_CATCH(fdc_command(perpend, 2),
+ TRACE(ft_t_err,"Perpendicular mode entry failed!"));
+ TRACE(ft_t_flow, "Perpendicular mode set");
+ perpend_mode = 1;
+ TRACE_EXIT 0;
+ }
+ TRACE_EXIT perpend_off();
+}
+
+static inline void fdc_setup_dma(char mode,
+ volatile void *addr, unsigned int count)
+{
+ /* Program the DMA controller.
+ */
+ disable_dma(fdc.dma);
+ clear_dma_ff(fdc.dma);
+ set_dma_mode(fdc.dma, mode);
+ set_dma_addr(fdc.dma, virt_to_bus((void*)addr));
+ set_dma_count(fdc.dma, count);
+#ifdef GCC_2_4_5_BUG
+ /* This seemingly stupid construction confuses the gcc-2.4.5
+ * code generator enough to create correct code.
+ */
+ if (1) {
+ int i;
+
+ for (i = 0; i < 1; ++i) {
+ ftape_udelay(1);
+ }
+ }
+#endif
+ enable_dma(fdc.dma);
+}
+
+/* Setup fdc and dma for formatting the next segment
+ */
+int fdc_setup_formatting(buffer_struct * buff)
+{
+ unsigned long flags;
+ __u8 out[6] = {
+ FDC_FORMAT, 0x00, 3, 4 * FT_SECTORS_PER_SEGMENT, 0x00, 0x6b
+ };
+ TRACE_FUN(ft_t_any);
+
+ TRACE_CATCH(handle_perpend(buff->segment_id),);
+ /* Program the DMA controller.
+ */
+ TRACE(ft_t_fdc_dma,
+ "phys. addr. = %lx", virt_to_bus((void*) buff->ptr));
+ save_flags(flags);
+ cli(); /* could be called from ISR ! */
+ fdc_setup_dma(DMA_MODE_WRITE, buff->ptr, FT_SECTORS_PER_SEGMENT * 4);
+ /* Issue FDC command to start reading/writing.
+ */
+ out[1] = ft_drive_sel;
+ out[4] = buff->gap3;
+ TRACE_CATCH(fdc_setup_error = fdc_command(out, sizeof(out)),
+ restore_flags(flags); fdc_mode = fdc_idle);
+ restore_flags(flags);
+ TRACE_EXIT 0;
+}
+
+
+/* Setup Floppy Disk Controller and DMA to read or write the next cluster
+ * of good sectors from or to the current segment.
+ */
+int fdc_setup_read_write(buffer_struct * buff, __u8 operation)
+{
+ unsigned long flags;
+ __u8 out[9];
+ int dma_mode;
+ TRACE_FUN(ft_t_any);
+
+ switch(operation) {
+ case FDC_VERIFY:
+ if (fdc.type < i82077) {
+ operation = FDC_READ;
+ }
+ case FDC_READ:
+ case FDC_READ_DELETED:
+ dma_mode = DMA_MODE_READ;
+ TRACE(ft_t_fdc_dma, "xfer %d sectors to 0x%p",
+ buff->sector_count, buff->ptr);
+ TRACE_CATCH(perpend_off(),);
+ break;
+ case FDC_WRITE_DELETED:
+ TRACE(ft_t_noise, "deleting segment %d", buff->segment_id);
+ case FDC_WRITE:
+ dma_mode = DMA_MODE_WRITE;
+ /* When writing QIC-3020 tapes, turn on perpendicular mode
+ * if tape is moving in forward direction (even tracks).
+ */
+ TRACE_CATCH(handle_perpend(buff->segment_id),);
+ TRACE(ft_t_fdc_dma, "xfer %d sectors from 0x%p",
+ buff->sector_count, buff->ptr);
+ break;
+ default:
+ TRACE_ABORT(-EIO,
+ ft_t_bug, "bug: illegal operation parameter");
+ }
+ TRACE(ft_t_fdc_dma, "phys. addr. = %lx",virt_to_bus((void*)buff->ptr));
+ save_flags(flags);
+ cli(); /* could be called from ISR ! */
+ if (operation != FDC_VERIFY) {
+ fdc_setup_dma(dma_mode, buff->ptr,
+ FT_SECTOR_SIZE * buff->sector_count);
+ }
+ /* Issue FDC command to start reading/writing.
+ */
+ out[0] = operation;
+ out[1] = ft_drive_sel;
+ out[2] = buff->cyl;
+ out[3] = buff->head;
+ out[4] = buff->sect + buff->sector_offset;
+ out[5] = 3; /* Sector size of 1K. */
+ out[6] = out[4] + buff->sector_count - 1; /* last sector */
+ out[7] = 109; /* Gap length. */
+ out[8] = 0xff; /* No limit to transfer size. */
+ TRACE(ft_t_fdc_dma, "C: 0x%02x, H: 0x%02x, R: 0x%02x, cnt: 0x%02x",
+ out[2], out[3], out[4], out[6] - out[4] + 1);
+ restore_flags(flags);
+ TRACE_CATCH(fdc_setup_error = fdc_command(out, 9),fdc_mode = fdc_idle);
+ TRACE_EXIT 0;
+}
+
+int fdc_fifo_threshold(__u8 threshold,
+ int *fifo_state, int *lock_state, int *fifo_thr)
+{
+ const __u8 cmd0[] = {FDC_DUMPREGS};
+ __u8 cmd1[] = {FDC_CONFIGURE, 0, (0x0f & (threshold - 1)), 0};
+ const __u8 cmd2[] = {FDC_LOCK};
+ const __u8 cmd3[] = {FDC_UNLOCK};
+ __u8 reg[10];
+ __u8 stat;
+ int i;
+ int result;
+ TRACE_FUN(ft_t_any);
+
+ if (CLK_48MHZ && fdc.type >= i82078) {
+ cmd1[0] |= FDC_CLK48_BIT;
+ }
+ /* Dump fdc internal registers for examination
+ */
+ TRACE_CATCH(fdc_command(cmd0, NR_ITEMS(cmd0)),
+ TRACE(ft_t_warn, "dumpreg cmd failed, fifo unchanged"));
+ /* Now read fdc internal registers from fifo
+ */
+ for (i = 0; i < (int)NR_ITEMS(reg); ++i) {
+ fdc_read(&reg[i]);
+ TRACE(ft_t_fdc_dma, "Register %d = 0x%02x", i, reg[i]);
+ }
+ if (fifo_state && lock_state && fifo_thr) {
+ *fifo_state = (reg[8] & 0x20) == 0;
+ *lock_state = reg[7] & 0x80;
+ *fifo_thr = 1 + (reg[8] & 0x0f);
+ }
+ TRACE(ft_t_noise,
+ "original fifo state: %sabled, threshold %d, %slocked",
+ ((reg[8] & 0x20) == 0) ? "en" : "dis",
+ 1 + (reg[8] & 0x0f), (reg[7] & 0x80) ? "" : "not ");
+ /* If fdc is already locked, unlock it first ! */
+ if (reg[7] & 0x80) {
+ fdc_ready_wait(100);
+ TRACE_CATCH(fdc_issue_command(cmd3, NR_ITEMS(cmd3), &stat, 1),
+ TRACE(ft_t_bug, "FDC unlock command failed, "
+ "configuration unchanged"));
+ }
+ fdc_fifo_locked = 0;
+ /* Enable fifo and set threshold at xx bytes to allow a
+ * reasonably large latency and reduce number of dma bursts.
+ */
+ fdc_ready_wait(100);
+ if ((result = fdc_command(cmd1, NR_ITEMS(cmd1))) < 0) {
+ TRACE(ft_t_bug, "configure cmd failed, fifo unchanged");
+ }
+ /* Now lock configuration so reset will not change it
+ */
+ if(fdc_issue_command(cmd2, NR_ITEMS(cmd2), &stat, 1) < 0 ||
+ stat != 0x10) {
+ TRACE_ABORT(-EIO, ft_t_bug,
+ "FDC lock command failed, stat = 0x%02x", stat);
+ }
+ fdc_fifo_locked = 1;
+ TRACE_EXIT result;
+}
+
+static int fdc_fifo_enable(void)
+{
+ TRACE_FUN(ft_t_any);
+
+ if (fdc_fifo_locked) {
+ TRACE_ABORT(0, ft_t_warn, "Fifo not enabled because locked");
+ }
+ TRACE_CATCH(fdc_fifo_threshold(ft_fdc_threshold /* bytes */,
+ &fdc_fifo_state,
+ &fdc_lock_state,
+ &fdc_fifo_thr),);
+ TRACE_CATCH(fdc_fifo_threshold(ft_fdc_threshold /* bytes */,
+ NULL, NULL, NULL),);
+ TRACE_EXIT 0;
+}
+
+/* Determine fd controller type
+ */
+static __u8 fdc_save_state[2] = {0, 0};
+
+int fdc_probe(void)
+{
+ __u8 cmd[1];
+ __u8 stat[16]; /* must be able to hold dumpregs & save results */
+ int i;
+ TRACE_FUN(ft_t_any);
+
+ /* Try to find out what kind of fd controller we have to deal with
+ * Scheme borrowed from floppy driver:
+ * first try if FDC_DUMPREGS command works
+ * (this indicates that we have a 82072 or better)
+ * then try the FDC_VERSION command (82072 doesn't support this)
+ * then try the FDC_UNLOCK command (some older 82077's don't support this)
+ * then try the FDC_PARTID command (82078's support this)
+ */
+ cmd[0] = FDC_DUMPREGS;
+ if (fdc_issue_command(cmd, 1, stat, 1) != 0) {
+ TRACE_ABORT(no_fdc, ft_t_bug, "No FDC found");
+ }
+ if (stat[0] == 0x80) {
+ /* invalid command: must be pre 82072 */
+ TRACE_ABORT(i8272,
+ ft_t_warn, "Type 8272A/765A compatible FDC found");
+ }
+ fdc_result(&stat[1], 9);
+ fdc_save_state[0] = stat[7];
+ fdc_save_state[1] = stat[8];
+ cmd[0] = FDC_VERSION;
+ if (fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] == 0x80) {
+ TRACE_ABORT(i8272, ft_t_warn, "Type 82072 FDC found");
+ }
+ if (*stat != 0x90) {
+ TRACE_ABORT(i8272, ft_t_warn, "Unknown FDC found");
+ }
+ cmd[0] = FDC_UNLOCK;
+ if(fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] != 0x00) {
+ TRACE_ABORT(i8272, ft_t_warn,
+ "Type pre-1991 82077 FDC found, "
+ "treating it like a 82072");
+ }
+ if (fdc_save_state[0] & 0x80) { /* was locked */
+ cmd[0] = FDC_LOCK; /* restore lock */
+ (void)fdc_issue_command(cmd, 1, stat, 1);
+ TRACE(ft_t_warn, "FDC is already locked");
+ }
+ /* Test for a i82078 FDC */
+ cmd[0] = FDC_PARTID;
+ if (fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] == 0x80) {
+ /* invalid command: not a i82078xx type FDC */
+ for (i = 0; i < 4; ++i) {
+ outb_p(i, fdc.tdr);
+ if ((inb_p(fdc.tdr) & 0x03) != i) {
+ TRACE_ABORT(i82077,
+ ft_t_warn, "Type 82077 FDC found");
+ }
+ }
+ TRACE_ABORT(i82077AA, ft_t_warn, "Type 82077AA FDC found");
+ }
+ /* FDC_PARTID cmd succeeded */
+ switch (stat[0] >> 5) {
+ case 0x0:
+ /* i82078SL or i82078-1. The SL part cannot run at
+ * 2Mbps (the SL and -1 dies are identical; they are
+ * speed graded after production, according to Intel).
+ * Some SL's can be detected by doing a SAVE cmd and
+ * look at bit 7 of the first byte (the SEL3V# bit).
+ * If it is 0, the part runs off 3Volts, and hence it
+ * is a SL.
+ */
+ cmd[0] = FDC_SAVE;
+ if(fdc_issue_command(cmd, 1, stat, 16) < 0) {
+ TRACE(ft_t_err, "FDC_SAVE failed. Dunno why");
+ /* guess we better claim the fdc to be a i82078 */
+ TRACE_ABORT(i82078,
+ ft_t_warn,
+ "Type i82078 FDC (i suppose) found");
+ }
+ if ((stat[0] & FDC_SEL3V_BIT)) {
+ /* fdc running off 5Volts; Pray that it's a i82078-1
+ */
+ TRACE_ABORT(i82078_1, ft_t_warn,
+ "Type i82078-1 or 5Volt i82078SL FDC found");
+ }
+ TRACE_ABORT(i82078, ft_t_warn,
+ "Type 3Volt i82078SL FDC (1Mbps) found");
+ case 0x1:
+ case 0x2: /* S82078B */
+ /* The '78B isn't '78 compatible. Detect it as a '77AA */
+ TRACE_ABORT(i82077AA, ft_t_warn, "Type i82077AA FDC found");
+ case 0x3: /* NSC PC8744 core; used in several super-IO chips */
+ TRACE_ABORT(i82077AA,
+ ft_t_warn, "Type 82077AA compatible FDC found");
+ default:
+ TRACE(ft_t_warn, "A previously undetected FDC found");
+ TRACE_ABORT(i82077AA, ft_t_warn,
+ "Treating it as a 82077AA. Please report partid= %d",
+ stat[0]);
+ } /* switch(stat[ 0] >> 5) */
+ TRACE_EXIT no_fdc;
+}
+
+static int fdc_request_regions(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (ft_mach2 || ft_probe_fc10) {
+ if (check_region(fdc.sra, 8) < 0) {
+#ifndef BROKEN_FLOPPY_DRIVER
+ TRACE_EXIT -EBUSY;
+#else
+ TRACE(ft_t_warn,
+"address 0x%03x occupied (by floppy driver?), using it anyway", fdc.sra);
+#endif
+ }
+ request_region(fdc.sra, 8, "fdc (ft)");
+ } else {
+ if (check_region(fdc.sra, 6) < 0 ||
+ check_region(fdc.dir, 1) < 0) {
+#ifndef BROKEN_FLOPPY_DRIVER
+ TRACE_EXIT -EBUSY;
+#else
+ TRACE(ft_t_warn,
+"address 0x%03x occupied (by floppy driver?), using it anyway", fdc.sra);
+#endif
+ }
+ request_region(fdc.sra, 6, "fdc (ft)");
+ request_region(fdc.sra + 7, 1, "fdc (ft)");
+ }
+ TRACE_EXIT 0;
+}
+
+void fdc_release_regions(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (fdc.sra != 0) {
+ if (fdc.dor2 != 0) {
+ release_region(fdc.sra, 8);
+ } else {
+ release_region(fdc.sra, 6);
+ release_region(fdc.dir, 1);
+ }
+ }
+ TRACE_EXIT;
+}
+
+static int fdc_config_regs(unsigned int fdc_base,
+ unsigned int fdc_irq,
+ unsigned int fdc_dma)
+{
+ TRACE_FUN(ft_t_flow);
+
+ fdc.irq = fdc_irq;
+ fdc.dma = fdc_dma;
+ fdc.sra = fdc_base;
+ fdc.srb = fdc_base + 1;
+ fdc.dor = fdc_base + 2;
+ fdc.tdr = fdc_base + 3;
+ fdc.msr = fdc.dsr = fdc_base + 4;
+ fdc.fifo = fdc_base + 5;
+ fdc.dir = fdc.ccr = fdc_base + 7;
+ fdc.dor2 = (ft_mach2 || ft_probe_fc10) ? fdc_base + 6 : 0;
+ TRACE_CATCH(fdc_request_regions(), fdc.sra = 0);
+ TRACE_EXIT 0;
+}
+
+static int fdc_config(void)
+{
+ static int already_done = 0;
+ TRACE_FUN(ft_t_any);
+
+ if (already_done) {
+ TRACE_CATCH(fdc_request_regions(),);
+ *(fdc.hook) = fdc_isr; /* hook our handler in */
+ TRACE_EXIT 0;
+ }
+ if (ft_probe_fc10) {
+ int fc_type;
+
+ TRACE_CATCH(fdc_config_regs(ft_fdc_base,
+ ft_fdc_irq, ft_fdc_dma),);
+ fc_type = fc10_enable();
+ if (fc_type != 0) {
+ TRACE(ft_t_warn, "FC-%c0 controller found", '0' + fc_type);
+ fdc.type = fc10;
+ fdc.hook = &do_ftape;
+ *(fdc.hook) = fdc_isr; /* hook our handler in */
+ already_done = 1;
+ TRACE_EXIT 0;
+ } else {
+ TRACE(ft_t_warn, "FC-10/20 controller not found");
+ fdc_release_regions();
+ fdc.type = no_fdc;
+ ft_probe_fc10 = 0;
+ ft_fdc_base = 0x3f0;
+ ft_fdc_irq = 6;
+ ft_fdc_dma = 2;
+ }
+ }
+ TRACE(ft_t_warn, "fdc base: 0x%x, irq: %d, dma: %d",
+ ft_fdc_base, ft_fdc_irq, ft_fdc_dma);
+ TRACE_CATCH(fdc_config_regs(ft_fdc_base, ft_fdc_irq, ft_fdc_dma),);
+ fdc.hook = &do_ftape;
+ *(fdc.hook) = fdc_isr; /* hook our handler in */
+ already_done = 1;
+ TRACE_EXIT 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,70)
+static void ftape_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+#else
+static void ftape_interrupt(int irq, struct pt_regs *regs)
+#endif
+{
+ void (*handler) (void) = *fdc.hook;
+ TRACE_FUN(ft_t_any);
+
+ *fdc.hook = NULL;
+ if (handler) {
+ handler();
+ } else {
+ TRACE(ft_t_bug, "Unexpected ftape interrupt");
+ }
+ TRACE_EXIT;
+}
+
+int fdc_grab_irq_and_dma(void)
+{
+ TRACE_FUN(ft_t_any);
+
+ if (fdc.hook == &do_ftape) {
+ /* Get fast interrupt handler.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,70)
+ if (request_irq(fdc.irq, ftape_interrupt,
+ SA_INTERRUPT, "ft", ftape_id)) {
+ TRACE_ABORT(-EIO, ft_t_bug,
+ "Unable to grab IRQ%d for ftape driver",
+ fdc.irq);
+ }
+#else
+ if (request_irq(fdc.irq, ftape_interrupt, SA_INTERRUPT,
+ ftape_id)) {
+ TRACE_ABORT(-EIO, ft_t_bug,
+ "Unable to grab IRQ%d for ftape driver",
+ fdc.irq);
+ }
+#endif
+ if (request_dma(fdc.dma, ftape_id)) {
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,70)
+ free_irq(fdc.irq, ftape_id);
+#else
+ free_irq(fdc.irq);
+#endif
+ TRACE_ABORT(-EIO, ft_t_bug,
+ "Unable to grab DMA%d for ftape driver",
+ fdc.dma);
+ }
+ enable_irq(fdc.irq);
+ }
+ if (ft_fdc_base != 0x3f0 && (ft_fdc_dma == 2 || ft_fdc_irq == 6)) {
+ /* Using same dma channel or irq as standard fdc, need
+ * to disable the dma-gate on the std fdc. This
+ * couldn't be done in the floppy driver as some
+ * laptops are using the dma-gate to enter a low power
+ * or even suspended state :-(
+ */
+ outb_p(FDC_RESET_NOT, 0x3f2);
+ TRACE(ft_t_noise, "DMA-gate on standard fdc disabled");
+ }
+ TRACE_EXIT 0;
+}
+
+int fdc_release_irq_and_dma(void)
+{
+ TRACE_FUN(ft_t_any);
+
+ if (fdc.hook == &do_ftape) {
+ disable_dma(fdc.dma); /* just in case... */
+ free_dma(fdc.dma);
+ disable_irq(fdc.irq);
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,70)
+ free_irq(fdc.irq, ftape_id);
+#else
+ free_irq(fdc.irq);
+#endif
+ }
+ if (ft_fdc_base != 0x3f0 && (ft_fdc_dma == 2 || ft_fdc_irq == 6)) {
+ /* Using same dma channel as standard fdc, need to
+ * disable the dma-gate on the std fdc. This couldn't
+ * be done in the floppy driver as some laptops are
+ * using the dma-gate to enter a low power or even
+ * suspended state :-(
+ */
+ outb_p(FDC_RESET_NOT | FDC_DMA_MODE, 0x3f2);
+ TRACE(ft_t_noise, "DMA-gate on standard fdc enabled again");
+ }
+ TRACE_EXIT 0;
+}
+
+int fdc_init(void)
+{
+ TRACE_FUN(ft_t_any);
+
+ /* find a FDC to use */
+ TRACE_CATCH(fdc_config(),);
+ TRACE_CATCH(fdc_grab_irq_and_dma(), fdc_release_regions());
+ ftape_motor = 0;
+ fdc_catch_stray_interrupts(0); /* clear number of awainted
+ * stray interrupte
+ */
+ fdc_catch_stray_interrupts(1); /* one always comes (?) */
+ TRACE(ft_t_flow, "resetting fdc");
+ fdc_set_seek_rate(2); /* use nominal QIC step rate */
+ fdc_reset(); /* init fdc & clear track counters */
+ if (fdc.type == no_fdc) { /* no FC-10 or FC-20 found */
+ fdc.type = fdc_probe();
+ fdc_reset(); /* update with new knowledge */
+ }
+ if (fdc.type == no_fdc) {
+ fdc_release_irq_and_dma();
+ fdc_release_regions();
+ TRACE_EXIT -ENXIO;
+ }
+ if (fdc.type >= i82077) {
+ if (fdc_fifo_enable() < 0) {
+ TRACE(ft_t_warn, "couldn't enable fdc fifo !");
+ } else {
+ TRACE(ft_t_flow, "fdc fifo enabled and locked");
+ }
+ }
+ TRACE_EXIT 0;
+}
diff --git a/drivers/char/ftape/lowlevel/fdc-io.h b/drivers/char/ftape/lowlevel/fdc-io.h
new file mode 100644
index 000000000..77b2f5bb9
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/fdc-io.h
@@ -0,0 +1,257 @@
+#ifndef _FDC_IO_H
+#define _FDC_IO_H
+
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-io.h,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/10/05 19:18:06 $
+ *
+ * This file contains the declarations for the low level
+ * functions that communicate with the floppy disk controller,
+ * for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for
+ * Linux.
+ */
+
+#include <linux/fdreg.h>
+
+#include "../lowlevel/ftape-bsm.h"
+
+#define FDC_SK_BIT (0x20)
+#define FDC_MT_BIT (0x80)
+
+#define FDC_READ (FD_READ & ~(FDC_SK_BIT | FDC_MT_BIT))
+#define FDC_WRITE (FD_WRITE & ~FDC_MT_BIT)
+#define FDC_READ_DELETED (0x4c)
+#define FDC_WRITE_DELETED (0x49)
+#define FDC_VERIFY (0x56)
+#define FDC_READID (0x4a)
+#define FDC_SENSED (0x04)
+#define FDC_SENSEI (FD_SENSEI)
+#define FDC_FORMAT (FD_FORMAT)
+#define FDC_RECAL (FD_RECALIBRATE)
+#define FDC_SEEK (FD_SEEK)
+#define FDC_SPECIFY (FD_SPECIFY)
+#define FDC_RECALIBR (FD_RECALIBRATE)
+#define FDC_VERSION (FD_VERSION)
+#define FDC_PERPEND (FD_PERPENDICULAR)
+#define FDC_DUMPREGS (FD_DUMPREGS)
+#define FDC_LOCK (FD_LOCK)
+#define FDC_UNLOCK (FD_UNLOCK)
+#define FDC_CONFIGURE (FD_CONFIGURE)
+#define FDC_DRIVE_SPEC (0x8e) /* i82078 has this (any others?) */
+#define FDC_PARTID (0x18) /* i82078 has this */
+#define FDC_SAVE (0x2e) /* i82078 has this (any others?) */
+#define FDC_RESTORE (0x4e) /* i82078 has this (any others?) */
+
+#define FDC_STATUS_MASK (STATUS_BUSY | STATUS_DMA | STATUS_DIR | STATUS_READY)
+#define FDC_DATA_READY (STATUS_READY)
+#define FDC_DATA_OUTPUT (STATUS_DIR)
+#define FDC_DATA_READY_MASK (STATUS_READY | STATUS_DIR)
+#define FDC_DATA_OUT_READY (STATUS_READY | STATUS_DIR)
+#define FDC_DATA_IN_READY (STATUS_READY)
+#define FDC_BUSY (STATUS_BUSY)
+#define FDC_CLK48_BIT (0x80)
+#define FDC_SEL3V_BIT (0x40)
+
+#define ST0_INT_MASK (ST0_INTR)
+#define FDC_INT_NORMAL (ST0_INTR & 0x00)
+#define FDC_INT_ABNORMAL (ST0_INTR & 0x40)
+#define FDC_INT_INVALID (ST0_INTR & 0x80)
+#define FDC_INT_READYCH (ST0_INTR & 0xC0)
+#define ST0_SEEK_END (ST0_SE)
+#define ST3_TRACK_0 (ST3_TZ)
+
+#define FDC_RESET_NOT (0x04)
+#define FDC_DMA_MODE (0x08)
+#define FDC_MOTOR_0 (0x10)
+#define FDC_MOTOR_1 (0x20)
+
+typedef struct {
+ void (**hook) (void); /* our wedge into the isr */
+ enum {
+ no_fdc, i8272, i82077, i82077AA, fc10,
+ i82078, i82078_1
+ } type; /* FDC type */
+ unsigned int irq; /* FDC irq nr */
+ unsigned int dma; /* FDC dma channel nr */
+ __u16 sra; /* Status register A (PS/2 only) */
+ __u16 srb; /* Status register B (PS/2 only) */
+ __u16 dor; /* Digital output register */
+ __u16 tdr; /* Tape Drive Register (82077SL-1 &
+ 82078 only) */
+ __u16 msr; /* Main Status Register */
+ __u16 dsr; /* Datarate Select Register (8207x only) */
+ __u16 fifo; /* Data register / Fifo on 8207x */
+ __u16 dir; /* Digital Input Register */
+ __u16 ccr; /* Configuration Control Register */
+ __u16 dor2; /* Alternate dor on MACH-2 controller,
+ also used with FC-10, meaning unknown */
+} fdc_config_info;
+
+typedef enum {
+ fdc_data_rate_250 = 2,
+ fdc_data_rate_300 = 1, /* any fdc in default configuration */
+ fdc_data_rate_500 = 0,
+ fdc_data_rate_1000 = 3,
+ fdc_data_rate_2000 = 1, /* i82078-1: when using Data Rate Table #2 */
+} fdc_data_rate_type;
+
+typedef enum {
+ fdc_idle = 0,
+ fdc_reading_data = FDC_READ,
+ fdc_seeking = FDC_SEEK,
+ fdc_writing_data = FDC_WRITE,
+ fdc_deleting = FDC_WRITE_DELETED,
+ fdc_reading_id = FDC_READID,
+ fdc_recalibrating = FDC_RECAL,
+ fdc_formatting = FDC_FORMAT,
+ fdc_verifying = FDC_VERIFY
+} fdc_mode_enum;
+
+typedef enum {
+ waiting = 0,
+ reading,
+ writing,
+ formatting,
+ verifying,
+ deleting,
+ done,
+ error,
+ mmapped,
+} buffer_state_enum;
+
+typedef struct {
+ __u8 *address;
+ volatile buffer_state_enum status;
+ volatile __u8 *ptr;
+ volatile unsigned int bytes;
+ volatile unsigned int segment_id;
+
+ /* bitmap for remainder of segment not yet handled.
+ * one bit set for each bad sector that must be skipped.
+ */
+ volatile SectorMap bad_sector_map;
+
+ /* bitmap with bad data blocks in data buffer.
+ * the errors in this map may be retried.
+ */
+ volatile SectorMap soft_error_map;
+
+ /* bitmap with bad data blocks in data buffer
+ * the errors in this map may not be retried.
+ */
+ volatile SectorMap hard_error_map;
+
+ /* retry counter for soft errors.
+ */
+ volatile int retry;
+
+ /* sectors to skip on retry ???
+ */
+ volatile unsigned int skip;
+
+ /* nr of data blocks in data buffer
+ */
+ volatile unsigned int data_offset;
+
+ /* offset in segment for first sector to be handled.
+ */
+ volatile unsigned int sector_offset;
+
+ /* size of cluster of good sectors to be handled.
+ */
+ volatile unsigned int sector_count;
+
+ /* size of remaining part of segment to be handled.
+ */
+ volatile unsigned int remaining;
+
+ /* points to next segment (contiguous) to be handled,
+ * or is zero if no read-ahead is allowed.
+ */
+ volatile unsigned int next_segment;
+
+ /* flag being set if deleted data was read.
+ */
+ volatile int deleted;
+
+ /* floppy coordinates of first sector in segment */
+ volatile __u8 head;
+ volatile __u8 cyl;
+ volatile __u8 sect;
+
+ /* gap to use when formatting */
+ __u8 gap3;
+ /* flag set when buffer is mmaped */
+ int mmapped;
+} buffer_struct;
+
+/*
+ * fdc-io.c defined public variables
+ */
+extern volatile fdc_mode_enum fdc_mode;
+extern int fdc_setup_error; /* outdated ??? */
+extern struct wait_queue *ftape_wait_intr;
+extern int ftape_motor; /* fdc motor line state */
+extern volatile int ftape_current_cylinder; /* track nr FDC thinks we're on */
+extern volatile __u8 fdc_head; /* FDC head */
+extern volatile __u8 fdc_cyl; /* FDC track */
+extern volatile __u8 fdc_sect; /* FDC sector */
+extern fdc_config_info fdc; /* FDC hardware configuration */
+
+extern unsigned int ft_fdc_base;
+extern unsigned int ft_fdc_irq;
+extern unsigned int ft_fdc_dma;
+extern unsigned int ft_fdc_threshold;
+extern unsigned int ft_fdc_rate_limit;
+extern int ft_probe_fc10;
+extern int ft_mach2;
+/*
+ * fdc-io.c defined public functions
+ */
+extern void fdc_catch_stray_interrupts(int count);
+extern int fdc_ready_wait(unsigned int timeout);
+extern int fdc_command(const __u8 * cmd_data, int cmd_len);
+extern int fdc_result(__u8 * res_data, int res_len);
+extern int fdc_issue_command(const __u8 * out_data, int out_count,
+ __u8 * in_data, int in_count);
+extern int fdc_interrupt_wait(unsigned int time);
+extern int fdc_set_seek_rate(int seek_rate);
+extern int fdc_seek(int track);
+extern int fdc_sense_drive_status(int *st3);
+extern void fdc_motor(int motor);
+extern void fdc_reset(void);
+extern int fdc_recalibrate(void);
+extern void fdc_disable(void);
+extern int fdc_fifo_threshold(__u8 threshold,
+ int *fifo_state, int *lock_state, int *fifo_thr);
+extern void fdc_wait_calibrate(void);
+extern int fdc_sense_interrupt_status(int *st0, int *current_cylinder);
+extern void fdc_save_drive_specs(void);
+extern void fdc_restore_drive_specs(void);
+extern int fdc_set_data_rate(int rate);
+extern void fdc_set_write_precomp(int precomp);
+extern int fdc_release_irq_and_dma(void);
+extern void fdc_release_regions(void);
+extern int fdc_init(void);
+extern int fdc_setup_read_write(buffer_struct * buff, __u8 operation);
+extern int fdc_setup_formatting(buffer_struct * buff);
+#endif
diff --git a/drivers/char/ftape/lowlevel/fdc-isr.c b/drivers/char/ftape/lowlevel/fdc-isr.c
new file mode 100644
index 000000000..4c3975add
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/fdc-isr.c
@@ -0,0 +1,1185 @@
+/*
+ * Copyright (C) 1994-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-isr.c,v $
+ * $Revision: 1.9 $
+ * $Date: 1997/10/17 23:01:53 $
+ *
+ * This file contains the interrupt service routine and
+ * associated code for the QIC-40/80/3010/3020 floppy-tape driver
+ * "ftape" for Linux.
+ */
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#define volatile /* */
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/fdc-isr.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-calibr.h"
+#include "../lowlevel/ftape-bsm.h"
+
+/* Global vars.
+ */
+volatile int ft_expected_stray_interrupts = 0;
+volatile int ft_interrupt_seen = 0;
+volatile int ft_seek_completed = 0;
+volatile int ft_hide_interrupt = 0;
+/* Local vars.
+ */
+typedef enum {
+ no_error = 0, id_am_error = 0x01, id_crc_error = 0x02,
+ data_am_error = 0x04, data_crc_error = 0x08,
+ no_data_error = 0x10, overrun_error = 0x20,
+} error_cause;
+static int stop_read_ahead = 0;
+
+
+static void print_error_cause(int cause)
+{
+ TRACE_FUN(ft_t_any);
+
+ switch (cause) {
+ case no_data_error:
+ TRACE(ft_t_noise, "no data error");
+ break;
+ case id_am_error:
+ TRACE(ft_t_noise, "id am error");
+ break;
+ case id_crc_error:
+ TRACE(ft_t_noise, "id crc error");
+ break;
+ case data_am_error:
+ TRACE(ft_t_noise, "data am error");
+ break;
+ case data_crc_error:
+ TRACE(ft_t_noise, "data crc error");
+ break;
+ case overrun_error:
+ TRACE(ft_t_noise, "overrun error");
+ break;
+ default:
+ }
+ TRACE_EXIT;
+}
+
+static char *fdc_mode_txt(fdc_mode_enum mode)
+{
+ switch (mode) {
+ case fdc_idle:
+ return "fdc_idle";
+ case fdc_reading_data:
+ return "fdc_reading_data";
+ case fdc_seeking:
+ return "fdc_seeking";
+ case fdc_writing_data:
+ return "fdc_writing_data";
+ case fdc_reading_id:
+ return "fdc_reading_id";
+ case fdc_recalibrating:
+ return "fdc_recalibrating";
+ case fdc_formatting:
+ return "fdc_formatting";
+ case fdc_verifying:
+ return "fdc_verifying";
+ default:
+ return "unknown";
+ }
+}
+
+static inline error_cause decode_irq_cause(fdc_mode_enum mode, __u8 st[])
+{
+ error_cause cause = no_error;
+ TRACE_FUN(ft_t_any);
+
+ /* Valid st[], decode cause of interrupt.
+ */
+ switch (st[0] & ST0_INT_MASK) {
+ case FDC_INT_NORMAL:
+ TRACE(ft_t_fdc_dma,"normal completion: %s",fdc_mode_txt(mode));
+ break;
+ case FDC_INT_ABNORMAL:
+ TRACE(ft_t_flow, "abnormal completion %s", fdc_mode_txt(mode));
+ TRACE(ft_t_fdc_dma, "ST0: 0x%02x, ST1: 0x%02x, ST2: 0x%02x",
+ st[0], st[1], st[2]);
+ TRACE(ft_t_fdc_dma,
+ "C: 0x%02x, H: 0x%02x, R: 0x%02x, N: 0x%02x",
+ st[3], st[4], st[5], st[6]);
+ if (st[1] & 0x01) {
+ if (st[2] & 0x01) {
+ cause = data_am_error;
+ } else {
+ cause = id_am_error;
+ }
+ } else if (st[1] & 0x20) {
+ if (st[2] & 0x20) {
+ cause = data_crc_error;
+ } else {
+ cause = id_crc_error;
+ }
+ } else if (st[1] & 0x04) {
+ cause = no_data_error;
+ } else if (st[1] & 0x10) {
+ cause = overrun_error;
+ }
+ print_error_cause(cause);
+ break;
+ case FDC_INT_INVALID:
+ TRACE(ft_t_flow, "invalid completion %s", fdc_mode_txt(mode));
+ break;
+ case FDC_INT_READYCH:
+ if (st[0] & ST0_SEEK_END) {
+ TRACE(ft_t_flow, "drive poll completed");
+ } else {
+ TRACE(ft_t_flow, "ready change %s",fdc_mode_txt(mode));
+ }
+ break;
+ default:
+ break;
+ }
+ TRACE_EXIT cause;
+}
+
+static void update_history(error_cause cause)
+{
+ switch (cause) {
+ case id_am_error:
+ ft_history.id_am_errors++;
+ break;
+ case id_crc_error:
+ ft_history.id_crc_errors++;
+ break;
+ case data_am_error:
+ ft_history.data_am_errors++;
+ break;
+ case data_crc_error:
+ ft_history.data_crc_errors++;
+ break;
+ case overrun_error:
+ ft_history.overrun_errors++;
+ break;
+ case no_data_error:
+ ft_history.no_data_errors++;
+ break;
+ default:
+ }
+}
+
+static void skip_bad_sector(buffer_struct * buff)
+{
+ TRACE_FUN(ft_t_any);
+
+ /* Mark sector as soft error and skip it
+ */
+ if (buff->remaining > 0) {
+ ++buff->sector_offset;
+ ++buff->data_offset;
+ --buff->remaining;
+ buff->ptr += FT_SECTOR_SIZE;
+ buff->bad_sector_map >>= 1;
+ } else {
+ /* Hey, what is this????????????? C code: if we shift
+ * more than 31 bits, we get no shift. That's bad!!!!!!
+ */
+ ++buff->sector_offset; /* hack for error maps */
+ TRACE(ft_t_warn, "skipping last sector in segment");
+ }
+ TRACE_EXIT;
+}
+
+static void update_error_maps(buffer_struct * buff, unsigned int error_offset)
+{
+ int hard = 0;
+ TRACE_FUN(ft_t_any);
+
+ if (buff->retry < FT_SOFT_RETRIES) {
+ buff->soft_error_map |= (1 << error_offset);
+ } else {
+ buff->hard_error_map |= (1 << error_offset);
+ buff->soft_error_map &= ~buff->hard_error_map;
+ buff->retry = -1; /* will be set to 0 in setup_segment */
+ hard = 1;
+ }
+ TRACE(ft_t_noise, "sector %d : %s error\n"
+ KERN_INFO "hard map: 0x%08lx\n"
+ KERN_INFO "soft map: 0x%08lx",
+ FT_SECTOR(error_offset), hard ? "hard" : "soft",
+ (long) buff->hard_error_map, (long) buff->soft_error_map);
+ TRACE_EXIT;
+}
+
+static void print_progress(buffer_struct *buff, error_cause cause)
+{
+ TRACE_FUN(ft_t_any);
+
+ switch (cause) {
+ case no_error:
+ TRACE(ft_t_flow,"%d Sector(s) transfered", buff->sector_count);
+ break;
+ case no_data_error:
+ TRACE(ft_t_flow, "Sector %d not found",
+ FT_SECTOR(buff->sector_offset));
+ break;
+ case overrun_error:
+ /* got an overrun error on the first byte, must be a
+ * hardware problem
+ */
+ TRACE(ft_t_bug,
+ "Unexpected error: failing DMA or FDC controller ?");
+ break;
+ case data_crc_error:
+ TRACE(ft_t_flow, "Error in sector %d",
+ FT_SECTOR(buff->sector_offset - 1));
+ break;
+ case id_crc_error:
+ case id_am_error:
+ case data_am_error:
+ TRACE(ft_t_flow, "Error in sector %d",
+ FT_SECTOR(buff->sector_offset));
+ break;
+ default:
+ TRACE(ft_t_flow, "Unexpected error at sector %d",
+ FT_SECTOR(buff->sector_offset));
+ break;
+ }
+ TRACE_EXIT;
+}
+
+/*
+ * Error cause: Amount xferred: Action:
+ *
+ * id_am_error 0 mark bad and skip
+ * id_crc_error 0 mark bad and skip
+ * data_am_error 0 mark bad and skip
+ * data_crc_error % 1024 mark bad and skip
+ * no_data_error 0 retry on write
+ * mark bad and skip on read
+ * overrun_error [ 0..all-1 ] mark bad and skip
+ * no_error all continue
+ */
+
+/* the arg `sector' is returned by the fdc and tells us at which sector we
+ * are positioned at (relative to starting sector of segment)
+ */
+static void determine_verify_progress(buffer_struct *buff,
+ error_cause cause,
+ __u8 sector)
+{
+ TRACE_FUN(ft_t_any);
+
+ if (cause == no_error && sector == 1) {
+ buff->sector_offset = FT_SECTORS_PER_SEGMENT;
+ buff->remaining = 0;
+ if (TRACE_LEVEL >= ft_t_flow) {
+ print_progress(buff, cause);
+ }
+ } else {
+ buff->sector_offset = sector - buff->sect;
+ buff->remaining = FT_SECTORS_PER_SEGMENT - buff->sector_offset;
+ TRACE(ft_t_noise, "%ssector offset: 0x%04x",
+ (cause == no_error) ? "unexpected " : "",
+ buff->sector_offset);
+ switch (cause) {
+ case overrun_error:
+ break;
+#if 0
+ case no_data_error:
+ buff->retry = FT_SOFT_RETRIES;
+ if (buff->hard_error_map &&
+ buff->sector_offset > 1 &&
+ (buff->hard_error_map &
+ (1 << (buff->sector_offset-2)))) {
+ buff->retry --;
+ }
+ break;
+#endif
+ default:
+ buff->retry = FT_SOFT_RETRIES;
+ break;
+ }
+ if (TRACE_LEVEL >= ft_t_flow) {
+ print_progress(buff, cause);
+ }
+ /* Sector_offset points to the problem area Now adjust
+ * sector_offset so it always points one past he failing
+ * sector. I.e. skip the bad sector.
+ */
+ ++buff->sector_offset;
+ --buff->remaining;
+ update_error_maps(buff, buff->sector_offset - 1);
+ }
+ TRACE_EXIT;
+}
+
+static void determine_progress(buffer_struct *buff,
+ error_cause cause,
+ __u8 sector)
+{
+ unsigned int dma_residue;
+ TRACE_FUN(ft_t_any);
+
+ /* Using less preferred order of disable_dma and
+ * get_dma_residue because this seems to fail on at least one
+ * system if reversed!
+ */
+ dma_residue = get_dma_residue(fdc.dma);
+ disable_dma(fdc.dma);
+ if (cause != no_error || dma_residue != 0) {
+ TRACE(ft_t_noise, "%sDMA residue: 0x%04x",
+ (cause == no_error) ? "unexpected " : "",
+ dma_residue);
+ /* adjust to actual value: */
+ if (dma_residue == 0) {
+ /* this happens sometimes with overrun errors.
+ * I don't know whether we could ignore the
+ * overrun error. Play save.
+ */
+ buff->sector_count --;
+ } else {
+ buff->sector_count -= ((dma_residue +
+ (FT_SECTOR_SIZE - 1)) /
+ FT_SECTOR_SIZE);
+ }
+ }
+ /* Update var's influenced by the DMA operation.
+ */
+ if (buff->sector_count > 0) {
+ buff->sector_offset += buff->sector_count;
+ buff->data_offset += buff->sector_count;
+ buff->ptr += (buff->sector_count *
+ FT_SECTOR_SIZE);
+ buff->remaining -= buff->sector_count;
+ buff->bad_sector_map >>= buff->sector_count;
+ }
+ if (TRACE_LEVEL >= ft_t_flow) {
+ print_progress(buff, cause);
+ }
+ if (cause != no_error) {
+ if (buff->remaining == 0) {
+ TRACE(ft_t_warn, "foo?\n"
+ KERN_INFO "count : %d\n"
+ KERN_INFO "offset: %d\n"
+ KERN_INFO "soft : %08x\n"
+ KERN_INFO "hard : %08x",
+ buff->sector_count,
+ buff->sector_offset,
+ buff->soft_error_map,
+ buff->hard_error_map);
+ }
+ /* Sector_offset points to the problem area, except if we got
+ * a data_crc_error. In that case it points one past the
+ * failing sector.
+ *
+ * Now adjust sector_offset so it always points one past he
+ * failing sector. I.e. skip the bad sector.
+ */
+ if (cause != data_crc_error) {
+ skip_bad_sector(buff);
+ }
+ update_error_maps(buff, buff->sector_offset - 1);
+ }
+ TRACE_EXIT;
+}
+
+static int calc_steps(int cmd)
+{
+ if (ftape_current_cylinder > cmd) {
+ return ftape_current_cylinder - cmd;
+ } else {
+ return ftape_current_cylinder + cmd;
+ }
+}
+
+static void pause_tape(int retry, int mode)
+{
+ int result;
+ __u8 out[3] = {FDC_SEEK, ft_drive_sel, 0};
+ TRACE_FUN(ft_t_any);
+
+ /* We'll use a raw seek command to get the tape to rewind and
+ * stop for a retry.
+ */
+ ++ft_history.rewinds;
+ if (qic117_cmds[ftape_current_command].non_intr) {
+ TRACE(ft_t_warn, "motion command may be issued too soon");
+ }
+ if (retry && (mode == fdc_reading_data ||
+ mode == fdc_reading_id ||
+ mode == fdc_verifying)) {
+ ftape_current_command = QIC_MICRO_STEP_PAUSE;
+ ftape_might_be_off_track = 1;
+ } else {
+ ftape_current_command = QIC_PAUSE;
+ }
+ out[2] = calc_steps(ftape_current_command);
+ result = fdc_command(out, 3); /* issue QIC_117 command */
+ ftape_current_cylinder = out[ 2];
+ if (result < 0) {
+ TRACE(ft_t_noise, "qic-pause failed, status = %d", result);
+ } else {
+ ft_location.known = 0;
+ ft_runner_status = idle;
+ ft_hide_interrupt = 1;
+ ftape_tape_running = 0;
+ }
+ TRACE_EXIT;
+}
+
+static void continue_xfer(buffer_struct *buff,
+ fdc_mode_enum mode,
+ unsigned int skip)
+{
+ int write = 0;
+ TRACE_FUN(ft_t_any);
+
+ if (mode == fdc_writing_data || mode == fdc_deleting) {
+ write = 1;
+ }
+ /* This part can be removed if it never happens
+ */
+ if (skip > 0 &&
+ (ft_runner_status != running ||
+ (write && (buff->status != writing)) ||
+ (!write && (buff->status != reading &&
+ buff->status != verifying)))) {
+ TRACE(ft_t_err, "unexpected runner/buffer state %d/%d",
+ ft_runner_status, buff->status);
+ buff->status = error;
+ /* finish this buffer: */
+ (void)ftape_next_buffer(ft_queue_head);
+ ft_runner_status = aborting;
+ fdc_mode = fdc_idle;
+ } else if (buff->remaining > 0 && ftape_calc_next_cluster(buff) > 0) {
+ /* still sectors left in current segment, continue
+ * with this segment
+ */
+ if (fdc_setup_read_write(buff, mode) < 0) {
+ /* failed, abort operation
+ */
+ buff->bytes = buff->ptr - buff->address;
+ buff->status = error;
+ /* finish this buffer: */
+ (void)ftape_next_buffer(ft_queue_head);
+ ft_runner_status = aborting;
+ fdc_mode = fdc_idle;
+ }
+ } else {
+ /* current segment completed
+ */
+ unsigned int last_segment = buff->segment_id;
+ int eot = ((last_segment + 1) % ft_segments_per_track) == 0;
+ unsigned int next = buff->next_segment; /* 0 means stop ! */
+
+ buff->bytes = buff->ptr - buff->address;
+ buff->status = done;
+ buff = ftape_next_buffer(ft_queue_head);
+ if (eot) {
+ /* finished last segment on current track,
+ * can't continue
+ */
+ ft_runner_status = logical_eot;
+ fdc_mode = fdc_idle;
+ TRACE_EXIT;
+ }
+ if (next <= 0) {
+ /* don't continue with next segment
+ */
+ TRACE(ft_t_noise, "no %s allowed, stopping tape",
+ (write) ? "write next" : "read ahead");
+ pause_tape(0, mode);
+ ft_runner_status = idle; /* not quite true until
+ * next irq
+ */
+ TRACE_EXIT;
+ }
+ /* continue with next segment
+ */
+ if (buff->status != waiting) {
+ TRACE(ft_t_noise, "all input buffers %s, pausing tape",
+ (write) ? "empty" : "full");
+ pause_tape(0, mode);
+ ft_runner_status = idle; /* not quite true until
+ * next irq
+ */
+ TRACE_EXIT;
+ }
+ if (write && next != buff->segment_id) {
+ TRACE(ft_t_noise,
+ "segments out of order, aborting write");
+ ft_runner_status = do_abort;
+ fdc_mode = fdc_idle;
+ TRACE_EXIT;
+ }
+ ftape_setup_new_segment(buff, next, 0);
+ if (stop_read_ahead) {
+ buff->next_segment = 0;
+ stop_read_ahead = 0;
+ }
+ if (ftape_calc_next_cluster(buff) == 0 ||
+ fdc_setup_read_write(buff, mode) != 0) {
+ TRACE(ft_t_err, "couldn't start %s-ahead",
+ write ? "write" : "read");
+ ft_runner_status = do_abort;
+ fdc_mode = fdc_idle;
+ } else {
+ /* keep on going */
+ switch (ft_driver_state) {
+ case reading: buff->status = reading; break;
+ case verifying: buff->status = verifying; break;
+ case writing: buff->status = writing; break;
+ case deleting: buff->status = deleting; break;
+ default:
+ TRACE(ft_t_err,
+ "BUG: ft_driver_state %d should be one out of "
+ "{reading, writing, verifying, deleting}",
+ ft_driver_state);
+ buff->status = write ? writing : reading;
+ break;
+ }
+ }
+ }
+ TRACE_EXIT;
+}
+
+static void retry_sector(buffer_struct *buff,
+ int mode,
+ unsigned int skip)
+{
+ TRACE_FUN(ft_t_any);
+
+ TRACE(ft_t_noise, "%s error, will retry",
+ (mode == fdc_writing_data || mode == fdc_deleting) ? "write" : "read");
+ pause_tape(1, mode);
+ ft_runner_status = aborting;
+ buff->status = error;
+ buff->skip = skip;
+ TRACE_EXIT;
+}
+
+static unsigned int find_resume_point(buffer_struct *buff)
+{
+ int i = 0;
+ SectorMap mask;
+ SectorMap map;
+ TRACE_FUN(ft_t_any);
+
+ /* This function is to be called after all variables have been
+ * updated to point past the failing sector.
+ * If there are any soft errors before the failing sector,
+ * find the first soft error and return the sector offset.
+ * Otherwise find the last hard error.
+ * Note: there should always be at least one hard or soft error !
+ */
+ if (buff->sector_offset < 1 || buff->sector_offset > 32) {
+ TRACE(ft_t_bug, "BUG: sector_offset = %d",
+ buff->sector_offset);
+ TRACE_EXIT 0;
+ }
+ if (buff->sector_offset >= 32) { /* C-limitation on shift ! */
+ mask = 0xffffffff;
+ } else {
+ mask = (1 << buff->sector_offset) - 1;
+ }
+ map = buff->soft_error_map & mask;
+ if (map) {
+ while ((map & (1 << i)) == 0) {
+ ++i;
+ }
+ TRACE(ft_t_noise, "at sector %d", FT_SECTOR(i));
+ } else {
+ map = buff->hard_error_map & mask;
+ i = buff->sector_offset - 1;
+ if (map) {
+ while ((map & (1 << i)) == 0) {
+ --i;
+ }
+ TRACE(ft_t_noise, "after sector %d", FT_SECTOR(i));
+ ++i; /* first sector after last hard error */
+ } else {
+ TRACE(ft_t_bug, "BUG: no soft or hard errors");
+ }
+ }
+ TRACE_EXIT i;
+}
+
+/* check possible dma residue when formatting, update position record in
+ * buffer struct. This is, of course, modelled after determine_progress(), but
+ * we don't need to set up for retries because the format process cannot be
+ * interrupted (except at the end of the tape track).
+ */
+static int determine_fmt_progress(buffer_struct *buff, error_cause cause)
+{
+ unsigned int dma_residue;
+ TRACE_FUN(ft_t_any);
+
+ /* Using less preferred order of disable_dma and
+ * get_dma_residue because this seems to fail on at least one
+ * system if reversed!
+ */
+ dma_residue = get_dma_residue(fdc.dma);
+ disable_dma(fdc.dma);
+ if (cause != no_error || dma_residue != 0) {
+ TRACE(ft_t_info, "DMA residue = 0x%04x", dma_residue);
+ fdc_mode = fdc_idle;
+ switch(cause) {
+ case no_error:
+ ft_runner_status = aborting;
+ buff->status = idle;
+ break;
+ case overrun_error:
+ /* got an overrun error on the first byte, must be a
+ * hardware problem
+ */
+ TRACE(ft_t_bug,
+ "Unexpected error: failing DMA controller ?");
+ ft_runner_status = do_abort;
+ buff->status = error;
+ break;
+ default:
+ TRACE(ft_t_noise, "Unexpected error at segment %d",
+ buff->segment_id);
+ ft_runner_status = do_abort;
+ buff->status = error;
+ break;
+ }
+ TRACE_EXIT -EIO; /* can only retry entire track in format mode
+ */
+ }
+ /* Update var's influenced by the DMA operation.
+ */
+ buff->ptr += FT_SECTORS_PER_SEGMENT * 4;
+ buff->bytes -= FT_SECTORS_PER_SEGMENT * 4;
+ buff->remaining -= FT_SECTORS_PER_SEGMENT;
+ buff->segment_id ++; /* done with segment */
+ TRACE_EXIT 0;
+}
+
+/*
+ * Continue formatting, switch buffers if there is no data left in
+ * current buffer. This is, of course, modelled after
+ * continue_xfer(), but we don't need to set up for retries because
+ * the format process cannot be interrupted (except at the end of the
+ * tape track).
+ */
+static void continue_formatting(buffer_struct *buff)
+{
+ TRACE_FUN(ft_t_any);
+
+ if (buff->remaining <= 0) { /* no space left in dma buffer */
+ unsigned int next = buff->next_segment;
+
+ if (next == 0) { /* end of tape track */
+ buff->status = done;
+ ft_runner_status = logical_eot;
+ fdc_mode = fdc_idle;
+ TRACE(ft_t_noise, "Done formatting track %d",
+ ft_location.track);
+ TRACE_EXIT;
+ }
+ /*
+ * switch to next buffer!
+ */
+ buff->status = done;
+ buff = ftape_next_buffer(ft_queue_head);
+
+ if (buff->status != waiting || next != buff->segment_id) {
+ goto format_setup_error;
+ }
+ }
+ if (fdc_setup_formatting(buff) < 0) {
+ goto format_setup_error;
+ }
+ buff->status = formatting;
+ TRACE(ft_t_fdc_dma, "Formatting segment %d on track %d",
+ buff->segment_id, ft_location.track);
+ TRACE_EXIT;
+ format_setup_error:
+ ft_runner_status = do_abort;
+ fdc_mode = fdc_idle;
+ buff->status = error;
+ TRACE(ft_t_err, "Error setting up for segment %d on track %d",
+ buff->segment_id, ft_location.track);
+ TRACE_EXIT;
+
+}
+
+/* this handles writing, read id, reading and formatting
+ */
+static void handle_fdc_busy(buffer_struct *buff)
+{
+ static int no_data_error_count = 0;
+ int retry = 0;
+ error_cause cause;
+ __u8 in[7];
+ int skip;
+ fdc_mode_enum fmode = fdc_mode;
+ TRACE_FUN(ft_t_any);
+
+ if (fdc_result(in, 7) < 0) { /* better get it fast ! */
+ TRACE(ft_t_err,
+ "Probably fatal error during FDC Result Phase\n"
+ KERN_INFO
+ "drive may hang until (power on) reset :-(");
+ /* what to do next ????
+ */
+ TRACE_EXIT;
+ }
+ cause = decode_irq_cause(fdc_mode, in);
+#ifdef TESTING
+ { int i;
+ for (i = 0; i < (int)ft_nr_buffers; ++i)
+ TRACE(ft_t_any, "buffer[%d] status: %d, segment_id: %d",
+ i, ft_buffer[i]->status, ft_buffer[i]->segment_id);
+ }
+#endif
+ if (fmode == fdc_reading_data && ft_driver_state == verifying) {
+ fmode = fdc_verifying;
+ }
+ switch (fmode) {
+ case fdc_verifying:
+ if (ft_runner_status == aborting ||
+ ft_runner_status == do_abort) {
+ TRACE(ft_t_noise,"aborting %s",fdc_mode_txt(fdc_mode));
+ break;
+ }
+ if (buff->retry > 0) {
+ TRACE(ft_t_flow, "this is retry nr %d", buff->retry);
+ }
+ switch (cause) {
+ case no_error:
+ no_data_error_count = 0;
+ determine_verify_progress(buff, cause, in[5]);
+ if (in[2] & 0x40) {
+ /* This should not happen when verifying
+ */
+ TRACE(ft_t_warn,
+ "deleted data in segment %d/%d",
+ buff->segment_id,
+ FT_SECTOR(buff->sector_offset - 1));
+ buff->remaining = 0; /* abort transfer */
+ buff->hard_error_map = EMPTY_SEGMENT;
+ skip = 1;
+ } else {
+ skip = 0;
+ }
+ continue_xfer(buff, fdc_mode, skip);
+ break;
+ case no_data_error:
+ no_data_error_count ++;
+ case overrun_error:
+ retry ++;
+ case id_am_error:
+ case id_crc_error:
+ case data_am_error:
+ case data_crc_error:
+ determine_verify_progress(buff, cause, in[5]);
+ if (cause == no_data_error) {
+ if (no_data_error_count >= 2) {
+ TRACE(ft_t_warn,
+ "retrying because of successive "
+ "no data errors");
+ no_data_error_count = 0;
+ } else {
+ retry --;
+ }
+ } else {
+ no_data_error_count = 0;
+ }
+ if (retry) {
+ skip = find_resume_point(buff);
+ } else {
+ skip = buff->sector_offset;
+ }
+ if (retry && skip < 32) {
+ retry_sector(buff, fdc_mode, skip);
+ } else {
+ continue_xfer(buff, fdc_mode, skip);
+ }
+ update_history(cause);
+ break;
+ default:
+ /* Don't know why this could happen
+ * but find out.
+ */
+ determine_verify_progress(buff, cause, in[5]);
+ retry_sector(buff, fdc_mode, 0);
+ TRACE(ft_t_err, "Error: unexpected error");
+ break;
+ }
+ break;
+ case fdc_reading_data:
+#ifdef TESTING
+ /* I'm sorry, but: NOBODY ever used this trace
+ * messages for ages. I guess that Bas was the last person
+ * that ever really used this (thank you, between the lines)
+ */
+ if (cause == no_error) {
+ TRACE(ft_t_flow,"reading segment %d",buff->segment_id);
+ } else {
+ TRACE(ft_t_noise, "error reading segment %d",
+ buff->segment_id);
+ TRACE(ft_t_noise, "\n"
+ KERN_INFO
+ "IRQ:C: 0x%02x, H: 0x%02x, R: 0x%02x, N: 0x%02x\n"
+ KERN_INFO
+ "BUF:C: 0x%02x, H: 0x%02x, R: 0x%02x",
+ in[3], in[4], in[5], in[6],
+ buff->cyl, buff->head, buff->sect);
+ }
+#endif
+ if (ft_runner_status == aborting ||
+ ft_runner_status == do_abort) {
+ TRACE(ft_t_noise,"aborting %s",fdc_mode_txt(fdc_mode));
+ break;
+ }
+ if (buff->bad_sector_map == FAKE_SEGMENT) {
+ /* This condition occurs when reading a `fake'
+ * sector that's not accessible. Doesn't
+ * really matter as we would have ignored it
+ * anyway !
+ *
+ * Chance is that we're past the next segment
+ * now, so the next operation may fail and
+ * result in a retry.
+ */
+ buff->remaining = 0; /* skip failing sector */
+ /* buff->ptr = buff->address; */
+ /* fake success: */
+ continue_xfer(buff, fdc_mode, 1);
+ /* trace calls are expensive: place them AFTER
+ * the real stuff has been done.
+ *
+ */
+ TRACE(ft_t_noise, "skipping empty segment %d (read), size? %d",
+ buff->segment_id, buff->ptr - buff->address);
+ TRACE_EXIT;
+ }
+ if (buff->retry > 0) {
+ TRACE(ft_t_flow, "this is retry nr %d", buff->retry);
+ }
+ switch (cause) {
+ case no_error:
+ determine_progress(buff, cause, in[5]);
+ if (in[2] & 0x40) {
+ /* Handle deleted data in header segments.
+ * Skip segment and force read-ahead.
+ */
+ TRACE(ft_t_warn,
+ "deleted data in segment %d/%d",
+ buff->segment_id,
+ FT_SECTOR(buff->sector_offset - 1));
+ buff->deleted = 1;
+ buff->remaining = 0;/*abort transfer */
+ buff->soft_error_map |=
+ (-1L << buff->sector_offset);
+ if (buff->segment_id == 0) {
+ /* stop on next segment */
+ stop_read_ahead = 1;
+ }
+ /* force read-ahead: */
+ buff->next_segment =
+ buff->segment_id + 1;
+ skip = (FT_SECTORS_PER_SEGMENT -
+ buff->sector_offset);
+ } else {
+ skip = 0;
+ }
+ continue_xfer(buff, fdc_mode, skip);
+ break;
+ case no_data_error:
+ /* Tape started too far ahead of or behind the
+ * right sector. This may also happen in the
+ * middle of a segment !
+ *
+ * Handle no-data as soft error. If next
+ * sector fails too, a retry (with needed
+ * reposition) will follow.
+ */
+ retry ++;
+ case id_am_error:
+ case id_crc_error:
+ case data_am_error:
+ case data_crc_error:
+ case overrun_error:
+ retry += (buff->soft_error_map != 0 ||
+ buff->hard_error_map != 0);
+ determine_progress(buff, cause, in[5]);
+#if 1 || defined(TESTING)
+ if (cause == overrun_error) retry ++;
+#endif
+ if (retry) {
+ skip = find_resume_point(buff);
+ } else {
+ skip = buff->sector_offset;
+ }
+ /* Try to resume with next sector on single
+ * errors (let ecc correct it), but retry on
+ * no_data (we'll be past the target when we
+ * get here so we cannot retry) or on
+ * multiple errors (reduce chance on ecc
+ * failure).
+ */
+ /* cH: 23/02/97: if the last sector in the
+ * segment was a hard error, then there is
+ * no sense in a retry. This occasion seldom
+ * occurs but ... @:³²¸`@%&§$
+ */
+ if (retry && skip < 32) {
+ retry_sector(buff, fdc_mode, skip);
+ } else {
+ continue_xfer(buff, fdc_mode, skip);
+ }
+ update_history(cause);
+ break;
+ default:
+ /* Don't know why this could happen
+ * but find out.
+ */
+ determine_progress(buff, cause, in[5]);
+ retry_sector(buff, fdc_mode, 0);
+ TRACE(ft_t_err, "Error: unexpected error");
+ break;
+ }
+ break;
+ case fdc_reading_id:
+ if (cause == no_error) {
+ fdc_cyl = in[3];
+ fdc_head = in[4];
+ fdc_sect = in[5];
+ TRACE(ft_t_fdc_dma,
+ "id read: C: 0x%02x, H: 0x%02x, R: 0x%02x",
+ fdc_cyl, fdc_head, fdc_sect);
+ } else { /* no valid information, use invalid sector */
+ fdc_cyl = fdc_head = fdc_sect = 0;
+ TRACE(ft_t_flow, "Didn't find valid sector Id");
+ }
+ fdc_mode = fdc_idle;
+ break;
+ case fdc_deleting:
+ case fdc_writing_data:
+#ifdef TESTING
+ if (cause == no_error) {
+ TRACE(ft_t_flow, "writing segment %d", buff->segment_id);
+ } else {
+ TRACE(ft_t_noise, "error writing segment %d",
+ buff->segment_id);
+ }
+#endif
+ if (ft_runner_status == aborting ||
+ ft_runner_status == do_abort) {
+ TRACE(ft_t_flow, "aborting %s",fdc_mode_txt(fdc_mode));
+ break;
+ }
+ if (buff->retry > 0) {
+ TRACE(ft_t_flow, "this is retry nr %d", buff->retry);
+ }
+ if (buff->bad_sector_map == FAKE_SEGMENT) {
+ /* This condition occurs when trying to write to a
+ * `fake' sector that's not accessible. Doesn't really
+ * matter as it isn't used anyway ! Might be located
+ * at wrong segment, then we'll fail on the next
+ * segment.
+ */
+ TRACE(ft_t_noise, "skipping empty segment (write)");
+ buff->remaining = 0; /* skip failing sector */
+ /* fake success: */
+ continue_xfer(buff, fdc_mode, 1);
+ break;
+ }
+ switch (cause) {
+ case no_error:
+ determine_progress(buff, cause, in[5]);
+ continue_xfer(buff, fdc_mode, 0);
+ break;
+ case no_data_error:
+ case id_am_error:
+ case id_crc_error:
+ case data_am_error:
+ case overrun_error:
+ update_history(cause);
+ determine_progress(buff, cause, in[5]);
+ skip = find_resume_point(buff);
+ retry_sector(buff, fdc_mode, skip);
+ break;
+ default:
+ if (in[1] & 0x02) {
+ TRACE(ft_t_err, "media not writable");
+ } else {
+ TRACE(ft_t_bug, "unforeseen write error");
+ }
+ fdc_mode = fdc_idle;
+ break;
+ }
+ break; /* fdc_deleting || fdc_writing_data */
+ case fdc_formatting:
+ /* The interrupt comes after formatting a segment. We then
+ * have to set up QUICKLY for the next segment. But
+ * afterwards, there is plenty of time.
+ */
+ switch (cause) {
+ case no_error:
+ /* would like to keep most of the formatting stuff
+ * outside the isr code, but timing is too critical
+ */
+ if (determine_fmt_progress(buff, cause) >= 0) {
+ continue_formatting(buff);
+ }
+ break;
+ case no_data_error:
+ case id_am_error:
+ case id_crc_error:
+ case data_am_error:
+ case overrun_error:
+ default:
+ determine_fmt_progress(buff, cause);
+ update_history(cause);
+ if (in[1] & 0x02) {
+ TRACE(ft_t_err, "media not writable");
+ } else {
+ TRACE(ft_t_bug, "unforeseen write error");
+ }
+ break;
+ } /* cause */
+ break;
+ default:
+ TRACE(ft_t_warn, "Warning: unexpected irq during: %s",
+ fdc_mode_txt(fdc_mode));
+ fdc_mode = fdc_idle;
+ break;
+ }
+ TRACE_EXIT;
+}
+
+/* FDC interrupt service routine.
+ */
+void fdc_isr(void)
+{
+ static int isr_active = 0;
+#ifdef TESTING
+ unsigned int t0 = ftape_timestamp();
+#endif
+ TRACE_FUN(ft_t_any);
+
+ if (isr_active++) {
+ --isr_active;
+ TRACE(ft_t_bug, "BUG: nested interrupt, not good !");
+ *fdc.hook = fdc_isr; /* hook our handler into the fdc
+ * code again
+ */
+ TRACE_EXIT;
+ }
+ sti();
+ if (inb_p(fdc.msr) & FDC_BUSY) { /* Entering Result Phase */
+ ft_hide_interrupt = 0;
+ handle_fdc_busy(ftape_get_buffer(ft_queue_head));
+ if (ft_runner_status == do_abort) {
+ /* cease operation, remember tape position
+ */
+ TRACE(ft_t_flow, "runner aborting");
+ ft_runner_status = aborting;
+ ++ft_expected_stray_interrupts;
+ }
+ } else { /* !FDC_BUSY */
+ /* clear interrupt, cause should be gotten by issuing
+ * a Sense Interrupt Status command.
+ */
+ if (fdc_mode == fdc_recalibrating || fdc_mode == fdc_seeking) {
+ if (ft_hide_interrupt) {
+ int st0;
+ int pcn;
+
+ if (fdc_sense_interrupt_status(&st0, &pcn) < 0)
+ TRACE(ft_t_err,
+ "sense interrupt status failed");
+ ftape_current_cylinder = pcn;
+ TRACE(ft_t_flow, "handled hidden interrupt");
+ }
+ ft_seek_completed = 1;
+ fdc_mode = fdc_idle;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,16)
+ } else if (!waitqueue_active(&ftape_wait_intr)) {
+#else
+ } else if (!ftape_wait_intr) {
+#endif
+ if (ft_expected_stray_interrupts == 0) {
+ TRACE(ft_t_warn, "unexpected stray interrupt");
+ } else {
+ TRACE(ft_t_flow, "expected stray interrupt");
+ --ft_expected_stray_interrupts;
+ }
+ } else {
+ if (fdc_mode == fdc_reading_data ||
+ fdc_mode == fdc_verifying ||
+ fdc_mode == fdc_writing_data ||
+ fdc_mode == fdc_deleting ||
+ fdc_mode == fdc_formatting ||
+ fdc_mode == fdc_reading_id) {
+ if (inb_p(fdc.msr) & FDC_BUSY) {
+ TRACE(ft_t_bug,
+ "***** FDC failure, busy too late");
+ } else {
+ TRACE(ft_t_bug,
+ "***** FDC failure, no busy");
+ }
+ } else {
+ TRACE(ft_t_fdc_dma, "awaited stray interrupt");
+ }
+ }
+ ft_hide_interrupt = 0;
+ }
+ /* Handle sleep code.
+ */
+ if (!ft_hide_interrupt) {
+ ft_interrupt_seen ++;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,16)
+ if (waitqueue_active(&ftape_wait_intr)) {
+ wake_up_interruptible(&ftape_wait_intr);
+ }
+#else
+ if (ftape_wait_intr) {
+ wake_up_interruptible(&ftape_wait_intr);
+ }
+#endif
+ } else {
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,16)
+ TRACE(ft_t_flow, "hiding interrupt while %s",
+ waitqueue_active(&ftape_wait_intr) ? "waiting":"active");
+#else
+ TRACE(ft_t_flow, "hiding interrupt while %s",
+ ftape_wait_intr ? "waiting" : "active");
+#endif
+ }
+#ifdef TESTING
+ t0 = ftape_timediff(t0, ftape_timestamp());
+ if (t0 >= 1000) {
+ /* only tell us about long calls */
+ TRACE(ft_t_noise, "isr() duration: %5d usec", t0);
+ }
+#endif
+ *fdc.hook = fdc_isr; /* hook our handler into the fdc code again */
+ --isr_active;
+ TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/fdc-isr.h b/drivers/char/ftape/lowlevel/fdc-isr.h
index 123d4168f..065aa9789 100644
--- a/drivers/char/ftape/fdc-isr.h
+++ b/drivers/char/ftape/lowlevel/fdc-isr.h
@@ -2,7 +2,8 @@
#define _FDC_ISR_H
/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,25 +20,23 @@
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
- $Source: /home/bas/distr/ftape-2.03b/RCS/fdc-isr.h,v $
- $Author: bas $
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-isr.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:07 $
*
- $Revision: 1.8 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
- *
- * This file contains the low level functions
- * that communicate with the floppy disk controller,
- * for the QIC-40/80 floppy-tape driver for Linux.
+ * This file declares the global variables necessary to
+ * synchronize the interrupt service routine (isr) with the
+ * remainder of the QIC-40/80/3010/3020 floppy-tape driver
+ * "ftape" for Linux.
*/
/*
* fdc-isr.c defined public variables
*/
-extern volatile int expected_stray_interrupts; /* masks stray interrupts */
-extern volatile int seek_completed; /* flag set by isr */
-extern volatile int interrupt_seen; /* flag set by isr */
-extern volatile int expect_stray_interrupt;
+extern volatile int ft_expected_stray_interrupts; /* masks stray interrupts */
+extern volatile int ft_seek_completed; /* flag set by isr */
+extern volatile int ft_interrupt_seen; /* flag set by isr */
+extern volatile int ft_hide_interrupt; /* flag set by isr */
/*
* fdc-io.c defined public functions
diff --git a/drivers/char/ftape/lowlevel/ftape-bsm.c b/drivers/char/ftape/lowlevel/ftape-bsm.c
new file mode 100644
index 000000000..a79c468e3
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-bsm.c
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 1994-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-bsm.c,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/10/05 19:15:15 $
+ *
+ * This file contains the bad-sector map handling code for
+ * the QIC-117 floppy tape driver for Linux.
+ * QIC-40, QIC-80, QIC-3010 and QIC-3020 maps are implemented.
+ */
+
+#include <linux/string.h>
+
+#include <linux/ftape.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-bsm.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+
+/* Global vars.
+ */
+
+/* Local vars.
+ */
+static __u8 *bad_sector_map = NULL;
+static SectorCount *bsm_hash_ptr = NULL;
+
+typedef enum {
+ forward, backward
+} mode_type;
+
+#if 0
+/* fix_tape converts a normal QIC-80 tape into a 'wide' tape.
+ * For testing purposes only !
+ */
+void fix_tape(__u8 * buffer, ft_format_type new_code)
+{
+ static __u8 list[BAD_SECTOR_MAP_SIZE];
+ SectorMap *src_ptr = (SectorMap *) list;
+ __u8 *dst_ptr = bad_sector_map;
+ SectorMap map;
+ unsigned int sector = 1;
+ int i;
+
+ if (format_code != fmt_var && format_code != fmt_big) {
+ memcpy(list, bad_sector_map, sizeof(list));
+ memset(bad_sector_map, 0, sizeof(bad_sector_map));
+ while ((__u8 *) src_ptr - list < sizeof(list)) {
+ map = *src_ptr++;
+ if (map == EMPTY_SEGMENT) {
+ *(SectorMap *) dst_ptr = 0x800000 + sector;
+ dst_ptr += 3;
+ sector += SECTORS_PER_SEGMENT;
+ } else {
+ for (i = 0; i < SECTORS_PER_SEGMENT; ++i) {
+ if (map & 1) {
+ *(SewctorMap *) dst_ptr = sector;
+ dst_ptr += 3;
+ }
+ map >>= 1;
+ ++sector;
+ }
+ }
+ }
+ }
+ bad_sector_map_changed = 1;
+ *(buffer + 4) = new_code; /* put new format code */
+ if (format_code != fmt_var && new_code == fmt_big) {
+ PUT4(buffer, FT_6_HSEG_1, (__u32)GET2(buffer, 6));
+ PUT4(buffer, FT_6_HSEG_2, (__u32)GET2(buffer, 8));
+ PUT4(buffer, FT_6_FRST_SEG, (__u32)GET2(buffer, 10));
+ PUT4(buffer, FT_6_LAST_SEG, (__u32)GET2(buffer, 12));
+ memset(buffer+6, '\0', 8);
+ }
+ format_code = new_code;
+}
+
+#endif
+
+/* given buffer that contains a header segment, find the end of
+ * of the bsm list
+ */
+__u8 * ftape_find_end_of_bsm_list(__u8 * address)
+{
+ __u8 *ptr = address + FT_HEADER_END; /* start of bsm list */
+ __u8 *limit = address + FT_SEGMENT_SIZE;
+ while (ptr + 2 < limit) {
+ if (ptr[0] || ptr[1] || ptr[2]) {
+ ptr += 3;
+ } else {
+ return ptr;
+ }
+ }
+ return NULL;
+}
+
+static inline void put_sector(SectorCount *ptr, unsigned int sector)
+{
+ ptr->bytes[0] = sector & 0xff;
+ sector >>= 8;
+ ptr->bytes[1] = sector & 0xff;
+ sector >>= 8;
+ ptr->bytes[2] = sector & 0xff;
+}
+
+static inline unsigned int get_sector(SectorCount *ptr)
+{
+#if 1
+ unsigned int sector;
+
+ sector = ptr->bytes[0];
+ sector += ptr->bytes[1] << 8;
+ sector += ptr->bytes[2] << 16;
+
+ return sector;
+#else
+ /* GET4 gets the next four bytes in Intel little endian order
+ * and converts them to host byte order and handles unaligned
+ * access.
+ */
+ return (GET4(ptr, 0) & 0x00ffffff); /* back to host byte order */
+#endif
+}
+
+static void bsm_debug_fake(void)
+{
+ /* for testing of bad sector handling at end of tape
+ */
+#if 0
+ ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 3,
+ 0x000003e0;
+ ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 2,
+ 0xff3fffff;
+ ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 1,
+ 0xffffe000;
+#endif
+ /* Enable to test bad sector handling
+ */
+#if 0
+ ftape_put_bad_sector_entry(30, 0xfffffffe)
+ ftape_put_bad_sector_entry(32, 0x7fffffff);
+ ftape_put_bad_sector_entry(34, 0xfffeffff);
+ ftape_put_bad_sector_entry(36, 0x55555555);
+ ftape_put_bad_sector_entry(38, 0xffffffff);
+ ftape_put_bad_sector_entry(50, 0xffff0000);
+ ftape_put_bad_sector_entry(51, 0xffffffff);
+ ftape_put_bad_sector_entry(52, 0xffffffff);
+ ftape_put_bad_sector_entry(53, 0x0000ffff);
+#endif
+ /* Enable when testing multiple volume tar dumps.
+ */
+#if 0
+ {
+ int i;
+
+ for (i = ft_first_data_segment;
+ i <= ft_last_data_segment - 7; ++i) {
+ ftape_put_bad_sector_entry(i, EMPTY_SEGMENT);
+ }
+ }
+#endif
+ /* Enable when testing bit positions in *_error_map
+ */
+#if 0
+ {
+ int i;
+
+ for (i = first_data_segment; i <= last_data_segment; ++i) {
+ ftape_put_bad_sector_entry(i,
+ ftape_get_bad_sector_entry(i)
+ | 0x00ff00ff);
+ }
+ }
+#endif
+}
+
+static void print_bad_sector_map(void)
+{
+ unsigned int good_sectors;
+ unsigned int total_bad = 0;
+ int i;
+ TRACE_FUN(ft_t_flow);
+
+ if (ft_format_code == fmt_big ||
+ ft_format_code == fmt_var ||
+ ft_format_code == fmt_1100ft) {
+ SectorCount *ptr = (SectorCount *)bad_sector_map;
+ unsigned int sector;
+
+ while((sector = get_sector(ptr++)) != 0) {
+ if ((ft_format_code == fmt_big ||
+ ft_format_code == fmt_var) &&
+ sector & 0x800000) {
+ total_bad += FT_SECTORS_PER_SEGMENT - 3;
+ TRACE(ft_t_noise, "bad segment at sector: %6d",
+ sector & 0x7fffff);
+ } else {
+ ++total_bad;
+ TRACE(ft_t_noise, "bad sector: %6d", sector);
+ }
+ }
+ /* Display old ftape's end-of-file marks
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,0)
+ while ((sector = get_unaligned(((__u16*)ptr)++)) != 0) {
+ TRACE(ft_t_noise, "Old ftape eof mark: %4d/%2d",
+ sector, get_unaligned(((__u16*)ptr)++));
+ }
+#else
+ while ((sector = *((__u16*)ptr)++) != 0) {
+ TRACE(ft_t_noise, "Old ftape eof mark: %4d/%2d",
+ sector, *((__u16*)ptr)++);
+ }
+#endif
+ } else { /* fixed size format */
+ for (i = ft_first_data_segment;
+ i < (int)(ft_segments_per_track * ft_tracks_per_tape); ++i) {
+ SectorMap map = ((SectorMap *) bad_sector_map)[i];
+
+ if (map) {
+ TRACE(ft_t_noise,
+ "bsm for segment %4d: 0x%08x", i, (unsigned int)map);
+ total_bad += ((map == EMPTY_SEGMENT)
+ ? FT_SECTORS_PER_SEGMENT - 3
+ : count_ones(map));
+ }
+ }
+ }
+ good_sectors =
+ ((ft_segments_per_track * ft_tracks_per_tape - ft_first_data_segment)
+ * (FT_SECTORS_PER_SEGMENT - 3)) - total_bad;
+ TRACE(ft_t_info, "%d Kb usable on this tape", good_sectors);
+ if (total_bad == 0) {
+ TRACE(ft_t_info,
+ "WARNING: this tape has no bad blocks registered !");
+ } else {
+ TRACE(ft_t_info, "%d bad sectors", total_bad);
+ }
+ TRACE_EXIT;
+}
+
+
+void ftape_extract_bad_sector_map(__u8 * buffer)
+{
+ TRACE_FUN(ft_t_any);
+
+ /* Fill the bad sector map with the contents of buffer.
+ */
+ if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
+ /* QIC-3010/3020 and wide QIC-80 tapes no longer have a failed
+ * sector log but use this area to extend the bad sector map.
+ */
+ bad_sector_map = &buffer[FT_HEADER_END];
+ } else {
+ /* non-wide QIC-80 tapes have a failed sector log area that
+ * mustn't be included in the bad sector map.
+ */
+ bad_sector_map = &buffer[FT_FSL + FT_FSL_SIZE];
+ }
+ if (ft_format_code == fmt_1100ft ||
+ ft_format_code == fmt_var ||
+ ft_format_code == fmt_big) {
+ bsm_hash_ptr = (SectorCount *)bad_sector_map;
+ } else {
+ bsm_hash_ptr = NULL;
+ }
+ bsm_debug_fake();
+ if (TRACE_LEVEL >= ft_t_info) {
+ print_bad_sector_map();
+ }
+ TRACE_EXIT;
+}
+
+static inline SectorMap cvt2map(unsigned int sector)
+{
+ return 1 << (((sector & 0x7fffff) - 1) % FT_SECTORS_PER_SEGMENT);
+}
+
+static inline int cvt2segment(unsigned int sector)
+{
+ return ((sector & 0x7fffff) - 1) / FT_SECTORS_PER_SEGMENT;
+}
+
+static int forward_seek_entry(int segment_id,
+ SectorCount **ptr,
+ SectorMap *map)
+{
+ unsigned int sector;
+ int segment;
+
+ do {
+ sector = get_sector((*ptr)++);
+ segment = cvt2segment(sector);
+ } while (sector != 0 && segment < segment_id);
+ (*ptr) --; /* point to first sector >= segment_id */
+ /* Get all sectors in segment_id
+ */
+ if (sector == 0 || segment != segment_id) {
+ *map = 0;
+ return 0;
+ } else if ((sector & 0x800000) &&
+ (ft_format_code == fmt_var || ft_format_code == fmt_big)) {
+ *map = EMPTY_SEGMENT;
+ return FT_SECTORS_PER_SEGMENT;
+ } else {
+ int count = 1;
+ SectorCount *tmp_ptr = (*ptr) + 1;
+
+ *map = cvt2map(sector);
+ while ((sector = get_sector(tmp_ptr++)) != 0 &&
+ (segment = cvt2segment(sector)) == segment_id) {
+ *map |= cvt2map(sector);
+ ++count;
+ }
+ return count;
+ }
+}
+
+static int backwards_seek_entry(int segment_id,
+ SectorCount **ptr,
+ SectorMap *map)
+{
+ unsigned int sector;
+ int segment; /* max unsigned int */
+
+ if (*ptr <= (SectorCount *)bad_sector_map) {
+ *map = 0;
+ return 0;
+ }
+ do {
+ sector = get_sector(--(*ptr));
+ segment = cvt2segment(sector);
+ } while (*ptr > (SectorCount *)bad_sector_map && segment > segment_id);
+ if (segment > segment_id) { /* at start of list, no entry found */
+ *map = 0;
+ return 0;
+ } else if (segment < segment_id) {
+ /* before smaller entry, adjust for overshoot */
+ (*ptr) ++;
+ *map = 0;
+ return 0;
+ } else if ((sector & 0x800000) &&
+ (ft_format_code == fmt_big || ft_format_code == fmt_var)) {
+ *map = EMPTY_SEGMENT;
+ return FT_SECTORS_PER_SEGMENT;
+ } else { /* get all sectors in segment_id */
+ int count = 1;
+
+ *map = cvt2map(sector);
+ while(*ptr > (SectorCount *)bad_sector_map) {
+ sector = get_sector(--(*ptr));
+ segment = cvt2segment(sector);
+ if (segment != segment_id) {
+ break;
+ }
+ *map |= cvt2map(sector);
+ ++count;
+ }
+ if (segment < segment_id) {
+ (*ptr) ++;
+ }
+ return count;
+ }
+}
+
+void ftape_put_bad_sector_entry(int segment_id, SectorMap new_map)
+{
+ SectorCount *ptr = (SectorCount *)bad_sector_map;
+ int count;
+ int new_count;
+ SectorMap map;
+ TRACE_FUN(ft_t_any);
+
+ if (ft_format_code == fmt_1100ft ||
+ ft_format_code == fmt_var ||
+ ft_format_code == fmt_big) {
+ count = forward_seek_entry(segment_id, &ptr, &map);
+ new_count = count_ones(new_map);
+ /* If format code == 4 put empty segment instead of 32
+ * bad sectors.
+ */
+ if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
+ if (new_count == FT_SECTORS_PER_SEGMENT) {
+ new_count = 1;
+ }
+ if (count == FT_SECTORS_PER_SEGMENT) {
+ count = 1;
+ }
+ }
+ if (count != new_count) {
+ /* insert (or delete if < 0) new_count - count
+ * entries. Move trailing part of list
+ * including terminating 0.
+ */
+ SectorCount *hi_ptr = ptr;
+
+ do {
+ } while (get_sector(hi_ptr++) != 0);
+ /* Note: ptr is of type byte *, and each bad sector
+ * consumes 3 bytes.
+ */
+ memmove(ptr + new_count, ptr + count,
+ (size_t)(hi_ptr - (ptr + count))*sizeof(SectorCount));
+ }
+ TRACE(ft_t_noise, "putting map 0x%08x at %p, segment %d",
+ (unsigned int)new_map, ptr, segment_id);
+ if (new_count == 1 && new_map == EMPTY_SEGMENT) {
+ put_sector(ptr++, (0x800001 +
+ segment_id *
+ FT_SECTORS_PER_SEGMENT));
+ } else {
+ int i = 0;
+
+ while (new_map) {
+ if (new_map & 1) {
+ put_sector(ptr++,
+ 1 + segment_id *
+ FT_SECTORS_PER_SEGMENT + i);
+ }
+ ++i;
+ new_map >>= 1;
+ }
+ }
+ } else {
+ ((SectorMap *) bad_sector_map)[segment_id] = new_map;
+ }
+ TRACE_EXIT;
+}
+
+SectorMap ftape_get_bad_sector_entry(int segment_id)
+{
+ if (ft_used_header_segment == -1) {
+ /* When reading header segment we'll need a blank map.
+ */
+ return 0;
+ } else if (bsm_hash_ptr != NULL) {
+ /* Invariants:
+ * map - mask value returned on last call.
+ * bsm_hash_ptr - points to first sector greater or equal to
+ * first sector in last_referenced segment.
+ * last_referenced - segment id used in the last call,
+ * sector and map belong to this id.
+ * This code is designed for sequential access and retries.
+ * For true random access it may have to be redesigned.
+ */
+ static int last_reference = -1;
+ static SectorMap map = 0;
+
+ if (segment_id > last_reference) {
+ /* Skip all sectors before segment_id
+ */
+ forward_seek_entry(segment_id, &bsm_hash_ptr, &map);
+ } else if (segment_id < last_reference) {
+ /* Skip backwards until begin of buffer or
+ * first sector in segment_id
+ */
+ backwards_seek_entry(segment_id, &bsm_hash_ptr, &map);
+ } /* segment_id == last_reference : keep map */
+ last_reference = segment_id;
+ return map;
+ } else {
+ return ((SectorMap *) bad_sector_map)[segment_id];
+ }
+}
+
+/* This is simply here to prevent us from overwriting other kernel
+ * data. Writes will result in NULL Pointer dereference.
+ */
+void ftape_init_bsm(void)
+{
+ bad_sector_map = NULL;
+ bsm_hash_ptr = NULL;
+}
diff --git a/drivers/char/ftape/ftape-bsm.h b/drivers/char/ftape/lowlevel/ftape-bsm.h
index e84363d45..0dc7805da 100644
--- a/drivers/char/ftape/ftape-bsm.h
+++ b/drivers/char/ftape/lowlevel/ftape-bsm.h
@@ -2,7 +2,8 @@
#define _FTAPE_BSM_H
/*
- * Copyright (C) 1994-1995 Bas Laarhoven.
+ * Copyright (C) 1994-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,44 +20,48 @@
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-bsm.h,v $
- $Author: bas $
- *
- $Revision: 1.5 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-bsm.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:07 $
*
* This file contains definitions for the bad sector map handling
* routines for the QIC-117 floppy-tape driver for Linux.
*/
+#include <linux/ftape.h>
+#include <linux/ftape-header-segment.h>
+
#define EMPTY_SEGMENT (0xffffffff)
#define FAKE_SEGMENT (0xfffffffe)
-/* failed sector log size (only used if format code != 4).
- */
-#define FAILED_SECTOR_LOG_SIZE (2 * SECTOR_SIZE - 256)
-
/* maximum (format code 4) bad sector map size (bytes).
*/
#define BAD_SECTOR_MAP_SIZE (29 * SECTOR_SIZE - 256)
+/* format code 4 bad sector entry, ftape uses this
+ * internally for all format codes
+ */
+typedef __u32 SectorMap;
+/* variable and 1100 ft bad sector map entry. These three bytes represent
+ * a single sector address measured from BOT.
+ */
+typedef struct NewSectorMap {
+ __u8 bytes[3];
+} SectorCount __attribute__((packed));
+
+
/*
- * ftape-io.c defined global vars.
+ * ftape-bsm.c defined global vars.
*/
-extern bad_sector_map_changed;
/*
- * ftape-io.c defined global functions.
+ * ftape-bsm.c defined global functions.
*/
-extern void update_bad_sector_map(byte * buffer);
-extern void store_bad_sector_map(byte * buffer);
-extern void extract_bad_sector_map(byte * buffer);
-extern unsigned long get_bad_sector_entry(int segment_id);
-extern void put_bad_sector_entry(int segment_id, unsigned long mask);
-extern void add_segment_to_bad_sector_map(unsigned segment);
-extern void clear_bad_sector_map(int count);
-extern byte *find_end_of_bsm_list(byte * ptr, byte * limit);
+extern void update_bad_sector_map(__u8 * buffer);
+extern void ftape_extract_bad_sector_map(__u8 * buffer);
+extern SectorMap ftape_get_bad_sector_entry(int segment_id);
+extern void ftape_put_bad_sector_entry(int segment_id, SectorMap mask);
+extern __u8 *ftape_find_end_of_bsm_list(__u8 * address);
extern void ftape_init_bsm(void);
#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-buffer.c b/drivers/char/ftape/lowlevel/ftape-buffer.c
new file mode 100644
index 000000000..8de980562
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-buffer.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-buffer.c,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/10/16 23:33:11 $
+ *
+ * This file contains the allocator/dealloctor for ftape's dynamic dma
+ * buffer.
+ */
+
+#include <asm/segment.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/wrapper.h>
+#include <asm/dma.h>
+
+#include <linux/ftape.h>
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-tracing.h"
+
+/* DMA'able memory allocation stuff.
+ */
+
+/* Pure 2^n version of get_order */
+static inline int __get_order(size_t size)
+{
+ unsigned long order;
+
+ size = (size-1) >> (PAGE_SHIFT-1);
+ order = -1;
+ do {
+ size >>= 1;
+ order++;
+ } while (size);
+ return order;
+}
+
+static inline void *dmaalloc(size_t size)
+{
+ unsigned long addr;
+
+ if (size == 0) {
+ return NULL;
+ }
+ addr = __get_dma_pages(GFP_KERNEL, __get_order(size));
+ if (addr) {
+ int i;
+
+ for (i = MAP_NR(addr); i < MAP_NR(addr+size); i++) {
+ mem_map_reserve(i);
+ }
+ }
+ return (void *)addr;
+}
+
+static inline void dmafree(void *addr, size_t size)
+{
+ if (size > 0) {
+ int i;
+
+ for (i = MAP_NR((unsigned long)addr);
+ i < MAP_NR((unsigned long)addr+size); i++) {
+ mem_map_unreserve (i);
+ }
+ free_pages((unsigned long) addr, __get_order(size));
+ }
+}
+
+static int add_one_buffer(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (ft_nr_buffers >= FT_MAX_NR_BUFFERS) {
+ TRACE_EXIT -ENOMEM;
+ }
+ ft_buffer[ft_nr_buffers] = kmalloc(sizeof(buffer_struct), GFP_KERNEL);
+ if (ft_buffer[ft_nr_buffers] == NULL) {
+ TRACE_EXIT -ENOMEM;
+ }
+ memset(ft_buffer[ft_nr_buffers], 0, sizeof(buffer_struct));
+ ft_buffer[ft_nr_buffers]->address = dmaalloc(FT_BUFF_SIZE);
+ if (ft_buffer[ft_nr_buffers]->address == NULL) {
+ kfree(ft_buffer[ft_nr_buffers]);
+ ft_buffer[ft_nr_buffers] = NULL;
+ TRACE_EXIT -ENOMEM;
+ }
+ ft_nr_buffers ++;
+ TRACE(ft_t_info, "buffer nr #%d @ %p, dma area @ %p",
+ ft_nr_buffers,
+ ft_buffer[ft_nr_buffers-1],
+ ft_buffer[ft_nr_buffers-1]->address);
+ TRACE_EXIT 0;
+}
+
+static void del_one_buffer(void)
+{
+ TRACE_FUN(ft_t_flow);
+ if (ft_nr_buffers > 0) {
+ TRACE(ft_t_info, "releasing buffer nr #%d @ %p, dma area @ %p",
+ ft_nr_buffers,
+ ft_buffer[ft_nr_buffers-1],
+ ft_buffer[ft_nr_buffers-1]->address);
+ ft_nr_buffers --;
+ dmafree(ft_buffer[ft_nr_buffers]->address, FT_BUFF_SIZE);
+ kfree(ft_buffer[ft_nr_buffers]);
+ ft_buffer[ft_nr_buffers] = NULL;
+ }
+ TRACE_EXIT;
+}
+
+int ftape_set_nr_buffers(int cnt)
+{
+ int delta = cnt - ft_nr_buffers;
+ TRACE_FUN(ft_t_flow);
+
+ if (delta > 0) {
+ while (delta--) {
+ if (add_one_buffer() < 0) {
+ TRACE_EXIT -ENOMEM;
+ }
+ }
+ } else if (delta < 0) {
+ while (delta++) {
+ del_one_buffer();
+ }
+ }
+ ftape_zap_read_buffers();
+ TRACE_EXIT 0;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-buffer.h b/drivers/char/ftape/lowlevel/ftape-buffer.h
new file mode 100644
index 000000000..eec99cee8
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-buffer.h
@@ -0,0 +1,32 @@
+#ifndef _FTAPE_BUFFER_H
+#define _FTAPE_BUFFER_H
+
+/*
+ * Copyright (C) 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-buffer.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:08 $
+ *
+ * This file contains the allocator/dealloctor for ftape's dynamic dma
+ * buffer.
+ */
+
+extern int ftape_set_nr_buffers(int cnt);
+
+#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-calibr.c b/drivers/char/ftape/lowlevel/ftape-calibr.c
new file mode 100644
index 000000000..77ee01ecf
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-calibr.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-calibr.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:08 $
+ *
+ * GP calibration routine for processor speed dependent
+ * functions.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#if defined(__alpha__)
+# include <asm/hwrpb.h>
+#elif defined(__i386__)
+# include <linux/timex.h>
+#endif
+#include <linux/ftape.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-calibr.h"
+#include "../lowlevel/fdc-io.h"
+
+#undef DEBUG
+
+#if !defined(__alpha__) && !defined(__i386__)
+# error Ftape is not implemented for this architecture!
+#endif
+
+#if defined(__alpha__)
+static unsigned long ps_per_cycle = 0;
+#endif
+
+/*
+ * Note: On Intel PCs, the clock ticks at 100 Hz (HZ==100) which is
+ * too slow for certain timeouts (and that clock doesn't even tick
+ * when interrupts are disabled). For that reason, the 8254 timer is
+ * used directly to implement fine-grained timeouts. However, on
+ * Alpha PCs, the 8254 is *not* used to implement the clock tick
+ * (which is 1024 Hz, normally) and the 8254 timer runs at some
+ * "random" frequency (it seems to run at 18Hz, but its not safe to
+ * rely on this value). Instead, we use the Alpha's "rpcc"
+ * instruction to read cycle counts. As this is a 32 bit counter,
+ * it will overflow only once per 30 seconds (on a 200MHz machine),
+ * which is plenty.
+ */
+
+unsigned int ftape_timestamp(void)
+{
+#if defined(__alpha__)
+ unsigned long r;
+
+ asm volatile ("rpcc %0" : "=r" (r));
+ return r;
+#elif defined(__i386__)
+ unsigned long flags;
+ __u16 lo;
+ __u16 hi;
+
+ save_flags(flags);
+ cli();
+ outb_p(0x00, 0x43); /* latch the count ASAP */
+ lo = inb_p(0x40); /* read the latched count */
+ lo |= inb(0x40) << 8;
+ hi = jiffies;
+ restore_flags(flags);
+ return ((hi + 1) * (unsigned int) LATCH) - lo; /* downcounter ! */
+#endif
+}
+
+static unsigned int short_ftape_timestamp(void)
+{
+#if defined(__alpha__)
+ return ftape_timestamp();
+#elif defined(__i386__)
+ unsigned int count;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(0x00, 0x43); /* latch the count ASAP */
+ count = inb_p(0x40); /* read the latched count */
+ count |= inb(0x40) << 8;
+ restore_flags(flags);
+ return (LATCH - count); /* normal: downcounter */
+#endif
+}
+
+static unsigned int diff(unsigned int t0, unsigned int t1)
+{
+#if defined(__alpha__)
+ return (t1 <= t0) ? t1 + (1UL << 32) - t0 : t1 - t0;
+#elif defined(__i386__)
+ /*
+ * This is tricky: to work for both short and full ftape_timestamps
+ * we'll have to discriminate between these.
+ * If it _looks_ like short stamps with wrapping around we'll
+ * asume it are. This will generate a small error if it really
+ * was a (very large) delta from full ftape_timestamps.
+ */
+ return (t1 <= t0 && t0 <= LATCH) ? t1 + LATCH - t0 : t1 - t0;
+#endif
+}
+
+static unsigned int usecs(unsigned int count)
+{
+#if defined(__alpha__)
+ return (ps_per_cycle * count) / 1000000UL;
+#elif defined(__i386__)
+ return (10000 * count) / ((CLOCK_TICK_RATE + 50) / 100);
+#endif
+}
+
+unsigned int ftape_timediff(unsigned int t0, unsigned int t1)
+{
+ /*
+ * Calculate difference in usec for ftape_timestamp results t0 & t1.
+ * Note that on the i386 platform with short time-stamps, the
+ * maximum allowed timespan is 1/HZ or we'll lose ticks!
+ */
+ return usecs(diff(t0, t1));
+}
+
+/* To get an indication of the I/O performance,
+ * measure the duration of the inb() function.
+ */
+static void time_inb(void)
+{
+ int i;
+ int t0, t1;
+ unsigned long flags;
+ int status;
+ TRACE_FUN(ft_t_any);
+
+ save_flags(flags);
+ cli();
+ t0 = short_ftape_timestamp();
+ for (i = 0; i < 1000; ++i) {
+ status = inb(fdc.msr);
+ }
+ t1 = short_ftape_timestamp();
+ restore_flags(flags);
+ TRACE(ft_t_info, "inb() duration: %d nsec", ftape_timediff(t0, t1));
+ TRACE_EXIT;
+}
+
+static void init_clock(void)
+{
+#if defined(__i386__)
+ unsigned int t;
+ int i;
+ TRACE_FUN(ft_t_any);
+
+ /* Haven't studied on why, but there sometimes is a problem
+ * with the tick timer readout. The two bytes get swapped.
+ * This hack solves that problem by doing one extra input.
+ */
+ for (i = 0; i < 1000; ++i) {
+ t = short_ftape_timestamp();
+ if (t > LATCH) {
+ inb_p(0x40); /* get in sync again */
+ TRACE(ft_t_warn, "clock counter fixed");
+ break;
+ }
+ }
+#elif defined(__alpha__)
+#if CONFIG_FT_ALPHA_CLOCK == 0
+#error You must define and set CONFIG_FT_ALPHA_CLOCK in the Makefile !
+#endif
+ extern struct hwrpb_struct *hwrpb;
+ TRACE_FUN(ft_t_any);
+
+ if (hwrpb->cycle_freq != 0) {
+ ps_per_cycle = (1000*1000*1000*1000UL) / hwrpb->cycle_freq;
+ } else {
+ /*
+ * HELP: Linux 2.0.x doesn't set cycle_freq on my noname !
+ */
+ ps_per_cycle = (1000*1000*1000*1000UL) / CONFIG_FT_ALPHA_CLOCK;
+ }
+#endif
+ TRACE_EXIT;
+}
+
+/*
+ * Input: function taking int count as parameter.
+ * pointers to calculated calibration variables.
+ */
+void ftape_calibrate(char *name,
+ void (*fun) (unsigned int),
+ unsigned int *calibr_count,
+ unsigned int *calibr_time)
+{
+ static int first_time = 1;
+ int i;
+ unsigned int tc = 0;
+ unsigned int count;
+ unsigned int time;
+#if defined(__i386__)
+ unsigned int old_tc = 0;
+ unsigned int old_count = 1;
+ unsigned int old_time = 1;
+#endif
+ TRACE_FUN(ft_t_flow);
+
+ if (first_time) { /* get idea of I/O performance */
+ init_clock();
+ time_inb();
+ first_time = 0;
+ }
+ /* value of timeout must be set so that on very slow systems
+ * it will give a time less than one jiffy, and on
+ * very fast systems it'll give reasonable precision.
+ */
+
+ count = 40;
+ for (i = 0; i < 15; ++i) {
+ unsigned int t0;
+ unsigned int t1;
+ unsigned int once;
+ unsigned int multiple;
+ unsigned long flags;
+
+ *calibr_count =
+ *calibr_time = count; /* set TC to 1 */
+ save_flags(flags);
+ cli();
+ fun(0); /* dummy, get code into cache */
+ t0 = short_ftape_timestamp();
+ fun(0); /* overhead + one test */
+ t1 = short_ftape_timestamp();
+ once = diff(t0, t1);
+ t0 = short_ftape_timestamp();
+ fun(count); /* overhead + count tests */
+ t1 = short_ftape_timestamp();
+ multiple = diff(t0, t1);
+ restore_flags(flags);
+ time = ftape_timediff(0, multiple - once);
+ tc = (1000 * time) / (count - 1);
+ TRACE(ft_t_any, "once:%3d us,%6d times:%6d us, TC:%5d ns",
+ usecs(once), count - 1, usecs(multiple), tc);
+#if defined(__alpha__)
+ /*
+ * Increase the calibration count exponentially until the
+ * calibration time exceeds 100 ms.
+ */
+ if (time >= 100*1000) {
+ break;
+ }
+#elif defined(__i386__)
+ /*
+ * increase the count until the resulting time nears 2/HZ,
+ * then the tc will drop sharply because we lose LATCH counts.
+ */
+ if (tc <= old_tc / 2) {
+ time = old_time;
+ count = old_count;
+ break;
+ }
+ old_tc = tc;
+ old_count = count;
+ old_time = time;
+#endif
+ count *= 2;
+ }
+ *calibr_count = count - 1;
+ *calibr_time = time;
+ TRACE(ft_t_info, "TC for `%s()' = %d nsec (at %d counts)",
+ name, (1000 * *calibr_time) / *calibr_count, *calibr_count);
+ TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/calibr.h b/drivers/char/ftape/lowlevel/ftape-calibr.h
index 75bebe75f..0c7e75246 100644
--- a/drivers/char/ftape/calibr.h
+++ b/drivers/char/ftape/lowlevel/ftape-calibr.h
@@ -1,8 +1,8 @@
-#ifndef _CALIBRATE_H
-#define _CALIBRATE_H
+#ifndef _FTAPE_CALIBR_H
+#define _FTAPE_CALIBR_H
/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
+ * Copyright (C) 1993-1996 Bas Laarhoven.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,21 +19,19 @@
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
- $Source: /home/bas/distr/ftape-2.03b/RCS/calibr.h,v $
- $Author: bas $
- *
- $Revision: 1.20 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-calibr.h,v $
+ * $Revision: 1.1 $
+ * $Date: 1997/09/19 09:05:26 $
*
* This file contains a gp calibration routine for
* hardware dependent timeout functions.
*/
-#include <linux/timex.h>
-
-extern int calibrate(char *name, void (*fun) (int), int *calibr_count, int *calibr_time);
-extern unsigned timestamp(void);
-extern int timediff(int t0, int t1);
+extern void ftape_calibrate(char *name,
+ void (*fun) (unsigned int),
+ unsigned int *calibr_count,
+ unsigned int *calibr_time);
+extern unsigned int ftape_timestamp(void);
+extern unsigned int ftape_timediff(unsigned int t0, unsigned int t1);
-#endif
+#endif /* _FTAPE_CALIBR_H */
diff --git a/drivers/char/ftape/lowlevel/ftape-ctl.c b/drivers/char/ftape/lowlevel/ftape-ctl.c
new file mode 100644
index 000000000..1ce4c2843
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-ctl.c
@@ -0,0 +1,901 @@
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ctl.c,v $
+ * $Revision: 1.4 $
+ * $Date: 1997/11/11 14:37:44 $
+ *
+ * This file contains the non-read/write ftape functions for the
+ * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/wrapper.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6)
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#endif
+#include <asm/io.h>
+
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-bsm.h"
+
+/* Global vars.
+ */
+ftape_info ftape_status = {
+/* vendor information */
+ { 0, }, /* drive type */
+/* data rates */
+ 500, /* used data rate */
+ 500, /* drive max rate */
+ 500, /* fdc max rate */
+/* drive selection, either FTAPE_SEL_A/B/C/D */
+ -1, /* drive selection */
+/* flags set after decode the drive and tape status */
+ 0, /* formatted */
+ 1, /* no tape */
+ 1, /* write protected */
+ 1, /* new tape */
+/* values of last queried drive/tape status and error */
+ {{0,}}, /* last error code */
+ {{0,}}, /* drive status, configuration, tape status */
+/* cartridge geometry */
+ 20, /* tracks_per_tape */
+ 102, /* segments_per_track */
+/* location of header segments, etc. */
+ -1, /* used_header_segment */
+ -1, /* header_segment_1 */
+ -1, /* header_segment_2 */
+ -1, /* first_data_segment */
+ -1, /* last_data_segment */
+/* the format code as stored in the header segment */
+ fmt_normal, /* format code */
+/* the default for the qic std: unknown */
+ -1,
+/* is tape running? */
+ idle, /* runner_state */
+/* is tape reading/writing/verifying/formatting/deleting */
+ idle, /* driver state */
+/* flags fatal hardware error */
+ 1, /* failure */
+/* history record */
+ { 0, } /* history record */
+};
+
+int ftape_segments_per_head = 1020;
+int ftape_segments_per_cylinder = 4;
+int ftape_init_drive_needed = 1; /* need to be global for ftape_reset_drive()
+ * in ftape-io.c
+ */
+
+/* Local vars.
+ */
+static const vendor_struct vendors[] = QIC117_VENDORS;
+static const wakeup_method methods[] = WAKEUP_METHODS;
+
+const ftape_info *ftape_get_status(void)
+{
+#if defined(STATUS_PARANOYA)
+ static ftape_info get_status;
+
+ get_status = ftape_status;
+ return &get_status;
+#else
+ return &ftape_status; /* maybe return only a copy of it to assure
+ * read only access
+ */
+#endif
+}
+
+void ftape_set_status(const ftape_info *status)
+{
+ ftape_status = *status;
+}
+
+static int ftape_not_operational(int status)
+{
+ /* return true if status indicates tape can not be used.
+ */
+ return ((status ^ QIC_STATUS_CARTRIDGE_PRESENT) &
+ (QIC_STATUS_ERROR |
+ QIC_STATUS_CARTRIDGE_PRESENT |
+ QIC_STATUS_NEW_CARTRIDGE));
+}
+
+int ftape_seek_to_eot(void)
+{
+ int status;
+ TRACE_FUN(ft_t_any);
+
+ TRACE_CATCH(ftape_ready_wait(ftape_timeout.pause, &status),);
+ while ((status & QIC_STATUS_AT_EOT) == 0) {
+ if (ftape_not_operational(status)) {
+ TRACE_EXIT -EIO;
+ }
+ TRACE_CATCH(ftape_command_wait(QIC_PHYSICAL_FORWARD,
+ ftape_timeout.rewind,&status),);
+ }
+ TRACE_EXIT 0;
+}
+
+int ftape_seek_to_bot(void)
+{
+ int status;
+ TRACE_FUN(ft_t_any);
+
+ TRACE_CATCH(ftape_ready_wait(ftape_timeout.pause, &status),);
+ while ((status & QIC_STATUS_AT_BOT) == 0) {
+ if (ftape_not_operational(status)) {
+ TRACE_EXIT -EIO;
+ }
+ TRACE_CATCH(ftape_command_wait(QIC_PHYSICAL_REVERSE,
+ ftape_timeout.rewind,&status),);
+ }
+ TRACE_EXIT 0;
+}
+
+static int ftape_new_cartridge(void)
+{
+ ft_location.track = -1; /* force seek on first access */
+ ftape_zap_read_buffers();
+ ftape_zap_write_buffers();
+ return 0;
+}
+
+int ftape_abort_operation(void)
+{
+ int result = 0;
+ int status;
+ TRACE_FUN(ft_t_flow);
+
+ if (ft_runner_status == running) {
+ TRACE(ft_t_noise, "aborting runner, waiting");
+
+ ft_runner_status = do_abort;
+ /* set timeout so that the tape will run to logical EOT
+ * if we missed the last sector and there are no queue pulses.
+ */
+ result = ftape_dumb_stop();
+ }
+ if (ft_runner_status != idle) {
+ if (ft_runner_status == do_abort) {
+ TRACE(ft_t_noise, "forcing runner abort");
+ }
+ TRACE(ft_t_noise, "stopping tape");
+ result = ftape_stop_tape(&status);
+ ft_location.known = 0;
+ ft_runner_status = idle;
+ }
+ ftape_reset_buffer();
+ ftape_zap_read_buffers();
+ ftape_set_state(idle);
+ TRACE_EXIT result;
+}
+
+static int lookup_vendor_id(unsigned int vendor_id)
+{
+ int i = 0;
+
+ while (vendors[i].vendor_id != vendor_id) {
+ if (++i >= NR_ITEMS(vendors)) {
+ return -1;
+ }
+ }
+ return i;
+}
+
+void ftape_detach_drive(void)
+{
+ TRACE_FUN(ft_t_any);
+
+ TRACE(ft_t_flow, "disabling tape drive and fdc");
+ ftape_put_drive_to_sleep(ft_drive_type.wake_up);
+ fdc_catch_stray_interrupts(1); /* one always comes */
+ fdc_disable();
+ fdc_release_irq_and_dma();
+ fdc_release_regions();
+ TRACE_EXIT;
+}
+
+static void clear_history(void)
+{
+ ft_history.used = 0;
+ ft_history.id_am_errors =
+ ft_history.id_crc_errors =
+ ft_history.data_am_errors =
+ ft_history.data_crc_errors =
+ ft_history.overrun_errors =
+ ft_history.no_data_errors =
+ ft_history.retries =
+ ft_history.crc_errors =
+ ft_history.crc_failures =
+ ft_history.ecc_failures =
+ ft_history.corrected =
+ ft_history.defects =
+ ft_history.rewinds = 0;
+}
+
+int ftape_activate_drive(vendor_struct * drive_type)
+{
+ int result = 0;
+ TRACE_FUN(ft_t_flow);
+
+ /* If we already know the drive type, wake it up.
+ * Else try to find out what kind of drive is attached.
+ */
+ if (drive_type->wake_up != unknown_wake_up) {
+ TRACE(ft_t_flow, "enabling tape drive and fdc");
+ result = ftape_wakeup_drive(drive_type->wake_up);
+ if (result < 0) {
+ TRACE(ft_t_err, "known wakeup method failed");
+ }
+ } else {
+ wake_up_types method;
+ const ft_trace_t old_tracing = TRACE_LEVEL;
+ if (TRACE_LEVEL < ft_t_flow) {
+ SET_TRACE_LEVEL(ft_t_bug);
+ }
+
+ /* Try to awaken the drive using all known methods.
+ * Lower tracing for a while.
+ */
+ for (method=no_wake_up; method < NR_ITEMS(methods); ++method) {
+ drive_type->wake_up = method;
+#ifdef CONFIG_FT_TWO_DRIVES
+ /* Test setup for dual drive configuration.
+ * /dev/rft2 uses mountain wakeup
+ * /dev/rft3 uses colorado wakeup
+ * Other systems will use the normal scheme.
+ */
+ if ((ft_drive_sel < 2) ||
+ (ft_drive_sel == 2 && method == FT_WAKE_UP_1) ||
+ (ft_drive_sel == 3 && method == FT_WAKE_UP_2)) {
+ result=ftape_wakeup_drive(drive_type->wake_up);
+ } else {
+ result = -EIO;
+ }
+#else
+ result = ftape_wakeup_drive(drive_type->wake_up);
+#endif
+ if (result >= 0) {
+ TRACE(ft_t_warn, "drive wakeup method: %s",
+ methods[drive_type->wake_up].name);
+ break;
+ }
+ }
+ SET_TRACE_LEVEL(old_tracing);
+
+ if (method >= NR_ITEMS(methods)) {
+ /* no response at all, cannot open this drive */
+ drive_type->wake_up = unknown_wake_up;
+ TRACE(ft_t_err, "no tape drive found !");
+ result = -ENODEV;
+ }
+ }
+ TRACE_EXIT result;
+}
+
+int ftape_get_drive_status(void)
+{
+ int result;
+ int status;
+ TRACE_FUN(ft_t_flow);
+
+ ft_no_tape = ft_write_protected = 0;
+ /* Tape drive is activated now.
+ * First clear error status if present.
+ */
+ do {
+ result = ftape_ready_wait(ftape_timeout.reset, &status);
+ if (result < 0) {
+ if (result == -ETIME) {
+ TRACE(ft_t_err, "ftape_ready_wait timeout");
+ } else if (result == -EINTR) {
+ TRACE(ft_t_err, "ftape_ready_wait aborted");
+ } else {
+ TRACE(ft_t_err, "ftape_ready_wait failed");
+ }
+ TRACE_EXIT -EIO;
+ }
+ /* Clear error condition (drive is ready !)
+ */
+ if (status & QIC_STATUS_ERROR) {
+ unsigned int error;
+ qic117_cmd_t command;
+
+ TRACE(ft_t_err, "error status set");
+ result = ftape_report_error(&error, &command, 1);
+ if (result < 0) {
+ TRACE(ft_t_err,
+ "report_error_code failed: %d", result);
+ /* hope it's working next time */
+ ftape_reset_drive();
+ TRACE_EXIT -EIO;
+ } else if (error != 0) {
+ TRACE(ft_t_noise, "error code : %d", error);
+ TRACE(ft_t_noise, "error command: %d", command);
+ }
+ }
+ if (status & QIC_STATUS_NEW_CARTRIDGE) {
+ unsigned int error;
+ qic117_cmd_t command;
+ const ft_trace_t old_tracing = TRACE_LEVEL;
+ SET_TRACE_LEVEL(ft_t_bug);
+
+ /* Undocumented feature: Must clear (not present!)
+ * error here or we'll fail later.
+ */
+ ftape_report_error(&error, &command, 1);
+
+ SET_TRACE_LEVEL(old_tracing);
+ TRACE(ft_t_info, "status: new cartridge");
+ ft_new_tape = 1;
+ } else {
+ ft_new_tape = 0;
+ }
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ } while (status & QIC_STATUS_ERROR);
+
+ ft_no_tape = !(status & QIC_STATUS_CARTRIDGE_PRESENT);
+ ft_write_protected = (status & QIC_STATUS_WRITE_PROTECT) != 0;
+ if (ft_no_tape) {
+ TRACE(ft_t_warn, "no cartridge present");
+ } else {
+ if (ft_write_protected) {
+ TRACE(ft_t_noise, "Write protected cartridge");
+ }
+ }
+ TRACE_EXIT 0;
+}
+
+void ftape_log_vendor_id(void)
+{
+ int vendor_index;
+ TRACE_FUN(ft_t_flow);
+
+ ftape_report_vendor_id(&ft_drive_type.vendor_id);
+ vendor_index = lookup_vendor_id(ft_drive_type.vendor_id);
+ if (ft_drive_type.vendor_id == UNKNOWN_VENDOR &&
+ ft_drive_type.wake_up == wake_up_colorado) {
+ vendor_index = 0;
+ /* hack to get rid of all this mail */
+ ft_drive_type.vendor_id = 0;
+ }
+ if (vendor_index < 0) {
+ /* Unknown vendor id, first time opening device. The
+ * drive_type remains set to type found at wakeup
+ * time, this will probably keep the driver operating
+ * for this new vendor.
+ */
+ TRACE(ft_t_warn, "\n"
+ KERN_INFO "============ unknown vendor id ===========\n"
+ KERN_INFO "A new, yet unsupported tape drive is found\n"
+ KERN_INFO "Please report the following values:\n"
+ KERN_INFO " Vendor id : 0x%04x\n"
+ KERN_INFO " Wakeup method : %s\n"
+ KERN_INFO "And a description of your tape drive\n"
+ KERN_INFO "to "THE_FTAPE_MAINTAINER"\n"
+ KERN_INFO "==========================================",
+ ft_drive_type.vendor_id,
+ methods[ft_drive_type.wake_up].name);
+ ft_drive_type.speed = 0; /* unknown */
+ } else {
+ ft_drive_type.name = vendors[vendor_index].name;
+ ft_drive_type.speed = vendors[vendor_index].speed;
+ TRACE(ft_t_info, "tape drive type: %s", ft_drive_type.name);
+ /* scan all methods for this vendor_id in table */
+ while(ft_drive_type.wake_up != vendors[vendor_index].wake_up) {
+ if (vendor_index < NR_ITEMS(vendors) - 1 &&
+ vendors[vendor_index + 1].vendor_id
+ ==
+ ft_drive_type.vendor_id) {
+ ++vendor_index;
+ } else {
+ break;
+ }
+ }
+ if (ft_drive_type.wake_up != vendors[vendor_index].wake_up) {
+ TRACE(ft_t_warn, "\n"
+ KERN_INFO "==========================================\n"
+ KERN_INFO "wakeup type mismatch:\n"
+ KERN_INFO "found: %s, expected: %s\n"
+ KERN_INFO "please report this to "THE_FTAPE_MAINTAINER"\n"
+ KERN_INFO "==========================================",
+ methods[ft_drive_type.wake_up].name,
+ methods[vendors[vendor_index].wake_up].name);
+ }
+ }
+ TRACE_EXIT;
+}
+
+void ftape_calc_timeouts(unsigned int qic_std,
+ unsigned int data_rate,
+ unsigned int tape_len)
+{
+ int speed; /* deci-ips ! */
+ int ff_speed;
+ int length;
+ TRACE_FUN(ft_t_any);
+
+ /* tape transport speed
+ * data rate: QIC-40 QIC-80 QIC-3010 QIC-3020
+ *
+ * 250 Kbps 25 ips n/a n/a n/a
+ * 500 Kbps 50 ips 34 ips 22.6 ips n/a
+ * 1 Mbps n/a 68 ips 45.2 ips 22.6 ips
+ * 2 Mbps n/a n/a n/a 45.2 ips
+ *
+ * fast tape transport speed is at least 68 ips.
+ */
+ switch (qic_std) {
+ case QIC_TAPE_QIC40:
+ speed = (data_rate == 250) ? 250 : 500;
+ break;
+ case QIC_TAPE_QIC80:
+ speed = (data_rate == 500) ? 340 : 680;
+ break;
+ case QIC_TAPE_QIC3010:
+ speed = (data_rate == 500) ? 226 : 452;
+ break;
+ case QIC_TAPE_QIC3020:
+ speed = (data_rate == 1000) ? 226 : 452;
+ break;
+ default:
+ TRACE(ft_t_bug, "Unknown qic_std (bug) ?");
+ speed = 500;
+ break;
+ }
+ if (ft_drive_type.speed == 0) {
+ unsigned long t0;
+ static int dt = 0; /* keep gcc from complaining */
+ static int first_time = 1;
+
+ /* Measure the time it takes to wind to EOT and back to BOT.
+ * If the tape length is known, calculate the rewind speed.
+ * Else keep the time value for calculation of the rewind
+ * speed later on, when the length _is_ known.
+ * Ask for a report only when length and speed are both known.
+ */
+ if (first_time) {
+ ftape_seek_to_bot();
+ t0 = jiffies;
+ ftape_seek_to_eot();
+ ftape_seek_to_bot();
+ dt = (int) (((jiffies - t0) * FT_USPT) / 1000);
+ if (dt < 1) {
+ dt = 1; /* prevent div by zero on failures */
+ }
+ first_time = 0;
+ TRACE(ft_t_info,
+ "trying to determine seek timeout, got %d msec",
+ dt);
+ }
+ if (tape_len != 0) {
+ ft_drive_type.speed =
+ (2 * 12 * tape_len * 1000) / dt;
+ TRACE(ft_t_warn, "\n"
+ KERN_INFO "==========================================\n"
+ KERN_INFO "drive type: %s\n"
+ KERN_INFO "delta time = %d ms, length = %d ft\n"
+ KERN_INFO "has a maximum tape speed of %d ips\n"
+ KERN_INFO "please report this to "THE_FTAPE_MAINTAINER"\n"
+ KERN_INFO "==========================================",
+ ft_drive_type.name, dt, tape_len,
+ ft_drive_type.speed);
+ }
+ }
+ /* Handle unknown length tapes as very long ones. We'll
+ * determine the actual length from a header segment later.
+ * This is normal for all modern (Wide,TR1/2/3) formats.
+ */
+ if (tape_len <= 0) {
+ TRACE(ft_t_noise,
+ "Unknown tape length, using maximal timeouts");
+ length = QIC_TOP_TAPE_LEN; /* use worst case values */
+ } else {
+ length = tape_len; /* use actual values */
+ }
+ if (ft_drive_type.speed == 0) {
+ ff_speed = speed;
+ } else {
+ ff_speed = ft_drive_type.speed;
+ }
+ /* time to go from bot to eot at normal speed (data rate):
+ * time = (1+delta) * length (ft) * 12 (inch/ft) / speed (ips)
+ * delta = 10 % for seek speed, 20 % for rewind speed.
+ */
+ ftape_timeout.seek = (length * 132 * FT_SECOND) / speed;
+ ftape_timeout.rewind = (length * 144 * FT_SECOND) / (10 * ff_speed);
+ ftape_timeout.reset = 20 * FT_SECOND + ftape_timeout.rewind;
+ TRACE(ft_t_noise, "timeouts for speed = %d, length = %d\n"
+ KERN_INFO "seek timeout : %d sec\n"
+ KERN_INFO "rewind timeout: %d sec\n"
+ KERN_INFO "reset timeout : %d sec",
+ speed, length,
+ (ftape_timeout.seek + 500) / 1000,
+ (ftape_timeout.rewind + 500) / 1000,
+ (ftape_timeout.reset + 500) / 1000);
+ TRACE_EXIT;
+}
+
+/* This function calibrates the datarate (i.e. determines the maximal
+ * usable data rate) and sets the global variable ft_qic_std to qic_std
+ *
+ */
+int ftape_calibrate_data_rate(unsigned int qic_std)
+{
+ int rate = ft_fdc_rate_limit;
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ ft_qic_std = qic_std;
+
+ if (ft_qic_std == -1) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "Unable to determine data rate if QIC standard is unknown");
+ }
+
+ /* Select highest rate supported by both fdc and drive.
+ * Start with highest rate supported by the fdc.
+ */
+ while (fdc_set_data_rate(rate) < 0 && rate > 250) {
+ rate /= 2;
+ }
+ TRACE(ft_t_info,
+ "Highest FDC supported data rate: %d Kbps", rate);
+ ft_fdc_max_rate = rate;
+ do {
+ result = ftape_set_data_rate(rate, ft_qic_std);
+ } while (result == -EINVAL && (rate /= 2) > 250);
+ if (result < 0) {
+ TRACE_ABORT(-EIO, ft_t_err, "set datarate failed");
+ }
+ ft_data_rate = rate;
+ TRACE_EXIT 0;
+}
+
+int ftape_init_drive(void)
+{
+ int status;
+ qic_model model;
+ unsigned int qic_std;
+ unsigned int data_rate;
+ TRACE_FUN(ft_t_flow);
+
+ ftape_init_drive_needed = 0; /* don't retry if this fails ? */
+ TRACE_CATCH(ftape_report_raw_drive_status(&status),);
+ if (status & QIC_STATUS_CARTRIDGE_PRESENT) {
+ if (!(status & QIC_STATUS_AT_BOT)) {
+ /* Antique drives will get here after a soft reset,
+ * modern ones only if the driver is loaded when the
+ * tape wasn't rewound properly.
+ */
+ /* Tape should be at bot if new cartridge ! */
+ ftape_seek_to_bot();
+ }
+ if (!(status & QIC_STATUS_REFERENCED)) {
+ TRACE(ft_t_flow, "starting seek_load_point");
+ TRACE_CATCH(ftape_command_wait(QIC_SEEK_LOAD_POINT,
+ ftape_timeout.reset,
+ &status),);
+ }
+ }
+ ft_formatted = (status & QIC_STATUS_REFERENCED) != 0;
+ if (!ft_formatted) {
+ TRACE(ft_t_warn, "Warning: tape is not formatted !");
+ }
+
+ /* report configuration aborts when ftape_tape_len == -1
+ * unknown qic_std is okay if not formatted.
+ */
+ TRACE_CATCH(ftape_report_configuration(&model,
+ &data_rate,
+ &qic_std,
+ &ftape_tape_len),);
+
+ /* Maybe add the following to the /proc entry
+ */
+ TRACE(ft_t_info, "%s drive @ %d Kbps",
+ (model == prehistoric) ? "prehistoric" :
+ ((model == pre_qic117c) ? "pre QIC-117C" :
+ ((model == post_qic117b) ? "post QIC-117B" :
+ "post QIC-117D")), data_rate);
+
+ if (ft_formatted) {
+ /* initialize ft_used_data_rate to maximum value
+ * and set ft_qic_std
+ */
+ TRACE_CATCH(ftape_calibrate_data_rate(qic_std),);
+ if (ftape_tape_len == 0) {
+ TRACE(ft_t_info, "unknown length QIC-%s tape",
+ (ft_qic_std == QIC_TAPE_QIC40) ? "40" :
+ ((ft_qic_std == QIC_TAPE_QIC80) ? "80" :
+ ((ft_qic_std == QIC_TAPE_QIC3010)
+ ? "3010" : "3020")));
+ } else {
+ TRACE(ft_t_info, "%d ft. QIC-%s tape", ftape_tape_len,
+ (ft_qic_std == QIC_TAPE_QIC40) ? "40" :
+ ((ft_qic_std == QIC_TAPE_QIC80) ? "80" :
+ ((ft_qic_std == QIC_TAPE_QIC3010)
+ ? "3010" : "3020")));
+ }
+ ftape_calc_timeouts(ft_qic_std, ft_data_rate, ftape_tape_len);
+ /* soft write-protect QIC-40/QIC-80 cartridges used with a
+ * Colorado T3000 drive. Buggy hardware!
+ */
+ if ((ft_drive_type.vendor_id == 0x011c6) &&
+ ((ft_qic_std == QIC_TAPE_QIC40 ||
+ ft_qic_std == QIC_TAPE_QIC80) &&
+ !ft_write_protected)) {
+ TRACE(ft_t_warn, "\n"
+ KERN_INFO "The famous Colorado T3000 bug:\n"
+ KERN_INFO "%s drives can't write QIC40 and QIC80\n"
+ KERN_INFO "cartridges but don't set the write-protect flag!",
+ ft_drive_type.name);
+ ft_write_protected = 1;
+ }
+ } else {
+ /* Doesn't make too much sense to set the data rate
+ * because we don't know what to use for the write
+ * precompensation.
+ * Need to do this again when formatting the cartridge.
+ */
+ ft_data_rate = data_rate;
+ ftape_calc_timeouts(QIC_TAPE_QIC40,
+ data_rate,
+ ftape_tape_len);
+ }
+ ftape_new_cartridge();
+ TRACE_EXIT 0;
+}
+
+static void ftape_munmap(void)
+{
+ int i;
+ TRACE_FUN(ft_t_flow);
+
+ for (i = 0; i < ft_nr_buffers; i++) {
+ ft_buffer[i]->mmapped = 0;
+ }
+ TRACE_EXIT;
+}
+
+/* Map the dma buffers into the virtual address range given by vma.
+ * We only check the caller doesn't map non-existent buffers. We
+ * don't check for multiple mappings.
+ */
+int ftape_mmap(struct vm_area_struct *vma)
+{
+ int num_buffers;
+ int i;
+ TRACE_FUN(ft_t_flow);
+
+ if (ft_failure) {
+ TRACE_EXIT -ENODEV;
+ }
+ if ((vma_get_flags(vma) & (VM_READ|VM_WRITE)) == 0) {
+ TRACE_ABORT(-EINVAL, ft_t_err, "Undefined mmap() access");
+ }
+ if (vma_get_offset (vma) != 0) {
+ TRACE_ABORT(-EINVAL, ft_t_err, "offset must be 0");
+ }
+ if ((vma_get_end (vma) - vma_get_start (vma)) % FT_BUFF_SIZE != 0) {
+ TRACE_ABORT(-EINVAL, ft_t_err,
+ "size = %ld, should be a multiple of %d",
+ vma_get_end (vma) - vma_get_start (vma),
+ FT_BUFF_SIZE);
+ }
+ num_buffers = (vma_get_end (vma) - vma_get_start (vma)) / FT_BUFF_SIZE;
+ if (num_buffers > ft_nr_buffers) {
+ TRACE_ABORT(-EINVAL,
+ ft_t_err, "size = %ld, should be less than %d",
+ vma_get_end (vma) - vma_get_start (vma),
+ ft_nr_buffers * FT_BUFF_SIZE);
+ }
+ if (ft_driver_state != idle) {
+ /* this also clears the buffer states
+ */
+ ftape_abort_operation();
+ } else {
+ ftape_reset_buffer();
+ }
+ for (i = 0; i < num_buffers; i++) {
+ TRACE_CATCH(remap_page_range(vma_get_start (vma) +
+ i * FT_BUFF_SIZE,
+ virt_to_phys(ft_buffer[i]->address),
+ FT_BUFF_SIZE,
+ vma_get_page_prot (vma)),
+ _res = -EAGAIN);
+ TRACE(ft_t_noise, "remapped dma buffer @ %p to location @ %p",
+ ft_buffer[i]->address,
+ (void *)(vma_get_start(vma) + i * FT_BUFF_SIZE));
+ }
+ for (i = 0; i < num_buffers; i++) {
+ memset(ft_buffer[i]->address, 0xAA, FT_BUFF_SIZE);
+ ft_buffer[i]->mmapped++;
+ }
+ TRACE_EXIT 0;
+}
+
+static void ftape_init_driver(void); /* forward declaration */
+
+/* OPEN routine called by kernel-interface code
+ */
+int ftape_enable(int drive_selection)
+{
+ TRACE_FUN(ft_t_any);
+
+ if (ft_drive_sel == -1 || ft_drive_sel != drive_selection) {
+ /* Other selection than last time
+ */
+ ftape_init_driver();
+ }
+ ft_drive_sel = FTAPE_SEL(drive_selection);
+ ft_failure = 0;
+ TRACE_CATCH(fdc_init(),); /* init & detect fdc */
+ TRACE_CATCH(ftape_activate_drive(&ft_drive_type),
+ fdc_disable();
+ fdc_release_irq_and_dma();
+ fdc_release_regions());
+ TRACE_CATCH(ftape_get_drive_status(), ftape_detach_drive());
+ if (ft_drive_type.vendor_id == UNKNOWN_VENDOR) {
+ ftape_log_vendor_id();
+ }
+ if (ft_new_tape) {
+ ftape_init_drive_needed = 1;
+ }
+ if (!ft_no_tape && ftape_init_drive_needed) {
+ TRACE_CATCH(ftape_init_drive(), ftape_detach_drive());
+ }
+ ftape_munmap(); /* clear the mmap flag */
+ clear_history();
+ TRACE_EXIT 0;
+}
+
+/* release routine called by the high level interface modules
+ * zftape or sftape.
+ */
+void ftape_disable(void)
+{
+ int i;
+ TRACE_FUN(ft_t_any);
+
+ for (i = 0; i < ft_nr_buffers; i++) {
+ if (ft_buffer[i]->mmapped) {
+ TRACE(ft_t_noise, "first byte of buffer %d: 0x%02x",
+ i, *ft_buffer[i]->address);
+ }
+ }
+ if ((current->signal & _DONT_BLOCK) &&
+ !(current->signal & _NEVER_BLOCK) &&
+ ftape_tape_running) {
+ TRACE(ft_t_warn,
+ "Interrupted by fatal signal and tape still running");
+ ftape_dumb_stop();
+ ftape_abort_operation(); /* it's annoying */
+ } else {
+ ftape_set_state(idle);
+ }
+ ftape_detach_drive();
+ if (ft_history.used) {
+ TRACE(ft_t_info, "== Non-fatal errors this run: ==");
+ TRACE(ft_t_info, "fdc isr statistics:\n"
+ KERN_INFO " id_am_errors : %3d\n"
+ KERN_INFO " id_crc_errors : %3d\n"
+ KERN_INFO " data_am_errors : %3d\n"
+ KERN_INFO " data_crc_errors : %3d\n"
+ KERN_INFO " overrun_errors : %3d\n"
+ KERN_INFO " no_data_errors : %3d\n"
+ KERN_INFO " retries : %3d",
+ ft_history.id_am_errors, ft_history.id_crc_errors,
+ ft_history.data_am_errors, ft_history.data_crc_errors,
+ ft_history.overrun_errors, ft_history.no_data_errors,
+ ft_history.retries);
+ if (ft_history.used & 1) {
+ TRACE(ft_t_info, "ecc statistics:\n"
+ KERN_INFO " crc_errors : %3d\n"
+ KERN_INFO " crc_failures : %3d\n"
+ KERN_INFO " ecc_failures : %3d\n"
+ KERN_INFO " sectors corrected: %3d",
+ ft_history.crc_errors, ft_history.crc_failures,
+ ft_history.ecc_failures, ft_history.corrected);
+ }
+ if (ft_history.defects > 0) {
+ TRACE(ft_t_warn, "Warning: %d media defects!",
+ ft_history.defects);
+ }
+ if (ft_history.rewinds > 0) {
+ TRACE(ft_t_info, "tape motion statistics:\n"
+ KERN_INFO "repositions : %3d",
+ ft_history.rewinds);
+ }
+ }
+ ft_failure = 1;
+ TRACE_EXIT;
+}
+
+static void ftape_init_driver(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ ft_drive_type.vendor_id = UNKNOWN_VENDOR;
+ ft_drive_type.speed = 0;
+ ft_drive_type.wake_up = unknown_wake_up;
+ ft_drive_type.name = "Unknown";
+
+ ftape_timeout.seek = 650 * FT_SECOND;
+ ftape_timeout.reset = 670 * FT_SECOND;
+ ftape_timeout.rewind = 650 * FT_SECOND;
+ ftape_timeout.head_seek = 15 * FT_SECOND;
+ ftape_timeout.stop = 5 * FT_SECOND;
+ ftape_timeout.pause = 16 * FT_SECOND;
+
+ ft_qic_std = -1;
+ ftape_tape_len = 0; /* unknown */
+ ftape_current_command = 0;
+ ftape_current_cylinder = -1;
+
+ ft_segments_per_track = 102;
+ ftape_segments_per_head = 1020;
+ ftape_segments_per_cylinder = 4;
+ ft_tracks_per_tape = 20;
+
+ ft_failure = 1;
+
+ ft_formatted = 0;
+ ft_no_tape = 1;
+ ft_write_protected = 1;
+ ft_new_tape = 1;
+
+ ft_driver_state = idle;
+
+ ft_data_rate =
+ ft_fdc_max_rate = 500;
+ ft_drive_max_rate = 0; /* triggers set_rate_test() */
+
+ ftape_init_drive_needed = 1;
+
+ ft_header_segment_1 = -1;
+ ft_header_segment_2 = -1;
+ ft_used_header_segment = -1;
+ ft_first_data_segment = -1;
+ ft_last_data_segment = -1;
+
+ ft_location.track = -1;
+ ft_location.known = 0;
+
+ ftape_tape_running = 0;
+ ftape_might_be_off_track = 1;
+
+ ftape_new_cartridge(); /* init some tape related variables */
+ ftape_init_bsm();
+ TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-ctl.h b/drivers/char/ftape/lowlevel/ftape-ctl.h
new file mode 100644
index 000000000..ab7c535d7
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-ctl.h
@@ -0,0 +1,172 @@
+#ifndef _FTAPE_CTL_H
+#define _FTAPE_CTL_H
+
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ctl.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:09 $
+ *
+ * This file contains the non-standard IOCTL related definitions
+ * for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for
+ * Linux.
+ */
+
+#include <linux/ioctl.h>
+#include <linux/mtio.h>
+#include <linux/ftape-vendors.h>
+
+#include "../lowlevel/ftape-rw.h"
+#include <linux/ftape-header-segment.h>
+
+typedef struct {
+ int used; /* any reading or writing done */
+ /* isr statistics */
+ unsigned int id_am_errors; /* id address mark not found */
+ unsigned int id_crc_errors; /* crc error in id address mark */
+ unsigned int data_am_errors; /* data address mark not found */
+ unsigned int data_crc_errors; /* crc error in data field */
+ unsigned int overrun_errors; /* fdc access timing problem */
+ unsigned int no_data_errors; /* sector not found */
+ unsigned int retries; /* number of tape retries */
+ /* ecc statistics */
+ unsigned int crc_errors; /* crc error in data */
+ unsigned int crc_failures; /* bad data without crc error */
+ unsigned int ecc_failures; /* failed to correct */
+ unsigned int corrected; /* total sectors corrected */
+ /* general statistics */
+ unsigned int rewinds; /* number of tape rewinds */
+ unsigned int defects; /* bad sectors due to media defects */
+} history_record;
+
+/* this structure contains * ALL * information that we want
+ * our child modules to know about, but don't want them to
+ * modify.
+ */
+typedef struct {
+ /* vendor information */
+ vendor_struct fti_drive_type;
+ /* data rates */
+ unsigned int fti_used_data_rate;
+ unsigned int fti_drive_max_rate;
+ unsigned int fti_fdc_max_rate;
+ /* drive selection, either FTAPE_SEL_A/B/C/D */
+ int fti_drive_sel;
+ /* flags set after decode the drive and tape status */
+ unsigned int fti_formatted :1;
+ unsigned int fti_no_tape :1;
+ unsigned int fti_write_protected:1;
+ unsigned int fti_new_tape :1;
+ /* values of last queried drive/tape status and error */
+ ft_drive_error fti_last_error;
+ ft_drive_status fti_last_status;
+ /* cartridge geometry */
+ unsigned int fti_tracks_per_tape;
+ unsigned int fti_segments_per_track;
+ /* location of header segments, etc. */
+ int fti_used_header_segment;
+ int fti_header_segment_1;
+ int fti_header_segment_2;
+ int fti_first_data_segment;
+ int fti_last_data_segment;
+ /* the format code as stored in the header segment */
+ ft_format_type fti_format_code;
+ /* the following is the sole reason for the ftape_set_status() call */
+ unsigned int fti_qic_std;
+ /* is tape running? */
+ volatile enum runner_status_enum fti_runner_status;
+ /* is tape reading/writing/verifying/formatting/deleting */
+ buffer_state_enum fti_state;
+ /* flags fatal hardware error */
+ unsigned int fti_failure:1;
+ /* history record */
+ history_record fti_history;
+} ftape_info;
+
+/* vendor information */
+#define ft_drive_type ftape_status.fti_drive_type
+/* data rates */
+#define ft_data_rate ftape_status.fti_used_data_rate
+#define ft_drive_max_rate ftape_status.fti_drive_max_rate
+#define ft_fdc_max_rate ftape_status.fti_fdc_max_rate
+/* drive selection, either FTAPE_SEL_A/B/C/D */
+#define ft_drive_sel ftape_status.fti_drive_sel
+/* flags set after decode the drive and tape status */
+#define ft_formatted ftape_status.fti_formatted
+#define ft_no_tape ftape_status.fti_no_tape
+#define ft_write_protected ftape_status.fti_write_protected
+#define ft_new_tape ftape_status.fti_new_tape
+/* values of last queried drive/tape status and error */
+#define ft_last_error ftape_status.fti_last_error
+#define ft_last_status ftape_status.fti_last_status
+/* cartridge geometry */
+#define ft_tracks_per_tape ftape_status.fti_tracks_per_tape
+#define ft_segments_per_track ftape_status.fti_segments_per_track
+/* the format code as stored in the header segment */
+#define ft_format_code ftape_status.fti_format_code
+/* the qic status as returned by report drive configuration */
+#define ft_qic_std ftape_status.fti_qic_std
+#define ft_used_header_segment ftape_status.fti_used_header_segment
+#define ft_header_segment_1 ftape_status.fti_header_segment_1
+#define ft_header_segment_2 ftape_status.fti_header_segment_2
+#define ft_first_data_segment ftape_status.fti_first_data_segment
+#define ft_last_data_segment ftape_status.fti_last_data_segment
+/* is tape running? */
+#define ft_runner_status ftape_status.fti_runner_status
+/* is tape reading/writing/verifying/formatting/deleting */
+#define ft_driver_state ftape_status.fti_state
+/* flags fatal hardware error */
+#define ft_failure ftape_status.fti_failure
+/* history record */
+#define ft_history ftape_status.fti_history
+
+/* compatibility with old kernel versions
+ */
+#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13)
+#define _IOC_SIZE(cmd) (((cmd) & IOCSIZE_MASK) >> IOCSIZE_SHIFT)
+#define _IOC_DIR(cmd) (cmd)
+#define _IOC_WRITE IOC_IN
+#define _IOC_READ IOC_OUT
+#endif
+
+/*
+ * ftape-ctl.c defined global vars.
+ */
+extern ftape_info ftape_status;
+extern int ftape_segments_per_head;
+extern int ftape_segments_per_cylinder;
+extern int ftape_init_drive_needed;
+
+/*
+ * ftape-ctl.c defined global functions.
+ */
+extern int ftape_mmap(struct vm_area_struct *vma);
+extern int ftape_enable(int drive_selection);
+extern void ftape_disable(void);
+extern int ftape_seek_to_bot(void);
+extern int ftape_seek_to_eot(void);
+extern int ftape_abort_operation(void);
+extern void ftape_calc_timeouts(unsigned int qic_std,
+ unsigned int data_rate,
+ unsigned int tape_len);
+extern int ftape_calibrate_data_rate(unsigned int qic_std);
+extern int ftape_init_drive(void);
+extern const ftape_info *ftape_get_status(void);
+#endif
diff --git a/drivers/char/ftape/ecc.c b/drivers/char/ftape/lowlevel/ftape-ecc.c
index 19708ee68..e5632f674 100644
--- a/drivers/char/ftape/ecc.c
+++ b/drivers/char/ftape/lowlevel/ftape-ecc.c
@@ -1,72 +1,57 @@
-/* Yo, Emacs! we're -*- Linux-C -*-
+/*
*
* Copyright (c) 1993 Ning and David Mosberger.
+
+ This is based on code originally written by Bas Laarhoven (bas@vimec.nl)
+ and David L. Brown, Jr., and incorporates improvements suggested by
+ Kai Harrekilde-Petersen.
+
+ This program is free software; you can redistribute 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.
+
*
- * This is based on code originally written by Bas Laarhoven (bas@vimec.nl)
- * and David L. Brown, Jr., and incorporates improvements suggested by
- * Kai Harrekilde-Petersen.
- *
- * This program is free software; you can redistribute 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.
- *
- * $Source: /home/bas/distr/ftape-2.03b/RCS/ecc.c,v $
- * $Author: bas $
- *
- * $Revision: 1.32 $
- * $Date: 1995/04/22 07:30:15 $
- * $State: Beta $
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ecc.c,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/10/05 19:18:10 $
*
* This file contains the Reed-Solomon error correction code
* for the QIC-40/80 floppy-tape driver for Linux.
*/
#include <linux/ftape.h>
-#include <asm/byteorder.h>
-
-#include "tracing.h"
-#include "ecc.h"
-
-#ifdef TEST
-
-#undef TRACE()
-#undef TRACE_()
-#undef TRACE()
-#undef TRACEi()
-#undef TRACElx()
-#undef TRACE_FUN()
-#undef TRACE_EXIT
-#define printk printf
-#define TRACE_FUN( level, name) char __fun[] = name
-#define TRACE_EXIT
-#define TRACE_(l,m) { if (ftape_ecc_tracing >= (l) && (l) <= TOP_LEVEL) { \
- printk( "[%03d] " __FILE__ " (%s) - ", (int)ftape_trace_id++, __fun); \
- m; } }
-#define TRACE(l,m) TRACE_(l,printk(m".\n"))
-#define TRACEi(l,m,i) TRACE_(l,printk(m" %d.\n",i))
-#define TRACElx(l,m,i) TRACE_(l,printk(m" 0x%08lx.\n",i))
-
-int ftape_ecc_tracing = 1;
-unsigned char ftape_trace_id = 0;
-
-#endif /* TEST */
-/*
- * Notice: to minimize the potential for confusion, we use r to
- * denote the independent variable of the polynomials
- * in the Galois Field GF(2^8). We reserve x for polynomials
- * that that have coefficients in GF(2^8).
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-ecc.h"
+
+/* Machines that are big-endian should define macro BIG_ENDIAN.
+ * Unfortunately, there doesn't appear to be a standard include file
+ * that works for all OSs.
+ */
+
+#if defined(__sparc__) || defined(__hppa)
+#define BIG_ENDIAN
+#endif /* __sparc__ || __hppa */
+
+#if defined(__mips__)
+#error Find a smart way to determine the Endianness of the MIPS CPU
+#endif
+
+/* Notice: to minimize the potential for confusion, we use r to
+ * denote the independent variable of the polynomials in the
+ * Galois Field GF(2^8). We reserve x for polynomials that
+ * that have coefficients in GF(2^8).
*
* The Galois Field in which coefficient arithmetic is performed are
* the polynomials over Z_2 (i.e., 0 and 1) modulo the irreducible
@@ -96,22 +81,22 @@ unsigned char ftape_trace_id = 0;
* Notice that r^-1 = r^254 as exponent arithmetic is performed
* modulo 2^8-1 = 255.
*
- * For more information on Galois Fields and Reed-Solomon codes,
- * refer to any good book. I found _An Introduction to Error
- * Correcting Codes with Applications_ by S. A. Vanstone and
- * P. C. van Oorschot to be a good introduction into the former.
- * _CODING THEORY: The Essentials_ I found very useful for its
- * concise description of Reed-Solomon encoding/decoding.
+ * For more information on Galois Fields and Reed-Solomon codes, refer
+ * to any good book. I found _An Introduction to Error Correcting
+ * Codes with Applications_ by S. A. Vanstone and P. C. van Oorschot
+ * to be a good introduction into the former. _CODING THEORY: The
+ * Essentials_ I found very useful for its concise description of
+ * Reed-Solomon encoding/decoding.
*
*/
-typedef unsigned char Matrix[3][3];
+typedef __u8 Matrix[3][3];
/*
* gfpow[] is defined such that gfpow[i] returns r^i if
* i is in the range [0..255].
*/
-static const unsigned char gfpow[] =
+static const __u8 gfpow[] =
{
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
0x87, 0x89, 0x95, 0xad, 0xdd, 0x3d, 0x7a, 0xf4,
@@ -151,7 +136,7 @@ static const unsigned char gfpow[] =
* This is a log table. That is, gflog[r^i] returns i (modulo f(r)).
* gflog[0] is undefined and the first element is therefore not valid.
*/
-static const unsigned char gflog[256] =
+static const __u8 gflog[256] =
{
0xff, 0x00, 0x01, 0x63, 0x02, 0xc6, 0x64, 0x6a,
0x03, 0xcd, 0xc7, 0xbc, 0x65, 0x7e, 0x6b, 0x2a,
@@ -187,12 +172,10 @@ static const unsigned char gflog[256] =
0xf6, 0x87, 0xa5, 0x17, 0x3a, 0xa3, 0x3c, 0xb7
};
-/*
- * This is a multiplication table for the factor
- * 0xc0 (i.e., r^105 (modulo f(r)).
+/* This is a multiplication table for the factor 0xc0 (i.e., r^105 (mod f(r)).
* gfmul_c0[f] returns r^105 * f(r) (modulo f(r)).
*/
-static const unsigned char gfmul_c0[256] =
+static const __u8 gfmul_c0[256] =
{
0x00, 0xc0, 0x07, 0xc7, 0x0e, 0xce, 0x09, 0xc9,
0x1c, 0xdc, 0x1b, 0xdb, 0x12, 0xd2, 0x15, 0xd5,
@@ -229,10 +212,9 @@ static const unsigned char gfmul_c0[256] =
};
-/*
- * Returns V modulo 255 provided V is in the range -255,-254,...,509.
+/* Returns V modulo 255 provided V is in the range -255,-254,...,509.
*/
-static inline unsigned char mod255(int v)
+static inline __u8 mod255(int v)
{
if (v > 0) {
if (v < 255) {
@@ -246,19 +228,17 @@ static inline unsigned char mod255(int v)
}
-/*
- * Add two numbers in the field. Addition in this field is
- * equivalent to a bit-wise exclusive OR operation---subtraction
- * is therefore identical to addition.
+/* Add two numbers in the field. Addition in this field is equivalent
+ * to a bit-wise exclusive OR operation---subtraction is therefore
+ * identical to addition.
*/
-static inline unsigned char gfadd(unsigned char a, unsigned char b)
+static inline __u8 gfadd(__u8 a, __u8 b)
{
return a ^ b;
}
-/*
- * Add two vectors of numbers in the field. Each byte in A and B get
+/* Add two vectors of numbers in the field. Each byte in A and B gets
* added individually.
*/
static inline unsigned long gfadd_long(unsigned long a, unsigned long b)
@@ -267,10 +247,9 @@ static inline unsigned long gfadd_long(unsigned long a, unsigned long b)
}
-/*
- * Multiply two numbers in the field:
+/* Multiply two numbers in the field:
*/
-static inline unsigned char gfmul(unsigned char a, unsigned char b)
+static inline __u8 gfmul(__u8 a, __u8 b)
{
if (a && b) {
return gfpow[mod255(gflog[a] + gflog[b])];
@@ -280,11 +259,10 @@ static inline unsigned char gfmul(unsigned char a, unsigned char b)
}
-/*
- * Just like gfmul, except we have already looked up the log
- * of the second number.
+/* Just like gfmul, except we have already looked up the log of the
+ * second number.
*/
-static inline unsigned char gfmul_exp(unsigned char a, int b)
+static inline __u8 gfmul_exp(__u8 a, int b)
{
if (a) {
return gfpow[mod255(gflog[a] + b)];
@@ -294,162 +272,159 @@ static inline unsigned char gfmul_exp(unsigned char a, int b)
}
-/*
- * Just like gfmul_exp, except that A is a vector of numbers. That is,
- * each byte in A gets multiplied by gfpow[mod255(B)].
+/* Just like gfmul_exp, except that A is a vector of numbers. That
+ * is, each byte in A gets multiplied by gfpow[mod255(B)].
*/
static inline unsigned long gfmul_exp_long(unsigned long a, int b)
{
- TRACE_FUN(8, "gfmul_exp_long");
- unsigned char t;
+ __u8 t;
if (sizeof(long) == 4) {
- TRACE_EXIT;
- return
- ((t = a >> 24 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 24) : 0) |
- ((t = a >> 16 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 16) : 0) |
- ((t = a >> 8 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 8) : 0) |
- ((t = a >> 0 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 0) : 0);
-#if !defined(linux)
+ return (
+ ((t = (__u32)a >> 24 & 0xff) ?
+ (((__u32) gfpow[mod255(gflog[t] + b)]) << 24) : 0) |
+ ((t = (__u32)a >> 16 & 0xff) ?
+ (((__u32) gfpow[mod255(gflog[t] + b)]) << 16) : 0) |
+ ((t = (__u32)a >> 8 & 0xff) ?
+ (((__u32) gfpow[mod255(gflog[t] + b)]) << 8) : 0) |
+ ((t = (__u32)a >> 0 & 0xff) ?
+ (((__u32) gfpow[mod255(gflog[t] + b)]) << 0) : 0));
} else if (sizeof(long) == 8) {
- TRACE_EXIT;
- return
- ((t = a >> 56 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 56) : 0) |
- ((t = a >> 48 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 48) : 0) |
- ((t = a >> 40 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 40) : 0) |
- ((t = a >> 32 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 32) : 0) |
- ((t = a >> 24 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 24) : 0) |
- ((t = a >> 16 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 16) : 0) |
- ((t = a >> 8 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 8) : 0) |
- ((t = a >> 0 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 0) : 0);
-#endif
+ return (
+ ((t = (__u64)a >> 56 & 0xff) ?
+ (((__u64) gfpow[mod255(gflog[t] + b)]) << 56) : 0) |
+ ((t = (__u64)a >> 48 & 0xff) ?
+ (((__u64) gfpow[mod255(gflog[t] + b)]) << 48) : 0) |
+ ((t = (__u64)a >> 40 & 0xff) ?
+ (((__u64) gfpow[mod255(gflog[t] + b)]) << 40) : 0) |
+ ((t = (__u64)a >> 32 & 0xff) ?
+ (((__u64) gfpow[mod255(gflog[t] + b)]) << 32) : 0) |
+ ((t = (__u64)a >> 24 & 0xff) ?
+ (((__u64) gfpow[mod255(gflog[t] + b)]) << 24) : 0) |
+ ((t = (__u64)a >> 16 & 0xff) ?
+ (((__u64) gfpow[mod255(gflog[t] + b)]) << 16) : 0) |
+ ((t = (__u64)a >> 8 & 0xff) ?
+ (((__u64) gfpow[mod255(gflog[t] + b)]) << 8) : 0) |
+ ((t = (__u64)a >> 0 & 0xff) ?
+ (((__u64) gfpow[mod255(gflog[t] + b)]) << 0) : 0));
} else {
- TRACEx1(1, "Error: size of long is %d bytes", (int) sizeof(long));
+ TRACE_FUN(ft_t_any);
+ TRACE_ABORT(-1, ft_t_err, "Error: size of long is %d bytes",
+ (int)sizeof(long));
}
- TRACE_EXIT;
- return -1;
}
-/*
- * Divide two numbers in the field. Returns a/b (modulo f(x)).
+/* Divide two numbers in the field. Returns a/b (modulo f(x)).
*/
-static inline unsigned char gfdiv(unsigned char a, unsigned char b)
+static inline __u8 gfdiv(__u8 a, __u8 b)
{
- TRACE_FUN(8, "gfdiv");
if (!b) {
- TRACE(-1, "Error: division by zero");
- return 0xff;
+ TRACE_FUN(ft_t_any);
+ TRACE_ABORT(0xff, ft_t_bug, "Error: division by zero");
} else if (a == 0) {
return 0;
} else {
return gfpow[mod255(gflog[a] - gflog[b])];
}
- TRACE_EXIT;
}
-/*
- * The following functions return the inverse of the matrix of the
+/* The following functions return the inverse of the matrix of the
* linear system that needs to be solved to determine the error
* magnitudes. The first deals with matrices of rank 3, while the
* second deals with matrices of rank 2. The error indices are passed
- * in arguments L0,..,L2 (0=first sector, 31=last sector). The
- * error indices must be sorted in ascending order, i.e., L0<L1<L2.
+ * in arguments L0,..,L2 (0=first sector, 31=last sector). The error
+ * indices must be sorted in ascending order, i.e., L0<L1<L2.
*
- * The linear system that needs to be solved for the error
- * magnitudes is A * b = s, where s is the known vector of
- * syndromes, b is the vector of error magnitudes and A in
- * the ORDER=3 case:
+ * The linear system that needs to be solved for the error magnitudes
+ * is A * b = s, where s is the known vector of syndromes, b is the
+ * vector of error magnitudes and A in the ORDER=3 case:
*
* A_3 = {{1/r^L[0], 1/r^L[1], 1/r^L[2]},
* { 1, 1, 1},
- * { r^L[0], r^L[1], r^L[2]}}
+ * { r^L[0], r^L[1], r^L[2]}}
*/
-static inline int gfinv3(unsigned char l0, unsigned char l1, unsigned char l2, Matrix Ainv)
+static inline int gfinv3(__u8 l0,
+ __u8 l1,
+ __u8 l2,
+ Matrix Ainv)
{
- TRACE_FUN(8, "gfinv3");
- unsigned char det;
- unsigned char t20, t10, t21, t12, t01, t02;
+ __u8 det;
+ __u8 t20, t10, t21, t12, t01, t02;
int log_det;
/* compute some intermediate results: */
- t20 = gfpow[l2 - l0]; /* t20 = r^l2/r^l0 */
- t10 = gfpow[l1 - l0]; /* t10 = r^l1/r^l0 */
- t21 = gfpow[l2 - l1]; /* t21 = r^l2/r^l1 */
+ t20 = gfpow[l2 - l0]; /* t20 = r^l2/r^l0 */
+ t10 = gfpow[l1 - l0]; /* t10 = r^l1/r^l0 */
+ t21 = gfpow[l2 - l1]; /* t21 = r^l2/r^l1 */
t12 = gfpow[l1 - l2 + 255]; /* t12 = r^l1/r^l2 */
t01 = gfpow[l0 - l1 + 255]; /* t01 = r^l0/r^l1 */
t02 = gfpow[l0 - l2 + 255]; /* t02 = r^l0/r^l2 */
- /*
- * Calculate the determinant of matrix A_3^-1 (sometimes called
- * the Vandermonde determinant):
+ /* Calculate the determinant of matrix A_3^-1 (sometimes
+ * called the Vandermonde determinant):
*/
det = gfadd(t20, gfadd(t10, gfadd(t21, gfadd(t12, gfadd(t01, t02)))));
if (!det) {
- TRACE(1, "Inversion failed (3 CRC errors, >0 CRC failures)");
- TRACE_EXIT;
- return 0;
+ TRACE_FUN(ft_t_any);
+ TRACE_ABORT(0, ft_t_err,
+ "Inversion failed (3 CRC errors, >0 CRC failures)");
}
log_det = 255 - gflog[det];
- /*
- * Now, calculate all of the coefficients:
+ /* Now, calculate all of the coefficients:
*/
- Ainv[0][0] = gfmul_exp(gfadd(gfpow[l1], gfpow[l2]), log_det);
- Ainv[0][1] = gfmul_exp(gfadd(t21, t12), log_det);
- Ainv[0][2] = gfmul_exp(gfadd(gfpow[255 - l1], gfpow[255 - l2]), log_det);
+ Ainv[0][0]= gfmul_exp(gfadd(gfpow[l1], gfpow[l2]), log_det);
+ Ainv[0][1]= gfmul_exp(gfadd(t21, t12), log_det);
+ Ainv[0][2]= gfmul_exp(gfadd(gfpow[255 - l1], gfpow[255 - l2]),log_det);
- Ainv[1][0] = gfmul_exp(gfadd(gfpow[l0], gfpow[l2]), log_det);
- Ainv[1][1] = gfmul_exp(gfadd(t20, t02), log_det);
- Ainv[1][2] = gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l2]), log_det);
+ Ainv[1][0]= gfmul_exp(gfadd(gfpow[l0], gfpow[l2]), log_det);
+ Ainv[1][1]= gfmul_exp(gfadd(t20, t02), log_det);
+ Ainv[1][2]= gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l2]),log_det);
- Ainv[2][0] = gfmul_exp(gfadd(gfpow[l0], gfpow[l1]), log_det);
- Ainv[2][1] = gfmul_exp(gfadd(t10, t01), log_det);
- Ainv[2][2] = gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l1]), log_det);
+ Ainv[2][0]= gfmul_exp(gfadd(gfpow[l0], gfpow[l1]), log_det);
+ Ainv[2][1]= gfmul_exp(gfadd(t10, t01), log_det);
+ Ainv[2][2]= gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l1]),log_det);
- TRACE_EXIT;
return 1;
}
-static inline int gfinv2(unsigned char l0, unsigned char l1, Matrix Ainv)
+static inline int gfinv2(__u8 l0, __u8 l1, Matrix Ainv)
{
- TRACE_FUN(8, "gfinv2");
- unsigned char det;
- unsigned char t1, t2;
+ __u8 det;
+ __u8 t1, t2;
int log_det;
t1 = gfpow[255 - l0];
t2 = gfpow[255 - l1];
det = gfadd(t1, t2);
if (!det) {
- TRACE(1, "Inversion failed (2 CRC errors, >0 CRC failures)");
- TRACE_EXIT;
- return 0;
+ TRACE_FUN(ft_t_any);
+ TRACE_ABORT(0, ft_t_err,
+ "Inversion failed (2 CRC errors, >0 CRC failures)");
}
log_det = 255 - gflog[det];
- /*
- * Now, calculate all of the coefficients:
+ /* Now, calculate all of the coefficients:
*/
Ainv[0][0] = Ainv[1][0] = gfpow[log_det];
Ainv[0][1] = gfmul_exp(t2, log_det);
Ainv[1][1] = gfmul_exp(t1, log_det);
- TRACE_EXIT;
return 1;
}
-/*
- * Multiply matrix A by vector S and return result in vector B.
- * M is assumed to be of order NxN, S and B of order Nx1.
+/* Multiply matrix A by vector S and return result in vector B. M is
+ * assumed to be of order NxN, S and B of order Nx1.
*/
-static inline void gfmat_mul(int n, Matrix A, unsigned char *s, unsigned char *b)
+static inline void gfmat_mul(int n, Matrix A,
+ __u8 *s, __u8 *b)
{
int i, j;
- unsigned char dot_prod;
+ __u8 dot_prod;
for (i = 0; i < n; ++i) {
dot_prod = 0;
@@ -462,20 +437,18 @@ static inline void gfmat_mul(int n, Matrix A, unsigned char *s, unsigned char *b
-/*
- * The Reed Solomon ECC codes are computed over the N-th byte of each
+/* The Reed Solomon ECC codes are computed over the N-th byte of each
* block, where N=SECTOR_SIZE. There are up to 29 blocks of data, and
- * 3 blocks of ECC. The blocks are stored contiguously in memory.
- * A segment, consequently, is assumed to have at least 4 blocks:
- * one or more data blocks plus three ECC blocks.
+ * 3 blocks of ECC. The blocks are stored contiguously in memory. A
+ * segment, consequently, is assumed to have at least 4 blocks: one or
+ * more data blocks plus three ECC blocks.
*
* Notice: In QIC-80 speak, a CRC error is a sector with an incorrect
* CRC. A CRC failure is a sector with incorrect data, but
* a valid CRC. In the error control literature, the former
* is usually called "erasure", the latter "error."
*/
-/*
- * Compute the parity bytes for C columns of data, where C is the
+/* Compute the parity bytes for C columns of data, where C is the
* number of bytes that fit into a long integer. We use a linear
* feed-back register to do this. The parity bytes P[0], P[STRIDE],
* P[2*STRIDE] are computed such that:
@@ -485,19 +458,21 @@ static inline void gfmat_mul(int n, Matrix A, unsigned char *s, unsigned char *b
* where k = NBLOCKS,
* p(x) = P[0] + P[STRIDE]*x + P[2*STRIDE]*x^2, and
* m(x) = sum_{i=0}^k m_i*x^i.
- * m_i = DATA[i*SECTOR_SIZE]
+ * m_i = DATA[i*SECTOR_SIZE]
*/
-static inline void set_parity(unsigned long *data, int nblocks, unsigned long *p, int stride)
+static inline void set_parity(unsigned long *data,
+ int nblocks,
+ unsigned long *p,
+ int stride)
{
- TRACE_FUN(8, "set_parity");
unsigned long p0, p1, p2, t1, t2, *end;
- end = data + nblocks * (SECTOR_SIZE / sizeof(long));
+ end = data + nblocks * (FT_SECTOR_SIZE / sizeof(long));
p0 = p1 = p2 = 0;
while (data < end) {
- /*
- * The new parity bytes p0_i, p1_i, p2_i are computed from the old
- * values p0_{i-1}, p1_{i-1}, p2_{i-1} recursively as:
+ /* The new parity bytes p0_i, p1_i, p2_i are computed
+ * from the old values p0_{i-1}, p1_{i-1}, p2_{i-1}
+ * recursively as:
*
* p0_i = p1_{i-1} + r^105 * (m_{i-1} - p0_{i-1})
* p1_i = p2_{i-1} + r^105 * (m_{i-1} - p0_{i-1})
@@ -510,43 +485,43 @@ static inline void set_parity(unsigned long *data, int nblocks, unsigned long *p
* Multiply each byte in t1 by 0xc0:
*/
if (sizeof(long) == 4) {
- t2 = ((unsigned long) gfmul_c0[t1 >> 24 & 0xff]) << 24 |
- ((unsigned long) gfmul_c0[t1 >> 16 & 0xff]) << 16 |
- ((unsigned long) gfmul_c0[t1 >> 8 & 0xff]) << 8 |
- ((unsigned long) gfmul_c0[t1 >> 0 & 0xff]) << 0;
-#if !defined(linux)
+ t2= (((__u32) gfmul_c0[(__u32)t1 >> 24 & 0xff]) << 24 |
+ ((__u32) gfmul_c0[(__u32)t1 >> 16 & 0xff]) << 16 |
+ ((__u32) gfmul_c0[(__u32)t1 >> 8 & 0xff]) << 8 |
+ ((__u32) gfmul_c0[(__u32)t1 >> 0 & 0xff]) << 0);
} else if (sizeof(long) == 8) {
- t2 = ((unsigned long) gfmul_c0[t1 >> 56 & 0xff]) << 56 |
- ((unsigned long) gfmul_c0[t1 >> 48 & 0xff]) << 48 |
- ((unsigned long) gfmul_c0[t1 >> 40 & 0xff]) << 40 |
- ((unsigned long) gfmul_c0[t1 >> 32 & 0xff]) << 32 |
- ((unsigned long) gfmul_c0[t1 >> 24 & 0xff]) << 24 |
- ((unsigned long) gfmul_c0[t1 >> 16 & 0xff]) << 16 |
- ((unsigned long) gfmul_c0[t1 >> 8 & 0xff]) << 8 |
- ((unsigned long) gfmul_c0[t1 >> 0 & 0xff]) << 0;
-#endif
+ t2= (((__u64) gfmul_c0[(__u64)t1 >> 56 & 0xff]) << 56 |
+ ((__u64) gfmul_c0[(__u64)t1 >> 48 & 0xff]) << 48 |
+ ((__u64) gfmul_c0[(__u64)t1 >> 40 & 0xff]) << 40 |
+ ((__u64) gfmul_c0[(__u64)t1 >> 32 & 0xff]) << 32 |
+ ((__u64) gfmul_c0[(__u64)t1 >> 24 & 0xff]) << 24 |
+ ((__u64) gfmul_c0[(__u64)t1 >> 16 & 0xff]) << 16 |
+ ((__u64) gfmul_c0[(__u64)t1 >> 8 & 0xff]) << 8 |
+ ((__u64) gfmul_c0[(__u64)t1 >> 0 & 0xff]) << 0);
} else {
- TRACEx1(1, "Error: long is of size %d", (int) sizeof(long));
+ TRACE_FUN(ft_t_any);
+ TRACE(ft_t_err, "Error: long is of size %d",
+ (int) sizeof(long));
+ TRACE_EXIT;
}
p0 = gfadd_long(t2, p1);
p1 = gfadd_long(t2, p2);
p2 = t1;
- data += SECTOR_SIZE / sizeof(long);
+ data += FT_SECTOR_SIZE / sizeof(long);
}
*p = p0;
p += stride;
*p = p1;
p += stride;
*p = p2;
- TRACE_EXIT;
+ return;
}
-/*
- * Compute the 3 syndrome values. DATA should point to the first byte
+/* Compute the 3 syndrome values. DATA should point to the first byte
* of the column for which the syndromes are desired. The syndromes
- * are computed over the first NBLOCKS of rows. The three bytes will be
- * placed in S[0], S[1], and S[2].
+ * are computed over the first NBLOCKS of rows. The three bytes will
+ * be placed in S[0], S[1], and S[2].
*
* S[i] is the value of the "message" polynomial m(x) evaluated at the
* i-th root of the generator polynomial g(x).
@@ -561,8 +536,8 @@ static inline void set_parity(unsigned long *data, int nblocks, unsigned long *p
* slower if there are errors. The latter is hopefully the infrequent
* case.
*
- * To understand the alternative algorithm, notice that
- * set_parity(m, k, p) computes parity bytes such that:
+ * To understand the alternative algorithm, notice that set_parity(m,
+ * k, p) computes parity bytes such that:
*
* x^k * p(x) = m(x) (modulo g(x)).
*
@@ -580,16 +555,26 @@ static int compute_syndromes(unsigned long *data, int nblocks, unsigned long *s)
set_parity(data, nblocks, p, 1);
if (p[0] | p[1] | p[2]) {
- /*
- * Some of the checked columns do not have a zero syndrome. For
- * simplicity, we compute the syndromes for all columns that we
- * have computed the remainders for.
+ /* Some of the checked columns do not have a zero
+ * syndrome. For simplicity, we compute the syndromes
+ * for all columns that we have computed the
+ * remainders for.
*/
- s[0] = gfmul_exp_long(gfadd_long(p[0], gfmul_exp_long(gfadd_long(p[1],
- gfmul_exp_long(p[2], -1)), -1)), -nblocks);
+ s[0] = gfmul_exp_long(
+ gfadd_long(p[0],
+ gfmul_exp_long(
+ gfadd_long(p[1],
+ gfmul_exp_long(p[2], -1)),
+ -1)),
+ -nblocks);
s[1] = gfadd_long(gfadd_long(p[2], p[1]), p[0]);
- s[2] = gfmul_exp_long(gfadd_long(p[0], gfmul_exp_long(gfadd_long(p[1],
- gfmul_exp_long(p[2], 1)), 1)), nblocks);
+ s[2] = gfmul_exp_long(
+ gfadd_long(p[0],
+ gfmul_exp_long(
+ gfadd_long(p[1],
+ gfmul_exp_long(p[2], 1)),
+ 1)),
+ nblocks);
return 0;
} else {
return 1;
@@ -597,42 +582,40 @@ static int compute_syndromes(unsigned long *data, int nblocks, unsigned long *s)
}
-/*
- * Correct the block in the column pointed to by DATA. There are NBAD
+/* Correct the block in the column pointed to by DATA. There are NBAD
* CRC errors and their indices are in BAD_LOC[0], up to
* BAD_LOC[NBAD-1]. If NBAD>1, Ainv holds the inverse of the matrix
* of the linear system that needs to be solved to determine the error
* magnitudes. S[0], S[1], and S[2] are the syndrome values. If row
* j gets corrected, then bit j will be set in CORRECTION_MAP.
*/
-static inline int correct_block(unsigned char *data, int nblocks,
+static inline int correct_block(__u8 *data, int nblocks,
int nbad, int *bad_loc, Matrix Ainv,
- unsigned char *s,
- BAD_SECTOR * correction_map)
+ __u8 *s,
+ SectorMap * correction_map)
{
- TRACE_FUN(8, "correct_block");
int ncorrected = 0;
int i;
- unsigned char t1, t2;
- unsigned char c0, c1, c2; /* check bytes */
- unsigned char error_mag[3], log_error_mag;
- unsigned char *dp, l, e;
+ __u8 t1, t2;
+ __u8 c0, c1, c2; /* check bytes */
+ __u8 error_mag[3], log_error_mag;
+ __u8 *dp, l, e;
+ TRACE_FUN(ft_t_any);
switch (nbad) {
case 0:
/* might have a CRC failure: */
if (s[0] == 0) {
/* more than one error */
- TRACE(1, "ECC failed (0 CRC errors, >1 CRC failures)");
- TRACE_EXIT;
- return -1;
- } /* if */
+ TRACE_ABORT(-1, ft_t_err,
+ "ECC failed (0 CRC errors, >1 CRC failures)");
+ }
t1 = gfdiv(s[1], s[0]);
if ((bad_loc[nbad++] = gflog[t1]) >= nblocks) {
- TRACE(1, "ECC failed (0 CRC errors, >1 CRC failures): ");
- TRACEi(1, "attempt to correct data at ", bad_loc[0]);
- TRACE_EXIT;
- return -1;
+ TRACE(ft_t_err,
+ "ECC failed (0 CRC errors, >1 CRC failures)");
+ TRACE_ABORT(-1, ft_t_err,
+ "attempt to correct data at %d", bad_loc[0]);
}
error_mag[0] = s[1];
break;
@@ -644,25 +627,26 @@ static inline int correct_block(unsigned char *data, int nblocks,
Ainv[0][0] = gfpow[bad_loc[0]];
} else if (t1 == 0 || t2 == 0) {
/* one erasure and more than one error: */
- TRACE(1, "ECC failed (1 erasure, >1 error)");
- TRACE_EXIT;
- return -1;
+ TRACE_ABORT(-1, ft_t_err,
+ "ECC failed (1 erasure, >1 error)");
} else {
/* one erasure, one error: */
- if ((bad_loc[nbad++] = gflog[gfdiv(t1, t2)]) >= nblocks) {
- TRACE(1, "ECC failed (1 CRC errors, >1 CRC failures): ");
- TRACEi(1, "attempt to correct data at ", bad_loc[1]);
- TRACE_EXIT;
- return -1;
- } /* if */
+ if ((bad_loc[nbad++] = gflog[gfdiv(t1, t2)])
+ >= nblocks) {
+ TRACE(ft_t_err, "ECC failed "
+ "(1 CRC errors, >1 CRC failures)");
+ TRACE_ABORT(-1, ft_t_err,
+ "attempt to correct data at %d",
+ bad_loc[1]);
+ }
if (!gfinv2(bad_loc[0], bad_loc[1], Ainv)) {
- /* inversion failed---must have more than one error */
- TRACE_EXIT;
- return -1;
+ /* inversion failed---must have more
+ * than one error
+ */
+ TRACE_EXIT -1;
}
}
- /*
- * FALL THROUGH TO ERROR MAGNITUDE COMPUTATION:
+ /* FALL THROUGH TO ERROR MAGNITUDE COMPUTATION:
*/
case 2:
case 3:
@@ -671,17 +655,15 @@ static inline int correct_block(unsigned char *data, int nblocks,
break;
default:
- TRACE(1, "Internal Error: number of CRC errors > 3");
- TRACE_EXIT;
- return -1;
+ TRACE_ABORT(-1, ft_t_err,
+ "Internal Error: number of CRC errors > 3");
}
- /*
- * Perform correction by adding ERROR_MAG[i] to the byte at offset
- * BAD_LOC[i]. Also add the value of the computed error polynomial
- * to the syndrome values. If the correction was successful, the
- * resulting check bytes should be zero (i.e., the corrected data
- * is a valid code word).
+ /* Perform correction by adding ERROR_MAG[i] to the byte at
+ * offset BAD_LOC[i]. Also add the value of the computed
+ * error polynomial to the syndrome values. If the correction
+ * was successful, the resulting check bytes should be zero
+ * (i.e., the corrected data is a valid code word).
*/
c0 = s[0];
c1 = s[1];
@@ -691,7 +673,7 @@ static inline int correct_block(unsigned char *data, int nblocks,
if (e) {
/* correct the byte at offset L by magnitude E: */
l = bad_loc[i];
- dp = &data[l * SECTOR_SIZE];
+ dp = &data[l * FT_SECTOR_SIZE];
*dp = gfadd(*dp, e);
*correction_map |= 1 << l;
++ncorrected;
@@ -703,53 +685,46 @@ static inline int correct_block(unsigned char *data, int nblocks,
}
}
if (c0 || c1 || c2) {
- TRACE(1, "ECC self-check failed, too many errors");
- TRACE_EXIT;
- return -1;
+ TRACE_ABORT(-1, ft_t_err,
+ "ECC self-check failed, too many errors");
}
- TRACE_EXIT;
- return ncorrected;
+ TRACE_EXIT ncorrected;
}
#if defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID)
-/*
- * Perform a sanity check on the computed parity bytes:
+/* Perform a sanity check on the computed parity bytes:
*/
static int sanity_check(unsigned long *data, int nblocks)
{
- TRACE_FUN(8, "sanity_check");
+ TRACE_FUN(ft_t_any);
unsigned long s[3];
if (!compute_syndromes(data, nblocks, s)) {
- TRACE(-1, "Internal Error: syndrome self-check failed");
- TRACE_EXIT;
- return 0;
+ TRACE_ABORT(0, ft_bug,
+ "Internal Error: syndrome self-check failed");
}
- TRACE_EXIT;
- return 1;
+ TRACE_EXIT 1;
}
-#endif /* defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID) */
-
-
+#endif /* defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID) */
-/*
- * Compute the parity for an entire segment of data.
+/* Compute the parity for an entire segment of data.
*/
-int ecc_set_segment_parity(struct memory_segment *mseg)
+int ftape_ecc_set_segment_parity(struct memory_segment *mseg)
{
int i;
- unsigned char *parity_bytes;
+ __u8 *parity_bytes;
- parity_bytes = &mseg->data[(mseg->blocks - 3) * SECTOR_SIZE];
- for (i = 0; i < SECTOR_SIZE; i += sizeof(long)) {
+ parity_bytes = &mseg->data[(mseg->blocks - 3) * FT_SECTOR_SIZE];
+ for (i = 0; i < FT_SECTOR_SIZE; i += sizeof(long)) {
set_parity((unsigned long *) &mseg->data[i], mseg->blocks - 3,
(unsigned long *) &parity_bytes[i],
- SECTOR_SIZE / sizeof(long));
+ FT_SECTOR_SIZE / sizeof(long));
#ifdef ECC_PARANOID
- if (!sanity_check((unsigned long *) &mseg->data[i], mseg->blocks)) {
+ if (!sanity_check((unsigned long *) &mseg->data[i],
+ mseg->blocks)) {
return -1;
}
#endif /* ECC_PARANOID */
@@ -758,35 +733,33 @@ int ecc_set_segment_parity(struct memory_segment *mseg)
}
-/*
- * Checks and corrects (if possible) the segment MSEG. Returns one of
+/* Checks and corrects (if possible) the segment MSEG. Returns one of
* ECC_OK, ECC_CORRECTED, and ECC_FAILED.
*/
-int ecc_correct_data(struct memory_segment *mseg)
+int ftape_ecc_correct_data(struct memory_segment *mseg)
{
- TRACE_FUN(5, "ecc_correct_data");
int col, i, result;
int ncorrected = 0;
int nerasures = 0; /* # of erasures (CRC errors) */
int erasure_loc[3]; /* erasure locations */
unsigned long ss[3];
- unsigned char s[3];
+ __u8 s[3];
Matrix Ainv;
+ TRACE_FUN(ft_t_flow);
mseg->corrected = 0;
/* find first column that has non-zero syndromes: */
- for (col = 0; col < SECTOR_SIZE; col += sizeof(long)) {
+ for (col = 0; col < FT_SECTOR_SIZE; col += sizeof(long)) {
if (!compute_syndromes((unsigned long *) &mseg->data[col],
mseg->blocks, ss)) {
/* something is wrong---have to fix things */
break;
}
}
- if (col >= SECTOR_SIZE) {
+ if (col >= FT_SECTOR_SIZE) {
/* all syndromes are ok, therefore nothing to correct */
- TRACE_EXIT;
- return ECC_OK;
+ TRACE_EXIT ECC_OK;
}
/* count the number of CRC errors if there were any: */
if (mseg->read_bad) {
@@ -794,29 +767,27 @@ int ecc_correct_data(struct memory_segment *mseg)
if (BAD_CHECK(mseg->read_bad, i)) {
if (nerasures >= 3) {
/* this is too much for ECC */
- TRACE(1, "ECC failed (>3 CRC errors)");
- TRACE_EXIT;
- return ECC_FAILED;
+ TRACE_ABORT(ECC_FAILED, ft_t_err,
+ "ECC failed (>3 CRC errors)");
} /* if */
erasure_loc[nerasures++] = i;
}
}
}
/*
- * If there are at least 2 CRC errors, determine inverse of matrix
- * of linear system to be solved:
+ * If there are at least 2 CRC errors, determine inverse of matrix
+ * of linear system to be solved:
*/
switch (nerasures) {
case 2:
if (!gfinv2(erasure_loc[0], erasure_loc[1], Ainv)) {
- TRACE_EXIT;
- return ECC_FAILED;
+ TRACE_EXIT ECC_FAILED;
}
break;
case 3:
- if (!gfinv3(erasure_loc[0], erasure_loc[1], erasure_loc[2], Ainv)) {
- TRACE_EXIT;
- return ECC_FAILED;
+ if (!gfinv3(erasure_loc[0], erasure_loc[1],
+ erasure_loc[2], Ainv)) {
+ TRACE_EXIT ECC_FAILED;
}
break;
default:
@@ -830,21 +801,26 @@ int ecc_correct_data(struct memory_segment *mseg)
s[1] = ss[1];
s[2] = ss[2];
if (s[0] | s[1] | s[2]) {
-#ifdef __BIG_ENDIAN
- result = correct_block(&mseg->data[col + sizeof(long) - 1 - i],
+#ifdef BIG_ENDIAN
+ result = correct_block(
+ &mseg->data[col + sizeof(long) - 1 - i],
+ mseg->blocks,
+ nerasures,
+ erasure_loc,
+ Ainv,
+ s,
+ &mseg->corrected);
+#else
+ result = correct_block(&mseg->data[col + i],
mseg->blocks,
- nerasures, erasure_loc, Ainv, s,
- &mseg->corrected);
-#elif defined(__LITTLE_ENDIAN)
- result = correct_block(&mseg->data[col + i], mseg->blocks,
- nerasures, erasure_loc, Ainv, s,
+ nerasures,
+ erasure_loc,
+ Ainv,
+ s,
&mseg->corrected);
-#else
-#error "Huh? Neither __BIG_ENDIAN nor __LITTLE_ENDIAN?"
#endif
if (result < 0) {
- TRACE_EXIT;
- return ECC_FAILED;
+ TRACE_EXIT ECC_FAILED;
}
ncorrected += result;
}
@@ -854,27 +830,24 @@ int ecc_correct_data(struct memory_segment *mseg)
}
#ifdef ECC_SANITY_CHECK
- if (!sanity_check((unsigned long *) &mseg->data[col], mseg->blocks)) {
- TRACE_EXIT;
- return ECC_FAILED;
+ if (!sanity_check((unsigned long *) &mseg->data[col],
+ mseg->blocks)) {
+ TRACE_EXIT ECC_FAILED;
}
#endif /* ECC_SANITY_CHECK */
/* find next column with non-zero syndromes: */
- while ((col += sizeof(long)) < SECTOR_SIZE) {
- if (!compute_syndromes((unsigned long *) &mseg->data[col],
- mseg->blocks, ss)) {
+ while ((col += sizeof(long)) < FT_SECTOR_SIZE) {
+ if (!compute_syndromes((unsigned long *)
+ &mseg->data[col], mseg->blocks, ss)) {
/* something is wrong---have to fix things */
break;
}
}
- } while (col < SECTOR_SIZE);
+ } while (col < FT_SECTOR_SIZE);
if (ncorrected && nerasures == 0) {
- TRACE(2, "block contained error not caught by CRC");
+ TRACE(ft_t_warn, "block contained error not caught by CRC");
}
- TRACEi((ncorrected > 0) ? 4 : 8, "number of corrections:", ncorrected);
- TRACE_EXIT;
- return ncorrected ? ECC_CORRECTED : ECC_OK;
+ TRACE((ncorrected > 0) ? ft_t_noise : ft_t_any, "number of corrections: %d", ncorrected);
+ TRACE_EXIT ncorrected ? ECC_CORRECTED : ECC_OK;
}
-
-/*** end of ecc.c ***/
diff --git a/drivers/char/ftape/ecc.h b/drivers/char/ftape/lowlevel/ftape-ecc.h
index 890543ceb..4829146fe 100644
--- a/drivers/char/ftape/ecc.h
+++ b/drivers/char/ftape/lowlevel/ftape-ecc.h
@@ -1,40 +1,39 @@
+#ifndef _FTAPE_ECC_H_
+#define _FTAPE_ECC_H_
+
/*
* Copyright (C) 1993 Ning and David Mosberger.
* Original:
* Copyright (C) 1993 Bas Laarhoven.
* Copyright (C) 1992 David L. Brown, Jr.
+
+ This program is free software; you can redistribute 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.
+
*
- * This program is free software; you can redistribute 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.
- *
- *
- * $Source: /home/bas/distr/ftape-2.03b/RCS/ecc.h,v $
- * $Author: bas $
- *
- * $Revision: 1.20 $
- * $Date: 1995/01/08 14:16:21 $
- * $State: Beta $
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ecc.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:11 $
*
* This file contains the definitions for the
* Reed-Solomon error correction code
* for the QIC-40/80 tape streamer device driver.
*/
-#ifndef _ecc_h_
-#define _ecc_h_
-typedef unsigned long BAD_SECTOR;
+#include "../lowlevel/ftape-bsm.h"
+
#define BAD_CLEAR(entry) ((entry)=0)
#define BAD_SET(entry,sector) ((entry)|=(1<<(sector)))
#define BAD_CHECK(entry,sector) ((entry)&(1<<(sector)))
@@ -61,13 +60,13 @@ enum {
* is needed. DATA is the actual sector packed data from (or to) the
* tape.
*/
-struct memory_segment {
- BAD_SECTOR marked_bad;
- BAD_SECTOR read_bad;
- int blocks;
- unsigned char *data;
- BAD_SECTOR corrected;
-};
+ struct memory_segment {
+ SectorMap marked_bad;
+ SectorMap read_bad;
+ int blocks;
+ __u8 *data;
+ SectorMap corrected;
+ };
/*
* ecc.c defined global variables:
@@ -79,7 +78,7 @@ extern int ftape_ecc_tracing;
/*
* ecc.c defined global functions:
*/
-extern int ecc_correct_data(struct memory_segment *data);
-extern int ecc_set_segment_parity(struct memory_segment *data);
+extern int ftape_ecc_correct_data(struct memory_segment *data);
+extern int ftape_ecc_set_segment_parity(struct memory_segment *data);
-#endif /* _ecc_h_ */
+#endif /* _FTAPE_ECC_H_ */
diff --git a/drivers/char/ftape/lowlevel/ftape-format.c b/drivers/char/ftape/lowlevel/ftape-format.c
new file mode 100644
index 000000000..015911918
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-format.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-format.c,v $
+ * $Revision: 1.2.4.1 $
+ * $Date: 1997/11/14 16:05:39 $
+ *
+ * This file contains the code to support formatting of floppy
+ * tape cartridges with the QIC-40/80/3010/3020 floppy-tape
+ * driver "ftape" for Linux.
+ */
+
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-ecc.h"
+#include "../lowlevel/ftape-bsm.h"
+#include "../lowlevel/ftape-format.h"
+
+#if defined(TESTING)
+#define FT_FMT_SEGS_PER_BUF 50
+#else
+#define FT_FMT_SEGS_PER_BUF (FT_BUFF_SIZE/(4*FT_SECTORS_PER_SEGMENT))
+#endif
+
+/*
+ * first segment of the new buffer
+ */
+static int switch_segment = 0;
+
+/*
+ * at most 256 segments fit into one 32 kb buffer. Even TR-1 cartridges have
+ * more than this many segments per track, so better be careful.
+ *
+ * buffer_struct *buff: buffer to store the formatting coordinates in
+ * int start: starting segment for this buffer.
+ * int spt: segments per track
+ *
+ * Note: segment ids are relative to the start of the track here.
+ */
+static void setup_format_buffer(buffer_struct *buff, int start, int spt,
+ __u8 gap3)
+{
+ int to_do = spt - start;
+ TRACE_FUN(ft_t_flow);
+
+ if (to_do > FT_FMT_SEGS_PER_BUF) {
+ to_do = FT_FMT_SEGS_PER_BUF;
+ }
+ buff->ptr = buff->address;
+ buff->remaining = to_do * FT_SECTORS_PER_SEGMENT; /* # sectors */
+ buff->bytes = buff->remaining * 4; /* need 4 bytes per sector */
+ buff->gap3 = gap3;
+ buff->segment_id = start;
+ buff->next_segment = start + to_do;
+ if (buff->next_segment >= spt) {
+ buff->next_segment = 0; /* 0 means: stop runner */
+ }
+ buff->status = waiting; /* tells the isr that it can use
+ * this buffer
+ */
+ TRACE_EXIT;
+}
+
+
+/*
+ * start formatting a new track.
+ */
+int ftape_format_track(const unsigned int track, const __u8 gap3)
+{
+ unsigned long flags;
+ buffer_struct *tail, *head;
+ int status;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE_CATCH(ftape_ready_wait(ftape_timeout.pause, &status),);
+ if (track & 1) {
+ if (!(status & QIC_STATUS_AT_EOT)) {
+ TRACE_CATCH(ftape_seek_to_eot(),);
+ }
+ } else {
+ if (!(status & QIC_STATUS_AT_BOT)) {
+ TRACE_CATCH(ftape_seek_to_bot(),);
+ }
+ }
+ ftape_abort_operation(); /* this sets ft_head = ft_tail = 0 */
+ ftape_set_state(formatting);
+
+ TRACE(ft_t_noise,
+ "Formatting track %d, logical: from segment %d to %d",
+ track, track * ft_segments_per_track,
+ (track + 1) * ft_segments_per_track - 1);
+
+ /*
+ * initialize the buffer switching protocol for this track
+ */
+ head = ftape_get_buffer(ft_queue_head); /* tape isn't running yet */
+ tail = ftape_get_buffer(ft_queue_tail); /* tape isn't running yet */
+ switch_segment = 0;
+ do {
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ setup_format_buffer(tail, switch_segment,
+ ft_segments_per_track, gap3);
+ switch_segment = tail->next_segment;
+ } while ((switch_segment != 0) &&
+ ((tail = ftape_next_buffer(ft_queue_tail)) != head));
+ /* go */
+ head->status = formatting;
+ TRACE_CATCH(ftape_seek_head_to_track(track),);
+ TRACE_CATCH(ftape_command(QIC_LOGICAL_FORWARD),);
+ save_flags(flags); cli();
+ TRACE_CATCH(fdc_setup_formatting(head), restore_flags(flags));
+ restore_flags(flags);
+ TRACE_EXIT 0;
+}
+
+/* return segment id of segment currently being formatted and do the
+ * buffer switching stuff.
+ */
+int ftape_format_status(unsigned int *segment_id)
+{
+ buffer_struct *tail = ftape_get_buffer(ft_queue_tail);
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ while (switch_segment != 0 &&
+ ftape_get_buffer(ft_queue_head) != tail) {
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ /* need more buffers, first wait for empty buffer
+ */
+ TRACE_CATCH(ftape_wait_segment(formatting),);
+ /* don't worry for gap3. If we ever hit this piece of code,
+ * then all buffer already have the correct gap3 set!
+ */
+ setup_format_buffer(tail, switch_segment,
+ ft_segments_per_track, tail->gap3);
+ switch_segment = tail->next_segment;
+ if (switch_segment != 0) {
+ tail = ftape_next_buffer(ft_queue_tail);
+ }
+ }
+ /* should runner stop ?
+ */
+ if (ft_runner_status == aborting || ft_runner_status == do_abort) {
+ buffer_struct *head = ftape_get_buffer(ft_queue_head);
+ TRACE(ft_t_warn, "Error formatting segment %d",
+ ftape_get_buffer(ft_queue_head)->segment_id);
+ (void)ftape_abort_operation();
+ TRACE_EXIT (head->status != error) ? -EAGAIN : -EIO;
+ }
+ /*
+ * don't care if the timer expires, this is just kind of a
+ * "select" operation that lets the calling process sleep
+ * until something has happened
+ */
+ if (fdc_interrupt_wait(5 * FT_SECOND) < 0) {
+ TRACE(ft_t_noise, "End of track %d at segment %d",
+ ft_location.track,
+ ftape_get_buffer(ft_queue_head)->segment_id);
+ result = 1; /* end of track, unlock module */
+ } else {
+ result = 0;
+ }
+ /*
+ * the calling process should use the seg id to determine
+ * which parts of the dma buffers can be safely overwritten
+ * with new data.
+ */
+ *segment_id = ftape_get_buffer(ft_queue_head)->segment_id;
+ /*
+ * Internally we start counting segment ids from the start of
+ * each track when formatting, but externally we keep them
+ * relative to the start of the tape:
+ */
+ *segment_id += ft_location.track * ft_segments_per_track;
+ TRACE_EXIT result;
+}
+
+/*
+ * The segment id is relative to the start of the tape
+ */
+int ftape_verify_segment(const unsigned int segment_id, SectorMap *bsm)
+{
+ int result;
+ int verify_done = 0;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "Verifying segment %d", segment_id);
+
+ if (ft_driver_state != verifying) {
+ TRACE(ft_t_noise, "calling ftape_abort_operation");
+ if (ftape_abort_operation() < 0) {
+ TRACE(ft_t_err, "ftape_abort_operation failed");
+ TRACE_EXIT -EIO;
+ }
+ }
+ *bsm = 0x00000000;
+ ftape_set_state(verifying);
+ for (;;) {
+ buffer_struct *tail;
+ /*
+ * Allow escape from this loop on signal
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ /*
+ * Search all full buffers for the first matching the
+ * wanted segment. Clear other buffers on the fly.
+ */
+ tail = ftape_get_buffer(ft_queue_tail);
+ while (!verify_done && tail->status == done) {
+ /*
+ * Allow escape from this loop on signal !
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ if (tail->segment_id == segment_id) {
+ /* If out buffer is already full,
+ * return its contents.
+ */
+ TRACE(ft_t_flow, "found segment in cache: %d",
+ segment_id);
+ if ((tail->soft_error_map |
+ tail->hard_error_map) != 0) {
+ TRACE(ft_t_info,"bsm[%d] = 0x%08lx",
+ segment_id,
+ (unsigned long)
+ (tail->soft_error_map |
+ tail->hard_error_map));
+ *bsm = (tail->soft_error_map |
+ tail->hard_error_map);
+ }
+ verify_done = 1;
+ } else {
+ TRACE(ft_t_flow,"zapping segment in cache: %d",
+ tail->segment_id);
+ }
+ tail->status = waiting;
+ tail = ftape_next_buffer(ft_queue_tail);
+ }
+ if (!verify_done && tail->status == verifying) {
+ if (tail->segment_id == segment_id) {
+ switch(ftape_wait_segment(verifying)) {
+ case 0:
+ break;
+ case -EINTR:
+ TRACE_ABORT(-EINTR, ft_t_warn,
+ "interrupted by "
+ "non-blockable signal");
+ break;
+ default:
+ ftape_abort_operation();
+ ftape_set_state(verifying);
+ /* be picky */
+ TRACE_ABORT(-EIO, ft_t_warn,
+ "wait_segment failed");
+ }
+ } else {
+ /* We're reading the wrong segment,
+ * stop runner.
+ */
+ TRACE(ft_t_noise, "verifying wrong segment");
+ ftape_abort_operation();
+ ftape_set_state(verifying);
+ }
+ }
+ /* should runner stop ?
+ */
+ if (ft_runner_status == aborting) {
+ buffer_struct *head = ftape_get_buffer(ft_queue_head);
+ if (head->status == error ||
+ head->status == verifying) {
+ /* no data or overrun error */
+ head->status = waiting;
+ }
+ TRACE_CATCH(ftape_dumb_stop(),);
+ } else {
+ /* If just passed last segment on tape: wait
+ * for BOT or EOT mark. Sets ft_runner_status to
+ * idle if at lEOT and successful
+ */
+ TRACE_CATCH(ftape_handle_logical_eot(),);
+ }
+ if (verify_done) {
+ TRACE_EXIT 0;
+ }
+ /* Now at least one buffer is idle!
+ * Restart runner & tape if needed.
+ */
+ /* We could optimize the following a little bit. We know that
+ * the bad sector map is empty.
+ */
+ tail = ftape_get_buffer(ft_queue_tail);
+ if (tail->status == waiting) {
+ buffer_struct *head = ftape_get_buffer(ft_queue_head);
+
+ ftape_setup_new_segment(head, segment_id, -1);
+ ftape_calc_next_cluster(head);
+ if (ft_runner_status == idle) {
+ result = ftape_start_tape(segment_id,
+ head->sector_offset);
+ switch(result) {
+ case 0:
+ break;
+ case -ETIME:
+ case -EINTR:
+ TRACE_ABORT(result, ft_t_err, "Error: "
+ "segment %d unreachable",
+ segment_id);
+ break;
+ default:
+ *bsm = EMPTY_SEGMENT;
+ TRACE_EXIT 0;
+ break;
+ }
+ }
+ head->status = verifying;
+ fdc_setup_read_write(head, FDC_VERIFY);
+ }
+ }
+ /* not reached */
+ TRACE_EXIT -EIO;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-format.h b/drivers/char/ftape/lowlevel/ftape-format.h
new file mode 100644
index 000000000..f15161566
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-format.h
@@ -0,0 +1,37 @@
+#ifndef _FTAPE_FORMAT_H
+#define _FTAPE_FORMAT_H
+
+/*
+ * Copyright (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-format.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:13 $
+ *
+ * This file contains the low level definitions for the
+ * formatting support for the QIC-40/80/3010/3020 floppy-tape
+ * driver "ftape" for Linux.
+ */
+
+#ifdef __KERNEL__
+extern int ftape_format_track(const unsigned int track, const __u8 gap3);
+extern int ftape_format_status(unsigned int *segment_id);
+extern int ftape_verify_segment(const unsigned int segment_id, SectorMap *bsm);
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-init.c b/drivers/char/ftape/lowlevel/ftape-init.c
new file mode 100644
index 000000000..7a8d15f5e
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-init.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * This file contains the code that interfaces the kernel
+ * for the QIC-40/80/3010/3020 floppy-tape driver for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/major.h>
+
+#include <linux/ftape.h>
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,16)
+#include <linux/init.h>
+#else
+#define __initdata
+#define __initfunc(__arg) __arg
+#endif
+#include <linux/qic117.h>
+#ifdef CONFIG_ZFTAPE
+#include <linux/zftape.h>
+#endif
+
+#include "../lowlevel/ftape-init.h"
+#include "../lowlevel/ftape_syms.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/ftape-buffer.h"
+#include "../lowlevel/ftape-proc.h"
+#include "../lowlevel/ftape-tracing.h"
+
+/* Global vars.
+ */
+char ft_src[] __initdata = "$Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-init.c,v $";
+char ft_rev[] __initdata = "$Revision: 1.8 $";
+char ft_dat[] __initdata = "$Date: 1997/11/06 00:38:08 $";
+
+
+/* Called by modules package when installing the driver
+ * or by kernel during the initialization phase
+ */
+__initfunc(int ftape_init(void))
+{
+ TRACE_FUN(ft_t_flow);
+
+#ifdef MODULE
+ printk(KERN_INFO FTAPE_VERSION "\n");
+ if (TRACE_LEVEL >= ft_t_info) {
+ printk(
+KERN_INFO "(c) 1993-1996 Bas Laarhoven (bas@vimec.nl)\n"
+KERN_INFO "(c) 1995-1996 Kai Harrekilde-Petersen (khp@dolphinics.no)\n"
+KERN_INFO "(c) 1996-1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)\n"
+KERN_INFO "QIC-117 driver for QIC-40/80/3010/3020 floppy tape drives\n"
+KERN_INFO "Compiled for Linux version %s"
+#ifdef MODVERSIONS
+ " with versioned symbols"
+#endif
+ "\n", UTS_RELEASE);
+ }
+#else /* !MODULE */
+ /* print a short no-nonsense boot message */
+ printk(KERN_INFO FTAPE_VERSION " for Linux " UTS_RELEASE "\n");
+#endif /* MODULE */
+ TRACE(ft_t_info, "installing QIC-117 floppy tape hardware drive ... ");
+ TRACE(ft_t_info, "ftape_init @ 0x%p", ftape_init);
+ /* Allocate the DMA buffers. They are deallocated at cleanup() time.
+ */
+#if TESTING
+#ifdef MODULE
+ while (ftape_set_nr_buffers(CONFIG_FT_NR_BUFFERS) < 0) {
+ ftape_sleep(FT_SECOND/20);
+ if (current->signal & ~current->blocked) {
+ (void)ftape_set_nr_buffers(0);
+ TRACE(ft_t_bug,
+ "Killed by signal while allocating buffers.");
+ TRACE_ABORT(-EINTR,
+ ft_t_bug, "Free up memory and retry");
+ }
+ }
+#else
+ TRACE_CATCH(ftape_set_nr_buffers(CONFIG_FT_NR_BUFFERS),
+ (void)ftape_set_nr_buffers(0));
+#endif
+#else
+ TRACE_CATCH(ftape_set_nr_buffers(CONFIG_FT_NR_BUFFERS),
+ (void)ftape_set_nr_buffers(0));
+#endif
+ ft_drive_sel = -1;
+ ft_failure = 1; /* inhibit any operation but open */
+ ftape_udelay_calibrate(); /* must be before fdc_wait_calibrate ! */
+ fdc_wait_calibrate();
+#if (LINUX_VERSION_CODE >= KERNEL_VER(1,2,0) && \
+ LINUX_VERSION_CODE < KERNEL_VER(2,1,18))
+ register_symtab(&ftape_symbol_table); /* add global ftape symbols */
+#endif
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS)
+ (void)ftape_proc_init();
+#endif
+#ifdef CONFIG_ZFTAPE
+ (void)zft_init();
+#endif
+ TRACE_EXIT 0;
+}
+
+#ifdef MODULE
+
+#ifndef CONFIG_FT_NO_TRACE_AT_ALL
+static int ft_tracing = -1;
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+#define FT_MOD_PARM(var,type,desc) \
+ MODULE_PARM(var,type); MODULE_PARM_DESC(var,desc)
+
+FT_MOD_PARM(ft_fdc_base, "i", "Base address of FDC controller.");
+FT_MOD_PARM(ft_fdc_irq, "i", "IRQ (interrupt channel) to use.");
+FT_MOD_PARM(ft_fdc_dma, "i", "DMA channel to use.");
+FT_MOD_PARM(ft_fdc_threshold, "i", "Threshold of the FDC Fifo.");
+FT_MOD_PARM(ft_fdc_rate_limit, "i", "Maximal data rate for FDC.");
+FT_MOD_PARM(ft_probe_fc10, "i",
+ "If non-zero, probe for a Colorado FC-10/FC-20 controller.");
+FT_MOD_PARM(ft_mach2, "i",
+ "If non-zero, probe for a Mountain MACH-2 controller.");
+FT_MOD_PARM(ft_tracing, "i",
+ "Amount of debugging output, 0 <= tracing <= 8, default 3.");
+MODULE_AUTHOR(
+ "(c) 1993-1996 Bas Laarhoven (bas@vimec.nl), "
+ "(c) 1995-1996 Kai Harrekilde-Petersen (khp@dolphinics.no), "
+ "(c) 1996, 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)");
+MODULE_DESCRIPTION(
+ "QIC-117 driver for QIC-40/80/3010/3020 floppy tape drives.");
+#endif
+
+#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13)
+char kernel_version[] = UTS_RELEASE;
+#endif
+
+/* Called by modules package when installing the driver
+ */
+int init_module(void)
+{
+#ifndef CONFIG_FT_NO_TRACE_AT_ALL
+ if (ft_tracing != -1) {
+ ftape_tracing = ft_tracing;
+ }
+#endif
+ return ftape_init();
+}
+
+/* Called by modules package when removing the driver
+ */
+void cleanup_module(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS)
+ ftape_proc_destroy();
+#endif
+ (void)ftape_set_nr_buffers(0);
+ printk(KERN_INFO "ftape: unloaded.\n");
+ TRACE_EXIT;
+}
+#endif /* MODULE */
diff --git a/drivers/char/ftape/kernel-interface.h b/drivers/char/ftape/lowlevel/ftape-init.h
index 9843390c7..eae86b3dc 100644
--- a/drivers/char/ftape/kernel-interface.h
+++ b/drivers/char/ftape/lowlevel/ftape-init.h
@@ -1,8 +1,9 @@
-#ifndef _KERNEL_INTERFACE_H
-#define _KERNEL_INTERFACE_H
+#ifndef _FTAPE_INIT_H
+#define _FTAPE_INIT_H
/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,14 +20,12 @@
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
- $Source: /home/bas/distr/ftape-2.03b/RCS/kernel-interface.h,v $
- $Author: bas $
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-init.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:16 $
*
- $Revision: 1.24 $
- $Date: 1995/04/30 13:15:14 $
- $State: Beta $
- *
- * ----Description----
+ * This file contains the definitions for the interface to
+ * the Linux kernel for floppy tape driver ftape.
*
*/
@@ -44,22 +43,17 @@
#define QIC117_TAPE_MAJOR 27
#endif
-#define FTAPE_NO_REWIND 4 /* mask for minor nr */
-
-/* kernel-interface.c defined global variables.
+/* ftape-init.c defined global variables.
*/
-extern byte *tape_buffer[];
+#if defined(MODULE) && LINUX_VERSION_CODE <= KERNEL_VER(1,2,13)
extern char kernel_version[];
+#endif
-/* kernel-interface.c defined global functions.
+/* ftape-init.c defined global functions not defined in ftape.h
*/
-asmlinkage extern int init_module(void);
+#ifdef MODULE
+asmlinkage extern int init_module (void);
asmlinkage extern void cleanup_module(void);
-
-/* kernel global functions not (yet) standard accessible
- * (linked at load time by modules package).
- */
-asmlinkage extern sys_sgetmask(void);
-asmlinkage extern sys_ssetmask(int);
+#endif
#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-io.c b/drivers/char/ftape/lowlevel/ftape-io.c
new file mode 100644
index 000000000..af81d1802
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-io.c
@@ -0,0 +1,1018 @@
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996 Kai Harrekilde-Petersen,
+ * (C) 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-io.c,v $
+ * $Revision: 1.4 $
+ * $Date: 1997/11/11 14:02:36 $
+ *
+ * This file contains the general control functions for the
+ * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/ioctl.h>
+#include <linux/mtio.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-init.h"
+#include "../lowlevel/ftape-calibr.h"
+
+/* Global vars.
+ */
+/* NOTE: sectors start numbering at 1, all others at 0 ! */
+ft_timeout_table ftape_timeout;
+unsigned int ftape_tape_len = 0;
+volatile qic117_cmd_t ftape_current_command;
+const struct qic117_command_table qic117_cmds[] = QIC117_COMMANDS;
+int ftape_might_be_off_track;
+
+/* Local vars.
+ */
+static int diagnostic_mode = 0;
+static unsigned int ftape_udelay_count;
+static unsigned int ftape_udelay_time;
+
+void ftape_udelay(unsigned int usecs)
+{
+ volatile int count = (ftape_udelay_count * usecs +
+ ftape_udelay_count - 1) / ftape_udelay_time;
+ volatile int i;
+
+ while (count-- > 0) {
+ for (i = 0; i < 20; ++i);
+ }
+}
+
+void ftape_udelay_calibrate(void)
+{
+ ftape_calibrate("ftape_udelay",
+ ftape_udelay, &ftape_udelay_count, &ftape_udelay_time);
+}
+
+/* Delay (msec) routine.
+ */
+void ftape_sleep(unsigned int time)
+{
+ TRACE_FUN(ft_t_any);
+
+ time *= 1000; /* msecs -> usecs */
+ if (time < FT_USPT) {
+ /* Time too small for scheduler, do a busy wait ! */
+ ftape_udelay(time);
+ } else {
+ unsigned long flags;
+ unsigned int ticks = (time + FT_USPT - 1) / FT_USPT;
+
+ TRACE(ft_t_any, "%d msec, %d ticks", time/1000, ticks);
+ current->timeout = jiffies + ticks;
+ current->state = TASK_INTERRUPTIBLE;
+ save_flags(flags);
+ sti();
+ do {
+ while (current->state != TASK_RUNNING) {
+ schedule();
+ }
+ /* Mmm. Isn't current->blocked == 0xffffffff ?
+ */
+ if (current->signal & ~current->blocked) {
+ TRACE(ft_t_err,
+ "awoken by non-blocked signal :-(");
+ break; /* exit on signal */
+ }
+ } while (current->timeout > 0);
+ restore_flags(flags);
+ }
+ TRACE_EXIT;
+}
+
+/* send a command or parameter to the drive
+ * Generates # of step pulses.
+ */
+static inline int ft_send_to_drive(int arg)
+{
+ /* Always wait for a command_timeout period to separate
+ * individuals commands and/or parameters.
+ */
+ ftape_sleep(3 * FT_MILLISECOND);
+ /* Keep cylinder nr within range, step towards home if possible.
+ */
+ if (ftape_current_cylinder >= arg) {
+ return fdc_seek(ftape_current_cylinder - arg);
+ } else {
+ return fdc_seek(ftape_current_cylinder + arg);
+ }
+}
+
+/* forward */ int ftape_report_raw_drive_status(int *status);
+
+static int ft_check_cmd_restrictions(qic117_cmd_t command)
+{
+ int status = -1;
+ TRACE_FUN(ft_t_any);
+
+ TRACE(ft_t_flow, "%s", qic117_cmds[command].name);
+ /* A new motion command during an uninterruptible (motion)
+ * command requires a ready status before the new command can
+ * be issued. Otherwise a new motion command needs to be
+ * checked against required status.
+ */
+ if (qic117_cmds[command].cmd_type == motion &&
+ qic117_cmds[ftape_current_command].non_intr) {
+ ftape_report_raw_drive_status(&status);
+ if ((status & QIC_STATUS_READY) == 0) {
+ TRACE(ft_t_noise,
+ "motion cmd (%d) during non-intr cmd (%d)",
+ command, ftape_current_command);
+ TRACE(ft_t_noise, "waiting until drive gets ready");
+ ftape_ready_wait(ftape_timeout.seek,
+ &status);
+ }
+ }
+ if (qic117_cmds[command].mask != 0) {
+ __u8 difference;
+ /* Some commands do require a certain status:
+ */
+ if (status == -1) { /* not yet set */
+ ftape_report_raw_drive_status(&status);
+ }
+ difference = ((status ^ qic117_cmds[command].state) &
+ qic117_cmds[command].mask);
+ /* Wait until the drive gets
+ * ready. This may last forever if
+ * the drive never gets ready...
+ */
+ while ((difference & QIC_STATUS_READY) != 0) {
+ TRACE(ft_t_noise, "command %d issued while not ready",
+ command);
+ TRACE(ft_t_noise, "waiting until drive gets ready");
+ if (ftape_ready_wait(ftape_timeout.seek,
+ &status) == -EINTR) {
+ /* Bail out on signal !
+ */
+ TRACE_ABORT(-EINTR, ft_t_warn,
+ "interrupted by non-blockable signal");
+ }
+ difference = ((status ^ qic117_cmds[command].state) &
+ qic117_cmds[command].mask);
+ }
+ while ((difference & QIC_STATUS_ERROR) != 0) {
+ int err;
+ qic117_cmd_t cmd;
+
+ TRACE(ft_t_noise,
+ "command %d issued while error pending",
+ command);
+ TRACE(ft_t_noise, "clearing error status");
+ ftape_report_error(&err, &cmd, 1);
+ ftape_report_raw_drive_status(&status);
+ difference = ((status ^ qic117_cmds[command].state) &
+ qic117_cmds[command].mask);
+ if ((difference & QIC_STATUS_ERROR) != 0) {
+ /* Bail out on fatal signal !
+ */
+ FT_SIGNAL_EXIT(_NEVER_BLOCK);
+ }
+ }
+ if (difference) {
+ /* Any remaining difference can't be solved
+ * here.
+ */
+ if (difference & (QIC_STATUS_CARTRIDGE_PRESENT |
+ QIC_STATUS_NEW_CARTRIDGE |
+ QIC_STATUS_REFERENCED)) {
+ TRACE(ft_t_warn,
+ "Fatal: tape removed or reinserted !");
+ ft_failure = 1;
+ } else {
+ TRACE(ft_t_err, "wrong state: 0x%02x should be: 0x%02x",
+ status & qic117_cmds[command].mask,
+ qic117_cmds[command].state);
+ }
+ TRACE_EXIT -EIO;
+ }
+ if (~status & QIC_STATUS_READY & qic117_cmds[command].mask) {
+ TRACE_ABORT(-EBUSY, ft_t_err, "Bad: still busy!");
+ }
+ }
+ TRACE_EXIT 0;
+}
+
+/* Issue a tape command:
+ */
+int ftape_command(qic117_cmd_t command)
+{
+ int result = 0;
+ static int level = 0;
+ TRACE_FUN(ft_t_any);
+
+ if ((unsigned int)command > NR_ITEMS(qic117_cmds)) {
+ /* This is a bug we'll want to know about too.
+ */
+ TRACE_ABORT(-EIO, ft_t_bug, "bug - bad command: %d", command);
+ }
+ if (++level > 5) { /* This is a bug we'll want to know about. */
+ --level;
+ TRACE_ABORT(-EIO, ft_t_bug, "bug - recursion for command: %d",
+ command);
+ }
+ /* disable logging and restriction check for some commands,
+ * check all other commands that have a prescribed starting
+ * status.
+ */
+ if (diagnostic_mode) {
+ TRACE(ft_t_flow, "diagnostic command %d", command);
+ } else if (command == QIC_REPORT_DRIVE_STATUS ||
+ command == QIC_REPORT_NEXT_BIT) {
+ TRACE(ft_t_any, "%s", qic117_cmds[command].name);
+ } else {
+ TRACE_CATCH(ft_check_cmd_restrictions(command), --level);
+ }
+ /* Now all conditions are met or result was < 0.
+ */
+ result = ft_send_to_drive((unsigned int)command);
+ if (qic117_cmds[command].cmd_type == motion &&
+ command != QIC_LOGICAL_FORWARD && command != QIC_STOP_TAPE) {
+ ft_location.known = 0;
+ }
+ ftape_current_command = command;
+ --level;
+ TRACE_EXIT result;
+}
+
+/* Send a tape command parameter:
+ * Generates command # of step pulses.
+ * Skips tape-status call !
+ */
+int ftape_parameter(unsigned int parameter)
+{
+ TRACE_FUN(ft_t_any);
+
+ TRACE(ft_t_flow, "called with parameter = %d", parameter);
+ TRACE_EXIT ft_send_to_drive(parameter + 2);
+}
+
+/* Wait for the drive to get ready.
+ * timeout time in milli-seconds
+ * Returned status is valid if result != -EIO
+ *
+ * Should we allow to be killed by SIGINT? (^C)
+ * Would be nice at least for large timeouts.
+ */
+int ftape_ready_wait(unsigned int timeout, int *status)
+{
+ unsigned long t0;
+ unsigned int poll_delay;
+ int signal_retries;
+ TRACE_FUN(ft_t_any);
+
+ /* the following ** REALLY ** reduces the system load when
+ * e.g. one simply rewinds or retensions. The tape is slow
+ * anyway. It is really not necessary to detect error
+ * conditions with 1/10 seconds granularity
+ *
+ * On my AMD 133MHZ 486: 100 ms: 23% system load
+ * 1 sec: 5%
+ * 5 sec: 0.6%, yeah
+ */
+ if (timeout <= FT_SECOND) {
+ poll_delay = 100 * FT_MILLISECOND;
+ signal_retries = 20; /* two seconds */
+ } else if (timeout < 20 * FT_SECOND) {
+ TRACE(ft_t_flow, "setting poll delay to 1 second");
+ poll_delay = FT_SECOND;
+ signal_retries = 2; /* two seconds */
+ } else {
+ TRACE(ft_t_flow, "setting poll delay to 5 seconds");
+ poll_delay = 5 * FT_SECOND;
+ signal_retries = 1; /* five seconds */
+ }
+ for (;;) {
+ t0 = jiffies;
+ TRACE_CATCH(ftape_report_raw_drive_status(status),);
+ if (*status & QIC_STATUS_READY) {
+ TRACE_EXIT 0;
+ }
+ if (!signal_retries--) {
+ FT_SIGNAL_EXIT(_NEVER_BLOCK);
+ }
+ if ((int)timeout >= 0) {
+ /* this will fail when jiffies wraps around about
+ * once every year :-)
+ */
+ timeout -= ((jiffies - t0) * FT_SECOND) / HZ;
+ if (timeout <= 0) {
+ TRACE_ABORT(-ETIME, ft_t_err, "timeout");
+ }
+ ftape_sleep(poll_delay);
+ timeout -= poll_delay;
+ } else {
+ ftape_sleep(poll_delay);
+ }
+ }
+ TRACE_EXIT -ETIME;
+}
+
+/* Issue command and wait up to timeout milli seconds for drive ready
+ */
+int ftape_command_wait(qic117_cmd_t command, unsigned int timeout, int *status)
+{
+ int result;
+
+ /* Drive should be ready, issue command
+ */
+ result = ftape_command(command);
+ if (result >= 0) {
+ result = ftape_ready_wait(timeout, status);
+ }
+ return result;
+}
+
+int ftape_parameter_wait(unsigned int parm, unsigned int timeout, int *status)
+{
+ int result;
+
+ /* Drive should be ready, issue command
+ */
+ result = ftape_parameter(parm);
+ if (result >= 0) {
+ result = ftape_ready_wait(timeout, status);
+ }
+ return result;
+}
+
+/*--------------------------------------------------------------------------
+ * Report operations
+ */
+
+/* Query the drive about its status. The command is sent and
+ result_length bits of status are returned (2 extra bits are read
+ for start and stop). */
+
+int ftape_report_operation(int *status,
+ qic117_cmd_t command,
+ int result_length)
+{
+ int i, st3;
+ unsigned int t0;
+ unsigned int dt;
+ TRACE_FUN(ft_t_any);
+
+ TRACE_CATCH(ftape_command(command),);
+ t0 = ftape_timestamp();
+ i = 0;
+ do {
+ ++i;
+ ftape_sleep(3 * FT_MILLISECOND); /* see remark below */
+ TRACE_CATCH(fdc_sense_drive_status(&st3),);
+ dt = ftape_timediff(t0, ftape_timestamp());
+ /* Ack should be asserted within Ttimout + Tack = 6 msec.
+ * Looks like some drives fail to do this so extend this
+ * period to 300 msec.
+ */
+ } while (!(st3 & ST3_TRACK_0) && dt < 300000);
+ if (!(st3 & ST3_TRACK_0)) {
+ TRACE(ft_t_err,
+ "No acknowledge after %u msec. (%i iter)", dt / 1000, i);
+ TRACE_ABORT(-EIO, ft_t_err, "timeout on Acknowledge");
+ }
+ /* dt may be larger than expected because of other tasks
+ * scheduled while we were sleeping.
+ */
+ if (i > 1 && dt > 6000) {
+ TRACE(ft_t_err, "Acknowledge after %u msec. (%i iter)",
+ dt / 1000, i);
+ }
+ *status = 0;
+ for (i = 0; i < result_length + 1; i++) {
+ TRACE_CATCH(ftape_command(QIC_REPORT_NEXT_BIT),);
+ TRACE_CATCH(fdc_sense_drive_status(&st3),);
+ if (i < result_length) {
+ *status |= ((st3 & ST3_TRACK_0) ? 1 : 0) << i;
+ } else if ((st3 & ST3_TRACK_0) == 0) {
+ TRACE_ABORT(-EIO, ft_t_err, "missing status stop bit");
+ }
+ }
+ /* this command will put track zero and index back into normal state */
+ (void)ftape_command(QIC_REPORT_NEXT_BIT);
+ TRACE_EXIT 0;
+}
+
+/* Report the current drive status. */
+
+int ftape_report_raw_drive_status(int *status)
+{
+ int result;
+ int count = 0;
+ TRACE_FUN(ft_t_any);
+
+ do {
+ result = ftape_report_operation(status,
+ QIC_REPORT_DRIVE_STATUS, 8);
+ } while (result < 0 && ++count <= 3);
+ if (result < 0) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "report_operation failed after %d trials", count);
+ }
+ if ((*status & 0xff) == 0xff) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "impossible drive status 0xff");
+ }
+ if (*status & QIC_STATUS_READY) {
+ ftape_current_command = QIC_NO_COMMAND; /* completed */
+ }
+ ft_last_status.status.drive_status = (__u8)(*status & 0xff);
+ TRACE_EXIT 0;
+}
+
+int ftape_report_drive_status(int *status)
+{
+ TRACE_FUN(ft_t_any);
+
+ TRACE_CATCH(ftape_report_raw_drive_status(status),);
+ if (*status & QIC_STATUS_NEW_CARTRIDGE ||
+ !(*status & QIC_STATUS_CARTRIDGE_PRESENT)) {
+ ft_failure = 1; /* will inhibit further operations */
+ TRACE_EXIT -EIO;
+ }
+ if (*status & QIC_STATUS_READY && *status & QIC_STATUS_ERROR) {
+ /* Let caller handle all errors */
+ TRACE_ABORT(1, ft_t_warn, "warning: error status set!");
+ }
+ TRACE_EXIT 0;
+}
+
+int ftape_report_error(unsigned int *error,
+ qic117_cmd_t *command, int report)
+{
+ static const ftape_error ftape_errors[] = QIC117_ERRORS;
+ int code;
+ TRACE_FUN(ft_t_any);
+
+ TRACE_CATCH(ftape_report_operation(&code, QIC_REPORT_ERROR_CODE, 16),);
+ *error = (unsigned int)(code & 0xff);
+ *command = (qic117_cmd_t)((code>>8)&0xff);
+ /* remember hardware status, maybe useful for status ioctls
+ */
+ ft_last_error.error.command = (__u8)*command;
+ ft_last_error.error.error = (__u8)*error;
+ if (!report) {
+ TRACE_EXIT 0;
+ }
+ if (*error == 0) {
+ TRACE_ABORT(0, ft_t_info, "No error");
+ }
+ TRACE(ft_t_info, "errorcode: %d", *error);
+ if (*error < NR_ITEMS(ftape_errors)) {
+ TRACE(ft_t_noise, "%sFatal ERROR:",
+ (ftape_errors[*error].fatal ? "" : "Non-"));
+ TRACE(ft_t_noise, "%s ...", ftape_errors[*error].message);
+ } else {
+ TRACE(ft_t_noise, "Unknown ERROR !");
+ }
+ if ((unsigned int)*command < NR_ITEMS(qic117_cmds) &&
+ qic117_cmds[*command].name != NULL) {
+ TRACE(ft_t_noise, "... caused by command \'%s\'",
+ qic117_cmds[*command].name);
+ } else {
+ TRACE(ft_t_noise, "... caused by unknown command %d",
+ *command);
+ }
+ TRACE_EXIT 0;
+}
+
+int ftape_in_error_state(int status)
+{
+ TRACE_FUN(ft_t_any);
+
+ if ((status & QIC_STATUS_READY) && (status & QIC_STATUS_ERROR)) {
+ TRACE_ABORT(1, ft_t_warn, "warning: error status set!");
+ }
+ TRACE_EXIT 0;
+}
+
+int ftape_report_configuration(qic_model *model,
+ unsigned int *rate,
+ int *qic_std,
+ int *tape_len)
+{
+ int result;
+ int config;
+ int status;
+ static const unsigned int qic_rates[ 4] = { 250, 2000, 500, 1000 };
+ TRACE_FUN(ft_t_any);
+
+ result = ftape_report_operation(&config,
+ QIC_REPORT_DRIVE_CONFIGURATION, 8);
+ if (result < 0) {
+ ft_last_status.status.drive_config = (__u8)0x00;
+ *model = prehistoric;
+ *rate = 500;
+ *qic_std = QIC_TAPE_QIC40;
+ *tape_len = 205;
+ TRACE_EXIT 0;
+ } else {
+ ft_last_status.status.drive_config = (__u8)(config & 0xff);
+ }
+ *rate = qic_rates[(config & QIC_CONFIG_RATE_MASK) >> QIC_CONFIG_RATE_SHIFT];
+ result = ftape_report_operation(&status, QIC_REPORT_TAPE_STATUS, 8);
+ if (result < 0) {
+ ft_last_status.status.tape_status = (__u8)0x00;
+ /* pre- QIC117 rev C spec. drive, QIC_CONFIG_80 bit is valid.
+ */
+ *qic_std = (config & QIC_CONFIG_80) ?
+ QIC_TAPE_QIC80 : QIC_TAPE_QIC40;
+ /* ?? how's about 425ft tapes? */
+ *tape_len = (config & QIC_CONFIG_LONG) ? 307 : 0;
+ *model = pre_qic117c;
+ result = 0;
+ } else {
+ ft_last_status.status.tape_status = (__u8)(status & 0xff);
+ *model = post_qic117b;
+ TRACE(ft_t_any, "report tape status result = %02x", status);
+ /* post- QIC117 rev C spec. drive, QIC_CONFIG_80 bit is
+ * invalid.
+ */
+ switch (status & QIC_TAPE_STD_MASK) {
+ case QIC_TAPE_QIC40:
+ case QIC_TAPE_QIC80:
+ case QIC_TAPE_QIC3020:
+ case QIC_TAPE_QIC3010:
+ *qic_std = status & QIC_TAPE_STD_MASK;
+ break;
+ default:
+ *qic_std = -1;
+ break;
+ }
+ switch (status & QIC_TAPE_LEN_MASK) {
+ case QIC_TAPE_205FT:
+ /* 205 or 425+ ft 550 Oe tape */
+ *tape_len = 0;
+ break;
+ case QIC_TAPE_307FT:
+ /* 307.5 ft 550 Oe Extended Length (XL) tape */
+ *tape_len = 307;
+ break;
+ case QIC_TAPE_VARIABLE:
+ /* Variable length 550 Oe tape */
+ *tape_len = 0;
+ break;
+ case QIC_TAPE_1100FT:
+ /* 1100 ft 550 Oe tape */
+ *tape_len = 1100;
+ break;
+ case QIC_TAPE_FLEX:
+ /* Variable length 900 Oe tape */
+ *tape_len = 0;
+ break;
+ default:
+ *tape_len = -1;
+ break;
+ }
+ if (*qic_std == -1 || *tape_len == -1) {
+ TRACE(ft_t_any,
+ "post qic-117b spec drive with unknown tape");
+ }
+ result = *tape_len == -1 ? -EIO : 0;
+ if (status & QIC_TAPE_WIDE) {
+ switch (*qic_std) {
+ case QIC_TAPE_QIC80:
+ TRACE(ft_t_info, "TR-1 tape detected");
+ break;
+ case QIC_TAPE_QIC3010:
+ TRACE(ft_t_info, "TR-2 tape detected");
+ break;
+ case QIC_TAPE_QIC3020:
+ TRACE(ft_t_info, "TR-3 tape detected");
+ break;
+ default:
+ TRACE(ft_t_warn,
+ "Unknown Travan tape type detected");
+ break;
+ }
+ }
+ }
+ TRACE_EXIT (result < 0) ? -EIO : 0;
+}
+
+int ftape_report_rom_version(int *version)
+{
+
+ if (ftape_report_operation(version, QIC_REPORT_ROM_VERSION, 8) < 0) {
+ return -EIO;
+ } else {
+ return 0;
+ }
+}
+
+int ftape_report_signature(int *signature)
+{
+ int result;
+
+ result = ftape_command(28);
+ result = ftape_report_operation(signature, 9, 8);
+ result = ftape_command(30);
+ return (result < 0) ? -EIO : 0;
+}
+
+void ftape_report_vendor_id(unsigned int *id)
+{
+ int result;
+ TRACE_FUN(ft_t_any);
+
+ /* We'll try to get a vendor id from the drive. First
+ * according to the QIC-117 spec, a 16-bit id is requested.
+ * If that fails we'll try an 8-bit version, otherwise we'll
+ * try an undocumented query.
+ */
+ result = ftape_report_operation((int *) id, QIC_REPORT_VENDOR_ID, 16);
+ if (result < 0) {
+ result = ftape_report_operation((int *) id,
+ QIC_REPORT_VENDOR_ID, 8);
+ if (result < 0) {
+ /* The following is an undocumented call found
+ * in the CMS code.
+ */
+ result = ftape_report_operation((int *) id, 24, 8);
+ if (result < 0) {
+ *id = UNKNOWN_VENDOR;
+ } else {
+ TRACE(ft_t_noise, "got old 8 bit id: %04x",
+ *id);
+ *id |= 0x20000;
+ }
+ } else {
+ TRACE(ft_t_noise, "got 8 bit id: %04x", *id);
+ *id |= 0x10000;
+ }
+ } else {
+ TRACE(ft_t_noise, "got 16 bit id: %04x", *id);
+ }
+ if (*id == 0x0047) {
+ int version;
+ int sign;
+
+ if (ftape_report_rom_version(&version) < 0) {
+ TRACE(ft_t_bug, "report rom version failed");
+ TRACE_EXIT;
+ }
+ TRACE(ft_t_noise, "CMS rom version: %d", version);
+ ftape_command(QIC_ENTER_DIAGNOSTIC_1);
+ ftape_command(QIC_ENTER_DIAGNOSTIC_1);
+ diagnostic_mode = 1;
+ if (ftape_report_operation(&sign, 9, 8) < 0) {
+ unsigned int error;
+ qic117_cmd_t command;
+
+ ftape_report_error(&error, &command, 1);
+ ftape_command(QIC_ENTER_PRIMARY_MODE);
+ diagnostic_mode = 0;
+ TRACE_EXIT; /* failure ! */
+ } else {
+ TRACE(ft_t_noise, "CMS signature: %02x", sign);
+ }
+ if (sign == 0xa5) {
+ result = ftape_report_operation(&sign, 37, 8);
+ if (result < 0) {
+ if (version >= 63) {
+ *id = 0x8880;
+ TRACE(ft_t_noise,
+ "This is an Iomega drive !");
+ } else {
+ *id = 0x0047;
+ TRACE(ft_t_noise,
+ "This is a real CMS drive !");
+ }
+ } else {
+ *id = 0x0047;
+ TRACE(ft_t_noise, "CMS status: %d", sign);
+ }
+ } else {
+ *id = UNKNOWN_VENDOR;
+ }
+ ftape_command(QIC_ENTER_PRIMARY_MODE);
+ diagnostic_mode = 0;
+ }
+ TRACE_EXIT;
+}
+
+static int qic_rate_code(unsigned int rate)
+{
+ switch (rate) {
+ case 250:
+ return QIC_CONFIG_RATE_250;
+ case 500:
+ return QIC_CONFIG_RATE_500;
+ case 1000:
+ return QIC_CONFIG_RATE_1000;
+ case 2000:
+ return QIC_CONFIG_RATE_2000;
+ default:
+ return QIC_CONFIG_RATE_500;
+ }
+}
+
+static int ftape_set_rate_test(unsigned int *max_rate)
+{
+ unsigned int error;
+ qic117_cmd_t command;
+ int status;
+ int supported = 0;
+ TRACE_FUN(ft_t_any);
+
+ /* Check if the drive does support the select rate command
+ * by testing all different settings. If any one is accepted
+ * we assume the command is supported, else not.
+ */
+ for (*max_rate = 2000; *max_rate >= 250; *max_rate /= 2) {
+ if (ftape_command(QIC_SELECT_RATE) < 0) {
+ continue;
+ }
+ if (ftape_parameter_wait(qic_rate_code(*max_rate),
+ 1 * FT_SECOND, &status) < 0) {
+ continue;
+ }
+ if (status & QIC_STATUS_ERROR) {
+ ftape_report_error(&error, &command, 0);
+ continue;
+ }
+ supported = 1; /* did accept a request */
+ break;
+ }
+ TRACE(ft_t_noise, "Select Rate command is%s supported",
+ supported ? "" : " not");
+ TRACE_EXIT supported;
+}
+
+int ftape_set_data_rate(unsigned int new_rate /* Kbps */, unsigned int qic_std)
+{
+ int status;
+ int result = 0;
+ unsigned int data_rate = new_rate;
+ static int supported = 0;
+ int rate_changed = 0;
+ qic_model dummy_model;
+ unsigned int dummy_qic_std, dummy_tape_len;
+ TRACE_FUN(ft_t_any);
+
+ if (ft_drive_max_rate == 0) { /* first time */
+ supported = ftape_set_rate_test(&ft_drive_max_rate);
+ }
+ if (supported) {
+ ftape_command(QIC_SELECT_RATE);
+ result = ftape_parameter_wait(qic_rate_code(new_rate),
+ 1 * FT_SECOND, &status);
+ if (result >= 0 && !(status & QIC_STATUS_ERROR)) {
+ rate_changed = 1;
+ }
+ }
+ TRACE_CATCH(result = ftape_report_configuration(&dummy_model,
+ &data_rate,
+ &dummy_qic_std,
+ &dummy_tape_len),);
+ if (data_rate != new_rate) {
+ if (!supported) {
+ TRACE(ft_t_warn, "Rate change not supported!");
+ } else if (rate_changed) {
+ TRACE(ft_t_warn, "Requested: %d, got %d",
+ new_rate, data_rate);
+ } else {
+ TRACE(ft_t_warn, "Rate change failed!");
+ }
+ result = -EINVAL;
+ }
+ /*
+ * Set data rate and write precompensation as specified:
+ *
+ * | QIC-40/80 | QIC-3010/3020
+ * rate | precomp | precomp
+ * ----------+-------------+--------------
+ * 250 Kbps. | 250 ns. | 0 ns.
+ * 500 Kbps. | 125 ns. | 0 ns.
+ * 1 Mbps. | 42 ns. | 0 ns.
+ * 2 Mbps | N/A | 0 ns.
+ */
+ if ((qic_std == QIC_TAPE_QIC40 && data_rate > 500) ||
+ (qic_std == QIC_TAPE_QIC80 && data_rate > 1000)) {
+ TRACE_ABORT(-EINVAL,
+ ft_t_warn, "Datarate too high for QIC-mode");
+ }
+ TRACE_CATCH(fdc_set_data_rate(data_rate),_res = -EINVAL);
+ ft_data_rate = data_rate;
+ if (qic_std == QIC_TAPE_QIC40 || qic_std == QIC_TAPE_QIC80) {
+ switch (data_rate) {
+ case 250:
+ fdc_set_write_precomp(250);
+ break;
+ default:
+ case 500:
+ fdc_set_write_precomp(125);
+ break;
+ case 1000:
+ fdc_set_write_precomp(42);
+ break;
+ }
+ } else {
+ fdc_set_write_precomp(0);
+ }
+ TRACE_EXIT result;
+}
+
+/* The next two functions are used to cope with excessive overrun errors
+ */
+int ftape_increase_threshold(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (fdc.type < i82077 || ft_fdc_threshold >= 12) {
+ TRACE_ABORT(-EIO, ft_t_err, "cannot increase fifo threshold");
+ }
+ if (fdc_fifo_threshold(++ft_fdc_threshold, NULL, NULL, NULL) < 0) {
+ TRACE(ft_t_err, "cannot increase fifo threshold");
+ ft_fdc_threshold --;
+ fdc_reset();
+ }
+ TRACE(ft_t_info, "New FIFO threshold: %d", ft_fdc_threshold);
+ TRACE_EXIT 0;
+}
+
+int ftape_half_data_rate(void)
+{
+ if (ft_data_rate < 500) {
+ return -1;
+ }
+ if (ftape_set_data_rate(ft_data_rate / 2, ft_qic_std) < 0) {
+ return -EIO;
+ }
+ ftape_calc_timeouts(ft_qic_std, ft_data_rate, ftape_tape_len);
+ return 0;
+}
+
+/* Seek the head to the specified track.
+ */
+int ftape_seek_head_to_track(unsigned int track)
+{
+ int status;
+ TRACE_FUN(ft_t_any);
+
+ ft_location.track = -1; /* remains set in case of error */
+ if (track >= ft_tracks_per_tape) {
+ TRACE_ABORT(-EINVAL, ft_t_bug, "track out of bounds");
+ }
+ TRACE(ft_t_flow, "seeking track %d", track);
+ TRACE_CATCH(ftape_command(QIC_SEEK_HEAD_TO_TRACK),);
+ TRACE_CATCH(ftape_parameter_wait(track, ftape_timeout.head_seek,
+ &status),);
+ ft_location.track = track;
+ ftape_might_be_off_track = 0;
+ TRACE_EXIT 0;
+}
+
+int ftape_wakeup_drive(wake_up_types method)
+{
+ int status;
+ int motor_on = 0;
+ TRACE_FUN(ft_t_any);
+
+ switch (method) {
+ case wake_up_colorado:
+ TRACE_CATCH(ftape_command(QIC_PHANTOM_SELECT),);
+ TRACE_CATCH(ftape_parameter(0 /* ft_drive_sel ?? */),);
+ break;
+ case wake_up_mountain:
+ TRACE_CATCH(ftape_command(QIC_SOFT_SELECT),);
+ ftape_sleep(FT_MILLISECOND); /* NEEDED */
+ TRACE_CATCH(ftape_parameter(18),);
+ break;
+ case wake_up_insight:
+ ftape_sleep(100 * FT_MILLISECOND);
+ motor_on = 1;
+ fdc_motor(motor_on); /* enable is done by motor-on */
+ case no_wake_up:
+ break;
+ default:
+ TRACE_EXIT -ENODEV; /* unknown wakeup method */
+ break;
+ }
+ /* If wakeup succeeded we shouldn't get an error here..
+ */
+ TRACE_CATCH(ftape_report_raw_drive_status(&status),
+ if (motor_on) {
+ fdc_motor(0);
+ });
+ TRACE_EXIT 0;
+}
+
+int ftape_put_drive_to_sleep(wake_up_types method)
+{
+ TRACE_FUN(ft_t_any);
+
+ switch (method) {
+ case wake_up_colorado:
+ TRACE_CATCH(ftape_command(QIC_PHANTOM_DESELECT),);
+ break;
+ case wake_up_mountain:
+ TRACE_CATCH(ftape_command(QIC_SOFT_DESELECT),);
+ break;
+ case wake_up_insight:
+ fdc_motor(0); /* enable is done by motor-on */
+ case no_wake_up: /* no wakeup / no sleep ! */
+ break;
+ default:
+ TRACE_EXIT -ENODEV; /* unknown wakeup method */
+ }
+ TRACE_EXIT 0;
+}
+
+int ftape_reset_drive(void)
+{
+ int result = 0;
+ int status;
+ unsigned int err_code;
+ qic117_cmd_t err_command;
+ int i;
+ TRACE_FUN(ft_t_any);
+
+ /* We want to re-establish contact with our drive. Fire a
+ * number of reset commands (single step pulses) and pray for
+ * success.
+ */
+ for (i = 0; i < 2; ++i) {
+ TRACE(ft_t_flow, "Resetting fdc");
+ fdc_reset();
+ ftape_sleep(10 * FT_MILLISECOND);
+ TRACE(ft_t_flow, "Reset command to drive");
+ result = ftape_command(QIC_RESET);
+ if (result == 0) {
+ ftape_sleep(1 * FT_SECOND); /* drive not
+ * accessible
+ * during 1 second
+ */
+ TRACE(ft_t_flow, "Re-selecting drive");
+
+ /* Strange, the QIC-117 specs don't mention
+ * this but the drive gets deselected after a
+ * soft reset ! So we need to enable it
+ * again.
+ */
+ if (ftape_wakeup_drive(ft_drive_type.wake_up) < 0) {
+ TRACE(ft_t_err, "Wakeup failed !");
+ }
+ TRACE(ft_t_flow, "Waiting until drive gets ready");
+ result= ftape_ready_wait(ftape_timeout.reset, &status);
+ if (result == 0 && (status & QIC_STATUS_ERROR)) {
+ result = ftape_report_error(&err_code,
+ &err_command, 1);
+ if (result == 0 && err_code == 27) {
+ /* Okay, drive saw reset
+ * command and responded as it
+ * should
+ */
+ break;
+ } else {
+ result = -EIO;
+ }
+ } else {
+ result = -EIO;
+ }
+ }
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ }
+ if (result != 0) {
+ TRACE(ft_t_err, "General failure to reset tape drive");
+ } else {
+ /* Restore correct settings: keep original rate
+ */
+ ftape_set_data_rate(ft_data_rate, ft_qic_std);
+ }
+ ftape_init_drive_needed = 1;
+ TRACE_EXIT result;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-io.h b/drivers/char/ftape/lowlevel/ftape-io.h
new file mode 100644
index 000000000..841f52d73
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-io.h
@@ -0,0 +1,94 @@
+#ifndef _FTAPE_IO_H
+#define _FTAPE_IO_H
+
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-io.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:18 $
+ *
+ * This file contains definitions for the glue part of the
+ * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ */
+
+#include <linux/qic117.h>
+#include <linux/ftape-vendors.h>
+
+typedef struct {
+ unsigned int seek;
+ unsigned int reset;
+ unsigned int rewind;
+ unsigned int head_seek;
+ unsigned int stop;
+ unsigned int pause;
+} ft_timeout_table;
+
+typedef enum {
+ prehistoric, pre_qic117c, post_qic117b, post_qic117d
+} qic_model;
+
+/*
+ * ftape-io.c defined global vars.
+ */
+extern ft_timeout_table ftape_timeout;
+extern unsigned int ftape_tape_len;
+extern volatile qic117_cmd_t ftape_current_command;
+extern const struct qic117_command_table qic117_cmds[];
+extern int ftape_might_be_off_track;
+
+/*
+ * ftape-io.c defined global functions.
+ */
+extern void ftape_udelay(unsigned int usecs);
+extern void ftape_udelay_calibrate(void);
+extern void ftape_sleep(unsigned int time);
+extern void ftape_report_vendor_id(unsigned int *id);
+extern int ftape_command(qic117_cmd_t command);
+extern int ftape_command_wait(qic117_cmd_t command,
+ unsigned int timeout,
+ int *status);
+extern int ftape_parameter(unsigned int parameter);
+extern int ftape_parameter_wait(unsigned int parameter,
+ unsigned int timeout,
+ int *status);
+extern int ftape_report_operation(int *status,
+ qic117_cmd_t command,
+ int result_length);
+extern int ftape_report_configuration(qic_model *model,
+ unsigned int *rate,
+ int *qic_std,
+ int *tape_len);
+extern int ftape_report_drive_status(int *status);
+extern int ftape_report_raw_drive_status(int *status);
+extern int ftape_report_status(int *status);
+extern int ftape_ready_wait(unsigned int timeout, int *status);
+extern int ftape_seek_head_to_track(unsigned int track);
+extern int ftape_in_error_state(int status);
+extern int ftape_set_data_rate(unsigned int new_rate, unsigned int qic_std);
+extern int ftape_report_error(unsigned int *error,
+ qic117_cmd_t *command,
+ int report);
+extern int ftape_reset_drive(void);
+extern int ftape_put_drive_to_sleep(wake_up_types method);
+extern int ftape_wakeup_drive(wake_up_types method);
+extern int ftape_increase_threshold(void);
+extern int ftape_half_data_rate(void);
+
+#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-proc.c b/drivers/char/ftape/lowlevel/ftape-proc.c
new file mode 100644
index 000000000..ebb6a3f04
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-proc.c
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-proc.c,v $
+ * $Revision: 1.11 $
+ * $Date: 1997/10/24 14:47:37 $
+ *
+ * This file contains the procfs interface for the
+ * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ */
+
+#include <linux/config.h>
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS)
+
+/* adding proc entries from inside a module is REALLY complicated
+ * for pre-2.1.28 kernels. I don't want to care about it.
+ */
+
+#include <linux/proc_fs.h>
+
+#include <linux/ftape.h>
+#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13) /* bail out */
+#error \
+Please disable CONFIG_FT_PROC_FS in "MCONFIG" or upgrade to a newer kernel!
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,16)
+#include <linux/init.h>
+#else
+#define __initdata
+#define __initfunc(__arg) __arg
+#endif
+#include <linux/qic117.h>
+
+
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-proc.h"
+#include "../lowlevel/ftape-tracing.h"
+
+static int ftape_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,28)
+
+#include <asm/segment.h> /* for memcpy_tofs() */
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,0)
+static long ftape_proc_read(struct inode* inode, struct file* file,
+ char* buf, unsigned long count);
+#else
+static int ftape_proc_read(struct inode* inode, struct file* file,
+ char* buf, int count);
+#endif
+
+#define FT_PROC_REGISTER(parent, child) proc_register_dynamic(parent, child)
+
+/*
+ * Structures for interfacing with the /proc filesystem.
+ * Router creates its own directory /proc/net/router with the folowing
+ * entries:
+ * config device configuration
+ * status global device statistics
+ * <device> entry for each WAN device
+ */
+
+/*
+ * Generic /proc/net/ftape/<file> file and inode operations
+ */
+
+
+static struct file_operations ftape_proc_fops =
+{
+ NULL, /* lseek */
+ ftape_proc_read, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL, /* can't fsync */
+};
+
+static struct inode_operations ftape_proc_inode_operations =
+{
+ &ftape_proc_fops,
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+};
+
+/*
+ * Proc filesystem directory entries.
+ */
+
+static int ftape_get_info(char *page, char **start, off_t off,
+ int count, int dummy)
+{
+ int dummy_eof;
+
+ return ftape_read_proc(page, start, off, count, &dummy_eof, NULL);
+}
+
+static struct proc_dir_entry proc_ftape = {
+ 0, /* low_ino */
+ sizeof("ftape")-1, /* namelen */
+ "ftape", /* name */
+ S_IFREG | S_IRUGO, /* mode */
+ 1, /* nlink */
+ 0, /* uid */
+ 0, /* gid */
+ 0, /* size */
+ &ftape_proc_inode_operations, /* ops */
+ ftape_get_info, /* get_info */
+ NULL, /* fill_inode */
+ NULL, /* next */
+ NULL, /* parent */
+ NULL, /* subdir */
+ NULL /* data */
+};
+
+/* Read ftape proc directory entry.
+ */
+
+#define PROC_BLOCK_SIZE PAGE_SIZE
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,0)
+static long ftape_proc_read(struct inode * inode, struct file * file,
+ char * buf, unsigned long nbytes)
+#else
+static int ftape_proc_read(struct inode * inode, struct file * file,
+ char * buf, int nbytes)
+#endif
+{
+ char *page;
+ int retval=0;
+ int eof=0;
+ int n, count;
+ char *start;
+ struct proc_dir_entry * dp;
+
+ if (nbytes < 0)
+ return -EINVAL;
+ dp = (struct proc_dir_entry *) inode->u.generic_ip;
+ if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+
+ while ((nbytes > 0) && !eof)
+ {
+ count = PROC_BLOCK_SIZE <= nbytes ? PROC_BLOCK_SIZE : nbytes;
+
+ start = NULL;
+ if (dp->get_info) {
+ /*
+ * Handle backwards compatibility with the old net
+ * routines.
+ *
+ * XXX What gives with the file->f_flags & O_ACCMODE
+ * test? Seems stupid to me....
+ */
+ n = dp->get_info(page, &start, file->f_pos, count,
+ (file->f_flags & O_ACCMODE) == O_RDWR);
+ if (n < count)
+ eof = 1;
+ } else
+ break;
+
+ if (!start) {
+ /*
+ * For proc files that are less than 4k
+ */
+ start = page + file->f_pos;
+ n -= file->f_pos;
+ if (n <= 0)
+ break;
+ if (n > count)
+ n = count;
+ }
+ if (n == 0)
+ break; /* End of file */
+ if (n < 0) {
+ if (retval == 0)
+ retval = n;
+ break;
+ }
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+ copy_to_user(buf, start, n);
+#else
+ memcpy_tofs(buf, start, n);
+#endif
+ file->f_pos += n; /* Move down the file */
+ nbytes -= n;
+ buf += n;
+ retval += n;
+ }
+ free_page((unsigned long) page);
+ return retval;
+}
+
+#else /* LINUX_VERSION_CODE < KERNEL_VER(2,1,28) */
+
+#define FT_PROC_REGISTER(parent, child) proc_register(parent, child)
+
+/*
+ * Proc filesystem directory entries.
+ */
+
+static struct proc_dir_entry proc_ftape = {
+ 0, /* low_ino */
+ sizeof("ftape")-1, /* namelen */
+ "ftape", /* name */
+ S_IFREG | S_IRUGO, /* mode */
+ 1, /* nlink */
+ 0, /* uid */
+ 0, /* gid */
+ 0, /* size */
+ NULL, /* ops */
+ NULL, /* get_info */
+ NULL, /* fill_inode */
+ NULL, /* next */
+ NULL, /* parent */
+ NULL, /* subdir */
+ NULL, /* data */
+ ftape_read_proc, /* read_proc */
+ NULL /* write_proc */
+};
+
+#endif
+
+static size_t get_driver_info(char *buf)
+{
+ const char *debug_level[] = { "bugs" ,
+ "errors",
+ "warnings",
+ "informational",
+ "noisy",
+ "program flow",
+ "fdc and dma",
+ "data flow",
+ "anything" };
+
+ return sprintf(buf,
+ "version : %s\n"
+ "used data rate: %d kbit/sec\n"
+ "dma memory : %d kb\n"
+ "debug messages: %s\n",
+ FTAPE_VERSION,
+ ft_data_rate,
+ FT_BUFF_SIZE * ft_nr_buffers >> 10,
+ debug_level[TRACE_LEVEL]);
+}
+
+static size_t get_tapedrive_info(char *buf)
+{
+ return sprintf(buf,
+ "vendor id : 0x%04x\n"
+ "drive name: %s\n"
+ "wind speed: %d ips\n"
+ "wakeup : %s\n"
+ "max. rate : %d kbit/sec\n",
+ ft_drive_type.vendor_id,
+ ft_drive_type.name,
+ ft_drive_type.speed,
+ ((ft_drive_type.wake_up == no_wake_up)
+ ? "No wakeup needed" :
+ ((ft_drive_type.wake_up == wake_up_colorado)
+ ? "Colorado" :
+ ((ft_drive_type.wake_up == wake_up_mountain)
+ ? "Mountain" :
+ ((ft_drive_type.wake_up == wake_up_insight)
+ ? "Motor on" :
+ "Unknown")))),
+ ft_drive_max_rate);
+}
+
+static size_t get_cartridge_info(char *buf)
+{
+ if (ftape_init_drive_needed) {
+ return sprintf(buf, "uninitialized\n");
+ }
+ if (ft_no_tape) {
+ return sprintf(buf, "no cartridge inserted\n");
+ }
+ return sprintf(buf,
+ "segments : %5d\n"
+ "tracks : %5d\n"
+ "length : %5dft\n"
+ "formatted : %3s\n"
+ "writable : %3s\n"
+ "QIC spec. : QIC-%s\n"
+ "fmt-code : %1d\n",
+ ft_segments_per_track,
+ ft_tracks_per_tape,
+ ftape_tape_len,
+ (ft_formatted == 1) ? "yes" : "no",
+ (ft_write_protected == 1) ? "no" : "yes",
+ ((ft_qic_std == QIC_TAPE_QIC40) ? "40" :
+ ((ft_qic_std == QIC_TAPE_QIC80) ? "80" :
+ ((ft_qic_std == QIC_TAPE_QIC3010) ? "3010" :
+ ((ft_qic_std == QIC_TAPE_QIC3020) ? "3020" :
+ "???")))),
+ ft_format_code);
+}
+
+static size_t get_controller_info(char *buf)
+{
+ const char *fdc_name[] = { "no fdc",
+ "i8272",
+ "i82077",
+ "i82077AA",
+ "Colorado FC-10 or FC-20",
+ "i82078",
+ "i82078_1" };
+
+ return sprintf(buf,
+ "FDC type : %s\n"
+ "FDC base : 0x%03x\n"
+ "FDC irq : %d\n"
+ "FDC dma : %d\n"
+ "FDC thr. : %d\n"
+ "max. rate : %d kbit/sec\n",
+ ft_mach2 ? "Mountain MACH-2" : fdc_name[fdc.type],
+ fdc.sra, fdc.irq, fdc.dma,
+ ft_fdc_threshold, ft_fdc_max_rate);
+}
+
+static size_t get_history_info(char *buf)
+{
+ size_t len;
+
+ len = sprintf(buf,
+ "\nFDC isr statistics\n"
+ " id_am_errors : %3d\n"
+ " id_crc_errors : %3d\n"
+ " data_am_errors : %3d\n"
+ " data_crc_errors : %3d\n"
+ " overrun_errors : %3d\n"
+ " no_data_errors : %3d\n"
+ " retries : %3d\n",
+ ft_history.id_am_errors, ft_history.id_crc_errors,
+ ft_history.data_am_errors, ft_history.data_crc_errors,
+ ft_history.overrun_errors, ft_history.no_data_errors,
+ ft_history.retries);
+ len += sprintf(buf + len,
+ "\nECC statistics\n"
+ " crc_errors : %3d\n"
+ " crc_failures : %3d\n"
+ " ecc_failures : %3d\n"
+ " sectors corrected: %3d\n",
+ ft_history.crc_errors, ft_history.crc_failures,
+ ft_history.ecc_failures, ft_history.corrected);
+ len += sprintf(buf + len,
+ "\ntape quality statistics\n"
+ " media defects : %3d\n",
+ ft_history.defects);
+ len += sprintf(buf + len,
+ "\ntape motion statistics\n"
+ " repositions : %3d\n",
+ ft_history.rewinds);
+ return len;
+}
+
+int ftape_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ char *ptr = page;
+ size_t len;
+
+ ptr += sprintf(ptr, "Kernel Driver\n\n");
+ ptr += get_driver_info(ptr);
+ ptr += sprintf(ptr, "\nTape Drive\n\n");
+ ptr += get_tapedrive_info(ptr);
+ ptr += sprintf(ptr, "\nFDC Controller\n\n");
+ ptr += get_controller_info(ptr);
+ ptr += sprintf(ptr, "\nTape Cartridge\n\n");
+ ptr += get_cartridge_info(ptr);
+ ptr += sprintf(ptr, "\nHistory Record\n\n");
+ ptr += get_history_info(ptr);
+
+ len = strlen(page);
+ *start = 0;
+ if (off+count >= len) {
+ *eof = 1;
+ } else {
+ *eof = 0;
+ }
+ return len;
+}
+
+__initfunc(int ftape_proc_init(void))
+{
+ return FT_PROC_REGISTER(&proc_root, &proc_ftape);
+}
+
+#ifdef MODULE
+void ftape_proc_destroy(void)
+{
+ proc_unregister(&proc_root, proc_ftape.low_ino);
+}
+#endif
+
+#endif /* defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS) */
diff --git a/drivers/char/ftape/lowlevel/ftape-proc.h b/drivers/char/ftape/lowlevel/ftape-proc.h
new file mode 100644
index 000000000..5673d5a41
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-proc.h
@@ -0,0 +1,37 @@
+#ifndef _FTAPE_PROC_H
+#define _FTAPE_PROC_H
+
+/*
+ * Copyright (C) 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-proc.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:20 $
+ *
+ * This file contains definitions for the procfs interface of the
+ * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ */
+
+#include <linux/proc_fs.h>
+
+extern struct proc_dir_entry proc_ftape;
+
+extern int ftape_proc_init(void);
+extern void ftape_proc_destroy(void);
+
+#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-read.c b/drivers/char/ftape/lowlevel/ftape-read.c
new file mode 100644
index 000000000..e67f099fe
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-read.c
@@ -0,0 +1,614 @@
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-read.c,v $
+ * $Revision: 1.6 $
+ * $Date: 1997/10/21 14:39:22 $
+ *
+ * This file contains the reading code
+ * for the QIC-117 floppy-tape driver for Linux.
+ *
+ */
+
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-ecc.h"
+#include "../lowlevel/ftape-bsm.h"
+
+/* Global vars.
+ */
+
+/* Local vars.
+ */
+
+void ftape_zap_read_buffers(void)
+{
+ int i;
+
+ for (i = 0; i < ft_nr_buffers; ++i) {
+/* changed to "fit" with dynamic allocation of tape_buffer. --khp */
+ ft_buffer[i]->status = waiting;
+ ft_buffer[i]->bytes = 0;
+ ft_buffer[i]->skip = 0;
+ ft_buffer[i]->retry = 0;
+ }
+/* ftape_reset_buffer(); */
+}
+
+static SectorMap convert_sector_map(buffer_struct * buff)
+{
+ int i = 0;
+ SectorMap bad_map = ftape_get_bad_sector_entry(buff->segment_id);
+ SectorMap src_map = buff->soft_error_map | buff->hard_error_map;
+ SectorMap dst_map = 0;
+ TRACE_FUN(ft_t_any);
+
+ if (bad_map || src_map) {
+ TRACE(ft_t_flow, "bad_map = 0x%08lx", (long) bad_map);
+ TRACE(ft_t_flow, "src_map = 0x%08lx", (long) src_map);
+ }
+ while (bad_map) {
+ while ((bad_map & 1) == 0) {
+ if (src_map & 1) {
+ dst_map |= (1 << i);
+ }
+ src_map >>= 1;
+ bad_map >>= 1;
+ ++i;
+ }
+ /* (bad_map & 1) == 1 */
+ src_map >>= 1;
+ bad_map >>= 1;
+ }
+ if (src_map) {
+ dst_map |= (src_map << i);
+ }
+ if (dst_map) {
+ TRACE(ft_t_flow, "dst_map = 0x%08lx", (long) dst_map);
+ }
+ TRACE_EXIT dst_map;
+}
+
+static int correct_and_copy_fraction(buffer_struct *buff, __u8 * destination,
+ int start, int size)
+{
+ struct memory_segment mseg;
+ int result;
+ SectorMap read_bad;
+ TRACE_FUN(ft_t_any);
+
+ mseg.read_bad = convert_sector_map(buff);
+ mseg.marked_bad = 0; /* not used... */
+ mseg.blocks = buff->bytes / FT_SECTOR_SIZE;
+ mseg.data = buff->address;
+ /* If there are no data sectors we can skip this segment.
+ */
+ if (mseg.blocks <= 3) {
+ TRACE_ABORT(0, ft_t_noise, "empty segment");
+ }
+ read_bad = mseg.read_bad;
+ ft_history.crc_errors += count_ones(read_bad);
+ result = ftape_ecc_correct_data(&mseg);
+ if (read_bad != 0 || mseg.corrected != 0) {
+ TRACE(ft_t_noise, "crc error map: 0x%08lx", (unsigned long)read_bad);
+ TRACE(ft_t_noise, "corrected map: 0x%08lx", (unsigned long)mseg.corrected);
+ ft_history.corrected += count_ones(mseg.corrected);
+ }
+ if (result == ECC_CORRECTED || result == ECC_OK) {
+ if (result == ECC_CORRECTED) {
+ TRACE(ft_t_info, "ecc corrected segment: %d", buff->segment_id);
+ }
+ if(start < 0) {
+ start= 0;
+ }
+ if((start+size) > ((mseg.blocks - 3) * FT_SECTOR_SIZE)) {
+ size = (mseg.blocks - 3) * FT_SECTOR_SIZE - start;
+ }
+ if (size < 0) {
+ size= 0;
+ }
+ if(size > 0) {
+ memcpy(destination + start, mseg.data + start, size);
+ }
+ if ((read_bad ^ mseg.corrected) & mseg.corrected) {
+ /* sectors corrected without crc errors set */
+ ft_history.crc_failures++;
+ }
+ TRACE_EXIT size; /* (mseg.blocks - 3) * FT_SECTOR_SIZE; */
+ } else {
+ ft_history.ecc_failures++;
+ TRACE_ABORT(-EAGAIN,
+ ft_t_err, "ecc failure on segment %d",
+ buff->segment_id);
+ }
+ TRACE_EXIT 0;
+}
+
+/* Read given segment into buffer at address.
+ */
+int ftape_read_segment_fraction(const int segment_id,
+ void *address,
+ const ft_read_mode_t read_mode,
+ const int start,
+ const int size)
+{
+ int result = 0;
+ int retry = 0;
+ int bytes_read = 0;
+ int read_done = 0;
+ TRACE_FUN(ft_t_flow);
+
+ ft_history.used |= 1;
+ TRACE(ft_t_data_flow, "segment_id = %d", segment_id);
+ if (ft_driver_state != reading) {
+ TRACE(ft_t_noise, "calling ftape_abort_operation");
+ TRACE_CATCH(ftape_abort_operation(),);
+ ftape_set_state(reading);
+ }
+ for(;;) {
+ buffer_struct *tail;
+ /* Allow escape from this loop on signal !
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ /* Search all full buffers for the first matching the
+ * wanted segment. Clear other buffers on the fly.
+ */
+ tail = ftape_get_buffer(ft_queue_tail);
+ while (!read_done && tail->status == done) {
+ /* Allow escape from this loop on signal !
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ if (tail->segment_id == segment_id) {
+ /* If out buffer is already full,
+ * return its contents.
+ */
+ TRACE(ft_t_flow, "found segment in cache: %d",
+ segment_id);
+ if (tail->deleted) {
+ /* Return a value that
+ * read_header_segment
+ * understands. As this
+ * should only occur when
+ * searching for the header
+ * segments it shouldn't be
+ * misinterpreted elsewhere.
+ */
+ TRACE_EXIT 0;
+ }
+ result = correct_and_copy_fraction(
+ tail,
+ address,
+ start,
+ size);
+ TRACE(ft_t_flow, "segment contains (bytes): %d",
+ result);
+ if (result < 0) {
+ if (result != -EAGAIN) {
+ TRACE_EXIT result;
+ }
+ /* keep read_done == 0, will
+ * trigger
+ * ftape_abort_operation
+ * because reading wrong
+ * segment.
+ */
+ TRACE(ft_t_err, "ecc failed, retry");
+ ++retry;
+ } else {
+ read_done = 1;
+ bytes_read = result;
+ }
+ } else {
+ TRACE(ft_t_flow,"zapping segment in cache: %d",
+ tail->segment_id);
+ }
+ tail->status = waiting;
+ tail = ftape_next_buffer(ft_queue_tail);
+ }
+ if (!read_done && tail->status == reading) {
+ if (tail->segment_id == segment_id) {
+ switch(ftape_wait_segment(reading)) {
+ case 0:
+ break;
+ case -EINTR:
+ TRACE_ABORT(-EINTR, ft_t_warn,
+ "interrupted by "
+ "non-blockable signal");
+ break;
+ default:
+ TRACE(ft_t_noise,
+ "wait_segment failed");
+ ftape_abort_operation();
+ ftape_set_state(reading);
+ break;
+ }
+ } else {
+ /* We're reading the wrong segment,
+ * stop runner.
+ */
+ TRACE(ft_t_noise, "reading wrong segment");
+ ftape_abort_operation();
+ ftape_set_state(reading);
+ }
+ }
+ /* should runner stop ?
+ */
+ if (ft_runner_status == aborting) {
+ buffer_struct *head = ftape_get_buffer(ft_queue_head);
+ switch(head->status) {
+ case error:
+ ft_history.defects +=
+ count_ones(head->hard_error_map);
+ case reading:
+ head->status = waiting;
+ break;
+ default:
+ break;
+ }
+ TRACE_CATCH(ftape_dumb_stop(),);
+ } else {
+ /* If just passed last segment on tape: wait
+ * for BOT or EOT mark. Sets ft_runner_status to
+ * idle if at lEOT and successful
+ */
+ TRACE_CATCH(ftape_handle_logical_eot(),);
+ }
+ /* If we got a segment: quit, or else retry up to limit.
+ *
+ * If segment to read is empty, do not start runner for it,
+ * but wait for next read call.
+ */
+ if (read_done ||
+ ftape_get_bad_sector_entry(segment_id) == EMPTY_SEGMENT ) {
+ /* bytes_read = 0; should still be zero */
+ TRACE_EXIT bytes_read;
+
+ }
+ if (retry > FT_RETRIES_ON_ECC_ERROR) {
+ ft_history.defects++;
+ TRACE_ABORT(-ENODATA, ft_t_err,
+ "too many retries on ecc failure");
+ }
+ /* Now at least one buffer is empty !
+ * Restart runner & tape if needed.
+ */
+ TRACE(ft_t_any, "head: %d, tail: %d, ft_runner_status: %d",
+ ftape_buffer_id(ft_queue_head),
+ ftape_buffer_id(ft_queue_tail),
+ ft_runner_status);
+ TRACE(ft_t_any, "buffer[].status, [head]: %d, [tail]: %d",
+ ftape_get_buffer(ft_queue_head)->status,
+ ftape_get_buffer(ft_queue_tail)->status);
+ tail = ftape_get_buffer(ft_queue_tail);
+ if (tail->status == waiting) {
+ buffer_struct *head = ftape_get_buffer(ft_queue_head);
+
+ ftape_setup_new_segment(head, segment_id, -1);
+ if (read_mode == FT_RD_SINGLE) {
+ /* disable read-ahead */
+ head->next_segment = 0;
+ }
+ ftape_calc_next_cluster(head);
+ if (ft_runner_status == idle) {
+ result = ftape_start_tape(segment_id,
+ head->sector_offset);
+ if (result < 0) {
+ TRACE_ABORT(result, ft_t_err, "Error: "
+ "segment %d unreachable",
+ segment_id);
+ }
+ }
+ head->status = reading;
+ fdc_setup_read_write(head, FDC_READ);
+ }
+ }
+ /* not reached */
+ TRACE_EXIT -EIO;
+}
+
+int ftape_read_header_segment(__u8 *address)
+{
+ int result;
+ int header_segment;
+ int first_failed = 0;
+ int status;
+ TRACE_FUN(ft_t_flow);
+
+ ft_used_header_segment = -1;
+ TRACE_CATCH(ftape_report_drive_status(&status),);
+ TRACE(ft_t_flow, "reading...");
+ /* We're looking for the first header segment.
+ * A header segment cannot contain bad sectors, therefor at the
+ * tape start, segments with bad sectors are (according to QIC-40/80)
+ * written with deleted data marks and must be skipped.
+ */
+ memset(address, '\0', (FT_SECTORS_PER_SEGMENT - 3) * FT_SECTOR_SIZE);
+ result = 0;
+#define HEADER_SEGMENT_BOUNDARY 68 /* why not 42? */
+ for (header_segment = 0;
+ header_segment < HEADER_SEGMENT_BOUNDARY && result == 0;
+ ++header_segment) {
+ /* Set no read-ahead, the isr will force read-ahead whenever
+ * it encounters deleted data !
+ */
+ result = ftape_read_segment(header_segment,
+ address,
+ FT_RD_SINGLE);
+ if (result < 0 && !first_failed) {
+ TRACE(ft_t_err, "header segment damaged, trying backup");
+ first_failed = 1;
+ result = 0; /* force read of next (backup) segment */
+ }
+ }
+ if (result < 0 || header_segment >= HEADER_SEGMENT_BOUNDARY) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "no readable header segment found");
+ }
+ TRACE_CATCH(ftape_abort_operation(),);
+ ft_used_header_segment = header_segment;
+ result = ftape_decode_header_segment(address);
+ TRACE_EXIT result;
+}
+
+int ftape_decode_header_segment(__u8 *address)
+{
+ unsigned int max_floppy_side;
+ unsigned int max_floppy_track;
+ unsigned int max_floppy_sector;
+ unsigned int new_tape_len;
+ TRACE_FUN(ft_t_flow);
+
+ if (GET4(address, FT_SIGNATURE) == FT_D2G_MAGIC) {
+ /* Ditto 2GB header segment. They encrypt the bad sector map.
+ * We decrypt it and store them in normal format.
+ * I hope this is correct.
+ */
+ int i;
+ TRACE(ft_t_warn,
+ "Found Ditto 2GB tape, "
+ "trying to decrypt bad sector map");
+ for (i=256; i < 29 * FT_SECTOR_SIZE; i++) {
+ address[i] = ~(address[i] - (i&0xff));
+ }
+ PUT4(address, 0,FT_HSEG_MAGIC);
+ } else if (GET4(address, FT_SIGNATURE) != FT_HSEG_MAGIC) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "wrong signature in header segment");
+ }
+ ft_format_code = (ft_format_type) address[FT_FMT_CODE];
+ if (ft_format_code != fmt_big) {
+ ft_header_segment_1 = GET2(address, FT_HSEG_1);
+ ft_header_segment_2 = GET2(address, FT_HSEG_2);
+ ft_first_data_segment = GET2(address, FT_FRST_SEG);
+ ft_last_data_segment = GET2(address, FT_LAST_SEG);
+ } else {
+ ft_header_segment_1 = GET4(address, FT_6_HSEG_1);
+ ft_header_segment_2 = GET4(address, FT_6_HSEG_2);
+ ft_first_data_segment = GET4(address, FT_6_FRST_SEG);
+ ft_last_data_segment = GET4(address, FT_6_LAST_SEG);
+ }
+ TRACE(ft_t_noise, "first data segment: %d", ft_first_data_segment);
+ TRACE(ft_t_noise, "last data segment: %d", ft_last_data_segment);
+ TRACE(ft_t_noise, "header segments are %d and %d",
+ ft_header_segment_1, ft_header_segment_2);
+
+ /* Verify tape parameters...
+ * QIC-40/80 spec: tape_parameters:
+ *
+ * segments-per-track segments_per_track
+ * tracks-per-cartridge tracks_per_tape
+ * max-floppy-side (segments_per_track *
+ * tracks_per_tape - 1) /
+ * ftape_segments_per_head
+ * max-floppy-track ftape_segments_per_head /
+ * ftape_segments_per_cylinder - 1
+ * max-floppy-sector ftape_segments_per_cylinder *
+ * FT_SECTORS_PER_SEGMENT
+ */
+ ft_segments_per_track = GET2(address, FT_SPT);
+ ft_tracks_per_tape = address[FT_TPC];
+ max_floppy_side = address[FT_FHM];
+ max_floppy_track = address[FT_FTM];
+ max_floppy_sector = address[FT_FSM];
+ TRACE(ft_t_noise, "(fmt/spt/tpc/fhm/ftm/fsm) = %d/%d/%d/%d/%d/%d",
+ ft_format_code, ft_segments_per_track, ft_tracks_per_tape,
+ max_floppy_side, max_floppy_track, max_floppy_sector);
+ new_tape_len = ftape_tape_len;
+ switch (ft_format_code) {
+ case fmt_425ft:
+ new_tape_len = 425;
+ break;
+ case fmt_normal:
+ if (ftape_tape_len == 0) { /* otherwise 307 ft */
+ new_tape_len = 205;
+ }
+ break;
+ case fmt_1100ft:
+ new_tape_len = 1100;
+ break;
+ case fmt_var:{
+ int segments_per_1000_inch = 1; /* non-zero default for switch */
+ switch (ft_qic_std) {
+ case QIC_TAPE_QIC40:
+ segments_per_1000_inch = 332;
+ break;
+ case QIC_TAPE_QIC80:
+ segments_per_1000_inch = 488;
+ break;
+ case QIC_TAPE_QIC3010:
+ segments_per_1000_inch = 730;
+ break;
+ case QIC_TAPE_QIC3020:
+ segments_per_1000_inch = 1430;
+ break;
+ }
+ new_tape_len = (1000 * ft_segments_per_track +
+ (segments_per_1000_inch - 1)) / segments_per_1000_inch;
+ break;
+ }
+ case fmt_big:{
+ int segments_per_1000_inch = 1; /* non-zero default for switch */
+ switch (ft_qic_std) {
+ case QIC_TAPE_QIC40:
+ segments_per_1000_inch = 332;
+ break;
+ case QIC_TAPE_QIC80:
+ segments_per_1000_inch = 488;
+ break;
+ case QIC_TAPE_QIC3010:
+ segments_per_1000_inch = 730;
+ break;
+ case QIC_TAPE_QIC3020:
+ segments_per_1000_inch = 1430;
+ break;
+ default:
+ TRACE_ABORT(-EIO, ft_t_bug,
+ "%x QIC-standard with fmt-code %d, please report",
+ ft_qic_std, ft_format_code);
+ }
+ new_tape_len = ((1000 * ft_segments_per_track +
+ (segments_per_1000_inch - 1)) /
+ segments_per_1000_inch);
+ break;
+ }
+ default:
+ TRACE_ABORT(-EIO, ft_t_err,
+ "unknown tape format, please report !");
+ }
+ if (new_tape_len != ftape_tape_len) {
+ ftape_tape_len = new_tape_len;
+ TRACE(ft_t_info, "calculated tape length is %d ft",
+ ftape_tape_len);
+ ftape_calc_timeouts(ft_qic_std, ft_data_rate, ftape_tape_len);
+ }
+ if (ft_segments_per_track == 0 && ft_tracks_per_tape == 0 &&
+ max_floppy_side == 0 && max_floppy_track == 0 &&
+ max_floppy_sector == 0) {
+ /* QIC-40 Rev E and earlier has no values in the header.
+ */
+ ft_segments_per_track = 68;
+ ft_tracks_per_tape = 20;
+ max_floppy_side = 1;
+ max_floppy_track = 169;
+ max_floppy_sector = 128;
+ }
+ /* This test will compensate for the wrong parameter on tapes
+ * formatted by Conner software.
+ */
+ if (ft_segments_per_track == 150 &&
+ ft_tracks_per_tape == 28 &&
+ max_floppy_side == 7 &&
+ max_floppy_track == 149 &&
+ max_floppy_sector == 128) {
+TRACE(ft_t_info, "the famous CONNER bug: max_floppy_side off by one !");
+ max_floppy_side = 6;
+ }
+ /* These tests will compensate for the wrong parameter on tapes
+ * formatted by ComByte Windows software.
+ *
+ * First, for 205 foot tapes
+ */
+ if (ft_segments_per_track == 100 &&
+ ft_tracks_per_tape == 28 &&
+ max_floppy_side == 9 &&
+ max_floppy_track == 149 &&
+ max_floppy_sector == 128) {
+TRACE(ft_t_info, "the ComByte bug: max_floppy_side incorrect!");
+ max_floppy_side = 4;
+ }
+ /* Next, for 307 foot tapes. */
+ if (ft_segments_per_track == 150 &&
+ ft_tracks_per_tape == 28 &&
+ max_floppy_side == 9 &&
+ max_floppy_track == 149 &&
+ max_floppy_sector == 128) {
+TRACE(ft_t_info, "the ComByte bug: max_floppy_side incorrect!");
+ max_floppy_side = 6;
+ }
+ /* This test will compensate for the wrong parameter on tapes
+ * formatted by Colorado Windows software.
+ */
+ if (ft_segments_per_track == 150 &&
+ ft_tracks_per_tape == 28 &&
+ max_floppy_side == 6 &&
+ max_floppy_track == 150 &&
+ max_floppy_sector == 128) {
+TRACE(ft_t_info, "the famous Colorado bug: max_floppy_track off by one !");
+ max_floppy_track = 149;
+ }
+ ftape_segments_per_head = ((max_floppy_sector/FT_SECTORS_PER_SEGMENT) *
+ (max_floppy_track + 1));
+ /* This test will compensate for some bug reported by Dima
+ * Brodsky. Seems to be a Colorado bug, either. (freebee
+ * Imation tape shipped together with Colorado T3000
+ */
+ if ((ft_format_code == fmt_var || ft_format_code == fmt_big) &&
+ ft_tracks_per_tape == 50 &&
+ max_floppy_side == 54 &&
+ max_floppy_track == 255 &&
+ max_floppy_sector == 128) {
+TRACE(ft_t_info, "the famous ??? bug: max_floppy_track off by one !");
+ max_floppy_track = 254;
+ }
+ /*
+ * Verify drive_configuration with tape parameters
+ */
+ if (ftape_segments_per_head == 0 || ftape_segments_per_cylinder == 0 ||
+ ((ft_segments_per_track * ft_tracks_per_tape - 1) / ftape_segments_per_head
+ != max_floppy_side) ||
+ (ftape_segments_per_head / ftape_segments_per_cylinder - 1 != max_floppy_track) ||
+ (ftape_segments_per_cylinder * FT_SECTORS_PER_SEGMENT != max_floppy_sector)
+#ifdef TESTING
+ || ((ft_format_code == fmt_var || ft_format_code == fmt_big) &&
+ (max_floppy_track != 254 || max_floppy_sector != 128))
+#endif
+ ) {
+ TRACE(ft_t_err,"Tape parameters inconsistency, please report");
+ TRACE(ft_t_err, "reported = %d/%d/%d/%d/%d/%d",
+ ft_format_code,
+ ft_segments_per_track,
+ ft_tracks_per_tape,
+ max_floppy_side,
+ max_floppy_track,
+ max_floppy_sector);
+ TRACE(ft_t_err, "required = %d/%d/%d/%d/%d/%d",
+ ft_format_code,
+ ft_segments_per_track,
+ ft_tracks_per_tape,
+ ((ft_segments_per_track * ft_tracks_per_tape -1) /
+ ftape_segments_per_head ),
+ (ftape_segments_per_head /
+ ftape_segments_per_cylinder - 1 ),
+ (ftape_segments_per_cylinder * FT_SECTORS_PER_SEGMENT));
+ TRACE_EXIT -EIO;
+ }
+ ftape_extract_bad_sector_map(address);
+ TRACE_EXIT 0;
+}
diff --git a/drivers/char/ftape/ftape-read.h b/drivers/char/ftape/lowlevel/ftape-read.h
index aeca35950..069f99f2a 100644
--- a/drivers/char/ftape/ftape-read.h
+++ b/drivers/char/ftape/lowlevel/ftape-read.h
@@ -2,7 +2,8 @@
#define _FTAPE_READ_H
/*
- * Copyright (C) 1994-1995 Bas Laarhoven.
+ * Copyright (C) 1994-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,27 +20,32 @@
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-read.h,v $
- $Author: bas $
- *
- $Revision: 1.13 $
- $Date: 1995/05/10 16:09:36 $
- $State: Beta $
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-read.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:22 $
*
* This file contains the definitions for the read functions
* for the QIC-117 floppy-tape driver for Linux.
*
*/
-/* ftape-read.c defined global vars.
- */
-
/* ftape-read.c defined global functions.
*/
-extern int _ftape_read(char *buff, int req_len);
-extern int read_header_segment(byte * address);
-extern int read_segment(unsigned segment, byte * address, int *eof_mark,
- int read_ahead);
+typedef enum {
+ FT_RD_SINGLE = 0,
+ FT_RD_AHEAD = 1,
+} ft_read_mode_t;
+
+extern int ftape_read_header_segment(__u8 *address);
+extern int ftape_decode_header_segment(__u8 *address);
+extern int ftape_read_segment_fraction(const int segment,
+ void *address,
+ const ft_read_mode_t read_mode,
+ const int start,
+ const int size);
+#define ftape_read_segment(segment, address, read_mode) \
+ ftape_read_segment_fraction(segment, address, read_mode, \
+ 0, FT_SEGMENT_SIZE)
extern void ftape_zap_read_buffers(void);
#endif /* _FTAPE_READ_H */
diff --git a/drivers/char/ftape/lowlevel/ftape-rw.c b/drivers/char/ftape/lowlevel/ftape-rw.c
new file mode 100644
index 000000000..e3e0243f0
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-rw.c
@@ -0,0 +1,1091 @@
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-rw.c,v $
+ * $Revision: 1.7 $
+ * $Date: 1997/10/28 14:26:49 $
+ *
+ * This file contains some common code for the segment read and
+ * segment write routines for the QIC-117 floppy-tape driver for
+ * Linux.
+ */
+
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/ftape-init.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-ecc.h"
+#include "../lowlevel/ftape-bsm.h"
+
+/* Global vars.
+ */
+int ft_nr_buffers = 0;
+buffer_struct *ft_buffer[FT_MAX_NR_BUFFERS] = {NULL, };
+static volatile int ft_head;
+static volatile int ft_tail; /* not volatile but need same type as head */
+int fdc_setup_error;
+location_record ft_location = {-1, 0};
+volatile int ftape_tape_running = 0;
+
+/* Local vars.
+ */
+static int overrun_count_offset = 0;
+static int inhibit_correction = 0;
+
+/* maxmimal allowed overshoot when fast seeking
+ */
+#define OVERSHOOT_LIMIT 10
+
+/* Increment cyclic buffer nr.
+ */
+buffer_struct *ftape_next_buffer(ft_buffer_queue_t pos)
+{
+ switch (pos) {
+ case ft_queue_head:
+ if (++ft_head >= ft_nr_buffers) {
+ ft_head = 0;
+ }
+ return ft_buffer[ft_head];
+ case ft_queue_tail:
+ if (++ft_tail >= ft_nr_buffers) {
+ ft_tail = 0;
+ }
+ return ft_buffer[ft_tail];
+ default:
+ return NULL;
+ }
+}
+int ftape_buffer_id(ft_buffer_queue_t pos)
+{
+ switch(pos) {
+ case ft_queue_head: return ft_head;
+ case ft_queue_tail: return ft_tail;
+ default: return -1;
+ }
+}
+buffer_struct *ftape_get_buffer(ft_buffer_queue_t pos)
+{
+ switch(pos) {
+ case ft_queue_head: return ft_buffer[ft_head];
+ case ft_queue_tail: return ft_buffer[ft_tail];
+ default: return NULL;
+ }
+}
+void ftape_reset_buffer(void)
+{
+ ft_head = ft_tail = 0;
+}
+
+buffer_state_enum ftape_set_state(buffer_state_enum new_state)
+{
+ buffer_state_enum old_state = ft_driver_state;
+
+ ft_driver_state = new_state;
+ return old_state;
+}
+/* Calculate Floppy Disk Controller and DMA parameters for a segment.
+ * head: selects buffer struct in array.
+ * offset: number of physical sectors to skip (including bad ones).
+ * count: number of physical sectors to handle (including bad ones).
+ */
+static int setup_segment(buffer_struct * buff,
+ int segment_id,
+ unsigned int sector_offset,
+ unsigned int sector_count,
+ int retry)
+{
+ SectorMap offset_mask;
+ SectorMap mask;
+ TRACE_FUN(ft_t_any);
+
+ buff->segment_id = segment_id;
+ buff->sector_offset = sector_offset;
+ buff->remaining = sector_count;
+ buff->head = segment_id / ftape_segments_per_head;
+ buff->cyl = (segment_id % ftape_segments_per_head) / ftape_segments_per_cylinder;
+ buff->sect = (segment_id % ftape_segments_per_cylinder) * FT_SECTORS_PER_SEGMENT + 1;
+ buff->deleted = 0;
+ offset_mask = (1 << buff->sector_offset) - 1;
+ mask = ftape_get_bad_sector_entry(segment_id) & offset_mask;
+ while (mask) {
+ if (mask & 1) {
+ offset_mask >>= 1; /* don't count bad sector */
+ }
+ mask >>= 1;
+ }
+ buff->data_offset = count_ones(offset_mask); /* good sectors to skip */
+ buff->ptr = buff->address + buff->data_offset * FT_SECTOR_SIZE;
+ TRACE(ft_t_flow, "data offset = %d sectors", buff->data_offset);
+ if (retry) {
+ buff->soft_error_map &= offset_mask; /* keep skipped part */
+ } else {
+ buff->hard_error_map = buff->soft_error_map = 0;
+ }
+ buff->bad_sector_map = ftape_get_bad_sector_entry(buff->segment_id);
+ if (buff->bad_sector_map != 0) {
+ TRACE(ft_t_noise, "segment: %d, bad sector map: %08lx",
+ buff->segment_id, (long)buff->bad_sector_map);
+ } else {
+ TRACE(ft_t_flow, "segment: %d", buff->segment_id);
+ }
+ if (buff->sector_offset > 0) {
+ buff->bad_sector_map >>= buff->sector_offset;
+ }
+ if (buff->sector_offset != 0 || buff->remaining != FT_SECTORS_PER_SEGMENT) {
+ TRACE(ft_t_flow, "sector offset = %d, count = %d",
+ buff->sector_offset, buff->remaining);
+ }
+ /* Segments with 3 or less sectors are not written with valid
+ * data because there is no space left for the ecc. The
+ * data written is whatever happens to be in the buffer.
+ * Reading such a segment will return a zero byte-count.
+ * To allow us to read/write segments with all bad sectors
+ * we fake one readable sector in the segment. This
+ * prevents having to handle these segments in a very
+ * special way. It is not important if the reading of this
+ * bad sector fails or not (the data is ignored). It is
+ * only read to keep the driver running.
+ *
+ * The QIC-40/80 spec. has no information on how to handle
+ * this case, so this is my interpretation.
+ */
+ if (buff->bad_sector_map == EMPTY_SEGMENT) {
+ TRACE(ft_t_flow, "empty segment %d, fake first sector good",
+ buff->segment_id);
+ if (buff->ptr != buff->address) {
+ TRACE(ft_t_bug, "This is a bug: %p/%p",
+ buff->ptr, buff->address);
+ }
+ buff->bad_sector_map = FAKE_SEGMENT;
+ }
+ fdc_setup_error = 0;
+ buff->next_segment = segment_id + 1;
+ TRACE_EXIT 0;
+}
+
+/* Calculate Floppy Disk Controller and DMA parameters for a new segment.
+ */
+int ftape_setup_new_segment(buffer_struct * buff, int segment_id, int skip)
+{
+ int result = 0;
+ static int old_segment_id = -1;
+ static buffer_state_enum old_ft_driver_state = idle;
+ int retry = 0;
+ unsigned offset = 0;
+ int count = FT_SECTORS_PER_SEGMENT;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_flow, "%s segment %d (old = %d)",
+ (ft_driver_state == reading || ft_driver_state == verifying)
+ ? "reading" : "writing",
+ segment_id, old_segment_id);
+ if (ft_driver_state != old_ft_driver_state) { /* when verifying */
+ old_segment_id = -1;
+ old_ft_driver_state = ft_driver_state;
+ }
+ if (segment_id == old_segment_id) {
+ ++buff->retry;
+ ++ft_history.retries;
+ TRACE(ft_t_flow, "setting up for retry nr %d", buff->retry);
+ retry = 1;
+ if (skip && buff->skip > 0) { /* allow skip on retry */
+ offset = buff->skip;
+ count -= offset;
+ TRACE(ft_t_flow, "skipping %d sectors", offset);
+ }
+ } else {
+ buff->retry = 0;
+ buff->skip = 0;
+ old_segment_id = segment_id;
+ }
+ result = setup_segment(buff, segment_id, offset, count, retry);
+ TRACE_EXIT result;
+}
+
+/* Determine size of next cluster of good sectors.
+ */
+int ftape_calc_next_cluster(buffer_struct * buff)
+{
+ /* Skip bad sectors.
+ */
+ while (buff->remaining > 0 && (buff->bad_sector_map & 1) != 0) {
+ buff->bad_sector_map >>= 1;
+ ++buff->sector_offset;
+ --buff->remaining;
+ }
+ /* Find next cluster of good sectors
+ */
+ if (buff->bad_sector_map == 0) { /* speed up */
+ buff->sector_count = buff->remaining;
+ } else {
+ SectorMap map = buff->bad_sector_map;
+
+ buff->sector_count = 0;
+ while (buff->sector_count < buff->remaining && (map & 1) == 0) {
+ ++buff->sector_count;
+ map >>= 1;
+ }
+ }
+ return buff->sector_count;
+}
+
+/* if just passed the last segment on a track, wait for BOT
+ * or EOT mark.
+ */
+int ftape_handle_logical_eot(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (ft_runner_status == logical_eot) {
+ int status;
+
+ TRACE(ft_t_noise, "tape at logical EOT");
+ TRACE_CATCH(ftape_ready_wait(ftape_timeout.seek, &status),);
+ if ((status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) == 0) {
+ TRACE_ABORT(-EIO, ft_t_err, "eot/bot not reached");
+ }
+ ft_runner_status = end_of_tape;
+ }
+ if (ft_runner_status == end_of_tape) {
+ TRACE(ft_t_noise, "runner stopped because of logical EOT");
+ ft_runner_status = idle;
+ }
+ TRACE_EXIT 0;
+}
+
+static int check_bot_eot(int status)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) {
+ ft_location.bot = ((ft_location.track & 1) == 0 ?
+ (status & QIC_STATUS_AT_BOT) != 0:
+ (status & QIC_STATUS_AT_EOT) != 0);
+ ft_location.eot = !ft_location.bot;
+ ft_location.segment = (ft_location.track +
+ (ft_location.bot ? 0 : 1)) * ft_segments_per_track - 1;
+ ft_location.sector = -1;
+ ft_location.known = 1;
+ TRACE(ft_t_flow, "tape at logical %s",
+ ft_location.bot ? "bot" : "eot");
+ TRACE(ft_t_flow, "segment = %d", ft_location.segment);
+ } else {
+ ft_location.known = 0;
+ }
+ TRACE_EXIT ft_location.known;
+}
+
+/* Read Id of first sector passing tape head.
+ */
+int ftape_read_id(void)
+{
+ int status;
+ __u8 out[2];
+ TRACE_FUN(ft_t_any);
+
+ /* Assume tape is running on entry, be able to handle
+ * situation where it stopped or is stopping.
+ */
+ ft_location.known = 0; /* default is location not known */
+ out[0] = FDC_READID;
+ out[1] = ft_drive_sel;
+ TRACE_CATCH(fdc_command(out, 2),);
+ switch (fdc_interrupt_wait(20 * FT_SECOND)) {
+ case 0:
+ if (fdc_sect == 0) {
+ if (ftape_report_drive_status(&status) >= 0 &&
+ (status & QIC_STATUS_READY)) {
+ ftape_tape_running = 0;
+ TRACE(ft_t_flow, "tape has stopped");
+ check_bot_eot(status);
+ }
+ } else {
+ ft_location.known = 1;
+ ft_location.segment = (ftape_segments_per_head
+ * fdc_head
+ + ftape_segments_per_cylinder
+ * fdc_cyl
+ + (fdc_sect - 1)
+ / FT_SECTORS_PER_SEGMENT);
+ ft_location.sector = ((fdc_sect - 1)
+ % FT_SECTORS_PER_SEGMENT);
+ ft_location.eot = ft_location.bot = 0;
+ }
+ break;
+ case -ETIME:
+ /* Didn't find id on tape, must be near end: Wait
+ * until stopped.
+ */
+ if (ftape_ready_wait(FT_FOREVER, &status) >= 0) {
+ ftape_tape_running = 0;
+ TRACE(ft_t_flow, "tape has stopped");
+ check_bot_eot(status);
+ }
+ break;
+ default:
+ /* Interrupted or otherwise failing
+ * fdc_interrupt_wait()
+ */
+ TRACE(ft_t_err, "fdc_interrupt_wait failed");
+ break;
+ }
+ if (!ft_location.known) {
+ TRACE_ABORT(-EIO, ft_t_flow, "no id found");
+ }
+ if (ft_location.sector == 0) {
+ TRACE(ft_t_flow, "passing segment %d/%d",
+ ft_location.segment, ft_location.sector);
+ } else {
+ TRACE(ft_t_fdc_dma, "passing segment %d/%d",
+ ft_location.segment, ft_location.sector);
+ }
+ TRACE_EXIT 0;
+}
+
+static int logical_forward(void)
+{
+ ftape_tape_running = 1;
+ return ftape_command(QIC_LOGICAL_FORWARD);
+}
+
+int ftape_stop_tape(int *pstatus)
+{
+ int retry = 0;
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ do {
+ result = ftape_command_wait(QIC_STOP_TAPE,
+ ftape_timeout.stop, pstatus);
+ if (result == 0) {
+ if ((*pstatus & QIC_STATUS_READY) == 0) {
+ result = -EIO;
+ } else {
+ ftape_tape_running = 0;
+ }
+ }
+ } while (result < 0 && ++retry <= 3);
+ if (result < 0) {
+ TRACE(ft_t_err, "failed ! (fatal)");
+ }
+ TRACE_EXIT result;
+}
+
+int ftape_dumb_stop(void)
+{
+ int result;
+ int status;
+ TRACE_FUN(ft_t_flow);
+
+ /* Abort current fdc operation if it's busy (probably read
+ * or write operation pending) with a reset.
+ */
+ if (fdc_ready_wait(100 /* usec */) < 0) {
+ TRACE(ft_t_noise, "aborting fdc operation");
+ fdc_reset();
+ }
+ /* Reading id's after the last segment on a track may fail
+ * but eventually the drive will become ready (logical eot).
+ */
+ result = ftape_report_drive_status(&status);
+ ft_location.known = 0;
+ do {
+ if (result == 0 && status & QIC_STATUS_READY) {
+ /* Tape is not running any more.
+ */
+ TRACE(ft_t_noise, "tape already halted");
+ check_bot_eot(status);
+ ftape_tape_running = 0;
+ } else if (ftape_tape_running) {
+ /* Tape is (was) still moving.
+ */
+#ifdef TESTING
+ ftape_read_id();
+#endif
+ result = ftape_stop_tape(&status);
+ } else {
+ /* Tape not yet ready but stopped.
+ */
+ result = ftape_ready_wait(ftape_timeout.pause,&status);
+ }
+ } while (ftape_tape_running && (current->signal & _NEVER_BLOCK) == 0);
+#ifndef TESTING
+ ft_location.known = 0;
+#endif
+ if (ft_runner_status == aborting || ft_runner_status == do_abort) {
+ ft_runner_status = idle;
+ }
+ TRACE_EXIT result;
+}
+
+/* Wait until runner has finished tail buffer.
+ *
+ */
+int ftape_wait_segment(buffer_state_enum state)
+{
+ int status;
+ int result = 0;
+ TRACE_FUN(ft_t_flow);
+
+ while (ft_buffer[ft_tail]->status == state) {
+ TRACE(ft_t_flow, "state: %d", ft_buffer[ft_tail]->status);
+ /* First buffer still being worked on, wait up to timeout.
+ *
+ * Note: we check two times for being killed. 50
+ * seconds are quite long. Note that
+ * fdc_interrupt_wait() is not killable by any
+ * means. ftape_read_segment() wants us to return
+ * -EINTR in case of a signal.
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ result = fdc_interrupt_wait(50 * FT_SECOND);
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ if (result < 0) {
+ TRACE_ABORT(result,
+ ft_t_err, "fdc_interrupt_wait failed");
+ }
+ if (fdc_setup_error) {
+ /* recover... FIXME */
+ TRACE_ABORT(-EIO, ft_t_err, "setup error");
+ }
+ }
+ if (ft_buffer[ft_tail]->status != error) {
+ TRACE_EXIT 0;
+ }
+ TRACE_CATCH(ftape_report_drive_status(&status),);
+ TRACE(ft_t_noise, "ftape_report_drive_status: 0x%02x", status);
+ if ((status & QIC_STATUS_READY) &&
+ (status & QIC_STATUS_ERROR)) {
+ unsigned int error;
+ qic117_cmd_t command;
+
+ /* Report and clear error state.
+ * In case the drive can't operate at the selected
+ * rate, select the next lower data rate.
+ */
+ ftape_report_error(&error, &command, 1);
+ if (error == 31 && command == QIC_LOGICAL_FORWARD) {
+ /* drive does not accept this data rate */
+ if (ft_data_rate > 250) {
+ TRACE(ft_t_info,
+ "Probable data rate conflict");
+ TRACE(ft_t_info,
+ "Lowering data rate to %d Kbps",
+ ft_data_rate / 2);
+ ftape_half_data_rate();
+ if (ft_buffer[ft_tail]->retry > 0) {
+ /* give it a chance */
+ --ft_buffer[ft_tail]->retry;
+ }
+ } else {
+ /* no rate is accepted... */
+ TRACE(ft_t_err, "We're dead :(");
+ }
+ } else {
+ TRACE(ft_t_err, "Unknown error");
+ }
+ TRACE_EXIT -EIO; /* g.p. error */
+ }
+ TRACE_EXIT 0;
+}
+
+/* forward */ static int seek_forward(int segment_id, int fast);
+
+static int fast_seek(int count, int reverse)
+{
+ int result = 0;
+ int status;
+ TRACE_FUN(ft_t_flow);
+
+ if (count > 0) {
+ /* If positioned at begin or end of tape, fast seeking needs
+ * special treatment.
+ * Starting from logical bot needs a (slow) seek to the first
+ * segment before the high speed seek. Most drives do this
+ * automatically but some older don't, so we treat them
+ * all the same.
+ * Starting from logical eot is even more difficult because
+ * we cannot (slow) reverse seek to the last segment.
+ * TO BE IMPLEMENTED.
+ */
+ inhibit_correction = 0;
+ if (ft_location.known &&
+ ((ft_location.bot && !reverse) ||
+ (ft_location.eot && reverse))) {
+ if (!reverse) {
+ /* (slow) skip to first segment on a track
+ */
+ seek_forward(ft_location.track * ft_segments_per_track, 0);
+ --count;
+ } else {
+ /* When seeking backwards from
+ * end-of-tape the number of erased
+ * gaps found seems to be higher than
+ * expected. Therefor the drive must
+ * skip some more segments than
+ * calculated, but we don't know how
+ * many. Thus we will prevent the
+ * re-calculation of offset and
+ * overshoot when seeking backwards.
+ */
+ inhibit_correction = 1;
+ count += 3; /* best guess */
+ }
+ }
+ } else {
+ TRACE(ft_t_flow, "warning: zero or negative count: %d", count);
+ }
+ if (count > 0) {
+ int i;
+ int nibbles = count > 255 ? 3 : 2;
+
+ if (count > 4095) {
+ TRACE(ft_t_noise, "skipping clipped at 4095 segment");
+ count = 4095;
+ }
+ /* Issue this tape command first. */
+ if (!reverse) {
+ TRACE(ft_t_noise, "skipping %d segment(s)", count);
+ result = ftape_command(nibbles == 3 ?
+ QIC_SKIP_EXTENDED_FORWARD : QIC_SKIP_FORWARD);
+ } else {
+ TRACE(ft_t_noise, "backing up %d segment(s)", count);
+ result = ftape_command(nibbles == 3 ?
+ QIC_SKIP_EXTENDED_REVERSE : QIC_SKIP_REVERSE);
+ }
+ if (result < 0) {
+ TRACE(ft_t_noise, "Skip command failed");
+ } else {
+ --count; /* 0 means one gap etc. */
+ for (i = 0; i < nibbles; ++i) {
+ if (result >= 0) {
+ result = ftape_parameter(count & 15);
+ count /= 16;
+ }
+ }
+ result = ftape_ready_wait(ftape_timeout.rewind, &status);
+ if (result >= 0) {
+ ftape_tape_running = 0;
+ }
+ }
+ }
+ TRACE_EXIT result;
+}
+
+static int validate(int id)
+{
+ /* Check to see if position found is off-track as reported
+ * once. Because all tracks in one direction lie next to
+ * each other, if off-track the error will be approximately
+ * 2 * ft_segments_per_track.
+ */
+ if (ft_location.track == -1) {
+ return 1; /* unforseen situation, don't generate error */
+ } else {
+ /* Use margin of ft_segments_per_track on both sides
+ * because ftape needs some margin and the error we're
+ * looking for is much larger !
+ */
+ int lo = (ft_location.track - 1) * ft_segments_per_track;
+ int hi = (ft_location.track + 2) * ft_segments_per_track;
+
+ return (id >= lo && id < hi);
+ }
+}
+
+static int seek_forward(int segment_id, int fast)
+{
+ int failures = 0;
+ int count;
+ static int margin = 1; /* fixed: stop this before target */
+ static int overshoot = 1;
+ static int min_count = 8;
+ int expected = -1;
+ int target = segment_id - margin;
+ int fast_seeking;
+ int prev_segment = ft_location.segment;
+ TRACE_FUN(ft_t_flow);
+
+ if (!ft_location.known) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "fatal: cannot seek from unknown location");
+ }
+ if (!validate(segment_id)) {
+ ftape_sleep(1 * FT_SECOND);
+ ft_failure = 1;
+ TRACE_ABORT(-EIO, ft_t_err,
+ "fatal: head off track (bad hardware?)");
+ }
+ TRACE(ft_t_noise, "from %d/%d to %d/0 - %d",
+ ft_location.segment, ft_location.sector,segment_id,margin);
+ count = target - ft_location.segment - overshoot;
+ fast_seeking = (fast &&
+ count > (min_count + (ft_location.bot ? 1 : 0)));
+ if (fast_seeking) {
+ TRACE(ft_t_noise, "fast skipping %d segments", count);
+ expected = segment_id - margin;
+ fast_seek(count, 0);
+ }
+ if (!ftape_tape_running) {
+ logical_forward();
+ }
+ while (ft_location.segment < segment_id) {
+ /* This requires at least one sector in a (bad) segment to
+ * have a valid and readable sector id !
+ * It looks like this is not guaranteed, so we must try
+ * to find a way to skip an EMPTY_SEGMENT. !!! FIXME !!!
+ */
+ if (ftape_read_id() < 0 || !ft_location.known ||
+ (current->signal & _DONT_BLOCK)) {
+ ft_location.known = 0;
+ if (!ftape_tape_running ||
+ ++failures > FT_SECTORS_PER_SEGMENT) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "read_id failed completely");
+ }
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ TRACE(ft_t_flow, "read_id failed, retry (%d)",
+ failures);
+ continue;
+ }
+ if (fast_seeking) {
+ TRACE(ft_t_noise, "ended at %d/%d (%d,%d)",
+ ft_location.segment, ft_location.sector,
+ overshoot, inhibit_correction);
+ if (!inhibit_correction &&
+ (ft_location.segment < expected ||
+ ft_location.segment > expected + margin)) {
+ int error = ft_location.segment - expected;
+ TRACE(ft_t_noise,
+ "adjusting overshoot from %d to %d",
+ overshoot, overshoot + error);
+ overshoot += error;
+ /* All overshoots have the same
+ * direction, so it should never
+ * become negative, but who knows.
+ */
+ if (overshoot < -5 ||
+ overshoot > OVERSHOOT_LIMIT) {
+ if (overshoot < 0) {
+ /* keep sane value */
+ overshoot = -5;
+ } else {
+ /* keep sane value */
+ overshoot = OVERSHOOT_LIMIT;
+ }
+ TRACE(ft_t_noise,
+ "clipped overshoot to %d",
+ overshoot);
+ }
+ }
+ fast_seeking = 0;
+ }
+ if (ft_location.known) {
+ if (ft_location.segment > prev_segment + 1) {
+ TRACE(ft_t_noise,
+ "missed segment %d while skipping",
+ prev_segment + 1);
+ }
+ prev_segment = ft_location.segment;
+ }
+ }
+ if (ft_location.segment > segment_id) {
+ TRACE_ABORT(-EIO,
+ ft_t_noise, "failed: skip ended at segment %d/%d",
+ ft_location.segment, ft_location.sector);
+ }
+ TRACE_EXIT 0;
+}
+
+static int skip_reverse(int segment_id, int *pstatus)
+{
+ int failures = 0;
+ static int overshoot = 1;
+ static int min_rewind = 2; /* 1 + overshoot */
+ static const int margin = 1; /* stop this before target */
+ int expected = 0;
+ int count = 1;
+ int short_seek;
+ int target = segment_id - margin;
+ TRACE_FUN(ft_t_flow);
+
+ if (ft_location.known && !validate(segment_id)) {
+ ftape_sleep(1 * FT_SECOND);
+ ft_failure = 1;
+ TRACE_ABORT(-EIO, ft_t_err,
+ "fatal: head off track (bad hardware?)");
+ }
+ do {
+ if (!ft_location.known) {
+ TRACE(ft_t_warn, "warning: location not known");
+ }
+ TRACE(ft_t_noise, "from %d/%d to %d/0 - %d",
+ ft_location.segment, ft_location.sector,
+ segment_id, margin);
+ /* min_rewind == 1 + overshoot_when_doing_minimum_rewind
+ * overshoot == overshoot_when_doing_larger_rewind
+ * Initially min_rewind == 1 + overshoot, optimization
+ * of both values will be done separately.
+ * overshoot and min_rewind can be negative as both are
+ * sums of three components:
+ * any_overshoot == rewind_overshoot -
+ * stop_overshoot -
+ * start_overshoot
+ */
+ if (ft_location.segment - target - (min_rewind - 1) < 1) {
+ short_seek = 1;
+ } else {
+ count = ft_location.segment - target - overshoot;
+ short_seek = (count < 1);
+ }
+ if (short_seek) {
+ count = 1; /* do shortest rewind */
+ expected = ft_location.segment - min_rewind;
+ if (expected/ft_segments_per_track != ft_location.track) {
+ expected = (ft_location.track *
+ ft_segments_per_track);
+ }
+ } else {
+ expected = target;
+ }
+ fast_seek(count, 1);
+ logical_forward();
+ if (ftape_read_id() < 0 || !ft_location.known ||
+ (current->signal & _DONT_BLOCK)) {
+ if ((!ftape_tape_running && !ft_location.known) ||
+ ++failures > FT_SECTORS_PER_SEGMENT) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "read_id failed completely");
+ }
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ TRACE_CATCH(ftape_report_drive_status(pstatus),);
+ TRACE(ft_t_noise, "ftape_read_id failed, retry (%d)",
+ failures);
+ continue;
+ }
+ TRACE(ft_t_noise, "ended at %d/%d (%d,%d,%d)",
+ ft_location.segment, ft_location.sector,
+ min_rewind, overshoot, inhibit_correction);
+ if (!inhibit_correction &&
+ (ft_location.segment < expected ||
+ ft_location.segment > expected + margin)) {
+ int error = expected - ft_location.segment;
+ if (short_seek) {
+ TRACE(ft_t_noise,
+ "adjusting min_rewind from %d to %d",
+ min_rewind, min_rewind + error);
+ min_rewind += error;
+ if (min_rewind < -5) {
+ /* is this right ? FIXME ! */
+ /* keep sane value */
+ min_rewind = -5;
+ TRACE(ft_t_noise,
+ "clipped min_rewind to %d",
+ min_rewind);
+ }
+ } else {
+ TRACE(ft_t_noise,
+ "adjusting overshoot from %d to %d",
+ overshoot, overshoot + error);
+ overshoot += error;
+ if (overshoot < -5 ||
+ overshoot > OVERSHOOT_LIMIT) {
+ if (overshoot < 0) {
+ /* keep sane value */
+ overshoot = -5;
+ } else {
+ /* keep sane value */
+ overshoot = OVERSHOOT_LIMIT;
+ }
+ TRACE(ft_t_noise,
+ "clipped overshoot to %d",
+ overshoot);
+ }
+ }
+ }
+ } while (ft_location.segment > segment_id);
+ if (ft_location.known) {
+ TRACE(ft_t_noise, "current location: %d/%d",
+ ft_location.segment, ft_location.sector);
+ }
+ TRACE_EXIT 0;
+}
+
+static int determine_position(void)
+{
+ int retry = 0;
+ int status;
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ if (!ftape_tape_running) {
+ /* This should only happen if tape is stopped by isr.
+ */
+ TRACE(ft_t_flow, "waiting for tape stop");
+ if (ftape_ready_wait(ftape_timeout.pause, &status) < 0) {
+ TRACE(ft_t_flow, "drive still running (fatal)");
+ ftape_tape_running = 1; /* ? */
+ }
+ } else {
+ ftape_report_drive_status(&status);
+ }
+ if (status & QIC_STATUS_READY) {
+ /* Drive must be ready to check error state !
+ */
+ TRACE(ft_t_flow, "drive is ready");
+ if (status & QIC_STATUS_ERROR) {
+ unsigned int error;
+ qic117_cmd_t command;
+
+ /* Report and clear error state, try to continue.
+ */
+ TRACE(ft_t_flow, "error status set");
+ ftape_report_error(&error, &command, 1);
+ ftape_ready_wait(ftape_timeout.reset, &status);
+ ftape_tape_running = 0; /* ? */
+ }
+ if (check_bot_eot(status)) {
+ if (ft_location.bot) {
+ if ((status & QIC_STATUS_READY) == 0) {
+ /* tape moving away from
+ * bot/eot, let's see if we
+ * can catch up with the first
+ * segment on this track.
+ */
+ } else {
+ TRACE(ft_t_flow,
+ "start tape from logical bot");
+ logical_forward(); /* start moving */
+ }
+ } else {
+ if ((status & QIC_STATUS_READY) == 0) {
+ TRACE(ft_t_noise, "waiting for logical end of track");
+ result = ftape_ready_wait(ftape_timeout.reset, &status);
+ /* error handling needed ? */
+ } else {
+ TRACE(ft_t_noise,
+ "tape at logical end of track");
+ }
+ }
+ } else {
+ TRACE(ft_t_flow, "start tape");
+ logical_forward(); /* start moving */
+ ft_location.known = 0; /* not cleared by logical forward ! */
+ }
+ }
+ /* tape should be moving now, start reading id's
+ */
+ while (!ft_location.known &&
+ retry++ < FT_SECTORS_PER_SEGMENT &&
+ (result = ftape_read_id()) < 0) {
+
+ TRACE(ft_t_flow, "location unknown");
+
+ /* exit on signal
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+
+ /* read-id somehow failed, tape may
+ * have reached end or some other
+ * error happened.
+ */
+ TRACE(ft_t_flow, "read-id failed");
+ TRACE_CATCH(ftape_report_drive_status(&status),);
+ TRACE(ft_t_err, "ftape_report_drive_status: 0x%02x", status);
+ if (status & QIC_STATUS_READY) {
+ ftape_tape_running = 0;
+ TRACE(ft_t_noise, "tape stopped for unknown reason! "
+ "status = 0x%02x", status);
+ if (status & QIC_STATUS_ERROR ||
+ !check_bot_eot(status)) {
+ /* oops, tape stopped but not at end!
+ */
+ TRACE_EXIT -EIO;
+ }
+ }
+ }
+ TRACE(ft_t_flow,
+ "tape is positioned at segment %d", ft_location.segment);
+ TRACE_EXIT ft_location.known ? 0 : -EIO;
+}
+
+/* Get the tape running and position it just before the
+ * requested segment.
+ * Seek tape-track and reposition as needed.
+ */
+int ftape_start_tape(int segment_id, int sector_offset)
+{
+ int track = segment_id / ft_segments_per_track;
+ int result = -EIO;
+ int status;
+ static int last_segment = -1;
+ static int bad_bus_timing = 0;
+ /* number of segments passing the head between starting the tape
+ * and being able to access the first sector.
+ */
+ static int start_offset = 1;
+ int retry;
+ TRACE_FUN(ft_t_flow);
+
+ /* If sector_offset > 0, seek into wanted segment instead of
+ * into previous.
+ * This allows error recovery if a part of the segment is bad
+ * (erased) causing the tape drive to generate an index pulse
+ * thus causing a no-data error before the requested sector
+ * is reached.
+ */
+ ftape_tape_running = 0;
+ TRACE(ft_t_noise, "target segment: %d/%d%s", segment_id, sector_offset,
+ ft_buffer[ft_head]->retry > 0 ? " retry" : "");
+ if (ft_buffer[ft_head]->retry > 0) { /* this is a retry */
+ int dist = segment_id - last_segment;
+
+ if ((int)ft_history.overrun_errors < overrun_count_offset) {
+ overrun_count_offset = ft_history.overrun_errors;
+ } else if (dist < 0 || dist > 50) {
+ overrun_count_offset = ft_history.overrun_errors;
+ } else if ((ft_history.overrun_errors -
+ overrun_count_offset) >= 8) {
+ if (ftape_increase_threshold() >= 0) {
+ --ft_buffer[ft_head]->retry;
+ overrun_count_offset =
+ ft_history.overrun_errors;
+ TRACE(ft_t_warn, "increased threshold because "
+ "of excessive overrun errors");
+ } else if (!bad_bus_timing && ft_data_rate >= 1000) {
+ ftape_half_data_rate();
+ --ft_buffer[ft_head]->retry;
+ bad_bus_timing = 1;
+ overrun_count_offset =
+ ft_history.overrun_errors;
+ TRACE(ft_t_warn, "reduced datarate because "
+ "of excessive overrun errors");
+ }
+ }
+ }
+ last_segment = segment_id;
+ if (ft_location.track != track ||
+ (ftape_might_be_off_track && ft_buffer[ft_head]->retry== 0)) {
+ /* current track unknown or not equal to destination
+ */
+ ftape_ready_wait(ftape_timeout.seek, &status);
+ ftape_seek_head_to_track(track);
+ /* overrun_count_offset = ft_history.overrun_errors; */
+ }
+ result = -EIO;
+ retry = 0;
+ while (result < 0 &&
+ retry++ <= 5 &&
+ !ft_failure &&
+ (current->signal & _DONT_BLOCK) == 0) {
+
+ if (retry && start_offset < 5) {
+ start_offset ++;
+ }
+ /* Check if we are able to catch the requested
+ * segment in time.
+ */
+ if ((ft_location.known || (determine_position() == 0)) &&
+ ft_location.segment >=
+ (segment_id -
+ ((ftape_tape_running || ft_location.bot)
+ ? 0 : start_offset))) {
+ /* Too far ahead (in or past target segment).
+ */
+ if (ftape_tape_running) {
+ if ((result = ftape_stop_tape(&status)) < 0) {
+ TRACE(ft_t_err,
+ "stop tape failed with code %d",
+ result);
+ break;
+ }
+ TRACE(ft_t_noise, "tape stopped");
+ ftape_tape_running = 0;
+ }
+ TRACE(ft_t_noise, "repositioning");
+ ++ft_history.rewinds;
+ if (segment_id % ft_segments_per_track < start_offset){
+ TRACE(ft_t_noise, "end of track condition\n"
+ KERN_INFO "segment_id : %d\n"
+ KERN_INFO "ft_segments_per_track: %d\n"
+ KERN_INFO "start_offset : %d",
+ segment_id, ft_segments_per_track,
+ start_offset);
+
+ /* If seeking to first segments on
+ * track better do a complete rewind
+ * to logical begin of track to get a
+ * more steady tape motion.
+ */
+ result = ftape_command_wait(
+ (ft_location.track & 1)
+ ? QIC_PHYSICAL_FORWARD
+ : QIC_PHYSICAL_REVERSE,
+ ftape_timeout.rewind, &status);
+ check_bot_eot(status); /* update location */
+ } else {
+ result= skip_reverse(segment_id - start_offset,
+ &status);
+ }
+ }
+ if (!ft_location.known) {
+ TRACE(ft_t_bug, "panic: location not known");
+ result = -EIO;
+ continue; /* while() will check for failure */
+ }
+ TRACE(ft_t_noise, "current segment: %d/%d",
+ ft_location.segment, ft_location.sector);
+ /* We're on the right track somewhere before the
+ * wanted segment. Start tape movement if needed and
+ * skip to just before or inside the requested
+ * segment. Keep tape running.
+ */
+ result = 0;
+ if (ft_location.segment <
+ (segment_id - ((ftape_tape_running || ft_location.bot)
+ ? 0 : start_offset))) {
+ if (sector_offset > 0) {
+ result = seek_forward(segment_id,
+ retry <= 3);
+ } else {
+ result = seek_forward(segment_id - 1,
+ retry <= 3);
+ }
+ }
+ if (result == 0 &&
+ ft_location.segment !=
+ (segment_id - (sector_offset > 0 ? 0 : 1))) {
+ result = -EIO;
+ }
+ }
+ if (result < 0) {
+ TRACE(ft_t_err, "failed to reposition");
+ } else {
+ ft_runner_status = running;
+ }
+ TRACE_EXIT result;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-rw.h b/drivers/char/ftape/lowlevel/ftape-rw.h
new file mode 100644
index 000000000..862461396
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-rw.h
@@ -0,0 +1,121 @@
+#ifndef _FTAPE_RW_H
+#define _FTAPE_RW_H
+
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-rw.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:25 $
+ *
+ * This file contains the definitions for the read and write
+ * functions for the QIC-117 floppy-tape driver for Linux.
+ *
+ * Claus-Justus Heine (1996/09/20): Add definition of format code 6
+ * Claus-Justus Heine (1996/10/04): Changed GET/PUT macros to cast to (__u8 *)
+ *
+ */
+
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/ftape-init.h"
+#include "../lowlevel/ftape-bsm.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,0)
+#include <asm/unaligned.h>
+
+#define GET2(address, offset) get_unaligned((__u16*)((__u8 *)address + offset))
+#define GET4(address, offset) get_unaligned((__u32*)((__u8 *)address + offset))
+#define GET8(address, offset) get_unaligned((__u64*)((__u8 *)address + offset))
+#define PUT2(address, offset , value) put_unaligned((value), (__u16*)((__u8 *)address + offset))
+#define PUT4(address, offset , value) put_unaligned((value), (__u32*)((__u8 *)address + offset))
+#define PUT8(address, offset , value) put_unaligned((value), (__u64*)((__u8 *)address + offset))
+#else
+#define GET2(address, offset) *(__u16*)((__u8 *)address + offset)
+#define GET4(address, offset) *(__u32*)((__u8 *)address + offset)
+#define GET8(address, offset) *(__u64*)((__u8 *)address + offset)
+#define PUT2(address, offset , value) *(__u16*)((__u8 *)address + offset) = (__u16)(value)
+#define PUT4(address, offset , value) *(__u32*)((__u8 *)address + offset) = (__u32)(value)
+#define PUT8(address, offset , value) *(__u64*)((__u8 *)address + offset) = (__u32)(value)
+#endif
+
+enum runner_status_enum {
+ idle = 0,
+ running,
+ do_abort,
+ aborting,
+ logical_eot,
+ end_of_tape,
+};
+
+typedef enum ft_buffer_queue {
+ ft_queue_head = 0,
+ ft_queue_tail = 1
+} ft_buffer_queue_t;
+
+
+typedef struct {
+ int track; /* tape head position */
+ volatile int segment; /* current segment */
+ volatile int sector; /* sector offset within current segment */
+ volatile unsigned int bot; /* logical begin of track */
+ volatile unsigned int eot; /* logical end of track */
+ volatile unsigned int known; /* validates bot, segment, sector */
+} location_record;
+
+/* Count nr of 1's in pattern.
+ */
+extern inline int count_ones(unsigned long mask)
+{
+ int bits;
+
+ for (bits = 0; mask != 0; mask >>= 1) {
+ if (mask & 1) {
+ ++bits;
+ }
+ }
+ return bits;
+}
+
+#define FT_MAX_NR_BUFFERS 16 /* arbitrary value */
+/* ftape-rw.c defined global vars.
+ */
+extern buffer_struct *ft_buffer[FT_MAX_NR_BUFFERS];
+extern int ft_nr_buffers;
+extern location_record ft_location;
+extern volatile int ftape_tape_running;
+
+/* ftape-rw.c defined global functions.
+ */
+extern int ftape_setup_new_segment(buffer_struct * buff,
+ int segment_id,
+ int offset);
+extern int ftape_calc_next_cluster(buffer_struct * buff);
+extern buffer_struct *ftape_next_buffer (ft_buffer_queue_t pos);
+extern buffer_struct *ftape_get_buffer (ft_buffer_queue_t pos);
+extern int ftape_buffer_id (ft_buffer_queue_t pos);
+extern void ftape_reset_buffer(void);
+extern int ftape_read_id(void);
+extern void ftape_tape_parameters(__u8 drive_configuration);
+extern int ftape_wait_segment(buffer_state_enum state);
+extern int ftape_dumb_stop(void);
+extern int ftape_start_tape(int segment_id, int offset);
+extern int ftape_stop_tape(int *pstatus);
+extern int ftape_handle_logical_eot(void);
+extern buffer_state_enum ftape_set_state(buffer_state_enum new_state);
+#endif /* _FTAPE_RW_H */
diff --git a/drivers/char/ftape/lowlevel/ftape-setup.c b/drivers/char/ftape/lowlevel/ftape-setup.c
new file mode 100644
index 000000000..5cb89c694
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-setup.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-setup.c,v $
+ * $Revision: 1.7 $
+ * $Date: 1997/10/10 09:57:06 $
+ *
+ * This file contains the code for processing the kernel command
+ * line options for the QIC-40/80/3010/3020 floppy-tape driver
+ * "ftape" for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+
+#include <linux/ftape.h>
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,16)
+#include <linux/init.h>
+#else
+#define __initdata
+#define __initfunc(__arg) __arg
+#endif
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/fdc-io.h"
+
+static struct param_table {
+ const char *name;
+ int *var;
+ int def_param;
+ int min;
+ int max;
+} config_params[] __initdata = {
+#ifndef CONFIG_FT_NO_TRACE_AT_ALL
+ { "tracing", &ftape_tracing, 3, ft_t_bug, ft_t_any},
+#endif
+ { "ioport", &ft_fdc_base, CONFIG_FT_FDC_BASE, 0x0, 0xfff},
+ { "irq", &ft_fdc_irq, CONFIG_FT_FDC_IRQ, 2, 15},
+ { "dma", &ft_fdc_dma, CONFIG_FT_FDC_DMA, 0, 3},
+ { "threshold", &ft_fdc_threshold, CONFIG_FT_FDC_THR, 1, 16},
+ { "datarate", &ft_fdc_rate_limit, CONFIG_FT_FDC_MAX_RATE, 500, 2000},
+ { "fc10", &ft_probe_fc10, CONFIG_FT_PROBE_FC10, 0, 1},
+ { "mach2", &ft_mach2, CONFIG_FT_MACH2, 0, 1}
+};
+
+__initfunc(void ftape_setup(char *str, int *ints))
+{
+ int i;
+ int param;
+ TRACE_FUN(ft_t_flow);
+
+ if (str) {
+ for (i=0; i < NR_ITEMS(config_params); i++) {
+ if (strcmp(str,config_params[i].name) == 0){
+ if (ints[0]) {
+ param = ints[1];
+ } else {
+ param = config_params[i].def_param;
+ }
+ if (param < config_params[i].min ||
+ param > config_params[i].max) {
+ TRACE(ft_t_err,
+ "parameter %s out of range %d ... %d",
+ config_params[i].name,
+ config_params[i].min,
+ config_params[i].max);
+ TRACE_EXIT;
+ }
+ if(config_params[i].var) {
+ TRACE(ft_t_info, "%s=%d", str, param);
+ *config_params[i].var = param;
+ }
+ TRACE_EXIT;
+ }
+ }
+ }
+ if (str) {
+ TRACE(ft_t_err, "unknown ftape option [%s]", str);
+
+ TRACE(ft_t_err, "allowed options are:");
+ for (i=0; i < NR_ITEMS(config_params); i++) {
+ TRACE(ft_t_err, " %s",config_params[i].name);
+ }
+ } else {
+ TRACE(ft_t_err, "botched ftape option");
+ }
+ TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-tracing.c b/drivers/char/ftape/lowlevel/ftape-tracing.c
new file mode 100644
index 000000000..95e3df8e8
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-tracing.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-tracing.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:27 $
+ *
+ * This file contains the reading code
+ * for the QIC-117 floppy-tape driver for Linux.
+ */
+
+#include <linux/ftape.h>
+#include "../lowlevel/ftape-tracing.h"
+
+/* Global vars.
+ */
+/* tracing
+ * set it to: to log :
+ * 0 bugs
+ * 1 + errors
+ * 2 + warnings
+ * 3 + information
+ * 4 + more information
+ * 5 + program flow
+ * 6 + fdc/dma info
+ * 7 + data flow
+ * 8 + everything else
+ */
+ft_trace_t ftape_tracing = ft_t_info; /* Default level: information and up */
+int ftape_function_nest_level = 0;
+
+/* Local vars.
+ */
+static __u8 trace_id = 0;
+static char spacing[] = "* ";
+
+void ftape_trace_call(const char *file, const char *name)
+{
+ char *indent;
+
+ /* Since printk seems not to work with "%*s" format
+ * we'll use this work-around.
+ */
+ if (ftape_function_nest_level < 0) {
+ printk(KERN_INFO "function nest level (%d) < 0\n",
+ ftape_function_nest_level);
+ ftape_function_nest_level = 0;
+ }
+ if (ftape_function_nest_level < sizeof(spacing)) {
+ indent = (spacing +
+ sizeof(spacing) - 1 -
+ ftape_function_nest_level);
+ } else {
+ indent = spacing;
+ }
+ printk(KERN_INFO "[%03d]%s+%s (%s)\n",
+ (int) trace_id++, indent, file, name);
+}
+
+void ftape_trace_exit(const char *file, const char *name)
+{
+ char *indent;
+
+ /* Since printk seems not to work with "%*s" format
+ * we'll use this work-around.
+ */
+ if (ftape_function_nest_level < 0) {
+ printk(KERN_INFO "function nest level (%d) < 0\n", ftape_function_nest_level);
+ ftape_function_nest_level = 0;
+ }
+ if (ftape_function_nest_level < sizeof(spacing)) {
+ indent = (spacing +
+ sizeof(spacing) - 1 -
+ ftape_function_nest_level);
+ } else {
+ indent = spacing;
+ }
+ printk(KERN_INFO "[%03d]%s-%s (%s)\n",
+ (int) trace_id++, indent, file, name);
+}
+
+void ftape_trace_log(const char *file, const char *function)
+{
+ char *indent;
+
+ /* Since printk seems not to work with "%*s" format
+ * we'll use this work-around.
+ */
+ if (ftape_function_nest_level < 0) {
+ printk(KERN_INFO "function nest level (%d) < 0\n", ftape_function_nest_level);
+ ftape_function_nest_level = 0;
+ }
+ if (ftape_function_nest_level < sizeof(spacing)) {
+ indent = (spacing +
+ sizeof(spacing) - 1 -
+ ftape_function_nest_level);
+ } else {
+ indent = spacing;
+ }
+ printk(KERN_INFO "[%03d]%s%s (%s) - ",
+ (int) trace_id++, indent, file, function);
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-tracing.h b/drivers/char/ftape/lowlevel/ftape-tracing.h
new file mode 100644
index 000000000..ac675568a
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-tracing.h
@@ -0,0 +1,180 @@
+#ifndef _FTAPE_TRACING_H
+#define _FTAPE_TRACING_H
+
+/*
+ * Copyright (C) 1994-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-tracing.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:28 $
+ *
+ * This file contains definitions that eases the debugging of the
+ * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+
+/*
+ * Be very careful with TRACE_EXIT and TRACE_ABORT.
+ *
+ * if (something) TRACE_EXIT error;
+ *
+ * will NOT work. Use
+ *
+ * if (something) {
+ * TRACE_EXIT error;
+ * }
+ *
+ * instead. Maybe a bit dangerous, but save lots of lines of code.
+ */
+
+#define LL_X "%d/%d KB"
+#define LL(x) (unsigned int)((__u64)(x)>>10), (unsigned int)((x)&1023)
+
+typedef enum {
+ ft_t_nil = -1,
+ ft_t_bug,
+ ft_t_err,
+ ft_t_warn,
+ ft_t_info,
+ ft_t_noise,
+ ft_t_flow,
+ ft_t_fdc_dma,
+ ft_t_data_flow,
+ ft_t_any
+} ft_trace_t;
+
+#ifdef CONFIG_FT_NO_TRACE_AT_ALL
+/* the compiler will optimize away most TRACE() macros
+ */
+#define FT_TRACE_TOP_LEVEL ft_t_bug
+#define TRACE_FUN(level) do {} while(0)
+#define TRACE_EXIT return
+#define TRACE(l, m, i...) \
+{ \
+ if ((ft_trace_t)(l) == FT_TRACE_TOP_LEVEL) { \
+ printk(KERN_INFO"ftape"__FILE__"("__FUNCTION__"):\n" \
+ KERN_INFO m".\n" ,##i); \
+ } \
+}
+#define SET_TRACE_LEVEL(l) if ((l) == (l)) do {} while(0)
+#define TRACE_LEVEL FT_TRACE_TOP_LEVEL
+
+#else
+
+#ifdef CONFIG_FT_NO_TRACE
+/* the compiler will optimize away many TRACE() macros
+ * the ftape_simple_trace_call() function simply increments
+ * the function nest level.
+ */
+#define FT_TRACE_TOP_LEVEL ft_t_warn
+#define TRACE_FUN(level) ftape_function_nest_level++
+#define TRACE_EXIT ftape_function_nest_level--; return
+
+#else
+#ifdef CONFIG_FT_FULL_DEBUG
+#define FT_TRACE_TOP_LEVEL ft_t_any
+#else
+#define FT_TRACE_TOP_LEVEL ft_t_flow
+#endif
+#define TRACE_FUN(level) \
+ const ft_trace_t _tracing = level; \
+ if (ftape_tracing >= (ft_trace_t)(level) && \
+ (ft_trace_t)(level) <= FT_TRACE_TOP_LEVEL) \
+ ftape_trace_call(__FILE__, __FUNCTION__); \
+ ftape_function_nest_level ++;
+
+#define TRACE_EXIT \
+ --ftape_function_nest_level; \
+ if (ftape_tracing >= (ft_trace_t)(_tracing) && \
+ (ft_trace_t)(_tracing) <= FT_TRACE_TOP_LEVEL) \
+ ftape_trace_exit(__FILE__, __FUNCTION__); \
+ return
+
+#endif
+
+#define TRACE(l, m, i...) \
+{ \
+ if (ftape_tracing >= (ft_trace_t)(l) && \
+ (ft_trace_t)(l) <= FT_TRACE_TOP_LEVEL) { \
+ ftape_trace_log(__FILE__, __FUNCTION__); \
+ printk(m".\n" ,##i); \
+ } \
+}
+
+#define SET_TRACE_LEVEL(l) \
+{ \
+ if ((ft_trace_t)(l) <= FT_TRACE_TOP_LEVEL) { \
+ ftape_tracing = (ft_trace_t)(l); \
+ } else { \
+ ftape_tracing = FT_TRACE_TOP_LEVEL; \
+ } \
+}
+#define TRACE_LEVEL \
+((ftape_tracing <= FT_TRACE_TOP_LEVEL) ? ftape_tracing : FT_TRACE_TOP_LEVEL)
+
+
+/* Global variables declared in tracing.c
+ */
+extern ft_trace_t ftape_tracing; /* sets default level */
+extern int ftape_function_nest_level;
+
+/* Global functions declared in tracing.c
+ */
+extern void ftape_trace_call(const char *file, const char *name);
+extern void ftape_trace_exit(const char *file, const char *name);
+extern void ftape_trace_log (const char *file, const char *name);
+
+#endif /* !defined(CONFIG_FT_NO_TRACE_AT_ALL) */
+
+/*
+ * Abort with a message.
+ */
+#define TRACE_ABORT(res, i...) \
+{ \
+ TRACE(##i); \
+ TRACE_EXIT res; \
+}
+
+/* The following transforms the common "if(result < 0) ... " into a
+ * one-liner.
+ */
+#define _TRACE_CATCH(level, fun, action) \
+{ \
+ int _res = (fun); \
+ if (_res < 0) { \
+ do { action /* */ ; } while(0); \
+ TRACE_ABORT(_res, level, "%s failed: %d", #fun, _res); \
+ } \
+}
+
+#define TRACE_CATCH(fun, fail) _TRACE_CATCH(ft_t_err, fun, fail)
+
+/* Abort the current function when signalled. This doesn't belong here,
+ * but rather into ftape-rw.h (maybe)
+ */
+#define FT_SIGNAL_EXIT(sig_mask) \
+ if (current->signal & (sig_mask)) { \
+ TRACE_ABORT(-EINTR, \
+ ft_t_warn, \
+ "interrupted by non-blockable signal"); \
+ }
+
+#endif /* _FTAPE_TRACING_H */
diff --git a/drivers/char/ftape/lowlevel/ftape-write.c b/drivers/char/ftape/lowlevel/ftape-write.c
new file mode 100644
index 000000000..a0f095b9a
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape-write.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 1993-1995 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-write.c,v $
+ * $Revision: 1.3.4.1 $
+ * $Date: 1997/11/14 18:07:04 $
+ *
+ * This file contains the writing code
+ * for the QIC-117 floppy-tape driver for Linux.
+ */
+
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-ecc.h"
+#include "../lowlevel/ftape-bsm.h"
+#include "../lowlevel/fdc-isr.h"
+
+/* Global vars.
+ */
+
+/* Local vars.
+ */
+static int last_write_failed = 0;
+
+void ftape_zap_write_buffers(void)
+{
+ int i;
+
+ for (i = 0; i < ft_nr_buffers; ++i) {
+ ft_buffer[i]->status = done;
+ }
+ ftape_reset_buffer();
+}
+
+static int copy_and_gen_ecc(void *destination,
+ const void *source,
+ const SectorMap bad_sector_map)
+{
+ int result;
+ struct memory_segment mseg;
+ int bads = count_ones(bad_sector_map);
+ TRACE_FUN(ft_t_any);
+
+ if (bads > 0) {
+ TRACE(ft_t_noise, "bad sectors in map: %d", bads);
+ }
+ if (bads + 3 >= FT_SECTORS_PER_SEGMENT) {
+ TRACE(ft_t_noise, "empty segment");
+ mseg.blocks = 0; /* skip entire segment */
+ result = 0; /* nothing written */
+ } else {
+ mseg.blocks = FT_SECTORS_PER_SEGMENT - bads;
+ mseg.data = destination;
+ memcpy(mseg.data, source, (mseg.blocks - 3) * FT_SECTOR_SIZE);
+ result = ftape_ecc_set_segment_parity(&mseg);
+ if (result < 0) {
+ TRACE(ft_t_err, "ecc_set_segment_parity failed");
+ } else {
+ result = (mseg.blocks - 3) * FT_SECTOR_SIZE;
+ }
+ }
+ TRACE_EXIT result;
+}
+
+
+int ftape_start_writing(const ft_write_mode_t mode)
+{
+ buffer_struct *head = ftape_get_buffer(ft_queue_head);
+ int segment_id = head->segment_id;
+ int result;
+ buffer_state_enum wanted_state = (mode == FT_WR_DELETE
+ ? deleting
+ : writing);
+ TRACE_FUN(ft_t_flow);
+
+ if ((ft_driver_state != wanted_state) || head->status != waiting) {
+ TRACE_EXIT 0;
+ }
+ ftape_setup_new_segment(head, segment_id, 1);
+ if (mode == FT_WR_SINGLE) {
+ /* stop tape instead of pause */
+ head->next_segment = 0;
+ }
+ ftape_calc_next_cluster(head); /* prepare */
+ head->status = ft_driver_state; /* either writing or deleting */
+ if (ft_runner_status == idle) {
+ TRACE(ft_t_noise,
+ "starting runner for segment %d", segment_id);
+ TRACE_CATCH(ftape_start_tape(segment_id,head->sector_offset),);
+ } else {
+ TRACE(ft_t_noise, "runner not idle, not starting tape");
+ }
+ /* go */
+ result = fdc_setup_read_write(head, (mode == FT_WR_DELETE
+ ? FDC_WRITE_DELETED : FDC_WRITE));
+ ftape_set_state(wanted_state); /* should not be necessary */
+ TRACE_EXIT result;
+}
+
+/* Wait until all data is actually written to tape.
+ *
+ * There is a problem: when the tape runs into logical EOT, then this
+ * failes. We need to restart the runner in this case.
+ */
+int ftape_loop_until_writes_done(void)
+{
+ buffer_struct *head;
+ TRACE_FUN(ft_t_flow);
+
+ while ((ft_driver_state == writing || ft_driver_state == deleting) &&
+ ftape_get_buffer(ft_queue_head)->status != done) {
+ /* set the runner status to idle if at lEOT */
+ TRACE_CATCH(ftape_handle_logical_eot(), last_write_failed = 1);
+ /* restart the tape if necessary */
+ if (ft_runner_status == idle) {
+ TRACE(ft_t_noise, "runner is idle, restarting");
+ if (ft_driver_state == deleting) {
+ TRACE_CATCH(ftape_start_writing(FT_WR_DELETE),
+ last_write_failed = 1);
+ } else {
+ TRACE_CATCH(ftape_start_writing(FT_WR_MULTI),
+ last_write_failed = 1);
+ }
+ }
+ TRACE(ft_t_noise, "tail: %d, head: %d",
+ ftape_buffer_id(ft_queue_tail),
+ ftape_buffer_id(ft_queue_head));
+ TRACE_CATCH(fdc_interrupt_wait(5 * FT_SECOND),
+ last_write_failed = 1);
+ head = ftape_get_buffer(ft_queue_head);
+ if (head->status == error) {
+ /* Allow escape from loop when signaled !
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ if (head->hard_error_map != 0) {
+ /* Implement hard write error recovery here
+ */
+ }
+ /* retry this one */
+ head->status = waiting;
+ if (ft_runner_status == aborting) {
+ ftape_dumb_stop();
+ }
+ if (ft_runner_status != idle) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "unexpected state: "
+ "ft_runner_status != idle");
+ }
+ ftape_start_writing(ft_driver_state == deleting
+ ? FT_WR_MULTI : FT_WR_DELETE);
+ }
+ TRACE(ft_t_noise, "looping until writes done");
+ }
+ ftape_set_state(idle);
+ TRACE_EXIT 0;
+}
+
+/* Write given segment from buffer at address to tape.
+ */
+static int write_segment(const int segment_id,
+ const void *address,
+ const ft_write_mode_t write_mode)
+{
+ int bytes_written = 0;
+ buffer_struct *tail;
+ buffer_state_enum wanted_state = (write_mode == FT_WR_DELETE
+ ? deleting : writing);
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "segment_id = %d", segment_id);
+ if (ft_driver_state != wanted_state) {
+ if (ft_driver_state == deleting ||
+ wanted_state == deleting) {
+ TRACE_CATCH(ftape_loop_until_writes_done(),);
+ }
+ TRACE(ft_t_noise, "calling ftape_abort_operation");
+ TRACE_CATCH(ftape_abort_operation(),);
+ ftape_zap_write_buffers();
+ ftape_set_state(wanted_state);
+ }
+ /* if all buffers full we'll have to wait...
+ */
+ ftape_wait_segment(wanted_state);
+ tail = ftape_get_buffer(ft_queue_tail);
+ switch(tail->status) {
+ case done:
+ ft_history.defects += count_ones(tail->hard_error_map);
+ break;
+ case waiting:
+ /* this could happen with multiple EMPTY_SEGMENTs, but
+ * shouldn't happen any more as we re-start the runner even
+ * with an empty segment.
+ */
+ bytes_written = -EAGAIN;
+ break;
+ case error:
+ /* setup for a retry
+ */
+ tail->status = waiting;
+ bytes_written = -EAGAIN; /* force retry */
+ if (tail->hard_error_map != 0) {
+ TRACE(ft_t_warn,
+ "warning: %d hard error(s) in written segment",
+ count_ones(tail->hard_error_map));
+ TRACE(ft_t_noise, "hard_error_map = 0x%08lx",
+ (long)tail->hard_error_map);
+ /* Implement hard write error recovery here
+ */
+ }
+ break;
+ default:
+ TRACE_ABORT(-EIO, ft_t_err,
+ "wait for empty segment failed, tail status: %d",
+ tail->status);
+ }
+ /* should runner stop ?
+ */
+ if (ft_runner_status == aborting) {
+ buffer_struct *head = ftape_get_buffer(ft_queue_head);
+ if (head->status == wanted_state) {
+ head->status = done; /* ???? */
+ }
+ /* don't call abort_operation(), we don't want to zap
+ * the dma buffers
+ */
+ TRACE_CATCH(ftape_dumb_stop(),);
+ } else {
+ /* If just passed last segment on tape: wait for BOT
+ * or EOT mark. Sets ft_runner_status to idle if at lEOT
+ * and successful
+ */
+ TRACE_CATCH(ftape_handle_logical_eot(),);
+ }
+ if (tail->status == done) {
+ /* now at least one buffer is empty, fill it with our
+ * data. skip bad sectors and generate ecc.
+ * copy_and_gen_ecc return nr of bytes written, range
+ * 0..29 Kb inclusive!
+ *
+ * Empty segments are handled inside coyp_and_gen_ecc()
+ */
+ if (write_mode != FT_WR_DELETE) {
+ TRACE_CATCH(bytes_written = copy_and_gen_ecc(
+ tail->address, address,
+ ftape_get_bad_sector_entry(segment_id)),);
+ }
+ tail->segment_id = segment_id;
+ tail->status = waiting;
+ tail = ftape_next_buffer(ft_queue_tail);
+ }
+ /* Start tape only if all buffers full or flush mode.
+ * This will give higher probability of streaming.
+ */
+ if (ft_runner_status != running &&
+ ((tail->status == waiting &&
+ ftape_get_buffer(ft_queue_head) == tail) ||
+ write_mode != FT_WR_ASYNC)) {
+ TRACE_CATCH(ftape_start_writing(write_mode),);
+ }
+ TRACE_EXIT bytes_written;
+}
+
+/* Write as much as fits from buffer to the given segment on tape
+ * and handle retries.
+ * Return the number of bytes written (>= 0), or:
+ * -EIO write failed
+ * -EINTR interrupted by signal
+ * -ENOSPC device full
+ */
+int ftape_write_segment(const int segment_id,
+ const void *buffer,
+ const ft_write_mode_t flush)
+{
+ int retry = 0;
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ ft_history.used |= 2;
+ if (segment_id >= ft_tracks_per_tape*ft_segments_per_track) {
+ /* tape full */
+ TRACE_ABORT(-ENOSPC, ft_t_err,
+ "invalid segment id: %d (max %d)",
+ segment_id,
+ ft_tracks_per_tape * ft_segments_per_track -1);
+ }
+ for (;;) {
+ if ((result = write_segment(segment_id, buffer, flush)) >= 0) {
+ if (result == 0) { /* empty segment */
+ TRACE(ft_t_noise,
+ "empty segment, nothing written");
+ }
+ TRACE_EXIT result;
+ }
+ if (result == -EAGAIN) {
+ if (++retry > 100) { /* give up */
+ TRACE_ABORT(-EIO, ft_t_err,
+ "write failed, >100 retries in segment");
+ }
+ TRACE(ft_t_warn, "write error, retry %d (%d)",
+ retry,
+ ftape_get_buffer(ft_queue_tail)->segment_id);
+ } else {
+ TRACE_ABORT(result, ft_t_err,
+ "write_segment failed, error: %d", result);
+ }
+ /* Allow escape from loop when signaled !
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ }
+}
diff --git a/drivers/char/ftape/ftape-write.h b/drivers/char/ftape/lowlevel/ftape-write.h
index 2cbf07668..0e7f898b7 100644
--- a/drivers/char/ftape/ftape-write.h
+++ b/drivers/char/ftape/lowlevel/ftape-write.h
@@ -2,7 +2,8 @@
#define _FTAPE_WRITE_H
/*
- * Copyright (C) 1994-1995 Bas Laarhoven.
+ * Copyright (C) 1994-1995 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,30 +20,34 @@
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-write.h,v $
- $Author: bas $
+ $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-write.h,v $
+ $Author: claus $
*
- $Revision: 1.13 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
+ $Revision: 1.2 $
+ $Date: 1997/10/05 19:18:30 $
+ $State: Exp $
*
* This file contains the definitions for the write functions
* for the QIC-117 floppy-tape driver for Linux.
*
*/
-/* ftape-write.c defined global vars.
- */
/* ftape-write.c defined global functions.
*/
-extern int _ftape_write(const char *buff, int req_len);
-extern int ftape_flush_buffers(void);
-extern int ftape_write_header_segments(byte * buffer);
-extern int ftape_update_header_segments(byte * buffer, int update);
-extern int write_segment(unsigned segment, byte * address, int flushing);
-extern int ftape_fix(void);
-extern void prevent_flush(void);
+typedef enum {
+ FT_WR_ASYNC = 0, /* start tape only when all buffers are full */
+ FT_WR_MULTI = 1, /* start tape, but don't necessarily stop */
+ FT_WR_SINGLE = 2, /* write a single segment and stop afterwards */
+ FT_WR_DELETE = 3 /* write deleted data marks */
+} ft_write_mode_t;
+
+extern int ftape_start_writing(const ft_write_mode_t mode);
+extern int ftape_write_segment(const int segment,
+ const void *address,
+ const ft_write_mode_t flushing);
extern void ftape_zap_write_buffers(void);
+extern int ftape_loop_until_writes_done(void);
#endif /* _FTAPE_WRITE_H */
+
diff --git a/drivers/char/ftape/lowlevel/ftape_syms.c b/drivers/char/ftape/lowlevel/ftape_syms.c
new file mode 100644
index 000000000..dce372118
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape_syms.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 1996-1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape_syms.c,v $
+ * $Revision: 1.4 $
+ * $Date: 1997/10/17 00:03:51 $
+ *
+ * This file contains the the symbols that the ftape low level
+ * part of the QIC-40/80/3010/3020 floppy-tape driver "ftape"
+ * exports to it's high level clients
+ */
+
+#include <linux/config.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/ftape.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-init.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-bsm.h"
+#include "../lowlevel/ftape-buffer.h"
+#include "../lowlevel/ftape-format.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+# define FT_KSYM(sym) EXPORT_SYMBOL(##sym);
+#else
+# define FT_KSYM(sym) X(##sym),
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+struct symbol_table ftape_symbol_table = {
+#include <linux/symtab_begin.h>
+#endif
+/* bad sector handling from ftape-bsm.c */
+FT_KSYM(ftape_get_bad_sector_entry)
+FT_KSYM(ftape_find_end_of_bsm_list)
+/* from ftape-rw.c */
+FT_KSYM(ftape_set_state)
+/* from ftape-ctl.c */
+FT_KSYM(ftape_seek_to_bot)
+FT_KSYM(ftape_seek_to_eot)
+FT_KSYM(ftape_abort_operation)
+FT_KSYM(ftape_get_status)
+FT_KSYM(ftape_enable)
+FT_KSYM(ftape_disable)
+FT_KSYM(ftape_mmap)
+FT_KSYM(ftape_calibrate_data_rate)
+/* from ftape-io.c */
+FT_KSYM(ftape_reset_drive)
+FT_KSYM(ftape_command)
+FT_KSYM(ftape_parameter)
+FT_KSYM(ftape_ready_wait)
+FT_KSYM(ftape_report_operation)
+FT_KSYM(ftape_report_error)
+/* from ftape-read.c */
+FT_KSYM(ftape_read_segment_fraction)
+FT_KSYM(ftape_zap_read_buffers)
+FT_KSYM(ftape_read_header_segment)
+FT_KSYM(ftape_decode_header_segment)
+/* from ftape-write.c */
+FT_KSYM(ftape_write_segment)
+FT_KSYM(ftape_start_writing)
+FT_KSYM(ftape_loop_until_writes_done)
+/* from ftape-buffer.h */
+FT_KSYM(ftape_set_nr_buffers)
+/* from ftape-format.h */
+FT_KSYM(ftape_format_track)
+FT_KSYM(ftape_format_status)
+FT_KSYM(ftape_verify_segment)
+/* from tracing.c */
+#ifndef CONFIG_FT_NO_TRACE_AT_ALL
+FT_KSYM(ftape_tracing)
+FT_KSYM(ftape_function_nest_level)
+FT_KSYM(ftape_trace_call)
+FT_KSYM(ftape_trace_exit)
+FT_KSYM(ftape_trace_log)
+#endif
+/* end of ksym table */
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+#include <linux/symtab_end.h>
+};
+#endif
diff --git a/drivers/char/ftape/lowlevel/ftape_syms.h b/drivers/char/ftape/lowlevel/ftape_syms.h
new file mode 100644
index 000000000..d31f83565
--- /dev/null
+++ b/drivers/char/ftape/lowlevel/ftape_syms.h
@@ -0,0 +1,39 @@
+#ifndef _FTAPE_SYMS_H
+#define _FTAPE_SYMS_H
+
+/*
+ * Copyright (C) 1996-1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape_syms.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:32 $
+ *
+ * This file contains the definitions needed to export symbols
+ * from the QIC-40/80/3010/3020 floppy-tape driver "ftape" for
+ * Linux.
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+#include <linux/module.h>
+/* ftape-vfs.c defined global vars.
+ */
+
+extern struct symbol_table ftape_symbol_table;
+#endif
+
+#endif
diff --git a/drivers/char/ftape/qic117.h b/drivers/char/ftape/qic117.h
deleted file mode 100644
index 9eb9bb65f..000000000
--- a/drivers/char/ftape/qic117.h
+++ /dev/null
@@ -1,280 +0,0 @@
-#ifndef _QIC117_H
-#define _QIC117_H
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute 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.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/qic117.h,v $
- $Author: bas $
- *
- $Revision: 1.27 $
- $Date: 1995/05/01 19:02:20 $
- $State: Beta $
- *
- * This file contains QIC-117 spec. related definitions
- * for the QIC-40/80 floppy-tape driver for Linux.
- *
- * These data were taken from the Quarter-Inch Cartridge
- * Drive Standards, Inc. document titled:
- * `Common Command Set Interface Specification for Flexible
- * Disk Controller Based Minicartridge Tape Drives'
- * document QIC-117 Revision C, 3 Dec 92.
- * For more information, contact:
- * Quarter-Inch Cartridge Drive Standards, Inc.
- * 311 East Carrillo Street
- * Santa Barbara, California 93101
- * Telephone (805) 963-3853
- * Fax (805) 962-1541
- *
- * Current QIC standard revisions (of interest) are:
- * QIC-80-MC, Rev. M, 15 Jun 95.
- * QIC-117, Rev. I, 15 Mar 95.
- * QIC-122, Rev. B, 6 Mar 91.
- * QIC-130, Rev. C, 2 Sep 92.
- * QIC-3010-MC, Rev. F, 14 Jun 95.
- * QIC-3020-MC, Rev. G, 31 Aug 95.
- * QIC-CRF3, Rev. B, 15 Jun 95.
- *
- */
-
-/*
- * QIC-117 common command set rev. I.
- * These commands are sent to the tape unit
- * as number of pulses over the step line.
- */
-
-#define QIC_RESET 1
-#define QIC_REPORT_NEXT_BIT 2
-#define QIC_PAUSE 3
-#define QIC_MICRO_STEP_PAUSE 4
-#define QIC_ALTERNATE_TIMEOUT 5
-#define QIC_REPORT_DRIVE_STATUS 6
-#define QIC_REPORT_ERROR_CODE 7
-#define QIC_REPORT_DRIVE_CONFIGURATION 8
-#define QIC_REPORT_ROM_VERSION 9
-#define QIC_LOGICAL_FORWARD 10
-#define QIC_PHYSICAL_REVERSE 11
-#define QIC_PHYSICAL_FORWARD 12
-#define QIC_SEEK_HEAD_TO_TRACK 13
-#define QIC_SEEK_LOAD_POINT 14
-#define QIC_ENTER_FORMAT_MODE 15
-#define QIC_WRITE_REFERENCE_BURST 16
-#define QIC_ENTER_VERIFY_MODE 17
-#define QIC_STOP_TAPE 18
-/* commands 19-20: reserved */
-#define QIC_MICRO_STEP_HEAD_UP 21
-#define QIC_MICRO_STEP_HEAD_DOWN 22
-#define QIC_SOFT_SELECT 23
-#define QIC_SOFT_DESELECT 24
-#define QIC_SKIP_REVERSE 25
-#define QIC_SKIP_FORWARD 26
-#define QIC_SELECT_RATE 27
-/* command 27, in ccs2: Select Rate or Format */
-#define QIC_ENTER_DIAGNOSTIC_1 28
-#define QIC_ENTER_DIAGNOSTIC_2 29
-#define QIC_ENTER_PRIMARY_MODE 30
-/* command 31: vendor unique */
-#define QIC_REPORT_VENDOR_ID 32
-#define QIC_REPORT_TAPE_STATUS 33
-#define QIC_SKIP_EXTENDED_REVERSE 34
-#define QIC_SKIP_EXTENDED_FORWARD 35
-#define QIC_CALIBRATE_TAPE_LENGTH 36
-#define QIC_REPORT_FORMAT_SEGMENTS 37
-#define QIC_SET_FORMAT_SEGMENTS 38
-/* commands 39-45: reserved */
-#define QIC_PHANTOM_SELECT 46
-#define QIC_PHANTOM_DESELECT 47
-
-typedef enum {
- discretional = 0, required, ccs1, ccs2
-} qic_compatibility;
-
-typedef enum {
- unused, mode, motion, report
-} command_types;
-
-struct qic117_command_table {
- char *name;
- byte mask;
- byte state;
- byte cmd_type;
- byte non_intr;
- byte level;
-};
-
-#define QIC117_COMMANDS {\
-/* command mask state cmd_type */\
-/* | name | | | non_intr */\
-/* | | | | | | level */\
-/* 0*/ {NULL, 0x00, 0x00, mode, 0, discretional},\
-/* 1*/ {"soft reset", 0x00, 0x00, motion, 1, required},\
-/* 2*/ {"report next bit", 0x00, 0x00, report, 0, required},\
-/* 3*/ {"pause", 0x36, 0x24, motion, 1, required},\
-/* 4*/ {"micro step pause", 0x36, 0x24, motion, 1, required},\
-/* 5*/ {"alternate command timeout", 0x00, 0x00, mode, 0, required},\
-/* 6*/ {"report drive status", 0x00, 0x00, report, 0, required},\
-/* 7*/ {"report error code", 0x01, 0x01, report, 0, required},\
-/* 8*/ {"report drive configuration",0x00, 0x00, report, 0, required},\
-/* 9*/ {"report rom version", 0x00, 0x00, report, 0, required},\
-/*10*/ {"logical forward", 0x37, 0x25, motion, 0, required},\
-/*11*/ {"physical reverse", 0x17, 0x05, motion, 0, required},\
-/*12*/ {"physical forward", 0x17, 0x05, motion, 0, required},\
-/*13*/ {"seek head to track", 0x37, 0x25, motion, 0, required},\
-/*14*/ {"seek load point", 0x17, 0x05, motion, 1, required},\
-/*15*/ {"enter format mode", 0x1f, 0x05, mode, 0, required},\
-/*16*/ {"write reference burst", 0x1f, 0x05, motion, 1, required},\
-/*17*/ {"enter verify mode", 0x37, 0x25, mode, 0, required},\
-/*18*/ {"stop tape", 0x00, 0x00, motion, 1, required},\
-/*19*/ {"reserved (19)", 0x00, 0x00, unused, 0, discretional},\
-/*20*/ {"reserved (20)", 0x00, 0x00, unused, 0, discretional},\
-/*21*/ {"micro step head up", 0x02, 0x00, motion, 0, required},\
-/*22*/ {"micro step head down", 0x02, 0x00, motion, 0, required},\
-/*23*/ {"soft select", 0x00, 0x00, mode, 0, discretional},\
-/*24*/ {"soft deselect", 0x00, 0x00, mode, 0, discretional},\
-/*25*/ {"skip segments reverse", 0x36, 0x24, motion, 1, required},\
-/*26*/ {"skip segments forward", 0x36, 0x24, motion, 1, required},\
-/*27*/ {"select rate [or format]", 0x03, 0x01, mode, 0, required /* [ccs2] */},\
-/*28*/ {"enter diag mode 1", 0x00, 0x00, mode, 0, discretional},\
-/*29*/ {"enter diag mode 2", 0x00, 0x00, mode, 0, discretional},\
-/*30*/ {"enter primary mode", 0x00, 0x00, mode, 0, required},\
-/*31*/ {"vendor unique (31)", 0x00, 0x00, unused, 0, discretional},\
-/*32*/ {"report vendor id", 0x00, 0x00, report, 0, required},\
-/*33*/ {"report tape status", 0x04, 0x04, report, 0, ccs1},\
-/*34*/ {"skip extended reverse", 0x36, 0x24, motion, 1, ccs1},\
-/*35*/ {"skip extended forward", 0x36, 0x24, motion, 1, ccs1},\
-/*36*/ {"calibrate tape length", 0x17, 0x05, motion, 1, ccs2},\
-/*37*/ {"report format segments", 0x17, 0x05, report, 0, ccs2},\
-/*38*/ {"set format segments", 0x17, 0x05, mode, 0, ccs2},\
-/*39*/ {"reserved (39)", 0x00, 0x00, unused, 0, discretional},\
-/*40*/ {"vendor unique (40)", 0x00, 0x00, unused, 0, discretional},\
-/*41*/ {"vendor unique (41)", 0x00, 0x00, unused, 0, discretional},\
-/*42*/ {"vendor unique (42)", 0x00, 0x00, unused, 0, discretional},\
-/*43*/ {"vendor unique (43)", 0x00, 0x00, unused, 0, discretional},\
-/*44*/ {"vendor unique (44)", 0x00, 0x00, unused, 0, discretional},\
-/*45*/ {"vendor unique (45)", 0x00, 0x00, unused, 0, discretional},\
-/*46*/ {"phantom select", 0x00, 0x00, mode, 0, discretional},\
-/*47*/ {"phantom deselect", 0x00, 0x00, mode, 0, discretional},\
-}
-
-/*
- * Status bits returned by QIC_REPORT_DRIVE_STATUS
- */
-
-#define QIC_STATUS_READY 0x01 /* Drive is ready or idle. */
-#define QIC_STATUS_ERROR 0x02 /* Error detected, must read
- error code to clear this */
-#define QIC_STATUS_CARTRIDGE_PRESENT 0x04 /* Tape is present */
-#define QIC_STATUS_WRITE_PROTECT 0x08 /* Tape is write protected */
-#define QIC_STATUS_NEW_CARTRIDGE 0x10 /* New cartridge inserted, must
- read error status to clear. */
-#define QIC_STATUS_REFERENCED 0x20 /* Cartridge appears to have been
- formatted. */
-#define QIC_STATUS_AT_BOT 0x40 /* Cartridge is at physical
- beginning of tape. */
-#define QIC_STATUS_AT_EOT 0x80 /* Cartridge is at physical end
- of tape. */
-/*
- * Status bits returned by QIC_REPORT_DRIVE_CONFIGURATION
- */
-
-#define QIC_CONFIG_RATE_MASK 0x18
-#define QIC_CONFIG_RATE_SHIFT 3
-#define QIC_CONFIG_RATE_250 0
-#define QIC_CONFIG_RATE_500 2
-#define QIC_CONFIG_RATE_1000 3
-#define QIC_CONFIG_RATE_2000 1
-
-#define QIC_CONFIG_LONG 0x40 /* Extra Length Tape Detected */
-#define QIC_CONFIG_80 0x80 /* QIC-80 detected. */
-
-/*
- * Status bits returned by QIC_REPORT_TAPE_STATUS
- */
-
-#define QIC_TAPE_STD_MASK 0x0f
-#define QIC_TAPE_QIC40 0x01
-#define QIC_TAPE_QIC80 0x02
-#define QIC_TAPE_QIC3020 0x03
-#define QIC_TAPE_QIC3010 0x04
-
-#define QIC_TAPE_LEN_MASK 0x70
-#define QIC_TAPE_205FT 0x10
-#define QIC_TAPE_307FT 0x20
-#define QIC_TAPE_400FT 0x30
-#define QIC_TAPE_1100FT 0x40
-#define QIC_TAPE_FLEX 0x60
-
-#define QIC_TAPE_WIDE 0x80
-
-/*
- * Errors: List of error codes, and their severity.
- */
-
-typedef struct {
- char *message; /* Text describing the error. */
- int fatal; /* Non-zero if the error is fatal. */
-} ftape_error;
-
-#define QIC117_ERRORS {\
- /* 0*/ { "No error", 0, },\
- /* 1*/ { "Command Received while Drive Not Ready", 0, },\
- /* 2*/ { "Cartridge Not Present or Removed", 1, },\
- /* 3*/ { "Motor Speed Error (not within 1%)", 1, },\
- /* 4*/ { "Motor Speed Fault (jammed, or gross speed error", 1, },\
- /* 5*/ { "Cartridge Write Protected", 1, },\
- /* 6*/ { "Undefined or Reserved Command Code", 1, },\
- /* 7*/ { "Illegal Track Address Specified for Seek", 1, },\
- /* 8*/ { "Illegal Command in Report Subcontext", 0, },\
- /* 9*/ { "Illegal Entry into a Diagnostic Mode", 1, },\
- /*10*/ { "Broken Tape Detected (based on hole sensor)", 1, },\
- /*11*/ { "Warning--Read Gain Setting Error", 1, },\
- /*12*/ { "Command Received While Error Status Pending (obs)", 1, },\
- /*13*/ { "Command Received While New Cartridge Pending", 1, },\
- /*14*/ { "Command Illegal or Undefined in Primary Mode", 1, },\
- /*15*/ { "Command Illegal or Undefined in Format Mode", 1, },\
- /*16*/ { "Command Illegal or Undefined in Verify Mode", 1, },\
- /*17*/ { "Logical Forward Not a Logical BOT or no Format Segments in Format Mode", 1, },\
- /*18*/ { "Logical EOT Before All Segments generated", 1, },\
- /*19*/ { "Command Illegal When Cartridge Not Referenced", 1, },\
- /*20*/ { "Self-Diagnostic Failed (cannot be cleared)", 1, },\
- /*21*/ { "Warning EEPROM Not Initialized, Defaults Set", 1, },\
- /*22*/ { "EEPROM Corrupted or Hardware Failure", 1, },\
- /*23*/ { "Motion Time-out Error", 1, },\
- /*24*/ { "Data Segment Too Long -- Logical Forward or Pause", 1, },\
- /*25*/ { "Transmit Overrun (obs)", 1, },\
- /*26*/ { "Power On Reset Occurred", 0, },\
- /*27*/ { "Software Reset Occurred", 0, },\
- /*28*/ { "Diagnostic Mode 1 Error", 1, },\
- /*29*/ { "Diagnostic Mode 2 Error", 1, },\
- /*30*/ { "Command Received During Non-Interruptible Process", 1, },\
- /*31*/ { "Rate or Format Selection Error", 1, },\
- /*32*/ { "Illegal Command While in High Speed Mode", 1, },\
- /*33*/ { "Illegal Seek Segment Value", 1, },\
- /*34*/ { "Invalid Media", 1, },\
- /*35*/ { "Head Positioning Failure", 1, },\
- /*36*/ { "Write Reference Burst Failure", 1, },\
- /*37*/ { "Prom Code Missing", 1, },\
- /*38*/ { "Invalid Format", 1, },\
- /*39*/ { "EOT/BOT System Failure", 1, },\
- /*40*/ { "Prom A Checksum Error", 1, },\
- /*41*/ { "Drive Wakeup Reset Occurred", 1, },\
- /*42*/ { "Prom B Checksum Error", 1, },\
- /*43*/ { "Illegal Entry into Format Mode", 1, },\
-}
-
-#endif /* _QIC117_H */
diff --git a/drivers/char/ftape/tracing.c b/drivers/char/ftape/tracing.c
deleted file mode 100644
index a2d1a4268..000000000
--- a/drivers/char/ftape/tracing.c
+++ /dev/null
@@ -1,103 +0,0 @@
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute 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.
-
- *
- * This file contains the reading code
- * for the QIC-117 floppy-tape driver for Linux.
- */
-
-#include <linux/ftape.h>
-
-#include "tracing.h"
-
-/* Global vars.
- */
-/* tracing
- * set it to: to get:
- * 0 bugs
- * 1 + errors
- * 2 + warnings
- * 3 + information
- * 4 + more information
- * 5 + program flow
- * 6 + fdc/dma info
- * 7 + data flow
- * 8 + everything else
- */
-int tracing = 3; /* Default level: report only errors */
-
-#ifndef NO_TRACE_AT_ALL
-
-byte trace_id = 0;
-int function_nest_level = 0;
-
-/* Local vars.
- */
-static char spacing[] = "* ";
-
-int trace_call(int level, char *file, char *name)
-{
- char *indent;
-
- if (tracing >= level && level <= TOP_LEVEL) {
- /* Since printk seems not to work with "%*s" format
- * we'll use this work-around.
- */
- if (function_nest_level < sizeof(spacing)) {
- indent = spacing + sizeof(spacing) - 1 - function_nest_level;
- } else {
- indent = spacing;
- }
- printk(KERN_INFO "[%03d]%s+%s (%s)\n", (int) trace_id++, indent, file, name);
- }
- return function_nest_level++;
-}
-
-void trace_exit(int level, char *file, char *name)
-{
- char *indent;
-
- if (tracing >= level && level <= TOP_LEVEL) {
- /* Since printk seems not to work with "%*s" format
- * we'll use this work-around.
- */
- if (function_nest_level < sizeof(spacing)) {
- indent = spacing + sizeof(spacing) - 1 - function_nest_level;
- } else {
- indent = spacing;
- }
- printk(KERN_INFO "[%03d]%s-%s (%s)\n", (int) trace_id++, indent, file, name);
- }
-}
-
-void trace_log(char *file, char *name)
-{
- char *indent;
-
- /* Since printk seems not to work with "%*s" format
- * we'll use this work-around.
- */
- if (function_nest_level < sizeof(spacing)) {
- indent = spacing + sizeof(spacing) - 1 - function_nest_level;
- } else {
- indent = spacing;
- }
- printk(KERN_INFO "[%03d]%s%s (%s) - ", (int) trace_id++, indent, file, name);
-}
-
-#endif /* NO_TRACE_AT_ALL */
diff --git a/drivers/char/ftape/tracing.h b/drivers/char/ftape/tracing.h
deleted file mode 100644
index 9341bc5af..000000000
--- a/drivers/char/ftape/tracing.h
+++ /dev/null
@@ -1,99 +0,0 @@
-#ifndef _TRACING_H
-#define _TRACING_H
-
-/*
- * Copyright (C) 1994-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute 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.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/tracing.h,v $
- $Author: bas $
- *
- $Revision: 1.10 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
- *
- * This file contains definitions that eases the debugging
- * of the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/kernel.h>
-
-#ifdef NO_TRACE_AT_ALL
-static inline void trace_dummy(void)
-{
-}
-
-#define TRACE_FUN( level, name) int _trace_dummy
-#define TRACE_EXIT _trace_dummy= 0
-#define TRACE_(l,m) trace_dummy()
-#define TRACE(l,m) trace_dummy()
-#define TRACEi(l,m,i) trace_dummy()
-#define TRACElx(l,m,i) trace_dummy()
-#define TRACEx1(l,m,a) trace_dummy()
-#define TRACEx2(l,m,a,b) trace_dummy()
-#define TRACEx3(l,m,a,b,c) trace_dummy()
-#define TRACEx4(l,m,a,b,c,d) trace_dummy()
-#define TRACEx5(l,m,a,b,c,d,e) trace_dummy()
-#define TRACEx6(l,m,a,b,c,d,e,f) trace_dummy()
-#else
-#ifdef NO_TRACE
-#define TOP_LEVEL 2
-#else
-#define TOP_LEVEL 10
-#endif
-
-#define TRACE_FUN( level, name) \
- char _trace_fun[] = name; \
- int _function_nest_level = trace_call( level, __FILE__, _trace_fun); \
- int _tracing = level
-
-#define TRACE_EXIT \
- function_nest_level = _function_nest_level; \
- trace_exit( _tracing, __FILE__, _trace_fun)
-
-#define TRACE_(l,m) \
-{ \
- if (tracing >= (l) && (l) <= TOP_LEVEL) { \
- trace_log( __FILE__, _trace_fun); \
- m; \
- } \
-}
-#define TRACE(l,m) TRACE_(l,printk(m".\n"))
-#define TRACEi(l,m,i) TRACE_(l,printk(m" %d.\n",i))
-#define TRACElx(l,m,i) TRACE_(l,printk(m" 0x%08lx.\n",i))
-#define TRACEx1(l,m,a) TRACE_(l,printk(m".\n",a))
-#define TRACEx2(l,m,a,b) TRACE_(l,printk(m".\n",a,b))
-#define TRACEx3(l,m,a,b,c) TRACE_(l,printk(m".\n",a,b,c))
-#define TRACEx4(l,m,a,b,c,d) TRACE_(l,printk(m".\n",a,b,c,d))
-#define TRACEx5(l,m,a,b,c,d,e) TRACE_(l,printk(m".\n",a,b,c,d,e))
-#define TRACEx6(l,m,a,b,c,d,e,f) TRACE_(l,printk(m".\n",a,b,c,d,e,f))
-
-/* Global variables declared in tracing.c
- */
-extern unsigned char trace_id;
-extern int tracing; /* sets default level */
-extern int function_nest_level;
-
-/* Global functions declared in tracing.c
- */
-extern int trace_call(int level, char *file, char *name);
-extern void trace_exit(int level, char *file, char *name);
-extern void trace_log(char *file, char *name);
-
-#endif /* NO_TRACE_AT_ALL */
-
-#endif
diff --git a/drivers/char/ftape/vendors.h b/drivers/char/ftape/vendors.h
deleted file mode 100644
index 9f8e39839..000000000
--- a/drivers/char/ftape/vendors.h
+++ /dev/null
@@ -1,127 +0,0 @@
-#ifndef _VENDORS_H
-#define _VENDORS_H
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute 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.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/vendors.h,v $
- $Author: bas $
- *
- $Revision: 1.34 $
- $Date: 1995/05/27 08:54:21 $
- $State: Beta $
- *
- * This file contains the supported drive types
- * with their QIC-117 spec. vendor code and
- * drive dependent configuration information.
- */
-
-typedef enum {
- unknown_wake_up = 0,
- no_wake_up,
- wake_up_colorado,
- wake_up_mountain,
- wake_up_insight,
-} wake_up_types;
-
-typedef struct {
- wake_up_types wake_up; /* see wake_up_types */
- char *name; /* Text describing the drive */
-} wakeup_method;
-
-/* Note: order of entries in WAKEUP_METHODS must be so that a variable
- * of type wake_up_types can be used as an index in the array.
- */
-#define WAKEUP_METHODS { \
- { unknown_wake_up, "Unknown" }, \
- { no_wake_up, "None" }, \
- { wake_up_colorado, "Colorado" }, \
- { wake_up_mountain, "Mountain" }, \
- { wake_up_insight, "Motor-on" }, \
-}
-
-typedef struct {
- unsigned int vendor_id; /* vendor id from drive */
- int speed; /* maximum tape transport speed (ips) */
- wake_up_types wake_up; /* see wake_up_types */
- char *name; /* Text describing the drive */
-} vendor_struct;
-
-#define UNKNOWN_VENDOR (-1)
-
-#define QIC117_VENDORS { \
-/* see _vendor_struct */ \
- { 0x00000, 82, wake_up_colorado, "Colorado DJ-10 (old)" }, \
- { 0x00047, 90, wake_up_colorado, "Colorado DJ-10/DJ-20" }, \
- { 0x011c2, 84, wake_up_colorado, "Colorado 700" }, \
- { 0x011c3, 90, wake_up_colorado, "Colorado 1400" }, \
- { 0x011c4, 84, wake_up_colorado, "Colorado DJ-10/DJ-20 (new)" }, \
- { 0x011c5, 84, wake_up_colorado, "HP Colorado T1000" }, \
- { 0x00005, 45, wake_up_mountain, "Archive 5580i" }, \
- { 0x10005, 50, wake_up_insight, "Insight 80Mb, Irwin 80SX" }, \
- { 0x00140, 74, wake_up_mountain, "Archive S.Hornet [Identity/Escom]" }, \
- { 0x00146, 72, wake_up_mountain, "Archive 31250Q [Escom]" }, \
- { 0x0014a, 100, wake_up_mountain, "Archive XL9250i [Conner/Escom]" }, \
- { 0x0014c, 98, wake_up_mountain, "Conner C250MQT" }, \
- { 0x0014e, 80, wake_up_mountain, "Conner C250MQ" }, \
- { 0x00150, 80, wake_up_mountain, "Conner TSM420R/TST800R" }, \
- { 0x00152, 80, wake_up_mountain, "Conner TSM850R" }, \
- { 0x00156, 80, wake_up_mountain, "Conner TSM850R/1700R/TST3200R" }, \
- { 0x00180, 0, wake_up_mountain, "Summit SE 150" }, \
- { 0x00181, 85, wake_up_mountain, "Summit SE 250, Mountain FS8000" }, \
- { 0x001c1, 82, no_wake_up, "Wangtek 3040F" }, \
- { 0x001c8, 64, no_wake_up, "Wangtek 3080F" }, \
- { 0x001c8, 64, wake_up_colorado, "Wangtek 3080F" }, \
- { 0x001ca, 67, no_wake_up, "Wangtek 3080F (new)" }, \
- { 0x001cc, 77, wake_up_colorado, "Wangtek 3200 / Teac 700" }, \
- { 0x001cd, 75, wake_up_colorado, "Reveal TB1400" }, \
- { 0x00380, 0, wake_up_colorado, "Exabyte EXB-1500" }, \
- { 0x08880, 64, no_wake_up, "Iomega 250" }, \
- { 0x08880, 64, wake_up_colorado, "Iomega 250, Ditto 800" }, \
- { 0x08880, 64, wake_up_insight, "Iomega 250" }, \
- { 0x08881, 0, wake_up_colorado, "Iomega 700" }, \
- { 0x08882, 80, wake_up_colorado, "Iomega 3200" }, \
- { 0x00021, 70, no_wake_up, "AIWA CT-803" }, \
- { 0x00021, 0, wake_up_mountain, "COREtape QIC80" }, \
-}
-
-#define QIC117_MAKE_CODES { \
- { 0, "Unassigned" }, \
- { 1, "Alloy Computer Products" }, \
- { 2, "3M" }, \
- { 3, "Tandberg Data" }, \
- { 4, "Colorado" }, \
- { 5, "Archive/Conner" }, \
- { 6, "Mountain/Summit Memory Systems" }, \
- { 7, "Wangtek/Rexon/Tecmar" }, \
- { 8, "Sony" }, \
- { 9, "Cipher Data Products" }, \
- { 10, "Irwin Magnetic Systems" }, \
- { 11, "Braemar" }, \
- { 12, "Verbatim" }, \
- { 13, "Core International" }, \
- { 14, "Exabyte" }, \
- { 15, "Teac" }, \
- { 16, "Gigatek" }, \
- { 17, "ComByte" }, \
- { 18, "PERTEC Memories" }, \
- { 71, "Colorado" }, \
- { 546, "Iomega Inc" }, \
-}
-
-#endif
diff --git a/drivers/char/ftape/zftape/.cvsignore b/drivers/char/ftape/zftape/.cvsignore
new file mode 100644
index 000000000..4671378ae
--- /dev/null
+++ b/drivers/char/ftape/zftape/.cvsignore
@@ -0,0 +1 @@
+.depend
diff --git a/drivers/char/ftape/zftape/Makefile b/drivers/char/ftape/zftape/Makefile
new file mode 100644
index 000000000..f0cb73a96
--- /dev/null
+++ b/drivers/char/ftape/zftape/Makefile
@@ -0,0 +1,54 @@
+#
+# Copyright (C) 1996, 1997 Claus-Justus Heine.
+#
+# This program is free software; you can redistribute 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.
+#
+# $Source: /homes/cvs/ftape-stacked/ftape/zftape/Makefile,v $
+# $Revision: 1.4 $
+# $Date: 1997/10/05 19:18:58 $
+#
+# Makefile for the QIC-40/80/3010/3020 zftape interface VFS to
+# ftape
+#
+
+#
+# This isn't used inside the kernel, only for my private development
+# version
+#
+ifndef TOPDIR
+TOPDIR=../..
+include $(TOPDIR)/MCONFIG
+endif
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+# ZFT_OBSOLETE - enable the MTIOC_ZFTAPE_GETBLKSZ ioctl. You should
+# leave this enabled for compatibility with taper.
+
+EXTRA_CFLAGS := -DZFT_OBSOLETE
+
+O_TARGET := zftape.o
+O_OBJS := zftape-rw.o zftape-ctl.o zftape-read.o \
+ zftape-write.o zftape-vtbl.o zftape-eof.o \
+ zftape-init.o zftape-buffers.o
+
+OX_OBJS += zftape_syms.o
+
+M_OBJS := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
+
diff --git a/drivers/char/ftape/zftape/zftape-buffers.c b/drivers/char/ftape/zftape/zftape-buffers.c
new file mode 100644
index 000000000..13594d02a
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-buffers.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 1995-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-buffers.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:59 $
+ *
+ * This file contains the dynamic buffer allocation routines
+ * of zftape
+ */
+
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <asm/segment.h>
+
+#include <linux/zftape.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,0)
+#include <linux/vmalloc.h>
+#endif
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-vtbl.h"
+
+/* global variables
+ */
+
+/* local varibales
+ */
+static unsigned int used_memory = 0;
+static unsigned int peak_memory = 0;
+
+void zft_memory_stats(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "Memory usage (vmalloc allocations):\n"
+ KERN_INFO "total allocated: %d\n"
+ KERN_INFO "peak allocation: %d",
+ used_memory, peak_memory);
+ peak_memory = used_memory;
+ TRACE_EXIT;
+}
+
+int zft_vcalloc_once(void *new, size_t size)
+{
+ TRACE_FUN(ft_t_flow);
+ if (zft_vmalloc_once(new, size) < 0) {
+ TRACE_EXIT -ENOMEM;
+ }
+ memset(*(void **)new, '\0', size);
+ TRACE_EXIT 0;
+}
+int zft_vmalloc_once(void *new, size_t size)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (*(void **)new != NULL || size == 0) {
+ TRACE_EXIT 0;
+ }
+ if ((*(void **)new = vmalloc(size)) == NULL) {
+ TRACE_EXIT -ENOMEM;
+ }
+ used_memory += size;
+ if (peak_memory < used_memory) {
+ peak_memory = used_memory;
+ }
+ TRACE_ABORT(0, ft_t_noise,
+ "allocated buffer @ %p, %d bytes", *(void **)new, size);
+}
+int zft_vcalloc_always(void *new, size_t size)
+{
+ TRACE_FUN(ft_t_flow);
+
+ zft_vfree(new, size);
+ TRACE_EXIT zft_vcalloc_once(new, size);
+}
+int zft_vmalloc_always(void *new, size_t size)
+{
+ TRACE_FUN(ft_t_flow);
+
+ zft_vfree(new, size);
+ TRACE_EXIT zft_vmalloc_once(new, size);
+}
+void zft_vfree(void *old, size_t size)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (*(void **)old) {
+ vfree(*(void **)old);
+ used_memory -= size;
+ TRACE(ft_t_noise, "released buffer @ %p, %d bytes",
+ *(void **)old, size);
+ *(void **)old = NULL;
+ }
+ TRACE_EXIT;
+}
+
+void *zft_kmalloc(size_t size)
+{
+ void *new;
+
+ while ((new = kmalloc(size, GFP_KERNEL)) == NULL) {
+ current->timeout = HZ/10;
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ }
+ memset(new, 0, size);
+ used_memory += size;
+ if (peak_memory < used_memory) {
+ peak_memory = used_memory;
+ }
+ return new;
+}
+
+void zft_kfree(void *old, size_t size)
+{
+ kfree(old);
+ used_memory -= size;
+}
+
+/* there are some more buffers that are allocated on demand.
+ * cleanup_module() calles this function to be sure to have released
+ * them
+ */
+void zft_uninit_mem(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ zft_vfree(&zft_hseg_buf, FT_SEGMENT_SIZE);
+ zft_vfree(&zft_deblock_buf, FT_SEGMENT_SIZE); zft_deblock_segment = -1;
+ zft_free_vtbl();
+ if (zft_cmpr_lock(0 /* don't load */) == 0) {
+ (*zft_cmpr_ops->cleanup)();
+ (*zft_cmpr_ops->reset)(); /* unlock it again */
+ }
+ zft_memory_stats();
+ TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/zftape/zftape-buffers.h b/drivers/char/ftape/zftape/zftape-buffers.h
new file mode 100644
index 000000000..c61498495
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-buffers.h
@@ -0,0 +1,56 @@
+#ifndef _FTAPE_DYNMEM_H
+#define _FTAPE_DYNMEM_H
+
+/*
+ * Copyright (C) 1995-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-buffers.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:59 $
+ *
+ * memory allocation routines.
+ *
+ */
+
+/* we do not allocate all of the really large buffer memory before
+ * someone tries to open the drive. ftape_open() may fail with
+ * -ENOMEM, but that's better having 200k of vmalloced memory which
+ * cannot be swapped out.
+ */
+
+extern void zft_memory_stats(void);
+extern int zft_vmalloc_once(void *new, size_t size);
+extern int zft_vcalloc_once(void *new, size_t size);
+extern int zft_vmalloc_always(void *new, size_t size);
+extern int zft_vcalloc_always(void *new, size_t size);
+extern void zft_vfree(void *old, size_t size);
+extern void *zft_kmalloc(size_t size);
+extern void zft_kfree(void *old, size_t size);
+
+/* called by cleanup_module()
+ */
+extern void zft_uninit_mem(void);
+
+#endif
+
+
+
+
+
+
+
diff --git a/drivers/char/ftape/zftape/zftape-ctl.c b/drivers/char/ftape/zftape/zftape-ctl.c
new file mode 100644
index 000000000..19d10c95b
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-ctl.c
@@ -0,0 +1,1502 @@
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-ctl.c,v $
+ * $Revision: 1.2.6.2 $
+ * $Date: 1997/11/14 18:07:33 $
+ *
+ * This file contains the non-read/write zftape functions
+ * for the QIC-40/80/3010/3020 floppy-tape driver for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+#include <linux/fcntl.h>
+
+#include <linux/zftape.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6)
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#endif
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-vtbl.h"
+
+/* Global vars.
+ */
+int zft_write_protected = 0; /* this is when cartridge rdonly or O_RDONLY */
+int zft_header_read = 0;
+int zft_offline = 0;
+unsigned int zft_unit = 0;
+int zft_resid = 0;
+int zft_mt_compression = 0;
+
+/* Local vars.
+ */
+static int going_offline = 0;
+
+typedef int (mt_fun)(int *argptr);
+typedef int (*mt_funp)(int *argptr);
+typedef struct
+{
+ mt_funp function;
+ unsigned offline : 1; /* op permitted if offline or no_tape */
+ unsigned write_protected : 1; /* op permitted if write-protected */
+ unsigned not_formatted : 1; /* op permitted if tape not formatted */
+ unsigned raw_mode : 1; /* op permitted if zft_mode == 0 */
+ unsigned need_idle_state : 1; /* need to call def_idle_state */
+ char *name;
+} fun_entry;
+
+static mt_fun mt_dummy, mt_reset, mt_fsr, mt_bsr, mt_rew, mt_offl, mt_nop,
+ mt_weof, mt_erase, mt_ras2, mt_setblk, mt_setdensity,
+ mt_seek, mt_tell, mt_reten, mt_eom, mt_fsf, mt_bsf,
+ mt_fsfm, mt_bsfm, mt_setdrvbuffer, mt_compression;
+
+static fun_entry mt_funs[]=
+{
+ {mt_reset , 1, 1, 1, 1, 0, "MT_RESET" }, /* 0 */
+ {mt_fsf , 0, 1, 0, 0, 1, "MT_FSF" },
+ {mt_bsf , 0, 1, 0, 0, 1, "MT_BSF" },
+ {mt_fsr , 0, 1, 0, 1, 1, "MT_FSR" },
+ {mt_bsr , 0, 1, 0, 1, 1, "MT_BSR" },
+ {mt_weof , 0, 0, 0, 0, 0, "MT_WEOF" }, /* 5 */
+ {mt_rew , 0, 1, 1, 1, 0, "MT_REW" },
+ {mt_offl , 0, 1, 1, 1, 0, "MT_OFFL" },
+ {mt_nop , 1, 1, 1, 1, 0, "MT_NOP" },
+ {mt_reten , 0, 1, 1, 1, 0, "MT_RETEN" },
+ {mt_bsfm , 0, 1, 0, 0, 1, "MT_BSFM" }, /* 10 */
+ {mt_fsfm , 0, 1, 0, 0, 1, "MT_FSFM" },
+ {mt_eom , 0, 1, 0, 0, 1, "MT_EOM" },
+ {mt_erase , 0, 0, 0, 1, 0, "MT_ERASE" },
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_RAS1" },
+ {mt_ras2 , 0, 0, 0, 1, 0, "MT_RAS2" },
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_RAS3" },
+ {mt_dummy , 1, 1, 1, 1, 0, "UNKNOWN" },
+ {mt_dummy , 1, 1, 1, 1, 0, "UNKNOWN" },
+ {mt_dummy , 1, 1, 1, 1, 0, "UNKNOWN" },
+ {mt_setblk , 1, 1, 1, 1, 1, "MT_SETBLK"}, /* 20 */
+ {mt_setdensity , 1, 1, 1, 1, 0, "MT_SETDENSITY"},
+ {mt_seek , 0, 1, 0, 1, 1, "MT_SEEK" },
+ {mt_dummy , 0, 1, 0, 1, 1, "MT_TELL" }, /* wr-only ?! */
+ {mt_setdrvbuffer, 1, 1, 1, 1, 0, "MT_SETDRVBUFFER" },
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_FSS" }, /* 25 */
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_BSS" },
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_WSM" },
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_LOCK" },
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_UNLOCK"},
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_LOAD" }, /* 30 */
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_UNLOAD"},
+ {mt_compression , 1, 1, 1, 0, 1, "MT_COMPRESSION"},
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_SETPART"},
+ {mt_dummy , 1, 1, 1, 1, 0, "MT_MKPART"}
+};
+
+#define NR_MT_CMDS NR_ITEMS(mt_funs)
+
+void zft_reset_position(zft_position *pos)
+{
+ TRACE_FUN(ft_t_flow);
+
+ pos->seg_byte_pos =
+ pos->volume_pos = 0;
+ if (zft_header_read) {
+ /* need to keep track of the volume table and
+ * compression map. We therefor simply
+ * position at the beginning of the first
+ * volume. This covers old ftape archives as
+ * well has various flavours of the
+ * compression map segments. The worst case is
+ * that the compression map shows up as a
+ * additional volume in front of all others.
+ */
+ pos->seg_pos = zft_find_volume(0)->start_seg;
+ pos->tape_pos = zft_calc_tape_pos(pos->seg_pos);
+ } else {
+ pos->tape_pos = 0;
+ pos->seg_pos = -1;
+ }
+ zft_just_before_eof = 0;
+ zft_deblock_segment = -1;
+ zft_io_state = zft_idle;
+ zft_zap_read_buffers();
+ zft_prevent_flush();
+ /* unlock the compresison module if it is loaded.
+ * The zero arg means not to try to load the module.
+ */
+ if (zft_cmpr_lock(0) == 0) {
+ (*zft_cmpr_ops->reset)(); /* unlock */
+ }
+ TRACE_EXIT;
+}
+
+static void zft_init_driver(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ zft_resid =
+ zft_header_read =
+ zft_old_ftape =
+ zft_offline =
+ zft_write_protected =
+ going_offline =
+ zft_mt_compression =
+ zft_header_changed =
+ zft_volume_table_changed =
+ zft_written_segments = 0;
+ zft_blk_sz = CONFIG_ZFT_DFLT_BLK_SZ;
+ zft_reset_position(&zft_pos); /* does most of the stuff */
+ ftape_zap_read_buffers();
+ ftape_set_state(idle);
+ TRACE_EXIT;
+}
+
+int zft_def_idle_state(void)
+{
+ int result = 0;
+ TRACE_FUN(ft_t_flow);
+
+ if (!zft_header_read) {
+ result = zft_read_header_segments();
+ } else if ((result = zft_flush_buffers()) >= 0 && zft_qic_mode) {
+ /* don't move past eof
+ */
+ (void)zft_close_volume(&zft_pos);
+ }
+ if (ftape_abort_operation() < 0) {
+ TRACE(ft_t_warn, "ftape_abort_operation() failed");
+ result = -EIO;
+ }
+ /* clear remaining read buffers */
+ zft_zap_read_buffers();
+ zft_io_state = zft_idle;
+ TRACE_EXIT result;
+}
+
+/*****************************************************************************
+ * *
+ * functions for the MTIOCTOP commands *
+ * *
+ *****************************************************************************/
+
+static int mt_dummy(int *dummy)
+{
+ TRACE_FUN(ft_t_flow);
+
+ TRACE_EXIT -ENOSYS;
+}
+
+static int mt_reset(int *dummy)
+{
+ TRACE_FUN(ft_t_flow);
+
+ (void)ftape_seek_to_bot();
+ TRACE_CATCH(ftape_reset_drive(),
+ zft_init_driver(); zft_uninit_mem(); zft_offline = 1);
+ /* fake a re-open of the device. This will set all flage and
+ * allocate buffers as appropriate. The new tape condition will
+ * force the open routine to do anything we need.
+ */
+ TRACE_CATCH(_zft_open(-1 /* fake reopen */, 0 /* dummy */),);
+ TRACE_EXIT 0;
+}
+
+static int mt_fsf(int *arg)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ result = zft_skip_volumes(*arg, &zft_pos);
+ zft_just_before_eof = 0;
+ TRACE_EXIT result;
+}
+
+static int mt_bsf(int *arg)
+{
+ int result = 0;
+ TRACE_FUN(ft_t_flow);
+
+ if (*arg != 0) {
+ result = zft_skip_volumes(-*arg + 1, &zft_pos);
+ }
+ TRACE_EXIT result;
+}
+
+static int seek_block(__s64 data_offset,
+ __s64 block_increment,
+ zft_position *pos)
+{
+ int result = 0;
+ __s64 new_block_pos;
+ __s64 vol_block_count;
+ const zft_volinfo *volume;
+ int exceed;
+ TRACE_FUN(ft_t_flow);
+
+ volume = zft_find_volume(pos->seg_pos);
+ if (volume->start_seg == 0 || volume->end_seg == 0) {
+ TRACE_EXIT -EIO;
+ }
+ new_block_pos = (zft_div_blksz(data_offset, volume->blk_sz)
+ + block_increment);
+ vol_block_count = zft_div_blksz(volume->size, volume->blk_sz);
+ if (new_block_pos < 0) {
+ TRACE(ft_t_noise,
+ "new_block_pos " LL_X " < 0", LL(new_block_pos));
+ zft_resid = (int)new_block_pos;
+ new_block_pos = 0;
+ exceed = 1;
+ } else if (new_block_pos > vol_block_count) {
+ TRACE(ft_t_noise,
+ "new_block_pos " LL_X " exceeds size of volume " LL_X,
+ LL(new_block_pos), LL(vol_block_count));
+ zft_resid = (int)(vol_block_count - new_block_pos);
+ new_block_pos = vol_block_count;
+ exceed = 1;
+ } else {
+ exceed = 0;
+ }
+ if (zft_use_compression && volume->use_compression) {
+ TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),);
+ result = (*zft_cmpr_ops->seek)(new_block_pos, pos, volume,
+ zft_deblock_buf);
+ pos->tape_pos = zft_calc_tape_pos(pos->seg_pos);
+ pos->tape_pos += pos->seg_byte_pos;
+ } else {
+ pos->volume_pos = zft_mul_blksz(new_block_pos, volume->blk_sz);
+ pos->tape_pos = zft_calc_tape_pos(volume->start_seg);
+ pos->tape_pos += pos->volume_pos;
+ pos->seg_pos = zft_calc_seg_byte_coord(&pos->seg_byte_pos,
+ pos->tape_pos);
+ }
+ zft_just_before_eof = volume->size == pos->volume_pos;
+ if (zft_just_before_eof) {
+ /* why this? because zft_file_no checks agains start
+ * and end segment of a volume. We do not want to
+ * advance to the next volume with this function.
+ */
+ TRACE(ft_t_noise, "set zft_just_before_eof");
+ zft_position_before_eof(pos, volume);
+ }
+ TRACE(ft_t_noise, "\n"
+ KERN_INFO "new_seg_pos : %d\n"
+ KERN_INFO "new_tape_pos: " LL_X "\n"
+ KERN_INFO "vol_size : " LL_X "\n"
+ KERN_INFO "seg_byte_pos: %d\n"
+ KERN_INFO "blk_sz : %d",
+ pos->seg_pos, LL(pos->tape_pos),
+ LL(volume->size), pos->seg_byte_pos,
+ volume->blk_sz);
+ if (!exceed) {
+ zft_resid = new_block_pos - zft_div_blksz(pos->volume_pos,
+ volume->blk_sz);
+ }
+ if (zft_resid < 0) {
+ zft_resid = -zft_resid;
+ }
+ TRACE_EXIT ((exceed || zft_resid != 0) && result >= 0) ? -EINVAL : result;
+}
+
+static int mt_fsr(int *arg)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ result = seek_block(zft_pos.volume_pos, *arg, &zft_pos);
+ TRACE_EXIT result;
+}
+
+static int mt_bsr(int *arg)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ result = seek_block(zft_pos.volume_pos, -*arg, &zft_pos);
+ TRACE_EXIT result;
+}
+
+static int mt_weof(int *arg)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE_CATCH(zft_flush_buffers(),);
+ result = zft_weof(*arg, &zft_pos);
+ TRACE_EXIT result;
+}
+
+static int mt_rew(int *dummy)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ if(zft_header_read) {
+ (void)zft_def_idle_state();
+ }
+ result = ftape_seek_to_bot();
+ zft_reset_position(&zft_pos);
+ TRACE_EXIT result;
+}
+
+static int mt_offl(int *dummy)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ going_offline= 1;
+ result = mt_rew(NULL);
+ TRACE_EXIT result;
+}
+
+static int mt_nop(int *dummy)
+{
+ TRACE_FUN(ft_t_flow);
+ /* should we set tape status?
+ */
+ if (!zft_offline) { /* offline includes no_tape */
+ (void)zft_def_idle_state();
+ }
+ TRACE_EXIT 0;
+}
+
+static int mt_reten(int *dummy)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ if(zft_header_read) {
+ (void)zft_def_idle_state();
+ }
+ result = ftape_seek_to_eot();
+ if (result >= 0) {
+ result = ftape_seek_to_bot();
+ }
+ TRACE_EXIT(result);
+}
+
+static int fsfbsfm(int arg, zft_position *pos)
+{
+ const zft_volinfo *vtbl;
+ __s64 block_pos;
+ TRACE_FUN(ft_t_flow);
+
+ /* What to do? This should seek to the next file-mark and
+ * position BEFORE. That is, a next write would just extend
+ * the current file. Well. Let's just seek to the end of the
+ * current file, if count == 1. If count > 1, then do a
+ * "mt_fsf(count - 1)", and then seek to the end of that file.
+ * If count == 0, do nothing
+ */
+ if (arg == 0) {
+ TRACE_EXIT 0;
+ }
+ zft_just_before_eof = 0;
+ TRACE_CATCH(zft_skip_volumes(arg < 0 ? arg : arg-1, pos),
+ if (arg > 0) {
+ zft_resid ++;
+ });
+ vtbl = zft_find_volume(pos->seg_pos);
+ block_pos = zft_div_blksz(vtbl->size, vtbl->blk_sz);
+ (void)seek_block(0, block_pos, pos);
+ if (pos->volume_pos != vtbl->size) {
+ zft_just_before_eof = 0;
+ zft_resid = 1;
+ /* we didn't managed to go there */
+ TRACE_ABORT(-EIO, ft_t_err,
+ "wanted file position " LL_X ", arrived at " LL_X,
+ LL(vtbl->size), LL(pos->volume_pos));
+ }
+ zft_just_before_eof = 1;
+ TRACE_EXIT 0;
+}
+
+static int mt_bsfm(int *arg)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ result = fsfbsfm(-*arg, &zft_pos);
+ TRACE_EXIT result;
+}
+
+static int mt_fsfm(int *arg)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ result = fsfbsfm(*arg, &zft_pos);
+ TRACE_EXIT result;
+}
+
+static int mt_eom(int *dummy)
+{
+ TRACE_FUN(ft_t_flow);
+
+ zft_skip_to_eom(&zft_pos);
+ TRACE_EXIT 0;
+}
+
+static int mt_erase(int *dummy)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ result = zft_erase();
+ TRACE_EXIT result;
+}
+
+static int mt_ras2(int *dummy)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ result = -ENOSYS;
+ TRACE_EXIT result;
+}
+
+/* Sets the new blocksize in BYTES
+ *
+ */
+static int mt_setblk(int *new_size)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if((unsigned int)(*new_size) > ZFT_MAX_BLK_SZ) {
+ TRACE_ABORT(-EINVAL, ft_t_info,
+ "desired blk_sz (%d) should be <= %d bytes",
+ *new_size, ZFT_MAX_BLK_SZ);
+ }
+ if ((*new_size & (FT_SECTOR_SIZE-1)) != 0) {
+ TRACE_ABORT(-EINVAL, ft_t_info,
+ "desired blk_sz (%d) must be a multiple of %d bytes",
+ *new_size, FT_SECTOR_SIZE);
+ }
+ if (*new_size == 0) {
+ if (zft_use_compression) {
+ TRACE_ABORT(-EINVAL, ft_t_info,
+ "Variable block size not yet "
+ "supported with compression");
+ }
+ *new_size = 1;
+ }
+ zft_blk_sz = *new_size;
+ TRACE_EXIT 0;
+}
+
+static int mt_setdensity(int *arg)
+{
+ TRACE_FUN(ft_t_flow);
+
+ SET_TRACE_LEVEL(*arg);
+ TRACE(TRACE_LEVEL, "tracing set to %d", TRACE_LEVEL);
+ if ((int)TRACE_LEVEL != *arg) {
+ TRACE_EXIT -EINVAL;
+ }
+ TRACE_EXIT 0;
+}
+
+static int mt_seek(int *new_block_pos)
+{
+ int result= 0;
+ TRACE_FUN(ft_t_any);
+
+ result = seek_block(0, (__s64)*new_block_pos, &zft_pos);
+ TRACE_EXIT result;
+}
+
+/* OK, this is totally different from SCSI, but the worst thing that can
+ * happen is that there is not enough defragmentated memory that can be
+ * allocated. Also, there is a hardwired limit of 16 dma buffers in the
+ * stock ftape module. This shouldn't bring the system down.
+ *
+ * NOTE: the argument specifies the total number of dma buffers to use.
+ * The driver needs at least 3 buffers to function at all.
+ *
+ */
+static int mt_setdrvbuffer(int *cnt)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (*cnt < 3) {
+ TRACE_EXIT -EINVAL;
+ }
+ TRACE_CATCH(ftape_set_nr_buffers(*cnt),);
+ TRACE_EXIT 0;
+}
+/* return the block position from start of volume
+ */
+static int mt_tell(int *arg)
+{
+ TRACE_FUN(ft_t_flow);
+
+ *arg = zft_div_blksz(zft_pos.volume_pos,
+ zft_find_volume(zft_pos.seg_pos)->blk_sz);
+ TRACE_EXIT 0;
+}
+
+static int mt_compression(int *arg)
+{
+ TRACE_FUN(ft_t_flow);
+
+ /* Ok. We could also check whether compression is available at
+ * all by trying to load the compression module. We could
+ * also check for a block size of 1 byte which is illegal
+ * with compression. Instead of doing it here we rely on
+ * zftape_write() to do the proper checks.
+ */
+ if ((unsigned int)*arg > 1) {
+ TRACE_EXIT -EINVAL;
+ }
+ if (*arg != 0 && zft_blk_sz == 1) { /* variable block size */
+ TRACE_ABORT(-EINVAL, ft_t_info,
+ "Compression not yet supported "
+ "with variable block size");
+ }
+ zft_mt_compression = *arg;
+ if ((zft_unit & ZFT_ZIP_MODE) == 0) {
+ zft_use_compression = zft_mt_compression;
+ }
+ TRACE_EXIT 0;
+}
+
+/* check whether write access is allowed. Write access is denied when
+ * + zft_write_protected == 1 -- this accounts for either hard write
+ * protection of the cartridge or for
+ * O_RDONLY access mode of the tape device
+ * + zft_offline == 1 -- this meany that there is either no tape
+ * or that the MTOFFLINE ioctl has been
+ * previously issued (`soft eject')
+ * + ft_formatted == 0 -- this means that the cartridge is not
+ * formatted
+ * Then we distinuguish two cases. When zft_qic_mode is TRUE, then we try
+ * to emulate a `traditional' (aka SCSI like) UN*X tape device. Therefore we
+ * deny writes when
+ * + zft_qic_mode ==1 &&
+ * (!zft_tape_at_lbot() && -- tape no at logical BOT
+ * !(zft_tape_at_eom() || -- tape not at logical EOM (or EOD)
+ * (zft_tape_at_eom() &&
+ * zft_old_ftape()))) -- we can't add new volume to tapes
+ * written by old ftape because ftape
+ * don't use the volume table
+ *
+ * when the drive is in true raw mode (aka /dev/rawft0) then we don't
+ * care about LBOT and EOM conditions. This device is intended for a
+ * user level program that wants to truly implement the QIC-80 compliance
+ * at the logical data layout level of the cartridge, i.e. implement all
+ * that volume table and volume directory stuff etc.<
+ */
+int zft_check_write_access(zft_position *pos)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (zft_offline) { /* offline includes no_tape */
+ TRACE_ABORT(-ENXIO,
+ ft_t_info, "tape is offline or no cartridge");
+ }
+ if (!ft_formatted) {
+ TRACE_ABORT(-EACCES, ft_t_info, "tape is not formatted");
+ }
+ if (zft_write_protected) {
+ TRACE_ABORT(-EACCES, ft_t_info, "cartridge write protected");
+ }
+ if (zft_qic_mode) {
+ /* check BOT condition */
+ if (!zft_tape_at_lbot(pos)) {
+ /* protect cartridges written by old ftape if
+ * not at BOT because they use the vtbl
+ * segment for storing data
+ */
+ if (zft_old_ftape) {
+ TRACE_ABORT(-EACCES, ft_t_warn,
+ "Cannot write to cartridges written by old ftape when not at BOT");
+ }
+ /* not at BOT, but allow writes at EOD, of course
+ */
+ if (!zft_tape_at_eod(pos)) {
+ TRACE_ABORT(-EACCES, ft_t_info,
+ "tape not at BOT and not at EOD");
+ }
+ }
+ /* fine. Now the tape is either at BOT or at EOD. */
+ }
+ /* or in raw mode in which case we don't care about BOT and EOD */
+ TRACE_EXIT 0;
+}
+
+/* decide when we should lock the module in memory, even when calling
+ * the release routine. This really is necessary for use with
+ * kerneld.
+ *
+ * NOTE: we MUST NOT use zft_write_protected, because this includes
+ * the file access mode as well which has no meaning with our
+ * asynchronous update scheme.
+ *
+ * Ugly, ugly. We need to look the module if we changed the block size.
+ * How sad! Need persistent modules storage!
+ *
+ * NOTE: I don't want to lock the module if the number of dma buffers
+ * has been changed. It's enough! Stop the story! Give me persisitent
+ * module storage! Do it!
+ */
+int zft_dirty(void)
+{
+ if (!ft_formatted || zft_offline) {
+ /* cannot be dirty if not formatted or offline */
+ return 0;
+ }
+ if (zft_blk_sz != CONFIG_ZFT_DFLT_BLK_SZ) {
+ /* blocksize changed, must lock */
+ return 1;
+ }
+ if (zft_mt_compression != 0) {
+ /* compression mode with /dev/qft, must lock */
+ return 1;
+ }
+ if (!zft_header_read) {
+ /* tape is logical at BOT, no lock */
+ return 0;
+ }
+ if (!zft_tape_at_lbot(&zft_pos)) {
+ /* somewhere inside a volume, lock tape */
+ return 1;
+ }
+ if (zft_volume_table_changed || zft_header_changed) {
+ /* header segments dirty if tape not write protected */
+ return !(ft_write_protected || zft_old_ftape);
+ }
+ return 0;
+}
+
+/* OPEN routine called by kernel-interface code
+ *
+ * NOTE: this is also called by mt_reset() with dev_minor == -1
+ * to fake a reopen after a reset.
+ */
+int _zft_open(unsigned int dev_minor, unsigned int access_mode)
+{
+ static unsigned int tape_unit;
+ static unsigned int file_access_mode;
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ if ((int)dev_minor == -1) {
+ /* fake reopen */
+ zft_unit = tape_unit;
+ access_mode = file_access_mode;
+ zft_init_driver(); /* reset all static data to defaults */
+ } else {
+ tape_unit = dev_minor;
+ file_access_mode = access_mode;
+ if ((result = ftape_enable(FTAPE_SEL(dev_minor))) < 0) {
+ TRACE_ABORT(-ENXIO, ft_t_err,
+ "ftape_enable failed: %d", result);
+ }
+ if (ft_new_tape || ft_no_tape || !ft_formatted ||
+ (FTAPE_SEL(zft_unit) != FTAPE_SEL(dev_minor)) ||
+ (zft_unit & ZFT_RAW_MODE) != (dev_minor & ZFT_RAW_MODE)) {
+ /* reset all static data to defaults,
+ */
+ zft_init_driver();
+ }
+ zft_unit = dev_minor;
+ }
+ zft_set_flags(zft_unit); /* decode the minor bits */
+ if (zft_blk_sz == 1 && zft_use_compression) {
+ ftape_disable(); /* resets ft_no_tape */
+ TRACE_ABORT(-ENODEV, ft_t_warn, "Variable block size not yet "
+ "supported with compression");
+ }
+ /* no need for most of the buffers when no tape or not
+ * formatted. for the read/write operations, it is the
+ * regardless whether there is no tape, a not-formatted tape
+ * or the whether the driver is soft offline.
+ * Nevertheless we allow some ioctls with non-formatted tapes,
+ * like rewind and reset.
+ */
+ if (ft_no_tape || !ft_formatted) {
+ zft_uninit_mem();
+ }
+ if (ft_no_tape) {
+ zft_offline = 1; /* so we need not test two variables */
+ }
+ if ((access_mode == O_WRONLY || access_mode == O_RDWR) &&
+ (ft_write_protected || ft_no_tape)) {
+ ftape_disable(); /* resets ft_no_tape */
+ TRACE_ABORT(ft_no_tape ? -ENXIO : -EROFS,
+ ft_t_warn, "wrong access mode %s cartridge",
+ ft_no_tape ? "without a" : "with write protected");
+ }
+ zft_write_protected = (access_mode == O_RDONLY ||
+ ft_write_protected != 0);
+ if (zft_write_protected) {
+ TRACE(ft_t_noise,
+ "read only access mode: %d, "
+ "drive write protected: %d",
+ access_mode == O_RDONLY,
+ ft_write_protected != 0);
+ }
+ if (!zft_offline) {
+ TRACE_CATCH(zft_vmalloc_once(&zft_deblock_buf,FT_SEGMENT_SIZE),
+ ftape_disable());
+ }
+ /* zft_seg_pos should be greater than the vtbl segpos but not
+ * if in compatability mode and only after we read in the
+ * header segments
+ *
+ * might also be a problem if the user makes a backup with a
+ * *qft* device and rewinds it with a raw device.
+ */
+ if (zft_qic_mode &&
+ !zft_old_ftape &&
+ zft_pos.seg_pos >= 0 &&
+ zft_header_read &&
+ zft_pos.seg_pos <= ft_first_data_segment) {
+ TRACE(ft_t_noise, "you probably mixed up the zftape devices!");
+ zft_reset_position(&zft_pos);
+ }
+ TRACE_EXIT 0;
+}
+
+/* RELEASE routine called by kernel-interface code
+ */
+int _zft_close(void)
+{
+ int result = 0;
+ TRACE_FUN(ft_t_flow);
+
+ if (zft_offline) {
+ /* call the hardware release routine. Puts the drive offline */
+ ftape_disable();
+ TRACE_EXIT 0;
+ }
+ if (!(ft_write_protected || zft_old_ftape)) {
+ result = zft_flush_buffers();
+ TRACE(ft_t_noise, "writing file mark at current position");
+ if (zft_qic_mode && zft_close_volume(&zft_pos) == 0) {
+ zft_move_past_eof(&zft_pos);
+ }
+ if ((zft_tape_at_lbot(&zft_pos) ||
+ !(zft_unit & FTAPE_NO_REWIND))) {
+ if (result >= 0) {
+ result = zft_update_header_segments();
+ } else {
+ TRACE(ft_t_err,
+ "Error: unable to update header segments");
+ }
+ }
+ }
+ ftape_abort_operation();
+ if (!(zft_unit & FTAPE_NO_REWIND)) {
+ TRACE(ft_t_noise, "rewinding tape");
+ if (ftape_seek_to_bot() < 0 && result >= 0) {
+ result = -EIO; /* keep old value */
+ }
+ zft_reset_position(&zft_pos);
+ }
+ zft_zap_read_buffers();
+ /* now free up memory as much as possible. We don't destroy
+ * the deblock buffer if it containes a valid segment.
+ */
+ if (zft_deblock_segment == -1) {
+ zft_vfree(&zft_deblock_buf, FT_SEGMENT_SIZE);
+ }
+ /* high level driver status, forces creation of a new volume
+ * when calling ftape_write again and not zft_just_before_eof
+ */
+ zft_io_state = zft_idle;
+ if (going_offline) {
+ zft_init_driver();
+ zft_uninit_mem();
+ going_offline = 0;
+ zft_offline = 1;
+ } else if (zft_dirty()) {
+ TRACE(ft_t_noise, "Keeping module locked in memory because:\n"
+ KERN_INFO "header segments need updating: %s\n"
+ KERN_INFO "tape not at BOT : %s",
+ (zft_volume_table_changed || zft_header_changed)
+ ? "yes" : "no",
+ zft_tape_at_lbot(&zft_pos) ? "no" : "yes");
+ } else if (zft_cmpr_lock(0 /* don't load */) == 0) {
+ (*zft_cmpr_ops->reset)(); /* unlock it again */
+ }
+ zft_memory_stats();
+ /* call the hardware release routine. Puts the drive offline */
+ ftape_disable();
+ TRACE_EXIT result;
+}
+
+/*
+ * the wrapper function around the wrapper MTIOCTOP ioctl
+ */
+static int mtioctop(struct mtop *mtop, int arg_size)
+{
+ int result = 0;
+ fun_entry *mt_fun_entry;
+ TRACE_FUN(ft_t_flow);
+
+ if (arg_size != sizeof(struct mtop) || mtop->mt_op >= NR_MT_CMDS) {
+ TRACE_EXIT -EINVAL;
+ }
+ TRACE(ft_t_noise, "calling MTIOCTOP command: %s",
+ mt_funs[mtop->mt_op].name);
+ mt_fun_entry= &mt_funs[mtop->mt_op];
+ zft_resid = mtop->mt_count;
+ if (!mt_fun_entry->offline && zft_offline) {
+ if (ft_no_tape) {
+ TRACE_ABORT(-ENXIO, ft_t_info, "no tape present");
+ } else {
+ TRACE_ABORT(-ENXIO, ft_t_info, "drive is offline");
+ }
+ }
+ if (!mt_fun_entry->not_formatted && !ft_formatted) {
+ TRACE_ABORT(-EACCES, ft_t_info, "tape is not formatted");
+ }
+ if (!mt_fun_entry->write_protected) {
+ TRACE_CATCH(zft_check_write_access(&zft_pos),);
+ }
+ if (mt_fun_entry->need_idle_state && !(zft_offline || !ft_formatted)) {
+ TRACE_CATCH(zft_def_idle_state(),);
+ }
+ if (!zft_qic_mode && !mt_fun_entry->raw_mode) {
+ TRACE_ABORT(-EACCES, ft_t_info,
+"Drive needs to be in QIC-80 compatibility mode for this command");
+ }
+ result = (mt_fun_entry->function)(&mtop->mt_count);
+ if (zft_tape_at_lbot(&zft_pos)) {
+ TRACE_CATCH(zft_update_header_segments(),);
+ }
+ if (result >= 0) {
+ zft_resid = 0;
+ }
+ TRACE_EXIT result;
+}
+
+/*
+ * standard MTIOCGET ioctl
+ */
+static int mtiocget(struct mtget *mtget, int arg_size)
+{
+ const zft_volinfo *volume;
+ __s64 max_tape_pos;
+ TRACE_FUN(ft_t_flow);
+
+ if (arg_size != sizeof(struct mtget)) {
+ TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d",
+ arg_size);
+ }
+ mtget->mt_type = ft_drive_type.vendor_id + 0x800000;
+ mtget->mt_dsreg = ft_last_status.space;
+ mtget->mt_erreg = ft_last_error.space; /* error register */
+ mtget->mt_resid = zft_resid; /* residuum of writes, reads and
+ * MTIOCTOP commands
+ */
+ if (!zft_offline) { /* neither no_tape nor soft offline */
+ mtget->mt_gstat = GMT_ONLINE(~0UL);
+ /* should rather return the status of the cartridge
+ * than the access mode of the file, therefor use
+ * ft_write_protected, not zft_write_protected
+ */
+ if (ft_write_protected) {
+ mtget->mt_gstat |= GMT_WR_PROT(~0UL);
+ }
+ if(zft_header_read) { /* this catches non-formatted */
+ volume = zft_find_volume(zft_pos.seg_pos);
+ mtget->mt_fileno = volume->count;
+ max_tape_pos = zft_capacity - zft_blk_sz;
+ if (zft_use_compression) {
+ max_tape_pos -= ZFT_CMPR_OVERHEAD;
+ }
+ if (zft_tape_at_eod(&zft_pos)) {
+ mtget->mt_gstat |= GMT_EOD(~0UL);
+ }
+ if (zft_pos.tape_pos > max_tape_pos) {
+ mtget->mt_gstat |= GMT_EOT(~0UL);
+ }
+ mtget->mt_blkno = zft_div_blksz(zft_pos.volume_pos,
+ volume->blk_sz);
+ if (zft_just_before_eof) {
+ mtget->mt_gstat |= GMT_EOF(~0UL);
+ }
+ if (zft_tape_at_lbot(&zft_pos)) {
+ mtget->mt_gstat |= GMT_BOT(~0UL);
+ }
+ } else {
+ mtget->mt_fileno = mtget->mt_blkno = -1;
+ if (mtget->mt_dsreg & QIC_STATUS_AT_BOT) {
+ mtget->mt_gstat |= GMT_BOT(~0UL);
+ }
+ }
+ } else {
+ if (ft_no_tape) {
+ mtget->mt_gstat = GMT_DR_OPEN(~0UL);
+ } else {
+ mtget->mt_gstat = 0UL;
+ }
+ mtget->mt_fileno = mtget->mt_blkno = -1;
+ }
+ TRACE_EXIT 0;
+}
+
+#ifdef MTIOCRDFTSEG
+/*
+ * Read a floppy tape segment. This is useful for manipulating the
+ * volume table, and read the old header segment before re-formatting
+ * the cartridge.
+ */
+static int mtiocrdftseg(struct mtftseg * mtftseg, int arg_size)
+{
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCRDFTSEG");
+ if (zft_qic_mode) {
+ TRACE_ABORT(-EACCES, ft_t_info,
+ "driver needs to be in raw mode for this ioctl");
+ }
+ if (arg_size != sizeof(struct mtftseg)) {
+ TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d",
+ arg_size);
+ }
+ if (zft_offline) {
+ TRACE_EXIT -ENXIO;
+ }
+ if (mtftseg->mt_mode != FT_RD_SINGLE &&
+ mtftseg->mt_mode != FT_RD_AHEAD) {
+ TRACE_ABORT(-EINVAL, ft_t_info, "invalid read mode");
+ }
+ if (!ft_formatted) {
+ TRACE_EXIT -EACCES; /* -ENXIO ? */
+
+ }
+ if (!zft_header_read) {
+ TRACE_CATCH(zft_def_idle_state(),);
+ }
+ if (mtftseg->mt_segno > ft_last_data_segment) {
+ TRACE_ABORT(-EINVAL, ft_t_info, "segment number is too large");
+ }
+ mtftseg->mt_result = ftape_read_segment(mtftseg->mt_segno,
+ zft_deblock_buf,
+ mtftseg->mt_mode);
+ if (mtftseg->mt_result < 0) {
+ /* a negativ result is not an ioctl error. if
+ * the user wants to read damaged tapes,
+ * it's up to her/him
+ */
+ TRACE_EXIT 0;
+ }
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+ if (copy_to_user(mtftseg->mt_data,
+ zft_deblock_buf,
+ mtftseg->mt_result) != 0) {
+ TRACE_EXIT -EFAULT;
+ }
+#else
+ TRACE_CATCH(verify_area(VERIFY_WRITE, mtftseg->mt_data,
+ mtftseg->mt_result),);
+ memcpy_tofs(mtftseg->mt_data, zft_deblock_buf,
+ mtftseg->mt_result);
+#endif
+ TRACE_EXIT 0;
+}
+#endif
+
+#ifdef MTIOCWRFTSEG
+/*
+ * write a floppy tape segment. This version features writing of
+ * deleted address marks, and gracefully ignores the (software)
+ * ft_formatted flag to support writing of header segments after
+ * formatting.
+ */
+static int mtiocwrftseg(struct mtftseg * mtftseg, int arg_size)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCWRFTSEG");
+ if (zft_write_protected || zft_qic_mode) {
+ TRACE_EXIT -EACCES;
+ }
+ if (arg_size != sizeof(struct mtftseg)) {
+ TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d",
+ arg_size);
+ }
+ if (zft_offline) {
+ TRACE_EXIT -ENXIO;
+ }
+ if (mtftseg->mt_mode != FT_WR_ASYNC &&
+ mtftseg->mt_mode != FT_WR_MULTI &&
+ mtftseg->mt_mode != FT_WR_SINGLE &&
+ mtftseg->mt_mode != FT_WR_DELETE) {
+ TRACE_ABORT(-EINVAL, ft_t_info, "invalid write mode");
+ }
+ /*
+ * We don't check for ft_formatted, because this gives
+ * only the software status of the driver.
+ *
+ * We assume that the user knows what it is
+ * doing. And rely on the low level stuff to fail
+ * when the tape isn't formatted. We only make sure
+ * that The header segment buffer is allocated,
+ * because it holds the bad sector map.
+ */
+ if (zft_hseg_buf == NULL) {
+ TRACE_EXIT -ENXIO;
+ }
+ if (mtftseg->mt_mode != FT_WR_DELETE) {
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+ if (copy_from_user(zft_deblock_buf,
+ mtftseg->mt_data,
+ FT_SEGMENT_SIZE) != 0) {
+ TRACE_EXIT -EFAULT;
+ }
+#else
+ TRACE_CATCH(verify_area(VERIFY_READ,
+ mtftseg->mt_data,
+ FT_SEGMENT_SIZE),);
+ memcpy_fromfs(zft_deblock_buf, mtftseg->mt_data,
+ FT_SEGMENT_SIZE);
+#endif
+ }
+ mtftseg->mt_result = ftape_write_segment(mtftseg->mt_segno,
+ zft_deblock_buf,
+ mtftseg->mt_mode);
+ if (mtftseg->mt_result >= 0 && mtftseg->mt_mode == FT_WR_SINGLE) {
+ /*
+ * a negativ result is not an ioctl error. if
+ * the user wants to write damaged tapes,
+ * it's up to her/him
+ */
+ if ((result = ftape_loop_until_writes_done()) < 0) {
+ mtftseg->mt_result = result;
+ }
+ }
+ TRACE_EXIT 0;
+}
+#endif
+
+#ifdef MTIOCVOLINFO
+/*
+ * get information about volume positioned at.
+ */
+static int mtiocvolinfo(struct mtvolinfo *volinfo, int arg_size)
+{
+ const zft_volinfo *volume;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCVOLINFO");
+ if (arg_size != sizeof(struct mtvolinfo)) {
+ TRACE_ABORT(-EINVAL,
+ ft_t_info, "bad argument size: %d", arg_size);
+ }
+ if (zft_offline) {
+ TRACE_EXIT -ENXIO;
+ }
+ if (!ft_formatted) {
+ TRACE_EXIT -EACCES;
+ }
+ TRACE_CATCH(zft_def_idle_state(),);
+ volume = zft_find_volume(zft_pos.seg_pos);
+ volinfo->mt_volno = volume->count;
+ volinfo->mt_blksz = volume->blk_sz == 1 ? 0 : volume->blk_sz;
+ volinfo->mt_size = volume->size >> 10;
+ volinfo->mt_rawsize = ((zft_calc_tape_pos(volume->end_seg + 1) >> 10) -
+ (zft_calc_tape_pos(volume->start_seg) >> 10));
+ volinfo->mt_cmpr = volume->use_compression;
+ TRACE_EXIT 0;
+}
+#endif
+
+#ifdef ZFT_OBSOLETE
+static int mtioc_zftape_getblksz(struct mtblksz *blksz, int arg_size)
+{
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "\n"
+ KERN_INFO "Mag tape ioctl command: MTIOC_ZTAPE_GETBLKSZ\n"
+ KERN_INFO "This ioctl is here merely for compatibility.\n"
+ KERN_INFO "Please use MTIOCVOLINFO instead");
+ if (arg_size != sizeof(struct mtblksz)) {
+ TRACE_ABORT(-EINVAL,
+ ft_t_info, "bad argument size: %d", arg_size);
+ }
+ if (zft_offline) {
+ TRACE_EXIT -ENXIO;
+ }
+ if (!ft_formatted) {
+ TRACE_EXIT -EACCES;
+ }
+ TRACE_CATCH(zft_def_idle_state(),);
+ blksz->mt_blksz = zft_find_volume(zft_pos.seg_pos)->blk_sz;
+ TRACE_EXIT 0;
+}
+#endif
+
+#ifdef MTIOCGETSIZE
+/*
+ * get the capacity of the tape cartridge.
+ */
+static int mtiocgetsize(struct mttapesize *size, int arg_size)
+{
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "Mag tape ioctl command: MTIOC_ZFTAPE_GETSIZE");
+ if (arg_size != sizeof(struct mttapesize)) {
+ TRACE_ABORT(-EINVAL,
+ ft_t_info, "bad argument size: %d", arg_size);
+ }
+ if (zft_offline) {
+ TRACE_EXIT -ENXIO;
+ }
+ if (!ft_formatted) {
+ TRACE_EXIT -EACCES;
+ }
+ TRACE_CATCH(zft_def_idle_state(),);
+ size->mt_capacity = (unsigned int)(zft_capacity>>10);
+ size->mt_used = (unsigned int)(zft_get_eom_pos()>>10);
+ TRACE_EXIT 0;
+}
+#endif
+
+static int mtiocpos(struct mtpos *mtpos, int arg_size)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCPOS");
+ if (arg_size != sizeof(struct mtpos)) {
+ TRACE_ABORT(-EINVAL,
+ ft_t_info, "bad argument size: %d", arg_size);
+ }
+ result = mt_tell((int *)&mtpos->mt_blkno);
+ TRACE_EXIT result;
+}
+
+#ifdef MTIOCFTFORMAT
+/*
+ * formatting of floppy tape cartridges. This is intended to be used
+ * together with the MTIOCFTCMD ioctl and the new mmap feature
+ */
+
+/*
+ * This function uses ftape_decode_header_segment() to inform the low
+ * level ftape module about the new parameters.
+ *
+ * It erases the hseg_buf. The calling process must specify all
+ * parameters to assure proper operation.
+ *
+ * return values: -EINVAL - wrong argument size
+ * -EINVAL - if ftape_decode_header_segment() failed.
+ */
+static int set_format_parms(struct ftfmtparms *p, __u8 *hseg_buf)
+{
+ ft_trace_t old_level = TRACE_LEVEL;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "MTIOCFTFORMAT operation FTFMT_SETPARMS");
+ memset(hseg_buf, 0, FT_SEGMENT_SIZE);
+ PUT4(hseg_buf, FT_SIGNATURE, FT_HSEG_MAGIC);
+
+ /* fill in user specified parameters
+ */
+ hseg_buf[FT_FMT_CODE] = (__u8)p->ft_fmtcode;
+ PUT2(hseg_buf, FT_SPT, p->ft_spt);
+ hseg_buf[FT_TPC] = (__u8)p->ft_tpc;
+ hseg_buf[FT_FHM] = (__u8)p->ft_fhm;
+ hseg_buf[FT_FTM] = (__u8)p->ft_ftm;
+
+ /* fill in sane defaults to make ftape happy.
+ */
+ hseg_buf[FT_FSM] = (__u8)128; /* 128 is hard wired all over ftape */
+ if (p->ft_fmtcode == fmt_big) {
+ PUT4(hseg_buf, FT_6_HSEG_1, 0);
+ PUT4(hseg_buf, FT_6_HSEG_2, 1);
+ PUT4(hseg_buf, FT_6_FRST_SEG, 2);
+ PUT4(hseg_buf, FT_6_LAST_SEG, p->ft_spt * p->ft_tpc - 1);
+ } else {
+ PUT2(hseg_buf, FT_HSEG_1, 0);
+ PUT2(hseg_buf, FT_HSEG_2, 1);
+ PUT2(hseg_buf, FT_FRST_SEG, 2);
+ PUT2(hseg_buf, FT_LAST_SEG, p->ft_spt * p->ft_tpc - 1);
+ }
+
+ /* Synchronize with the low level module. This is particularly
+ * needed for unformatted cartridges as the QIC std was previously
+ * unknown BUT is needed to set data rate and to calculate timeouts.
+ */
+ TRACE_CATCH(ftape_calibrate_data_rate(p->ft_qicstd&QIC_TAPE_STD_MASK),
+ _res = -EINVAL);
+
+ /* The following will also recalcualte the timeouts for the tape
+ * length and QIC std we want to format to.
+ * abort with -EINVAL rather than -EIO
+ */
+ SET_TRACE_LEVEL(ft_t_warn);
+ TRACE_CATCH(ftape_decode_header_segment(hseg_buf),
+ SET_TRACE_LEVEL(old_level); _res = -EINVAL);
+ SET_TRACE_LEVEL(old_level);
+ TRACE_EXIT 0;
+}
+
+/*
+ * Return the internal SOFTWARE status of the kernel driver. This does
+ * NOT query the tape drive about its status.
+ */
+static int get_format_parms(struct ftfmtparms *p, __u8 *hseg_buffer)
+{
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "MTIOCFTFORMAT operation FTFMT_GETPARMS");
+ p->ft_qicstd = ft_qic_std;
+ p->ft_fmtcode = ft_format_code;
+ p->ft_fhm = hseg_buffer[FT_FHM];
+ p->ft_ftm = hseg_buffer[FT_FTM];
+ p->ft_spt = ft_segments_per_track;
+ p->ft_tpc = ft_tracks_per_tape;
+ TRACE_EXIT 0;
+}
+
+static int mtiocftformat(struct mtftformat *mtftformat, int arg_size)
+{
+ int result;
+ union fmt_arg *arg = &mtftformat->fmt_arg;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCFTFORMAT");
+ if (zft_offline) {
+ if (ft_no_tape) {
+ TRACE_ABORT(-ENXIO, ft_t_info, "no tape present");
+ } else {
+ TRACE_ABORT(-ENXIO, ft_t_info, "drive is offline");
+ }
+ }
+ if (zft_qic_mode) {
+ TRACE_ABORT(-EACCES, ft_t_info,
+ "driver needs to be in raw mode for this ioctl");
+ }
+ if (zft_hseg_buf == NULL) {
+ TRACE_CATCH(zft_vcalloc_once(&zft_hseg_buf, FT_SEGMENT_SIZE),);
+ }
+ zft_header_read = 0;
+ switch(mtftformat->fmt_op) {
+ case FTFMT_SET_PARMS:
+ TRACE_CATCH(set_format_parms(&arg->fmt_parms, zft_hseg_buf),);
+ TRACE_EXIT 0;
+ case FTFMT_GET_PARMS:
+ TRACE_CATCH(get_format_parms(&arg->fmt_parms, zft_hseg_buf),);
+ TRACE_EXIT 0;
+ case FTFMT_FORMAT_TRACK:
+ if ((ft_formatted && zft_check_write_access(&zft_pos) < 0) ||
+ (!ft_formatted && zft_write_protected)) {
+ TRACE_ABORT(-EACCES, ft_t_info, "Write access denied");
+ }
+ TRACE_CATCH(ftape_format_track(arg->fmt_track.ft_track,
+ arg->fmt_track.ft_gap3),);
+ TRACE_EXIT 0;
+ case FTFMT_STATUS:
+ TRACE_CATCH(ftape_format_status(&arg->fmt_status.ft_segment),);
+ TRACE_EXIT 0;
+ case FTFMT_VERIFY:
+ TRACE_CATCH(ftape_verify_segment(arg->fmt_verify.ft_segment,
+ (SectorMap *)&arg->fmt_verify.ft_bsm),);
+ TRACE_EXIT 0;
+ default:
+ TRACE_ABORT(-EINVAL, ft_t_err, "Invalid format operation");
+ }
+ TRACE_EXIT result;
+}
+#endif
+
+#ifdef MTIOCFTCMD
+/*
+ * send a QIC-117 command to the drive, with optional timeouts,
+ * parameter and result bits. This is intended to be used together
+ * with the formatting ioctl.
+ */
+static int mtiocftcmd(struct mtftcmd *ftcmd, int arg_size)
+{
+ int i;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCFTCMD");
+ if (!suser()) {
+ TRACE_ABORT(-EPERM, ft_t_info,
+ "only the superuser may send raw qic-117 commands");
+ }
+ if (zft_qic_mode) {
+ TRACE_ABORT(-EACCES, ft_t_info,
+ "driver needs to be in raw mode for this ioctl");
+ }
+ if (arg_size != sizeof(struct mtftcmd)) {
+ TRACE_ABORT(-EINVAL,
+ ft_t_info, "bad argument size: %d", arg_size);
+ }
+ if (ftcmd->ft_wait_before) {
+ TRACE_CATCH(ftape_ready_wait(ftcmd->ft_wait_before,
+ &ftcmd->ft_status),);
+ }
+ if (ftcmd->ft_status & QIC_STATUS_ERROR)
+ goto ftmtcmd_error;
+ if (ftcmd->ft_result_bits != 0) {
+ TRACE_CATCH(ftape_report_operation(&ftcmd->ft_result,
+ ftcmd->ft_cmd,
+ ftcmd->ft_result_bits),);
+ } else {
+ TRACE_CATCH(ftape_command(ftcmd->ft_cmd),);
+ if (ftcmd->ft_status & QIC_STATUS_ERROR)
+ goto ftmtcmd_error;
+ for (i = 0; i < ftcmd->ft_parm_cnt; i++) {
+ TRACE_CATCH(ftape_parameter(ftcmd->ft_parms[i]&0x0f),);
+ if (ftcmd->ft_status & QIC_STATUS_ERROR)
+ goto ftmtcmd_error;
+ }
+ }
+ if (ftcmd->ft_wait_after != 0) {
+ TRACE_CATCH(ftape_ready_wait(ftcmd->ft_wait_after,
+ &ftcmd->ft_status),);
+ }
+ftmtcmd_error:
+ if (ftcmd->ft_status & QIC_STATUS_ERROR) {
+ TRACE(ft_t_noise, "error status set");
+ TRACE_CATCH(ftape_report_error(&ftcmd->ft_error,
+ &ftcmd->ft_cmd, 1),);
+ }
+ TRACE_EXIT 0; /* this is not an i/o error */
+}
+#endif
+
+/* IOCTL routine called by kernel-interface code
+ */
+int _zft_ioctl(unsigned int command, void * arg)
+{
+ int result;
+ union { struct mtop mtop;
+ struct mtget mtget;
+ struct mtpos mtpos;
+#ifdef MTIOCRDFTSEG
+ struct mtftseg mtftseg;
+#endif
+#ifdef MTIOCVOLINFO
+ struct mtvolinfo mtvolinfo;
+#endif
+#ifdef MTIOCGETSIZE
+ struct mttapesize mttapesize;
+#endif
+#ifdef MTIOCFTFORMAT
+ struct mtftformat mtftformat;
+#endif
+#ifdef ZFT_OBSOLETE
+ struct mtblksz mtblksz;
+#endif
+#ifdef MTIOCFTCMD
+ struct mtftcmd mtftcmd;
+#endif
+ } krnl_arg;
+ int arg_size = _IOC_SIZE(command);
+ int dir = _IOC_DIR(command);
+ TRACE_FUN(ft_t_flow);
+
+ /* This check will only catch arguments that are too large !
+ */
+ if (dir & (_IOC_READ | _IOC_WRITE) && arg_size > sizeof(krnl_arg)) {
+ TRACE_ABORT(-EINVAL,
+ ft_t_info, "bad argument size: %d", arg_size);
+ }
+ if (dir & _IOC_WRITE) {
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+ if (copy_from_user(&krnl_arg, arg, arg_size) != 0) {
+ TRACE_EXIT -EFAULT;
+ }
+#else
+ TRACE_CATCH(verify_area(VERIFY_READ, arg, arg_size),);
+ memcpy_fromfs(&krnl_arg, arg, arg_size);
+#endif
+ }
+ TRACE(ft_t_flow, "called with ioctl command: 0x%08x", command);
+ switch (command) {
+ case MTIOCTOP:
+ result = mtioctop(&krnl_arg.mtop, arg_size);
+ break;
+ case MTIOCGET:
+ result = mtiocget(&krnl_arg.mtget, arg_size);
+ break;
+ case MTIOCPOS:
+ result = mtiocpos(&krnl_arg.mtpos, arg_size);
+ break;
+#ifdef MTIOCVOLINFO
+ case MTIOCVOLINFO:
+ result = mtiocvolinfo(&krnl_arg.mtvolinfo, arg_size);
+ break;
+#endif
+#ifdef ZFT_OBSOLETE
+ case MTIOC_ZFTAPE_GETBLKSZ:
+ result = mtioc_zftape_getblksz(&krnl_arg.mtblksz, arg_size);
+ break;
+#endif
+#ifdef MTIOCRDFTSEG
+ case MTIOCRDFTSEG: /* read a segment via ioctl */
+ result = mtiocrdftseg(&krnl_arg.mtftseg, arg_size);
+ break;
+#endif
+#ifdef MTIOCWRFTSEG
+ case MTIOCWRFTSEG: /* write a segment via ioctl */
+ result = mtiocwrftseg(&krnl_arg.mtftseg, arg_size);
+ break;
+#endif
+#ifdef MTIOCGETSIZE
+ case MTIOCGETSIZE:
+ result = mtiocgetsize(&krnl_arg.mttapesize, arg_size);
+ break;
+#endif
+#ifdef MTIOCFTFORMAT
+ case MTIOCFTFORMAT:
+ result = mtiocftformat(&krnl_arg.mtftformat, arg_size);
+ break;
+#endif
+#ifdef MTIOCFTCMD
+ case MTIOCFTCMD:
+ result = mtiocftcmd(&krnl_arg.mtftcmd, arg_size);
+ break;
+#endif
+ default:
+ result = -EINVAL;
+ break;
+ }
+ if ((result >= 0) && (dir & _IOC_READ)) {
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+ if (copy_to_user(arg, &krnl_arg, arg_size) != 0) {
+ TRACE_EXIT -EFAULT;
+ }
+#else
+ TRACE_CATCH(verify_area(VERIFY_WRITE, arg, arg_size),);
+ memcpy_tofs(arg, &krnl_arg, arg_size);
+#endif
+ }
+ TRACE_EXIT result;
+}
diff --git a/drivers/char/ftape/zftape/zftape-ctl.h b/drivers/char/ftape/zftape/zftape-ctl.h
new file mode 100644
index 000000000..91a2f2db7
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-ctl.h
@@ -0,0 +1,60 @@
+#ifndef _ZFTAPE_CTL_H
+#define _ZFTAPE_CTL_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-ctl.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:02 $
+ *
+ * This file contains the non-standard IOCTL related definitions
+ * for the QIC-40/80 floppy-tape driver for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/ioctl.h>
+#include <linux/mtio.h>
+
+#include "../zftape/zftape-rw.h"
+
+#ifdef CONFIG_ZFTAPE_MODULE
+#define ftape_status (*zft_status)
+#endif
+
+extern int zft_offline;
+extern int zft_mt_compression;
+extern int zft_write_protected;
+extern int zft_header_read;
+extern unsigned int zft_unit;
+extern int zft_resid;
+
+extern void zft_reset_position(zft_position *pos);
+extern int zft_check_write_access(zft_position *pos);
+extern int zft_def_idle_state(void);
+extern int zft_dirty(void);
+
+/* hooks for the VFS interface
+ */
+extern int _zft_open(unsigned int dev_minor, unsigned int access_mode);
+extern int _zft_close(void);
+extern int _zft_ioctl(unsigned int command, void *arg);
+#endif
+
+
+
diff --git a/drivers/char/ftape/zftape/zftape-eof.c b/drivers/char/ftape/zftape/zftape-eof.c
new file mode 100644
index 000000000..0dfd27ff6
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-eof.c
@@ -0,0 +1,207 @@
+/*
+ * I use these routines just to decide when I have to fake a
+ * volume-table to preserve compatability to original ftape.
+ */
+/*
+ * Copyright (C) 1994-1995 Bas Laarhoven.
+ *
+ * Modified for zftape 1996, 1997 Claus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-eof.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:02 $
+ *
+ * This file contains the eof mark handling code
+ * for the QIC-40/80 floppy-tape driver for Linux.
+ */
+
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include <linux/zftape.h>
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-eof.h"
+
+/* Global vars.
+ */
+
+/* a copy of the failed sector log from the header segment.
+ */
+eof_mark_union *zft_eof_map;
+
+/* number of eof marks (entries in bad sector log) on tape.
+ */
+int zft_nr_eof_marks = -1;
+
+
+/* Local vars.
+ */
+
+static char linux_tape_label[] = "Linux raw format V";
+enum {
+ min_fmt_version = 1, max_fmt_version = 2
+};
+static unsigned ftape_fmt_version = 0;
+
+
+/* Ftape (mis)uses the bad sector log to record end-of-file marks.
+ * Initially (when the tape is erased) all entries in the bad sector
+ * log are added to the tape's bad sector map. The bad sector log then
+ * is cleared.
+ *
+ * The bad sector log normally contains entries of the form:
+ * even 16-bit word: segment number of bad sector
+ * odd 16-bit word: encoded date
+ * There can be a total of 448 entries (1792 bytes).
+ *
+ * My guess is that no program is using this bad sector log (the *
+ * format seems useless as there is no indication of the bad sector
+ * itself, only the segment) However, if any program does use the bad
+ * sector log, the format used by ftape will let the program think
+ * there are some bad sectors and no harm is done.
+ *
+ * The eof mark entries that ftape stores in the bad sector log: even
+ * 16-bit word: segment number of eof mark odd 16-bit word: sector
+ * number of eof mark [1..32]
+ *
+ * The zft_eof_map as maintained is a sorted list of eof mark entries.
+ *
+ *
+ * The tape name field in the header segments is used to store a linux
+ * tape identification string and a version number. This way the tape
+ * can be recognized as a Linux raw format tape when using tools under
+ * other OS's.
+ *
+ * 'Wide' QIC tapes (format code 4) don't have a failed sector list
+ * anymore. That space is used for the (longer) bad sector map that
+ * now is a variable length list too. We now store our end-of-file
+ * marker list after the bad-sector-map on tape. The list is delimited
+ * by a (__u32) 0 entry.
+ */
+
+int zft_ftape_validate_label(char *label)
+{
+ static char tmp_label[45];
+ int result = 0;
+ TRACE_FUN(ft_t_any);
+
+ memcpy(tmp_label, label, FT_LABEL_SZ);
+ tmp_label[FT_LABEL_SZ] = '\0';
+ TRACE(ft_t_noise, "tape label = `%s'", tmp_label);
+ ftape_fmt_version = 0;
+ if (memcmp(label, linux_tape_label, strlen(linux_tape_label)) == 0) {
+ int pos = strlen(linux_tape_label);
+ while (label[pos] >= '0' && label[pos] <= '9') {
+ ftape_fmt_version *= 10;
+ ftape_fmt_version = label[ pos++] - '0';
+ }
+ result = (ftape_fmt_version >= min_fmt_version &&
+ ftape_fmt_version <= max_fmt_version);
+ }
+ TRACE(ft_t_noise, "format version = %d", ftape_fmt_version);
+ TRACE_EXIT result;
+}
+
+static __u8 * find_end_of_eof_list(__u8 * ptr, __u8 * limit)
+{
+ while (ptr + 3 < limit) {
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,0)
+ if (get_unaligned((__u32*)ptr)) {
+ ++(__u32*)ptr;
+ } else {
+ return ptr;
+ }
+#else
+ if (*(__u32*)ptr) {
+ ++(__u32*)ptr;
+ } else {
+ return ptr;
+ }
+#endif
+ }
+ return NULL;
+}
+
+void zft_ftape_extract_file_marks(__u8* address)
+{
+ int i;
+ TRACE_FUN(ft_t_any);
+
+ zft_eof_map = NULL;
+ if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
+ __u8* end;
+ __u8* start = ftape_find_end_of_bsm_list(address);
+
+ zft_nr_eof_marks = 0;
+ if (start) {
+ start += 3; /* skip end of list mark */
+ end = find_end_of_eof_list(start,
+ address + FT_SEGMENT_SIZE);
+ if (end && end - start <= FT_FSL_SIZE) {
+ zft_nr_eof_marks = ((end - start) /
+ sizeof(eof_mark_union));
+ zft_eof_map = (eof_mark_union *)start;
+ } else {
+ TRACE(ft_t_err,
+ "EOF Mark List is too long or damaged!");
+ }
+ } else {
+ TRACE(ft_t_err,
+ "Bad Sector List is too long or damaged !");
+ }
+ } else {
+ zft_eof_map = (eof_mark_union *)&address[FT_FSL];
+ zft_nr_eof_marks = GET2(address, FT_FSL_CNT);
+ }
+ TRACE(ft_t_noise, "number of file marks: %d", zft_nr_eof_marks);
+ if (ftape_fmt_version == 1) {
+ TRACE(ft_t_info, "swapping version 1 fields");
+ /* version 1 format uses swapped sector and segment
+ * fields, correct that !
+ */
+ for (i = 0; i < zft_nr_eof_marks; ++i) {
+ __u16 tmp = GET2(&zft_eof_map[i].mark.segment,0);
+ PUT2(&zft_eof_map[i].mark.segment, 0,
+ GET2(&zft_eof_map[i].mark.date,0));
+ PUT2(&zft_eof_map[i].mark.date, 0, tmp);
+ }
+ }
+ for (i = 0; i < zft_nr_eof_marks; ++i) {
+ TRACE(ft_t_noise, "eof mark: %5d/%2d",
+ GET2(&zft_eof_map[i].mark.segment, 0),
+ GET2(&zft_eof_map[i].mark.date,0));
+ }
+ TRACE_EXIT;
+}
+
+void zft_clear_ftape_file_marks(void)
+{
+ TRACE_FUN(ft_t_flow);
+ /* Clear failed sector log: remove all tape marks. We
+ * don't use old ftape-style EOF-marks.
+ */
+ TRACE(ft_t_info, "Clearing old ftape's eof map");
+ memset(zft_eof_map, 0, zft_nr_eof_marks * sizeof(__u32));
+ zft_nr_eof_marks = 0;
+ PUT2(zft_hseg_buf, FT_FSL_CNT, 0); /* nr of eof-marks */
+ zft_header_changed = 1;
+ zft_update_label(zft_hseg_buf);
+ TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/ftape-eof.h b/drivers/char/ftape/zftape/zftape-eof.h
index 58df1f019..26568c26c 100644
--- a/drivers/char/ftape/ftape-eof.h
+++ b/drivers/char/ftape/zftape/zftape-eof.h
@@ -1,10 +1,9 @@
-
-
-#ifndef _FTAPE_EOF_H
-#define _FTAPE_EOF_H
+#ifndef _ZFTAPE_EOF_H
+#define _ZFTAPE_EOF_H
/*
* Copyright (C) 1994-1995 Bas Laarhoven.
+ * adaptaed for zftape 1996, 1997 by Claus Heine
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,35 +20,33 @@
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-eof.h,v $
- $Author: bas $
- *
- $Revision: 1.12 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-eof.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:03 $
*
* Definitions and declarations for the end of file markers
* for the QIC-40/80 floppy-tape driver for Linux.
*/
+#include <linux/ftape-header-segment.h>
+#include "../zftape/zftape-buffers.h"
+/* failed sector log size (only used if format code != 4).
+ */
+
+typedef union {
+ ft_fsl_entry mark;
+ __u32 entry;
+} eof_mark_union;
+
/* ftape-eof.c defined global vars.
*/
-extern int failed_sector_log_changed;
-extern int eof_mark;
+extern int zft_nr_eof_marks;
+extern eof_mark_union *zft_eof_map;
/* ftape-eof.c defined global functions.
*/
-extern void clear_eof_mark_if_set(unsigned segment, unsigned byte_count);
-extern void reset_eof_list(void);
-extern int check_for_eof(unsigned segment);
-extern int ftape_weof(unsigned count, unsigned segment, unsigned sector);
-extern int ftape_erase(void);
-extern void put_file_mark_in_map(unsigned segment, unsigned sector);
-extern void extract_file_marks(byte * address);
-extern int update_failed_sector_log(byte * buffer);
-extern int ftape_seek_eom(void);
-extern int ftape_seek_eof(unsigned count);
-extern int ftape_file_no(daddr_t * file, daddr_t * block);
-extern int ftape_validate_label(char *label);
+extern void zft_ftape_extract_file_marks(__u8* address);
+extern int zft_ftape_validate_label(char* label);
+extern void zft_clear_ftape_file_marks(void);
#endif
diff --git a/drivers/char/ftape/zftape/zftape-init.c b/drivers/char/ftape/zftape/zftape-init.c
new file mode 100644
index 000000000..c6a4f4506
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-init.c
@@ -0,0 +1,512 @@
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * This file contains the code that registers the zftape frontend
+ * to the ftape floppy tape driver for Linux
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <asm/segment.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/major.h>
+#include <linux/malloc.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+#include <linux/fcntl.h>
+#include <linux/wrapper.h>
+
+#include <linux/zftape.h>
+#if LINUX_VERSION_CODE >=KERNEL_VER(2,1,16)
+#include <linux/init.h>
+#else
+#define __initdata
+#define __initfunc(__arg) __arg
+#endif
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-buffers.h"
+#include "../zftape/zftape_syms.h"
+
+char zft_src[] __initdata = "$Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-init.c,v $";
+char zft_rev[] __initdata = "$Revision: 1.8 $";
+char zft_dat[] __initdata = "$Date: 1997/11/06 00:48:56 $";
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+MODULE_AUTHOR("(c) 1996, 1997 Claus-Justus Heine "
+ "(claus@momo.math.rwth-aachen.de)");
+MODULE_DESCRIPTION(ZFTAPE_VERSION " - "
+ "VFS interface for the Linux floppy tape driver. "
+ "Support for QIC-113 compatible volume table "
+ "and builtin compression (lzrw3 algorithm)");
+MODULE_SUPPORTED_DEVICE("char-major-27");
+#endif
+
+/* Global vars.
+ */
+struct zft_cmpr_ops *zft_cmpr_ops = NULL;
+const ftape_info *zft_status;
+
+/* Local vars.
+ */
+static int busy_flag = 0;
+static int orig_sigmask;
+
+/* the interface to the kernel vfs layer
+ */
+
+/* Note about llseek():
+ *
+ * st.c and tpqic.c update fp->f_pos but don't implment llseek() and
+ * initialize the llseek component of the file_ops struct with NULL.
+ * This means that the user will get the default seek, but the tape
+ * device will not respect the new position, but happily read from the
+ * old position. Think a zftape specific llseek() function would be
+ * better, returning -ESPIPE. TODO.
+ */
+
+static int zft_open (struct inode *ino, struct file *filep);
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,31)
+static int zft_close(struct inode *ino, struct file *filep);
+#else
+static void zft_close(struct inode *ino, struct file *filep);
+#endif
+static int zft_ioctl(struct inode *ino, struct file *filep,
+ unsigned int command, unsigned long arg);
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,56)
+static int zft_mmap(struct file *filep, struct vm_area_struct *vma);
+#else
+static int zft_mmap(struct inode *ino, struct file *filep,
+ struct vm_area_struct *vma);
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60)
+static ssize_t zft_read (struct file *fp, char *buff,
+ size_t req_len, loff_t *ppos);
+static ssize_t zft_write(struct file *fp, const char *buff,
+ size_t req_len, loff_t *ppos);
+#elif LINUX_VERSION_CODE >= KERNEL_VER(2,1,0)
+static long zft_read (struct inode *ino, struct file *fp, char *buff,
+ unsigned long req_len);
+static long zft_write(struct inode *ino, struct file *fp, const char *buff,
+ unsigned long req_len);
+#else
+static int zft_read (struct inode *ino, struct file *fp, char *buff,
+ int req_len);
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,0)
+static int zft_write(struct inode *ino, struct file *fp, const char *buff,
+ int req_len);
+#else
+static int zft_write(struct inode *ino, struct file *fp, char *buff,
+ int req_len);
+#endif
+#endif
+
+static struct file_operations zft_cdev =
+{
+ NULL, /* llseek */
+ zft_read, /* read */
+ zft_write, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ zft_ioctl, /* ioctl */
+ zft_mmap, /* mmap */
+ zft_open, /* open */
+ zft_close, /* release */
+ NULL, /* fsync */
+};
+
+/* Open floppy tape device
+ */
+static int zft_open(struct inode *ino, struct file *filep)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+ if (!MOD_IN_USE) {
+ MOD_INC_USE_COUNT; /* lock module in memory */
+ }
+#else
+ MOD_INC_USE_COUNT; /* sets MOD_VISITED and MOD_USED_ONCE,
+ * locking is done with can_unload()
+ */
+#endif
+ TRACE(ft_t_flow, "called for minor %d", MINOR(ino->i_rdev));
+ if (busy_flag) {
+ TRACE_ABORT(-EBUSY, ft_t_warn, "failed: already busy");
+ }
+ busy_flag = 1;
+ if ((MINOR(ino->i_rdev) & ~(ZFT_MINOR_OP_MASK | FTAPE_NO_REWIND))
+ >
+ FTAPE_SEL_D) {
+ busy_flag = 0;
+#if defined(MODULE) && LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+ if (!zft_dirty()) {
+ MOD_DEC_USE_COUNT; /* unlock module in memory */
+ }
+#endif
+ TRACE_ABORT(-ENXIO, ft_t_err, "failed: illegal unit nr");
+ }
+ orig_sigmask = current->blocked;
+ current->blocked = _BLOCK_ALL;
+ result = _zft_open(MINOR(ino->i_rdev), filep->f_flags & O_ACCMODE);
+ if (result < 0) {
+ current->blocked = orig_sigmask; /* restore mask */
+ busy_flag = 0;
+#if defined(MODULE) && LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+ if (!zft_dirty()) {
+ MOD_DEC_USE_COUNT; /* unlock module in memory */
+ }
+#endif
+ TRACE_ABORT(result, ft_t_err, "_ftape_open failed");
+ } else {
+ /* Mask signals that will disturb proper operation of the
+ * program that is calling.
+ */
+ current->blocked = orig_sigmask | _DO_BLOCK;
+ TRACE_EXIT 0;
+ }
+}
+
+/* Close floppy tape device
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,31)
+static int zft_close(struct inode *ino, struct file *filep)
+#else
+static void zft_close(struct inode *ino, struct file *filep)
+#endif
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ if (!busy_flag || MINOR(ino->i_rdev) != zft_unit) {
+ TRACE(ft_t_err, "failed: not busy or wrong unit");
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,31)
+ TRACE_EXIT 0;
+#else
+ TRACE_EXIT; /* keep busy_flag !(?) */
+#endif
+ }
+ current->blocked = _BLOCK_ALL;
+ result = _zft_close();
+ if (result < 0) {
+ TRACE(ft_t_err, "_zft_close failed");
+ }
+ current->blocked = orig_sigmask; /* restore before open state */
+ busy_flag = 0;
+#if defined(MODULE) && LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+ if (!zft_dirty()) {
+ MOD_DEC_USE_COUNT; /* unlock module in memory */
+ }
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,31)
+ TRACE_EXIT 0;
+#else
+ TRACE_EXIT;
+#endif
+}
+
+/* Ioctl for floppy tape device
+ */
+static int zft_ioctl(struct inode *ino, struct file *filep,
+ unsigned int command, unsigned long arg)
+{
+ int result = -EIO;
+ int old_sigmask;
+ TRACE_FUN(ft_t_flow);
+
+ if (!busy_flag || MINOR(ino->i_rdev) != zft_unit || ft_failure) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "failed: not busy, failure or wrong unit");
+ }
+ old_sigmask = current->blocked; /* save mask */
+ current->blocked = _BLOCK_ALL;
+ /* This will work as long as sizeof(void *) == sizeof(long) */
+ result = _zft_ioctl(command, (void *) arg);
+ current->blocked = old_sigmask; /* restore mask */
+ TRACE_EXIT result;
+}
+
+/* Ioctl for floppy tape device
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,56)
+static int zft_mmap(struct file *filep, struct vm_area_struct *vma)
+#else
+static int zft_mmap(struct inode *ino,
+ struct file *filep,
+ struct vm_area_struct *vma)
+#endif
+{
+ int result = -EIO;
+ int old_sigmask;
+ TRACE_FUN(ft_t_flow);
+
+ if (!busy_flag ||
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,56)
+ MINOR(filep->f_dentry->d_inode->i_rdev) != zft_unit ||
+#else
+ MINOR(ino->i_rdev) != zft_unit ||
+#endif
+ ft_failure)
+ {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "failed: not busy, failure or wrong unit");
+ }
+ old_sigmask = current->blocked; /* save mask */
+ current->blocked = _BLOCK_ALL;
+ if ((result = ftape_mmap(vma)) >= 0) {
+#ifndef MSYNC_BUG_WAS_FIXED
+ static struct vm_operations_struct dummy = { NULL, };
+ vma->vm_ops = &dummy;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,45)
+ vma->vm_dentry = dget(filep->f_dentry);
+#else
+ vma_set_inode (vma, ino);
+ inode_inc_count (ino);
+#endif
+ }
+ current->blocked = old_sigmask; /* restore mask */
+ TRACE_EXIT result;
+}
+
+/* Read from floppy tape device
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60)
+static ssize_t zft_read(struct file *fp, char *buff,
+ size_t req_len, loff_t *ppos)
+#elif LINUX_VERSION_CODE >= KERNEL_VER(2,1,0)
+static long zft_read(struct inode *ino, struct file *fp, char *buff,
+ unsigned long req_len)
+#else
+static int zft_read(struct inode *ino, struct file *fp, char *buff,
+ int req_len)
+#endif
+{
+ int result = -EIO;
+ int old_sigmask;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60)
+ struct inode *ino = fp->f_dentry->d_inode;
+#endif
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_data_flow, "called with count: %ld", (unsigned long)req_len);
+ if (!busy_flag || MINOR(ino->i_rdev) != zft_unit || ft_failure) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "failed: not busy, failure or wrong unit");
+ }
+ old_sigmask = current->blocked; /* save mask */
+ current->blocked = _BLOCK_ALL;
+ result = _zft_read(buff, req_len);
+ current->blocked = old_sigmask; /* restore mask */
+ TRACE(ft_t_data_flow, "return with count: %d", result);
+ TRACE_EXIT result;
+}
+
+/* Write to tape device
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60)
+static ssize_t zft_write(struct file *fp, const char *buff,
+ size_t req_len, loff_t *ppos)
+#elif LINUX_VERSION_CODE >= KERNEL_VER(2,1,0)
+static long zft_write(struct inode *ino, struct file *fp, const char *buff,
+ unsigned long req_len)
+#elif LINUX_VERSION_CODE >= KERNEL_VER(1,3,0)
+static int zft_write(struct inode *ino, struct file *fp, const char *buff,
+ int req_len)
+#else
+static int zft_write(struct inode *ino, struct file *fp, char *buff,
+ int req_len)
+#endif
+{
+ int result = -EIO;
+ int old_sigmask;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60)
+ struct inode *ino = fp->f_dentry->d_inode;
+#endif
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_flow, "called with count: %ld", (unsigned long)req_len);
+ if (!busy_flag || MINOR(ino->i_rdev) != zft_unit || ft_failure) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "failed: not busy, failure or wrong unit");
+ }
+ old_sigmask = current->blocked; /* save mask */
+ current->blocked = _BLOCK_ALL;
+ result = _zft_write(buff, req_len);
+ current->blocked = old_sigmask; /* restore mask */
+ TRACE(ft_t_data_flow, "return with count: %d", result);
+ TRACE_EXIT result;
+}
+
+/* END OF VFS INTERFACE
+ *
+ *****************************************************************************/
+
+/* driver/module initialization
+ */
+
+/* the compression module has to call this function to hook into the zftape
+ * code
+ */
+int zft_cmpr_register(struct zft_cmpr_ops *new_ops)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (zft_cmpr_ops != NULL) {
+ TRACE_EXIT -EBUSY;
+ } else {
+ zft_cmpr_ops = new_ops;
+ TRACE_EXIT 0;
+ }
+}
+
+struct zft_cmpr_ops *zft_cmpr_unregister(void)
+{
+ struct zft_cmpr_ops *old_ops = zft_cmpr_ops;
+ TRACE_FUN(ft_t_flow);
+
+ zft_cmpr_ops = NULL;
+ TRACE_EXIT old_ops;
+}
+
+/* lock the zft-compressor() module.
+ */
+int zft_cmpr_lock(int try_to_load)
+{
+ if (zft_cmpr_ops == NULL) {
+#ifdef CONFIG_KERNELD
+ if (try_to_load) {
+ request_module("zft-compressor");
+ if (zft_cmpr_ops == NULL) {
+ return -ENOSYS;
+ }
+ } else {
+ return -ENOSYS;
+ }
+#else
+ return -ENOSYS;
+#endif
+ }
+ (*zft_cmpr_ops->lock)();
+ return 0;
+}
+
+#ifdef CONFIG_ZFT_COMPRESSOR
+extern int zft_compressor_init(void);
+#endif
+
+/* Called by modules package when installing the driver or by kernel
+ * during the initialization phase
+ */
+__initfunc(int zft_init(void))
+{
+ TRACE_FUN(ft_t_flow);
+
+#ifdef MODULE
+ printk(KERN_INFO ZFTAPE_VERSION "\n");
+ if (TRACE_LEVEL >= ft_t_info) {
+ printk(
+KERN_INFO
+"(c) 1996, 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)\n"
+KERN_INFO
+"vfs interface for ftape floppy tape driver.\n"
+KERN_INFO
+"Support for QIC-113 compatible volume table, dynamic memory allocation\n"
+KERN_INFO
+"and builtin compression (lzrw3 algorithm).\n"
+KERN_INFO
+"Compiled for Linux version %s"
+#ifdef MODVERSIONS
+ " with versioned symbols"
+#endif
+ "\n", UTS_RELEASE);
+ }
+#else /* !MODULE */
+ /* print a short no-nonsense boot message */
+ printk(KERN_INFO ZFTAPE_VERSION " for Linux " UTS_RELEASE "\n");
+#endif /* MODULE */
+ TRACE(ft_t_info, "zft_init @ 0x%p", zft_init);
+ TRACE(ft_t_info,
+ "installing zftape VFS interface for ftape driver ...");
+ TRACE_CATCH(register_chrdev(QIC117_TAPE_MAJOR, "zft", &zft_cdev),);
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,2,0)
+# if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+ register_symtab(&zft_symbol_table); /* add global zftape symbols */
+# endif
+#endif
+#ifdef CONFIG_ZFT_COMPRESSOR
+ (void)zft_compressor_init();
+#endif
+ zft_status = ftape_get_status(); /* fetch global data of ftape
+ * hardware driver
+ */
+ TRACE_EXIT 0;
+}
+
+
+#ifdef MODULE
+#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13) && defined(MODULE)
+char kernel_version[] = UTS_RELEASE;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+/* Called by modules package before trying to unload the module
+ */
+static int can_unload(void)
+{
+ return (zft_dirty() || busy_flag) ? -EBUSY : 0;
+}
+#endif
+/* Called by modules package when installing the driver
+ */
+int init_module(void)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+ if (!mod_member_present(&__this_module, can_unload)) {
+ return -EBUSY;
+ }
+ __this_module.can_unload = can_unload;
+#endif
+ return zft_init();
+}
+
+/* Called by modules package when removing the driver
+ */
+void cleanup_module(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (unregister_chrdev(QIC117_TAPE_MAJOR, "zft") != 0) {
+ TRACE(ft_t_warn, "failed");
+ } else {
+ TRACE(ft_t_info, "successful");
+ }
+ zft_uninit_mem(); /* release remaining memory, if any */
+ printk(KERN_INFO "zftape successfully unloaded.\n");
+ TRACE_EXIT;
+}
+
+#endif /* MODULE */
diff --git a/drivers/char/ftape/zftape/zftape-init.h b/drivers/char/ftape/zftape/zftape-init.h
new file mode 100644
index 000000000..39d83f78c
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-init.h
@@ -0,0 +1,85 @@
+#ifndef _ZFTAPE_INIT_H
+#define _ZFTAPE_INIT_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-init.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:05 $
+ *
+ * This file contains definitions and macro for the vfs
+ * interface defined by zftape
+ *
+ */
+
+#include <linux/ftape-header-segment.h>
+
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-bsm.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-buffer.h"
+#include "../lowlevel/ftape-format.h"
+
+#include "../zftape/zftape-rw.h"
+
+#ifdef MODULE
+#define ftape_status (*zft_status)
+#endif
+
+extern const ftape_info *zft_status; /* needed for zftape-vtbl.h */
+
+#include "../zftape/zftape-vtbl.h"
+
+struct zft_cmpr_ops {
+ int (*write)(int *write_cnt,
+ __u8 *dst_buf, const int seg_sz,
+ const __u8 *src_buf, const int req_len,
+ const zft_position *pos, const zft_volinfo *volume);
+ int (*read)(int *read_cnt,
+ __u8 *dst_buf, const int req_len,
+ const __u8 *src_buf, const int seg_sz,
+ const zft_position *pos, const zft_volinfo *volume);
+ int (*seek)(unsigned int new_block_pos,
+ zft_position *pos, const zft_volinfo *volume,
+ __u8 *buffer);
+ void (*lock) (void);
+ void (*reset) (void);
+ void (*cleanup)(void);
+};
+
+extern struct zft_cmpr_ops *zft_cmpr_ops;
+/* zftape-init.c defined global functions.
+ */
+extern int zft_cmpr_register(struct zft_cmpr_ops *new_ops);
+extern struct zft_cmpr_ops *zft_cmpr_unregister(void);
+extern int zft_cmpr_lock(int try_to_load);
+
+#ifdef MODULE
+
+asmlinkage extern int init_module(void);
+asmlinkage extern void cleanup_module(void);
+
+#endif
+
+#endif
+
+
diff --git a/drivers/char/ftape/zftape/zftape-read.c b/drivers/char/ftape/zftape/zftape-read.c
new file mode 100644
index 000000000..c7d319fac
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-read.c
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-read.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:06 $
+ *
+ * This file contains the high level reading code
+ * for the QIC-117 floppy-tape driver for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+#include <linux/zftape.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6)
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#endif
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-vtbl.h"
+
+/* Global vars.
+ */
+int zft_just_before_eof = 0;
+
+/* Local vars.
+ */
+static int buf_len_rd = 0;
+
+void zft_zap_read_buffers(void)
+{
+ buf_len_rd = 0;
+}
+
+int zft_read_header_segments(void)
+{
+ TRACE_FUN(ft_t_flow);
+
+ zft_header_read = 0;
+ TRACE_CATCH(zft_vmalloc_once(&zft_hseg_buf, FT_SEGMENT_SIZE),);
+ TRACE_CATCH(ftape_read_header_segment(zft_hseg_buf),);
+ TRACE(ft_t_info, "Segments written since first format: %d",
+ (int)GET4(zft_hseg_buf, FT_SEG_CNT));
+ zft_qic113 = (ft_format_code != fmt_normal &&
+ ft_format_code != fmt_1100ft &&
+ ft_format_code != fmt_425ft);
+ TRACE(ft_t_info, "ft_first_data_segment: %d, ft_last_data_segment: %d",
+ ft_first_data_segment, ft_last_data_segment);
+ zft_capacity = zft_get_capacity();
+ zft_old_ftape = zft_ftape_validate_label(&zft_hseg_buf[FT_LABEL]);
+ if (zft_old_ftape) {
+ TRACE(ft_t_info,
+"Found old ftaped tape, emulating eof marks, entering read-only mode");
+ zft_ftape_extract_file_marks(zft_hseg_buf);
+ TRACE_CATCH(zft_fake_volume_headers(zft_eof_map,
+ zft_nr_eof_marks),);
+ } else {
+ /* the specs say that the volume table must be
+ * initialized with zeroes during formatting, so it
+ * MUST be readable, i.e. contain vaid ECC
+ * information.
+ */
+ TRACE_CATCH(ftape_read_segment(ft_first_data_segment,
+ zft_deblock_buf,
+ FT_RD_SINGLE),);
+ TRACE_CATCH(zft_extract_volume_headers(zft_deblock_buf),);
+ }
+ zft_header_read = 1;
+ zft_set_flags(zft_unit);
+ zft_reset_position(&zft_pos);
+ TRACE_EXIT 0;
+}
+
+int zft_fetch_segment_fraction(const unsigned int segment, void *buffer,
+ const ft_read_mode_t read_mode,
+ const unsigned int start,
+ const unsigned int size)
+{
+ int seg_sz;
+ TRACE_FUN(ft_t_flow);
+
+ if (segment == zft_deblock_segment) {
+ TRACE(ft_t_data_flow,
+ "re-using segment %d already in deblock buffer",
+ segment);
+ seg_sz = zft_get_seg_sz(segment);
+ if (start > seg_sz) {
+ TRACE_ABORT(-EINVAL, ft_t_bug,
+ "trying to read beyond end of segment:\n"
+ KERN_INFO "seg_sz : %d\n"
+ KERN_INFO "start : %d\n"
+ KERN_INFO "segment: %d",
+ seg_sz, start, segment);
+ }
+ if ((start + size) > seg_sz) {
+ TRACE_EXIT seg_sz - start;
+ }
+ TRACE_EXIT size;
+ }
+ seg_sz = ftape_read_segment_fraction(segment, buffer, read_mode,
+ start, size);
+ TRACE(ft_t_data_flow, "segment %d, result %d", segment, seg_sz);
+ if ((int)seg_sz >= 0 && start == 0 && size == FT_SEGMENT_SIZE) {
+ /* this implicitly assumes that we are always called with
+ * buffer == zft_deblock_buf
+ */
+ zft_deblock_segment = segment;
+ } else {
+ zft_deblock_segment = -1;
+ }
+ TRACE_EXIT seg_sz;
+}
+
+/*
+ * out:
+ *
+ * int *read_cnt: the number of bytes we removed from the
+ * zft_deblock_buf (result)
+ *
+ * int *to_do : the remaining size of the read-request. Is changed.
+ *
+ * in:
+ *
+ * char *buff : buff is the address of the upper part of the user
+ * buffer, that hasn't been filled with data yet.
+ * int buf_pos_read: copy of buf_pos_rd
+ * int buf_len_read: copy of buf_len_rd
+ * char *zft_deblock_buf: ftape_zft_deblock_buf
+ *
+ * returns the amount of data actually copied to the user-buffer
+ *
+ * to_do MUST NOT SHRINK except to indicate an EOT. In this case to_do
+ * has to be set to 0. We cannot return -ENOSPC, because we return the
+ * amount of data actually * copied to the user-buffer
+ */
+static int zft_simple_read (int *read_cnt,
+ __u8 *dst_buf,
+ const int to_do,
+ const __u8 *src_buf,
+ const int seg_sz,
+ const zft_position *pos,
+ const zft_volinfo *volume)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (seg_sz - pos->seg_byte_pos < to_do) {
+ *read_cnt = seg_sz - pos->seg_byte_pos;
+ } else {
+ *read_cnt = to_do;
+ }
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+ if (copy_to_user(dst_buf,
+ src_buf + pos->seg_byte_pos, *read_cnt) != 0) {
+ TRACE_EXIT -EFAULT;
+ }
+#else
+ TRACE_CATCH(verify_area(VERIFY_WRITE, dst_buf, *read_cnt),);
+ memcpy_tofs(dst_buf, src_buf + pos->seg_byte_pos, *read_cnt);
+#endif
+ TRACE(ft_t_noise, "nr bytes just read: %d", *read_cnt);
+ TRACE_EXIT *read_cnt;
+}
+
+/* req_len: gets clipped due to EOT of EOF.
+ * req_clipped: is a flag indicating whether req_len was clipped or not
+ * volume: contains information on current volume (blk_sz etc.)
+ */
+static int check_read_access(int *req_len,
+ const zft_volinfo **volume,
+ int *req_clipped,
+ const zft_position *pos)
+{
+ static __s64 remaining = 0;
+ static int eod;
+ TRACE_FUN(ft_t_flow);
+
+ if (zft_io_state != zft_reading) {
+ if (zft_offline) { /* offline includes no_tape */
+ TRACE_ABORT(-ENXIO, ft_t_warn,
+ "tape is offline or no cartridge");
+ }
+ if (!ft_formatted) {
+ TRACE_ABORT(-EACCES,
+ ft_t_warn, "tape is not formatted");
+ }
+ /* now enter defined state, read header segment if not
+ * already done and flush write buffers
+ */
+ TRACE_CATCH(zft_def_idle_state(),);
+ zft_io_state = zft_reading;
+ if (zft_tape_at_eod(pos)) {
+ eod = 1;
+ TRACE_EXIT 1;
+ }
+ eod = 0;
+ *volume = zft_find_volume(pos->seg_pos);
+ /* get the space left until EOF */
+ remaining = zft_check_for_eof(*volume, pos);
+ buf_len_rd = 0;
+ TRACE(ft_t_noise, "remaining: " LL_X ", vol_no: %d",
+ LL(remaining), (*volume)->count);
+ } else if (zft_tape_at_eod(pos)) {
+ if (++eod > 2) {
+ TRACE_EXIT -EIO; /* st.c also returns -EIO */
+ } else {
+ TRACE_EXIT 1;
+ }
+ }
+ if ((*req_len % (*volume)->blk_sz) != 0) {
+ /* this message is informational only. The user gets the
+ * proper return value
+ */
+ TRACE_ABORT(-EINVAL, ft_t_info,
+ "req_len %d not a multiple of block size %d",
+ *req_len, (*volume)->blk_sz);
+ }
+ /* As GNU tar doesn't accept partial read counts when the
+ * multiple volume flag is set, we make sure to return the
+ * requested amount of data. Except, of course, at the end of
+ * the tape or file mark.
+ */
+ remaining -= *req_len;
+ if (remaining <= 0) {
+ TRACE(ft_t_noise,
+ "clipped request from %d to %d.",
+ *req_len, (int)(*req_len + remaining));
+ *req_len += remaining;
+ *req_clipped = 1;
+ } else {
+ *req_clipped = 0;
+ }
+ TRACE_EXIT 0;
+}
+
+/* this_segs_size: the current segment's size.
+ * buff: the USER-SPACE buffer provided by the calling function.
+ * req_len: how much data should be read at most.
+ * volume: contains information on current volume (blk_sz etc.)
+ */
+static int empty_deblock_buf(__u8 *usr_buf, const int req_len,
+ const __u8 *src_buf, const int seg_sz,
+ zft_position *pos,
+ const zft_volinfo *volume)
+{
+ int cnt;
+ int result = 0;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_data_flow, "this_segs_size: %d", seg_sz);
+ if (zft_use_compression && volume->use_compression) {
+ TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),);
+ TRACE_CATCH(result= (*zft_cmpr_ops->read)(&cnt,
+ usr_buf, req_len,
+ src_buf, seg_sz,
+ pos, volume),);
+ } else {
+ TRACE_CATCH(result= zft_simple_read (&cnt,
+ usr_buf, req_len,
+ src_buf, seg_sz,
+ pos, volume),);
+ }
+ pos->volume_pos += result;
+ pos->tape_pos += cnt;
+ pos->seg_byte_pos += cnt;
+ buf_len_rd -= cnt; /* remaining bytes in buffer */
+ TRACE(ft_t_data_flow, "buf_len_rd: %d, cnt: %d", buf_len_rd, cnt);
+ if(pos->seg_byte_pos >= seg_sz) {
+ pos->seg_pos++;
+ pos->seg_byte_pos = 0;
+ }
+ TRACE(ft_t_data_flow, "bytes moved out of deblock-buffer: %d", cnt);
+ TRACE_EXIT result;
+}
+
+
+/* note: we store the segment id of the segment that is inside the
+ * deblock buffer. This spares a lot of ftape_read_segment()s when we
+ * use small block-sizes. The block-size may be 1kb (SECTOR_SIZE). In
+ * this case a MTFSR 28 maybe still inside the same segment.
+ */
+int _zft_read(char* buff, int req_len)
+{
+ int req_clipped;
+ int result = 0;
+ int bytes_read = 0;
+ static unsigned int seg_sz = 0;
+ static const zft_volinfo *volume = NULL;
+ TRACE_FUN(ft_t_flow);
+
+ zft_resid = req_len;
+ result = check_read_access(&req_len, &volume,
+ &req_clipped, &zft_pos);
+ switch(result) {
+ case 0:
+ break; /* nothing special */
+ case 1:
+ TRACE(ft_t_noise, "EOD reached");
+ TRACE_EXIT 0; /* EOD */
+ default:
+ TRACE_ABORT(result, ft_t_noise,
+ "check_read_access() failed with result %d",
+ result);
+ TRACE_EXIT result;
+ }
+ while (req_len > 0) {
+ /* Allow escape from this loop on signal !
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ /* buf_len_rd == 0 means that we need to read a new
+ * segment.
+ */
+ if (buf_len_rd == 0) {
+ while((result = zft_fetch_segment(zft_pos.seg_pos,
+ zft_deblock_buf,
+ FT_RD_AHEAD)) == 0) {
+ zft_pos.seg_pos ++;
+ zft_pos.seg_byte_pos = 0;
+ }
+ if (result < 0) {
+ zft_resid -= bytes_read;
+ TRACE_ABORT(result, ft_t_noise,
+ "zft_fetch_segment(): %d",
+ result);
+ }
+ seg_sz = result;
+ buf_len_rd = seg_sz - zft_pos.seg_byte_pos;
+ }
+ TRACE_CATCH(result = empty_deblock_buf(buff,
+ req_len,
+ zft_deblock_buf,
+ seg_sz,
+ &zft_pos,
+ volume),
+ zft_resid -= bytes_read);
+ TRACE(ft_t_data_flow, "bytes just read: %d", result);
+ bytes_read += result; /* what we got so far */
+ buff += result; /* index in user-buffer */
+ req_len -= result; /* what's left from req_len */
+ } /* while (req_len > 0) */
+ if (req_clipped) {
+ TRACE(ft_t_data_flow,
+ "maybe partial count because of eof mark");
+ if (zft_just_before_eof && bytes_read == 0) {
+ /* req_len was > 0, but user didn't get
+ * anything the user has read in the eof-mark
+ */
+ zft_move_past_eof(&zft_pos);
+ ftape_abort_operation();
+ } else {
+ /* don't skip to the next file before the user
+ * tried to read a second time past EOF Just
+ * mark that we are at EOF and maybe decrement
+ * zft_seg_pos to stay in the same volume;
+ */
+ zft_just_before_eof = 1;
+ zft_position_before_eof(&zft_pos, volume);
+ TRACE(ft_t_noise, "just before eof");
+ }
+ }
+ zft_resid -= result; /* for MTSTATUS */
+ TRACE_EXIT bytes_read;
+}
diff --git a/drivers/char/ftape/zftape/zftape-read.h b/drivers/char/ftape/zftape/zftape-read.h
new file mode 100644
index 000000000..ad2e9dbc4
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-read.h
@@ -0,0 +1,53 @@
+#ifndef _ZFTAPE_READ_H
+#define _ZFTAPE_READ_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-read.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:07 $
+ *
+ * This file contains the definitions for the read functions
+ * for the zftape driver for Linux.
+ *
+ */
+
+#include "../lowlevel/ftape-read.h"
+
+/* ftape-read.c defined global vars.
+ */
+extern int zft_just_before_eof;
+
+/* ftape-read.c defined global functions.
+ */
+extern void zft_zap_read_buffers(void);
+extern int zft_read_header_segments(void);
+extern int zft_fetch_segment_fraction(const unsigned int segment,
+ void *buffer,
+ const ft_read_mode_t read_mode,
+ const unsigned int start,
+ const unsigned int size);
+#define zft_fetch_segment(segment, address, read_mode) \
+ zft_fetch_segment_fraction(segment, address, read_mode, \
+ 0, FT_SEGMENT_SIZE)
+/* hook for the VFS interface
+ */
+extern int _zft_read(char* buff, int req_len);
+
+#endif /* _ZFTAPE_READ_H */
diff --git a/drivers/char/ftape/zftape/zftape-rw.c b/drivers/char/ftape/zftape/zftape-rw.c
new file mode 100644
index 000000000..d8ae9f1e7
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-rw.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-rw.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:08 $
+ *
+ * This file contains some common code for the r/w code for
+ * zftape.
+ */
+
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+
+#include <linux/zftape.h>
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-vtbl.h"
+
+/* Global vars.
+ */
+
+__u8 *zft_deblock_buf = NULL;
+__u8 *zft_hseg_buf = NULL;
+int zft_deblock_segment = -1;
+zft_status_enum zft_io_state = zft_idle;
+int zft_header_changed = 0;
+int zft_bad_sector_map_changed = 0;
+int zft_qic113 = 0; /* conform to old specs. and old zftape */
+int zft_use_compression = 0;
+zft_position zft_pos = {
+ -1, /* seg_pos */
+ 0, /* seg_byte_pos */
+ 0, /* tape_pos */
+ 0 /* volume_pos */
+};
+unsigned int zft_blk_sz = CONFIG_ZFT_DFLT_BLK_SZ;
+__s64 zft_capacity = 0;
+
+unsigned int zft_written_segments = 0;
+int zft_label_changed = 0;
+
+/* Local vars.
+ */
+
+unsigned int zft_get_seg_sz(unsigned int segment)
+{
+ int size;
+ TRACE_FUN(ft_t_any);
+
+ size = FT_SEGMENT_SIZE -
+ count_ones(ftape_get_bad_sector_entry(segment))*FT_SECTOR_SIZE;
+ if (size > 0) {
+ TRACE_EXIT (unsigned)size;
+ } else {
+ TRACE_EXIT 0;
+ }
+}
+
+/* ftape_set_flags(). Claus-Justus Heine, 1994/1995
+ */
+void zft_set_flags(unsigned minor_unit)
+{
+ TRACE_FUN(ft_t_flow);
+
+ zft_use_compression = zft_qic_mode = 0;
+ switch (minor_unit & ZFT_MINOR_OP_MASK) {
+ case (ZFT_Q80_MODE | ZFT_ZIP_MODE):
+ case ZFT_ZIP_MODE:
+ zft_use_compression = 1;
+ case 0:
+ case ZFT_Q80_MODE:
+ zft_qic_mode = 1;
+ if (zft_mt_compression) { /* override the default */
+ zft_use_compression = 1;
+ }
+ break;
+ case ZFT_RAW_MODE:
+ TRACE(ft_t_noise, "switching to raw mode");
+ break;
+ default:
+ TRACE(ft_t_warn, "Warning:\n"
+ KERN_INFO "Wrong combination of minor device bits.\n"
+ KERN_INFO "Switching to raw read-only mode.");
+ zft_write_protected = 1;
+ break;
+ }
+ TRACE_EXIT;
+}
+
+/* computes the segment and byte offset inside the segment
+ * corresponding to tape_pos.
+ *
+ * tape_pos gives the offset in bytes from the beginning of the
+ * ft_first_data_segment *seg_byte_pos is the offset in the current
+ * segment in bytes
+ *
+ * Of, if this routine was called often one should cache the last data
+ * pos it was called with, but actually this is only needed in
+ * ftape_seek_block(), that is, almost never.
+ */
+int zft_calc_seg_byte_coord(int *seg_byte_pos, __s64 tape_pos)
+{
+ int segment;
+ int seg_sz;
+ TRACE_FUN(ft_t_flow);
+
+ if (tape_pos == 0) {
+ *seg_byte_pos = 0;
+ segment = ft_first_data_segment;
+ } else {
+ seg_sz = 0;
+
+ for (segment = ft_first_data_segment;
+ ((tape_pos > 0) && (segment <= ft_last_data_segment));
+ segment++) {
+ seg_sz = zft_get_seg_sz(segment);
+ tape_pos -= seg_sz;
+ }
+ if(tape_pos >= 0) {
+ /* the case tape_pos > != 0 means that the
+ * argument tape_pos lies beyond the EOT.
+ */
+ *seg_byte_pos= 0;
+ } else { /* tape_pos < 0 */
+ segment--;
+ *seg_byte_pos= tape_pos + seg_sz;
+ }
+ }
+ TRACE_EXIT(segment);
+}
+
+/* ftape_calc_tape_pos().
+ *
+ * computes the offset in bytes from the beginning of the
+ * ft_first_data_segment inverse to ftape_calc_seg_byte_coord
+ *
+ * We should do some caching. But how:
+ *
+ * Each time the header segments are read in, this routine is called
+ * with ft_tracks_per_tape*segments_per_track argumnet. So this should be
+ * the time to reset the cache.
+ *
+ * Also, it might be in the future that the bad sector map gets
+ * changed. -> reset the cache
+ */
+static int seg_pos = 0;
+static __s64 tape_pos = 0;
+
+__s64 zft_get_capacity(void)
+{
+ seg_pos = ft_first_data_segment;
+ tape_pos = 0;
+
+ while (seg_pos <= ft_last_data_segment) {
+ tape_pos += zft_get_seg_sz(seg_pos ++);
+ }
+ return tape_pos;
+}
+
+__s64 zft_calc_tape_pos(int segment)
+{
+ int d1, d2, d3;
+ TRACE_FUN(ft_t_any);
+
+ if (segment > ft_last_data_segment) {
+ TRACE_EXIT zft_capacity;
+ }
+ if (segment < ft_first_data_segment) {
+ TRACE_EXIT 0;
+ }
+ d2 = segment - seg_pos;
+ if (-d2 > 10) {
+ d1 = segment - ft_first_data_segment;
+ if (-d2 > d1) {
+ tape_pos = 0;
+ seg_pos = ft_first_data_segment;
+ d2 = d1;
+ }
+ }
+ if (d2 > 10) {
+ d3 = ft_last_data_segment - segment;
+ if (d2 > d3) {
+ tape_pos = zft_capacity;
+ seg_pos = ft_last_data_segment + 1;
+ d2 = -d3;
+ }
+ }
+ if (d2 > 0) {
+ while (seg_pos < segment) {
+ tape_pos += zft_get_seg_sz(seg_pos++);
+ }
+ } else {
+ while (seg_pos > segment) {
+ tape_pos -= zft_get_seg_sz(--seg_pos);
+ }
+ }
+ TRACE(ft_t_noise, "new cached pos: %d", seg_pos);
+
+ TRACE_EXIT tape_pos;
+}
+
+/* copy Z-label string to buffer, keeps track of the correct offset in
+ * `buffer'
+ */
+void zft_update_label(__u8 *buffer)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (strncmp(&buffer[FT_LABEL], ZFTAPE_LABEL,
+ sizeof(ZFTAPE_LABEL)-1) != 0) {
+ TRACE(ft_t_info, "updating label from \"%s\" to \"%s\"",
+ &buffer[FT_LABEL], ZFTAPE_LABEL);
+ strcpy(&buffer[FT_LABEL], ZFTAPE_LABEL);
+ memset(&buffer[FT_LABEL] + sizeof(ZFTAPE_LABEL) - 1, ' ',
+ FT_LABEL_SZ - sizeof(ZFTAPE_LABEL + 1));
+ PUT4(buffer, FT_LABEL_DATE, 0);
+ zft_label_changed = zft_header_changed = 1; /* changed */
+ }
+ TRACE_EXIT;
+}
+
+int zft_verify_write_segments(unsigned int segment,
+ __u8 *data, size_t size,
+ __u8 *buffer)
+{
+ int result;
+ __u8 *write_buf;
+ __u8 *src_buf;
+ int single;
+ int seg_pos;
+ int seg_sz;
+ int remaining;
+ ft_write_mode_t write_mode;
+ TRACE_FUN(ft_t_flow);
+
+ seg_pos = segment;
+ seg_sz = zft_get_seg_sz(seg_pos);
+ src_buf = data;
+ single = size <= seg_sz;
+ remaining = size;
+ do {
+ TRACE(ft_t_noise, "\n"
+ KERN_INFO "remaining: %d\n"
+ KERN_INFO "seg_sz : %d\n"
+ KERN_INFO "segment : %d",
+ remaining, seg_sz, seg_pos);
+ if (remaining == seg_sz) {
+ write_buf = src_buf;
+ write_mode = single ? FT_WR_SINGLE : FT_WR_MULTI;
+ remaining = 0;
+ } else if (remaining > seg_sz) {
+ write_buf = src_buf;
+ write_mode = FT_WR_ASYNC; /* don't start tape */
+ remaining -= seg_sz;
+ } else { /* remaining < seg_sz */
+ write_buf = buffer;
+ memcpy(write_buf, src_buf, remaining);
+ memset(&write_buf[remaining],'\0',seg_sz-remaining);
+ write_mode = single ? FT_WR_SINGLE : FT_WR_MULTI;
+ remaining = 0;
+ }
+ if ((result = ftape_write_segment(seg_pos,
+ write_buf,
+ write_mode)) != seg_sz) {
+ TRACE(ft_t_err, "Error: "
+ "Couldn't write segment %d", seg_pos);
+ TRACE_EXIT result < 0 ? result : -EIO; /* bail out */
+ }
+ zft_written_segments ++;
+ seg_sz = zft_get_seg_sz(++seg_pos);
+ src_buf += result;
+ } while (remaining > 0);
+ if (ftape_get_status()->fti_state == writing) {
+ TRACE_CATCH(ftape_loop_until_writes_done(),);
+ TRACE_CATCH(ftape_abort_operation(),);
+ zft_prevent_flush();
+ }
+ seg_pos = segment;
+ src_buf = data;
+ remaining = size;
+ do {
+ TRACE_CATCH(result = ftape_read_segment(seg_pos, buffer,
+ single ? FT_RD_SINGLE
+ : FT_RD_AHEAD),);
+ if (memcmp(src_buf, buffer,
+ remaining > result ? result : remaining) != 0) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "Failed to verify written segment %d",
+ seg_pos);
+ }
+ remaining -= result;
+ TRACE(ft_t_noise, "verify successful:\n"
+ KERN_INFO "segment : %d\n"
+ KERN_INFO "segsize : %d\n"
+ KERN_INFO "remaining: %d",
+ seg_pos, result, remaining);
+ src_buf += seg_sz;
+ seg_pos++;
+ } while (remaining > 0);
+ TRACE_EXIT size;
+}
+
+
+/* zft_erase(). implemented compression-handling
+ *
+ * calculate the first data-segment when using/not using compression.
+ *
+ * update header-segment and compression-map-segment.
+ */
+int zft_erase(void)
+{
+ int result = 0;
+ TRACE_FUN(ft_t_flow);
+
+ if (!zft_header_read) {
+ TRACE_CATCH(zft_vmalloc_once((void **)&zft_hseg_buf,
+ FT_SEGMENT_SIZE),);
+ /* no need to read the vtbl and compression map */
+ TRACE_CATCH(ftape_read_header_segment(zft_hseg_buf),);
+ if ((zft_old_ftape =
+ zft_ftape_validate_label(&zft_hseg_buf[FT_LABEL]))) {
+ zft_ftape_extract_file_marks(zft_hseg_buf);
+ }
+ TRACE(ft_t_noise,
+ "ft_first_data_segment: %d, ft_last_data_segment: %d",
+ ft_first_data_segment, ft_last_data_segment);
+ zft_qic113 = (ft_format_code != fmt_normal &&
+ ft_format_code != fmt_1100ft &&
+ ft_format_code != fmt_425ft);
+ }
+ if (zft_old_ftape) {
+ zft_clear_ftape_file_marks();
+ zft_old_ftape = 0; /* no longer old ftape */
+ }
+ PUT2(zft_hseg_buf, FT_CMAP_START, 0);
+ zft_volume_table_changed = 1;
+ zft_capacity = zft_get_capacity();
+ zft_init_vtbl();
+ /* the rest must be done in ftape_update_header_segments
+ */
+ zft_header_read = 1;
+ zft_header_changed = 1; /* force update of timestamp */
+ result = zft_update_header_segments();
+
+ ftape_abort_operation();
+
+ zft_reset_position(&zft_pos);
+ zft_set_flags (zft_unit);
+ TRACE_EXIT result;
+}
+
+unsigned int zft_get_time(void)
+{
+ unsigned int date = FT_TIME_STAMP(2097, 11, 30, 23, 59, 59); /* fun */
+ return date;
+}
diff --git a/drivers/char/ftape/zftape/zftape-rw.h b/drivers/char/ftape/zftape/zftape-rw.h
new file mode 100644
index 000000000..a8622867b
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-rw.h
@@ -0,0 +1,102 @@
+#ifndef _ZFTAPE_RW_H
+#define _ZFTAPE_RW_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-rw.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:09 $
+ *
+ * This file contains the definitions for the read and write
+ * functions for the QIC-117 floppy-tape driver for Linux.
+ *
+ */
+
+#include "../zftape/zftape-buffers.h"
+
+#define SEGMENTS_PER_TAPE (ft_segments_per_track * ft_tracks_per_tape)
+
+/* QIC-113 Rev. G says that `a maximum of 63488 raw bytes may be
+ * compressed into a single frame'.
+ * Maybe we should stick to 32kb to make it more `beautiful'
+ */
+#define ZFT_MAX_BLK_SZ (62*1024) /* bytes */
+#if !defined(CONFIG_ZFT_DFLT_BLK_SZ)
+# define CONFIG_ZFT_DFLT_BLK_SZ (10*1024) /* bytes, default of gnu tar */
+#elif CONFIG_ZFT_DFLT_BLK_SZ == 0
+# undef CONFIG_ZFT_DFLT_BLK_SZ
+# define CONFIG_ZFT_DFLT_BLK_SZ 1
+#elif (CONFIG_ZFT_DFLT_BLK_SZ % 1024) != 0
+# error CONFIG_ZFT_DFLT_BLK_SZ must be 1 or a multiple of 1024
+#endif
+/* The *optional* compression routines need some overhead per tape
+ * block for their purposes. Instead of asking the actual compression
+ * implementation how much it needs, we restrict this overhead to be
+ * maximal of ZFT_CMPT_OVERHEAD size. We need this for EOT
+ * conditions. The tape is assumed to be logical at EOT when the
+ * distance from the physical EOT is less than
+ * one tape block + ZFT_CMPR_OVERHEAD
+ */
+#define ZFT_CMPR_OVERHEAD 16 /* bytes */
+
+typedef enum
+{
+ zft_idle = 0,
+ zft_reading,
+ zft_writing,
+} zft_status_enum;
+
+typedef struct /* all values measured in bytes */
+{
+ int seg_pos; /* segment currently positioned at */
+ int seg_byte_pos; /* offset in current segment */
+ __s64 tape_pos; /* real offset from BOT */
+ __s64 volume_pos; /* pos. in uncompressed data stream in
+ * current volume
+ */
+} zft_position;
+
+extern zft_position zft_pos;
+extern __u8 *zft_deblock_buf;
+extern __u8 *zft_hseg_buf;
+extern int zft_deblock_segment;
+extern zft_status_enum zft_io_state;
+extern int zft_header_changed;
+extern int zft_bad_sector_map_changed;
+extern int zft_qic113; /* conform to old specs. and old zftape */
+extern int zft_use_compression;
+extern unsigned int zft_blk_sz;
+extern __s64 zft_capacity;
+extern unsigned int zft_written_segments;
+extern int zft_label_changed;
+
+/* zftape-rw.c exported functions
+ */
+extern unsigned int zft_get_seg_sz(unsigned int segment);
+extern void zft_set_flags(unsigned int minor_unit);
+extern int zft_calc_seg_byte_coord(int *seg_byte_pos, __s64 tape_pos);
+extern __s64 zft_calc_tape_pos(int segment);
+extern __s64 zft_get_capacity(void);
+extern void zft_update_label(__u8 *buffer);
+extern int zft_erase(void);
+extern int zft_verify_write_segments(unsigned int segment,
+ __u8 *data, size_t size, __u8 *buffer);
+extern unsigned int zft_get_time(void);
+#endif /* _ZFTAPE_RW_H */
+
diff --git a/drivers/char/ftape/zftape/zftape-vtbl.c b/drivers/char/ftape/zftape/zftape-vtbl.c
new file mode 100644
index 000000000..cf0ef2325
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-vtbl.c
@@ -0,0 +1,758 @@
+/*
+ * Copyright (c) 1995-1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-vtbl.c,v $
+ * $Revision: 1.7.6.1 $
+ * $Date: 1997/11/24 13:48:31 $
+ *
+ * This file defines a volume table as defined in various QIC
+ * standards.
+ *
+ * This is a minimal implementation, just allowing ordinary DOS
+ * :( prgrams to identify the cartridge as used.
+ */
+
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <asm/segment.h>
+
+#include <linux/zftape.h>
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-vtbl.h"
+
+#define ZFT_CMAP_HACK /* leave this defined to hide the compression map */
+
+/*
+ * global variables
+ */
+int zft_qic_mode = 1; /* use the vtbl */
+int zft_old_ftape = 0; /* prevents old ftaped tapes to be overwritten */
+int zft_volume_table_changed = 0; /* for write_header_segments() */
+
+/*
+ * private variables (only exported for inline functions)
+ */
+LIST_HEAD(zft_vtbl);
+
+/* We could also allocate these dynamically when extracting the volume table
+ * sizeof(zft_volinfo) is about 32 or something close to that
+ */
+static zft_volinfo tape_vtbl = { {NULL, NULL}, 0, };
+static zft_volinfo eot_vtbl = { {NULL, NULL}, 0, };
+static zft_volinfo *cur_vtbl = NULL;
+
+inline void zft_new_vtbl_entry(void)
+{
+ struct list_head *tmp = &zft_last_vtbl->node;
+ zft_volinfo *new = zft_kmalloc(sizeof(zft_volinfo));
+
+ list_add(&new->node, tmp);
+ new->count = zft_eom_vtbl->count ++;
+}
+
+void zft_free_vtbl(void)
+{
+ for (;;) {
+ struct list_head *tmp = zft_vtbl.prev;
+ zft_volinfo *vtbl;
+
+ if (tmp == &zft_vtbl)
+ break;
+ list_del(tmp);
+ vtbl = list_entry(tmp, zft_volinfo, node);
+ zft_kfree(vtbl, sizeof(zft_volinfo));
+ }
+ INIT_LIST_HEAD(&zft_vtbl);
+ cur_vtbl = NULL;
+}
+
+/* initialize vtbl, called by ftape_new_cartridge()
+ */
+void zft_init_vtbl(void)
+{
+ zft_volinfo *new;
+
+ zft_free_vtbl();
+
+ /* Create the two dummy vtbl entries
+ */
+ new = zft_kmalloc(sizeof(zft_volinfo));
+ list_add(&new->node, &zft_vtbl);
+ new = zft_kmalloc(sizeof(zft_volinfo));
+ list_add(&new->node, &zft_vtbl);
+ zft_head_vtbl->end_seg = ft_first_data_segment;
+ zft_head_vtbl->blk_sz = zft_blk_sz;
+ zft_head_vtbl->count = -1;
+ zft_eom_vtbl->start_seg = ft_first_data_segment + 1;
+ zft_eom_vtbl->end_seg = ft_last_data_segment + 1;
+ zft_eom_vtbl->blk_sz = zft_blk_sz;
+ zft_eom_vtbl->count = 0;
+
+ /* Reset the pointer for zft_find_volume()
+ */
+ cur_vtbl = zft_eom_vtbl;
+
+ /* initialize the dummy vtbl entries for zft_qic_mode == 0
+ */
+ eot_vtbl.start_seg = ft_last_data_segment + 1;
+ eot_vtbl.end_seg = ft_last_data_segment + 1;
+ eot_vtbl.blk_sz = zft_blk_sz;
+ eot_vtbl.count = -1;
+ tape_vtbl.start_seg = ft_first_data_segment;
+ tape_vtbl.end_seg = ft_last_data_segment;
+ tape_vtbl.blk_sz = zft_blk_sz;
+ tape_vtbl.size = zft_capacity;
+ tape_vtbl.count = 0;
+}
+
+/* check for a valid VTBL signature.
+ */
+static int vtbl_signature_valid(__u8 signature[4])
+{
+ const char *vtbl_ids[] = VTBL_IDS; /* valid signatures */
+ int j;
+
+ for (j = 0;
+ (j < NR_ITEMS(vtbl_ids)) && (memcmp(signature, vtbl_ids[j], 4) != 0);
+ j++);
+ return j < NR_ITEMS(vtbl_ids);
+}
+
+/* We used to store the block-size of the volume in the volume-label,
+ * using the keyword "blocksize". The blocksize written to the
+ * volume-label is in bytes.
+ *
+ * We use this now only for compatability with old zftape version. We
+ * store the blocksize directly as binary number in the vendor
+ * extension part of the volume entry.
+ */
+static int check_volume_label(const char *label, int *blk_sz)
+{
+ int valid_format;
+ char *blocksize;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "called with \"%s\" / \"%s\"", label, ZFT_VOL_NAME);
+ if (strncmp(label, ZFT_VOL_NAME, strlen(ZFT_VOL_NAME)) != 0) {
+ *blk_sz = 1; /* smallest block size that we allow */
+ valid_format = 0;
+ } else {
+ TRACE(ft_t_noise, "got old style zftape vtbl entry");
+ /* get the default blocksize */
+ /* use the kernel strstr() */
+ blocksize= strstr(label, " blocksize ");
+ if (blocksize) {
+ blocksize += strlen(" blocksize ");
+ for(*blk_sz= 0;
+ *blocksize >= '0' && *blocksize <= '9';
+ blocksize++) {
+ *blk_sz *= 10;
+ *blk_sz += *blocksize - '0';
+ }
+ if (*blk_sz > ZFT_MAX_BLK_SZ) {
+ *blk_sz= 1;
+ valid_format= 0;
+ } else {
+ valid_format = 1;
+ }
+ } else {
+ *blk_sz= 1;
+ valid_format= 0;
+ }
+ }
+ TRACE_EXIT valid_format;
+}
+
+/* check for a zftape volume
+ */
+static int check_volume(__u8 *entry, zft_volinfo *volume)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if(strncmp(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG,
+ strlen(ZFTAPE_SIG)) == 0) {
+ TRACE(ft_t_noise, "got new style zftape vtbl entry");
+ volume->blk_sz = GET2(entry, VTBL_EXT+EXT_ZFTAPE_BLKSZ);
+ volume->qic113 = entry[VTBL_EXT+EXT_ZFTAPE_QIC113];
+ TRACE_EXIT 1;
+ } else {
+ TRACE_EXIT check_volume_label(&entry[VTBL_DESC], &volume->blk_sz);
+ }
+}
+
+
+/* create zftape specific vtbl entry, the volume bounds are inserted
+ * in the calling function, zft_create_volume_headers()
+ */
+static void create_zft_volume(__u8 *entry, zft_volinfo *vtbl)
+{
+ TRACE_FUN(ft_t_flow);
+
+ memset(entry, 0, VTBL_SIZE);
+ memcpy(&entry[VTBL_SIG], VTBL_ID, 4);
+ sprintf(&entry[VTBL_DESC], ZFT_VOL_NAME" %03d", vtbl->count);
+ entry[VTBL_FLAGS] = (VTBL_FL_NOT_VERIFIED | VTBL_FL_SEG_SPANNING);
+ entry[VTBL_M_NO] = 1; /* multi_cartridge_count */
+ strcpy(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG);
+ PUT2(entry, VTBL_EXT+EXT_ZFTAPE_BLKSZ, vtbl->blk_sz);
+ if (zft_qic113) {
+ PUT8(entry, VTBL_DATA_SIZE, vtbl->size);
+ entry[VTBL_CMPR] = VTBL_CMPR_UNREG;
+ if (vtbl->use_compression) { /* use compression: */
+ entry[VTBL_CMPR] |= VTBL_CMPR_USED;
+ }
+ entry[VTBL_EXT+EXT_ZFTAPE_QIC113] = 1;
+ } else {
+ PUT4(entry, VTBL_DATA_SIZE, vtbl->size);
+ entry[VTBL_K_CMPR] = VTBL_CMPR_UNREG;
+ if (vtbl->use_compression) { /* use compression: */
+ entry[VTBL_K_CMPR] |= VTBL_CMPR_USED;
+ }
+ }
+ if (ft_format_code == fmt_big) {
+ /* SCSI like vtbl, store the number of used
+ * segments as 4 byte value
+ */
+ PUT4(entry, VTBL_SCSI_SEGS, vtbl->end_seg-vtbl->start_seg + 1);
+ } else {
+ /* normal, QIC-80MC like vtbl
+ */
+ PUT2(entry, VTBL_START, vtbl->start_seg);
+ PUT2(entry, VTBL_END, vtbl->end_seg);
+ }
+ TRACE_EXIT;
+}
+
+/* this one creates the volume headers for each volume. It is assumed
+ * that buffer already contains the old volume-table, so that vtbl
+ * entries without the zft_volume flag set can savely be ignored.
+ */
+void zft_create_volume_headers(__u8 *buffer)
+{
+ __u8 *entry;
+ struct list_head *tmp;
+ zft_volinfo *vtbl;
+ TRACE_FUN(ft_t_flow);
+
+#ifdef ZFT_CMAP_HACK
+ if((strncmp(&buffer[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG,
+ strlen(ZFTAPE_SIG)) == 0) &&
+ buffer[VTBL_EXT+EXT_ZFTAPE_CMAP] != 0) {
+ TRACE(ft_t_noise, "deleting cmap volume");
+ memmove(buffer, buffer + VTBL_SIZE,
+ FT_SEGMENT_SIZE - VTBL_SIZE);
+ }
+#endif
+ entry = buffer;
+ for (tmp = zft_head_vtbl->node.next;
+ tmp != &zft_eom_vtbl->node;
+ tmp = tmp->next) {
+ vtbl = list_entry(tmp, zft_volinfo, node);
+ /* we now fill in the values only for newly created volumes.
+ */
+ if (vtbl->new_volume) {
+ create_zft_volume(entry, vtbl);
+ vtbl->new_volume = 0; /* clear the flag */
+ }
+
+ DUMP_VOLINFO(ft_t_noise, &entry[VTBL_DESC], vtbl);
+ entry += VTBL_SIZE;
+ }
+ memset(entry, 0, FT_SEGMENT_SIZE - zft_eom_vtbl->count * VTBL_SIZE);
+ TRACE_EXIT;
+}
+
+/* write volume table to tape. Calls zft_create_volume_headers()
+ */
+int zft_update_volume_table(unsigned int segment)
+{
+ int result = 0;
+ __u8 *verify_buf = NULL;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE_CATCH(result = ftape_read_segment(ft_first_data_segment,
+ zft_deblock_buf,
+ FT_RD_SINGLE),);
+ zft_create_volume_headers(zft_deblock_buf);
+ TRACE(ft_t_noise, "writing volume table segment %d", segment);
+ if (zft_vmalloc_once(&verify_buf, FT_SEGMENT_SIZE) == 0) {
+ TRACE_CATCH(zft_verify_write_segments(segment,
+ zft_deblock_buf, result,
+ verify_buf),
+ zft_vfree(&verify_buf, FT_SEGMENT_SIZE));
+ zft_vfree(&verify_buf, FT_SEGMENT_SIZE);
+ } else {
+ TRACE_CATCH(ftape_write_segment(segment, zft_deblock_buf,
+ FT_WR_SINGLE),);
+ }
+ TRACE_EXIT 0;
+}
+
+/* non zftape volumes are handled in raw mode. Thus we need to
+ * calculate the raw amount of data contained in those segments.
+ */
+static void extract_alien_volume(__u8 *entry, zft_volinfo *vtbl)
+{
+ TRACE_FUN(ft_t_flow);
+
+ vtbl->size = (zft_calc_tape_pos(zft_last_vtbl->end_seg+1) -
+ zft_calc_tape_pos(zft_last_vtbl->start_seg));
+ vtbl->use_compression = 0;
+ vtbl->qic113 = zft_qic113;
+ if (vtbl->qic113) {
+ TRACE(ft_t_noise,
+ "Fake alien volume's size from " LL_X " to " LL_X,
+ LL(GET8(entry, VTBL_DATA_SIZE)), LL(vtbl->size));
+ } else {
+ TRACE(ft_t_noise,
+ "Fake alien volume's size from %d to " LL_X,
+ (int)GET4(entry, VTBL_DATA_SIZE), LL(vtbl->size));
+ }
+ TRACE_EXIT;
+}
+
+
+/* extract an zftape specific volume
+ */
+static void extract_zft_volume(__u8 *entry, zft_volinfo *vtbl)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (vtbl->qic113) {
+ vtbl->size = GET8(entry, VTBL_DATA_SIZE);
+ vtbl->use_compression =
+ (entry[VTBL_CMPR] & VTBL_CMPR_USED) != 0;
+ } else {
+ vtbl->size = GET4(entry, VTBL_DATA_SIZE);
+ if (entry[VTBL_K_CMPR] & VTBL_CMPR_UNREG) {
+ vtbl->use_compression =
+ (entry[VTBL_K_CMPR] & VTBL_CMPR_USED) != 0;
+ } else if (entry[VTBL_CMPR] & VTBL_CMPR_UNREG) {
+ vtbl->use_compression =
+ (entry[VTBL_CMPR] & VTBL_CMPR_USED) != 0;
+ } else {
+ TRACE(ft_t_warn, "Geeh! There is something wrong:\n"
+ KERN_INFO "QIC compression (Rev = K): %x\n"
+ KERN_INFO "QIC compression (Rev > K): %x",
+ entry[VTBL_K_CMPR], entry[VTBL_CMPR]);
+ }
+ }
+ TRACE_EXIT;
+}
+
+/* extract the volume table from buffer. "buffer" must already contain
+ * the vtbl-segment
+ */
+int zft_extract_volume_headers(__u8 *buffer)
+{
+ __u8 *entry;
+ TRACE_FUN(ft_t_flow);
+
+ zft_init_vtbl();
+ entry = buffer;
+#ifdef ZFT_CMAP_HACK
+ if ((strncmp(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG,
+ strlen(ZFTAPE_SIG)) == 0) &&
+ entry[VTBL_EXT+EXT_ZFTAPE_CMAP] != 0) {
+ TRACE(ft_t_noise, "ignoring cmap volume");
+ entry += VTBL_SIZE;
+ }
+#endif
+ /* the end of the vtbl is indicated by an invalid signature
+ */
+ while (vtbl_signature_valid(&entry[VTBL_SIG]) &&
+ (entry - buffer) < FT_SEGMENT_SIZE) {
+ zft_new_vtbl_entry();
+ if (ft_format_code == fmt_big) {
+ /* SCSI like vtbl, stores only the number of
+ * segments used
+ */
+ unsigned int num_segments= GET4(entry, VTBL_SCSI_SEGS);
+ zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg;
+ zft_last_vtbl->end_seg =
+ zft_last_vtbl->start_seg + num_segments - 1;
+ } else {
+ /* `normal', QIC-80 like vtbl
+ */
+ zft_last_vtbl->start_seg = GET2(entry, VTBL_START);
+ zft_last_vtbl->end_seg = GET2(entry, VTBL_END);
+ }
+ zft_eom_vtbl->start_seg = zft_last_vtbl->end_seg + 1;
+ /* check if we created this volume and get the
+ * blk_sz
+ */
+ zft_last_vtbl->zft_volume = check_volume(entry, zft_last_vtbl);
+ if (zft_last_vtbl->zft_volume == 0) {
+ extract_alien_volume(entry, zft_last_vtbl);
+ } else {
+ extract_zft_volume(entry, zft_last_vtbl);
+ }
+ DUMP_VOLINFO(ft_t_noise, &entry[VTBL_DESC], zft_last_vtbl);
+ entry +=VTBL_SIZE;
+ }
+#if 0
+/*
+ * undefine to test end of tape handling
+ */
+ zft_new_vtbl_entry();
+ zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg;
+ zft_last_vtbl->end_seg = ft_last_data_segment - 10;
+ zft_last_vtbl->blk_sz = zft_blk_sz;
+ zft_last_vtbl->zft_volume = 1;
+ zft_last_vtbl->qic113 = zft_qic113;
+ zft_last_vtbl->size = (zft_calc_tape_pos(zft_last_vtbl->end_seg+1)
+ - zft_calc_tape_pos(zft_last_vtbl->start_seg));
+#endif
+ TRACE_EXIT 0;
+}
+
+/* this functions translates the failed_sector_log, misused as
+ * EOF-marker list, into a virtual volume table. The table mustn't be
+ * written to tape, because this would occupy the first data segment,
+ * which should be the volume table, but is actualy the first segment
+ * that is filled with data (when using standard ftape). We assume,
+ * that we get a non-empty failed_sector_log.
+ */
+int zft_fake_volume_headers (eof_mark_union *eof_map, int num_failed_sectors)
+{
+ unsigned int segment, sector;
+ int have_eom = 0;
+ int vol_no;
+ TRACE_FUN(ft_t_flow);
+
+ if ((num_failed_sectors >= 2) &&
+ (GET2(&eof_map[num_failed_sectors - 1].mark.segment, 0)
+ ==
+ GET2(&eof_map[num_failed_sectors - 2].mark.segment, 0) + 1) &&
+ (GET2(&eof_map[num_failed_sectors - 1].mark.date, 0) == 1)) {
+ /* this should be eom. We keep the remainder of the
+ * tape as another volume.
+ */
+ have_eom = 1;
+ }
+ zft_init_vtbl();
+ zft_eom_vtbl->start_seg = ft_first_data_segment;
+ for(vol_no = 0; vol_no < num_failed_sectors - have_eom; vol_no ++) {
+ zft_new_vtbl_entry();
+
+ segment = GET2(&eof_map[vol_no].mark.segment, 0);
+ sector = GET2(&eof_map[vol_no].mark.date, 0);
+
+ zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg;
+ zft_last_vtbl->end_seg = segment;
+ zft_eom_vtbl->start_seg = segment + 1;
+ zft_last_vtbl->blk_sz = 1;
+ zft_last_vtbl->size =
+ (zft_calc_tape_pos(zft_last_vtbl->end_seg)
+ - zft_calc_tape_pos(zft_last_vtbl->start_seg)
+ + (sector-1) * FT_SECTOR_SIZE);
+ TRACE(ft_t_noise,
+ "failed sector log: segment: %d, sector: %d",
+ segment, sector);
+ DUMP_VOLINFO(ft_t_noise, "Faked volume", zft_last_vtbl);
+ }
+ if (!have_eom) {
+ zft_new_vtbl_entry();
+ zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg;
+ zft_last_vtbl->end_seg = ft_last_data_segment;
+ zft_eom_vtbl->start_seg = ft_last_data_segment + 1;
+ zft_last_vtbl->size = zft_capacity;
+ zft_last_vtbl->size -= zft_calc_tape_pos(zft_last_vtbl->start_seg);
+ zft_last_vtbl->blk_sz = 1;
+ DUMP_VOLINFO(ft_t_noise, "Faked volume",zft_last_vtbl);
+ }
+ TRACE_EXIT 0;
+}
+
+/* update the internal volume table
+ *
+ * if before start of last volume: erase all following volumes if
+ * inside a volume: set end of volume to infinity
+ *
+ * this function is intended to be called every time _ftape_write() is
+ * called
+ *
+ * return: 0 if no new volume was created, 1 if a new volume was
+ * created
+ *
+ * NOTE: we don't need to check for zft_mode as ftape_write() does
+ * that already. This function gets never called without accessing
+ * zftape via the *qft* devices
+ */
+
+int zft_open_volume(zft_position *pos, int blk_sz, int use_compression)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (!zft_qic_mode) {
+ TRACE_EXIT 0;
+ }
+ if (zft_tape_at_lbot(pos)) {
+ zft_init_vtbl();
+ if(zft_old_ftape) {
+ /* clear old ftape's eof marks */
+ zft_clear_ftape_file_marks();
+ zft_old_ftape = 0; /* no longer old ftape */
+ }
+ zft_reset_position(pos);
+ }
+ if (pos->seg_pos != zft_last_vtbl->end_seg + 1) {
+ TRACE_ABORT(-EIO, ft_t_bug,
+ "BUG: seg_pos: %d, zft_last_vtbl->end_seg: %d",
+ pos->seg_pos, zft_last_vtbl->end_seg);
+ }
+ TRACE(ft_t_noise, "create new volume");
+ if (zft_eom_vtbl->count >= ZFT_MAX_VOLUMES) {
+ TRACE_ABORT(-ENOSPC, ft_t_err,
+ "Error: maxmimal number of volumes exhausted "
+ "(maxmimum is %d)", ZFT_MAX_VOLUMES);
+ }
+ zft_new_vtbl_entry();
+ pos->volume_pos = pos->seg_byte_pos = 0;
+ zft_last_vtbl->start_seg = pos->seg_pos;
+ zft_last_vtbl->end_seg = ft_last_data_segment; /* infinity */
+ zft_last_vtbl->blk_sz = blk_sz;
+ zft_last_vtbl->size = zft_capacity;
+ zft_last_vtbl->zft_volume = 1;
+ zft_last_vtbl->use_compression = use_compression;
+ zft_last_vtbl->qic113 = zft_qic113;
+ zft_last_vtbl->new_volume = 1;
+ zft_last_vtbl->open = 1;
+ zft_volume_table_changed = 1;
+ zft_eom_vtbl->start_seg = ft_last_data_segment + 1;
+ TRACE_EXIT 0;
+}
+
+/* perform mtfsf, mtbsf, not allowed without zft_qic_mode
+ */
+int zft_skip_volumes(int count, zft_position *pos)
+{
+ const zft_volinfo *vtbl;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "count: %d", count);
+
+ vtbl= zft_find_volume(pos->seg_pos);
+ while (count > 0 && vtbl != zft_eom_vtbl) {
+ vtbl = list_entry(vtbl->node.next, zft_volinfo, node);
+ count --;
+ }
+ while (count < 0 && vtbl != zft_first_vtbl) {
+ vtbl = list_entry(vtbl->node.prev, zft_volinfo, node);
+ count ++;
+ }
+ pos->seg_pos = vtbl->start_seg;
+ pos->seg_byte_pos = 0;
+ pos->volume_pos = 0;
+ pos->tape_pos = zft_calc_tape_pos(pos->seg_pos);
+ zft_just_before_eof = vtbl->size == 0;
+ if (zft_cmpr_ops) {
+ (*zft_cmpr_ops->reset)();
+ }
+ zft_deblock_segment = -1; /* no need to keep cache */
+ TRACE(ft_t_noise, "repositioning to:\n"
+ KERN_INFO "zft_seg_pos : %d\n"
+ KERN_INFO "zft_seg_byte_pos : %d\n"
+ KERN_INFO "zft_tape_pos : " LL_X "\n"
+ KERN_INFO "zft_volume_pos : " LL_X "\n"
+ KERN_INFO "file number : %d",
+ pos->seg_pos, pos->seg_byte_pos,
+ LL(pos->tape_pos), LL(pos->volume_pos), vtbl->count);
+ zft_resid = count < 0 ? -count : count;
+ TRACE_EXIT zft_resid ? -EINVAL : 0;
+}
+
+/* the following simply returns the raw data position of the EOM
+ * marker, MTIOCSIZE ioctl
+ */
+__s64 zft_get_eom_pos(void)
+{
+ if (zft_qic_mode) {
+ return zft_calc_tape_pos(zft_eom_vtbl->start_seg);
+ } else {
+ /* there is only one volume in raw mode */
+ return zft_capacity;
+ }
+}
+
+/* skip to eom, used for MTEOM
+ */
+void zft_skip_to_eom(zft_position *pos)
+{
+ TRACE_FUN(ft_t_flow);
+ pos->seg_pos = zft_eom_vtbl->start_seg;
+ pos->seg_byte_pos =
+ pos->volume_pos =
+ zft_just_before_eof = 0;
+ pos->tape_pos = zft_calc_tape_pos(pos->seg_pos);
+ TRACE(ft_t_noise, "ftape positioned to segment %d, data pos " LL_X,
+ pos->seg_pos, LL(pos->tape_pos));
+ TRACE_EXIT;
+}
+
+/* write an EOF-marker by setting zft_last_vtbl->end_seg to seg_pos.
+ * NOTE: this function assumes that zft_last_vtbl points to a valid
+ * vtbl entry
+ *
+ * NOTE: this routine always positions before the EOF marker
+ */
+int zft_close_volume(zft_position *pos)
+{
+ TRACE_FUN(ft_t_any);
+
+ if (zft_vtbl_empty || !zft_last_vtbl->open) { /* should not happen */
+ TRACE(ft_t_noise, "There are no volumes to finish");
+ TRACE_EXIT -EIO;
+ }
+ if (pos->seg_byte_pos == 0 &&
+ pos->seg_pos != zft_last_vtbl->start_seg) {
+ pos->seg_pos --;
+ pos->seg_byte_pos = zft_get_seg_sz(pos->seg_pos);
+ }
+ zft_last_vtbl->end_seg = pos->seg_pos;
+ zft_last_vtbl->size = pos->volume_pos;
+ zft_volume_table_changed = 1;
+ zft_just_before_eof = 1;
+ zft_eom_vtbl->start_seg = zft_last_vtbl->end_seg + 1;
+ zft_last_vtbl->open = 0; /* closed */
+ TRACE_EXIT 0;
+}
+
+/* write count file-marks at current position.
+ *
+ * The tape is positioned after the eof-marker, that is at byte 0 of
+ * the segment following the eof-marker
+ *
+ * this function is only allowed in zft_qic_mode
+ *
+ * Only allowed when tape is at BOT or EOD.
+ */
+int zft_weof(unsigned int count, zft_position *pos)
+{
+
+ TRACE_FUN(ft_t_flow);
+
+ if (!count) { /* write zero EOF marks should be a real no-op */
+ TRACE_EXIT 0;
+ }
+ zft_volume_table_changed = 1;
+ if (zft_tape_at_lbot(pos)) {
+ zft_init_vtbl();
+ if(zft_old_ftape) {
+ /* clear old ftape's eof marks */
+ zft_clear_ftape_file_marks();
+ zft_old_ftape = 0; /* no longer old ftape */
+ }
+ }
+ if (zft_last_vtbl->open) {
+ zft_close_volume(pos);
+ zft_move_past_eof(pos);
+ count --;
+ }
+ /* now it's easy, just append eof-marks, that is empty
+ * volumes, to the end of the already recorded media.
+ */
+ while (count > 0 &&
+ pos->seg_pos <= ft_last_data_segment &&
+ zft_eom_vtbl->count < ZFT_MAX_VOLUMES) {
+ TRACE(ft_t_noise,
+ "Writing zero sized file at segment %d", pos->seg_pos);
+ zft_new_vtbl_entry();
+ zft_last_vtbl->start_seg = pos->seg_pos;
+ zft_last_vtbl->end_seg = pos->seg_pos;
+ zft_last_vtbl->size = 0;
+ zft_last_vtbl->blk_sz = zft_blk_sz;
+ zft_last_vtbl->zft_volume = 1;
+ zft_last_vtbl->use_compression = 0;
+ pos->tape_pos += zft_get_seg_sz(pos->seg_pos);
+ zft_eom_vtbl->start_seg = ++ pos->seg_pos;
+ count --;
+ }
+ if (count > 0) {
+ /* there are two possibilities: end of tape, or the
+ * maximum number of files is exhausted.
+ */
+ zft_resid = count;
+ TRACE(ft_t_noise,"Number of marks NOT written: %d", zft_resid);
+ if (zft_eom_vtbl->count == ZFT_MAX_VOLUMES) {
+ TRACE_ABORT(-EINVAL, ft_t_warn,
+ "maximum allowed number of files "
+ "exhausted: %d", ZFT_MAX_VOLUMES);
+ } else {
+ TRACE_ABORT(-ENOSPC,
+ ft_t_noise, "reached end of tape");
+ }
+ }
+ TRACE_EXIT 0;
+}
+
+const zft_volinfo *zft_find_volume(unsigned int seg_pos)
+{
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_any, "called with seg_pos %d",seg_pos);
+ if (!zft_qic_mode) {
+ if (seg_pos > ft_last_data_segment) {
+ TRACE_EXIT &eot_vtbl;
+ }
+ tape_vtbl.blk_sz = zft_blk_sz;
+ TRACE_EXIT &tape_vtbl;
+ }
+ if (seg_pos < zft_first_vtbl->start_seg) {
+ TRACE_EXIT (cur_vtbl = zft_first_vtbl);
+ }
+ while (seg_pos > cur_vtbl->end_seg) {
+ cur_vtbl = list_entry(cur_vtbl->node.next, zft_volinfo, node);
+ TRACE(ft_t_noise, "%d - %d", cur_vtbl->start_seg, cur_vtbl->end_seg);
+ }
+ while (seg_pos < cur_vtbl->start_seg) {
+ cur_vtbl = list_entry(cur_vtbl->node.prev, zft_volinfo, node);
+ TRACE(ft_t_noise, "%d - %d", cur_vtbl->start_seg, cur_vtbl->end_seg);
+ }
+ if (seg_pos > cur_vtbl->end_seg || seg_pos < cur_vtbl->start_seg) {
+ TRACE(ft_t_bug, "This cannot happen");
+ }
+ DUMP_VOLINFO(ft_t_noise, "", cur_vtbl);
+ TRACE_EXIT cur_vtbl;
+}
+
+/* this function really assumes that we are just before eof
+ */
+void zft_move_past_eof(zft_position *pos)
+{
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_noise, "old seg. pos: %d", pos->seg_pos);
+ pos->tape_pos += zft_get_seg_sz(pos->seg_pos++) - pos->seg_byte_pos;
+ pos->seg_byte_pos = 0;
+ pos->volume_pos = 0;
+ if (zft_cmpr_ops) {
+ (*zft_cmpr_ops->reset)();
+ }
+ zft_just_before_eof = 0;
+ zft_deblock_segment = -1; /* no need to cache it anymore */
+ TRACE(ft_t_noise, "new seg. pos: %d", pos->seg_pos);
+ TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/zftape/zftape-vtbl.h b/drivers/char/ftape/zftape/zftape-vtbl.h
new file mode 100644
index 000000000..7248db11c
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-vtbl.h
@@ -0,0 +1,229 @@
+#ifndef _ZFTAPE_VTBL_H
+#define _ZFTAPE_VTBL_H
+
+/*
+ * Copyright (c) 1995-1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-vtbl.h,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/10/28 14:30:09 $
+ *
+ * This file defines a volume table as defined in the QIC-80
+ * development standards.
+ */
+
+#include <linux/list.h>
+
+#include "../lowlevel/ftape-tracing.h"
+
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-rw.h"
+
+#define VTBL_SIZE 128 /* bytes */
+
+/* The following are offsets in the vtbl. */
+#define VTBL_SIG 0
+#define VTBL_START 4
+#define VTBL_END 6
+#define VTBL_DESC 8
+#define VTBL_DATE 52
+#define VTBL_FLAGS 56
+#define VTBL_FL_VENDOR_SPECIFIC (1<<0)
+#define VTBL_FL_MUTLI_CARTRIDGE (1<<1)
+#define VTBL_FL_NOT_VERIFIED (1<<2)
+#define VTBL_FL_REDIR_INHIBIT (1<<3)
+#define VTBL_FL_SEG_SPANNING (1<<4)
+#define VTBL_FL_DIRECTORY_LAST (1<<5)
+#define VTBL_FL_RESERVED_6 (1<<6)
+#define VTBL_FL_RESERVED_7 (1<<7)
+#define VTBL_M_NO 57
+#define VTBL_EXT 58
+#define EXT_ZFTAPE_SIG 0
+#define EXT_ZFTAPE_BLKSZ 10
+#define EXT_ZFTAPE_CMAP 12
+#define EXT_ZFTAPE_QIC113 13
+#define VTBL_PWD 84
+#define VTBL_DIR_SIZE 92
+#define VTBL_DATA_SIZE 96
+#define VTBL_OS_VERSION 104
+#define VTBL_SRC_DRIVE 106
+#define VTBL_DEV 122
+#define VTBL_RESERVED_1 123
+#define VTBL_CMPR 124
+#define VTBL_CMPR_UNREG 0x3f
+#define VTBL_CMPR_USED 0x80
+#define VTBL_FMT 125
+#define VTBL_RESERVED_2 126
+#define VTBL_RESERVED_3 127
+/* compatability with pre revision K */
+#define VTBL_K_CMPR 120
+
+/* the next is used by QIC-3020 tapes with format code 6 (>2^16
+ * segments) It is specified in QIC-113, Rev. G, Section 5 (SCSI
+ * volume table). The difference is simply, that we only store the
+ * number of segments used, not the starting segment.
+ */
+#define VTBL_SCSI_SEGS 4 /* is a 4 byte value */
+
+/* one vtbl is 128 bytes, that results in a maximum number of
+ * 29*1024/128 = 232 volumes.
+ */
+#define ZFT_MAX_VOLUMES (FT_SEGMENT_SIZE/VTBL_SIZE)
+#define VTBL_ID "VTBL"
+#define VTBL_IDS { VTBL_ID, "XTBL", "UTID", "EXVT" } /* other valid ids */
+#define ZFT_VOL_NAME "zftape volume" /* volume label used by me */
+#define ZFTAPE_SIG "LINUX ZFT"
+
+/* global variables
+ */
+typedef struct zft_internal_vtbl
+{
+ struct list_head node;
+ int count;
+ unsigned int start_seg; /* 32 bits are enough for now */
+ unsigned int end_seg; /* 32 bits are enough for now */
+ __s64 size; /* uncompressed size */
+ unsigned int blk_sz; /* block size for this volume */
+ unsigned int zft_volume :1; /* zftape created this volume */
+ unsigned int use_compression:1; /* compressed volume */
+ unsigned int qic113 :1; /* layout of compressed block
+ * info and vtbl conforms to
+ * QIC-113, Rev. G
+ */
+ unsigned int new_volume :1; /* it was created by us, this
+ * run. this allows the
+ * fields that aren't really
+ * used by zftape to be filled
+ * in by some user level
+ * program.
+ */
+ unsigned int open :1; /* just in progress of being
+ * written
+ */
+} zft_volinfo;
+
+extern struct list_head zft_vtbl;
+#define zft_head_vtbl list_entry(zft_vtbl.next, zft_volinfo, node)
+#define zft_eom_vtbl list_entry(zft_vtbl.prev, zft_volinfo, node)
+#define zft_last_vtbl list_entry(zft_eom_vtbl->node.prev, zft_volinfo, node)
+#define zft_first_vtbl list_entry(zft_head_vtbl->node.next, zft_volinfo, node)
+#define zft_vtbl_empty (zft_eom_vtbl->node.prev == &zft_head_vtbl->node)
+
+#define DUMP_VOLINFO(level, desc, info) \
+{ \
+ char tmp[21]; \
+ strncpy(tmp, desc, 20); \
+ tmp[20] = '\0'; \
+ TRACE(level, "Volume %d:\n" \
+ KERN_INFO "description : %s\n" \
+ KERN_INFO "first segment: %d\n" \
+ KERN_INFO "last segment: %d\n" \
+ KERN_INFO "size : " LL_X "\n" \
+ KERN_INFO "block size : %d\n" \
+ KERN_INFO "compression : %d\n" \
+ KERN_INFO "zftape volume: %d\n" \
+ KERN_INFO "QIC-113 conf.: %d", \
+ (info)->count, tmp, (info)->start_seg, (info)->end_seg, \
+ LL((info)->size), (info)->blk_sz, \
+ (info)->use_compression != 0, (info)->zft_volume != 0, \
+ (info)->qic113 != 0); \
+}
+
+extern int zft_qic_mode;
+extern int zft_old_ftape;
+extern int zft_volume_table_changed;
+
+/* exported functions */
+extern void zft_init_vtbl (void);
+extern void zft_free_vtbl (void);
+extern void zft_new_vtbl_entry (void);
+extern int zft_extract_volume_headers(__u8 *buffer);
+extern int zft_update_volume_table (unsigned int segment);
+extern int zft_open_volume (zft_position *pos,
+ int blk_sz, int use_compression);
+extern int zft_close_volume (zft_position *pos);
+extern const zft_volinfo *zft_find_volume(unsigned int seg_pos);
+extern int zft_skip_volumes (int count, zft_position *pos);
+extern __s64 zft_get_eom_pos (void);
+extern void zft_skip_to_eom (zft_position *pos);
+extern int zft_fake_volume_headers (eof_mark_union *eof_map,
+ int num_failed_sectors);
+extern int zft_weof (unsigned int count, zft_position *pos);
+extern void zft_move_past_eof (zft_position *pos);
+
+extern inline int zft_tape_at_eod (const zft_position *pos);
+extern inline int zft_tape_at_lbot (const zft_position *pos);
+extern inline void zft_position_before_eof (zft_position *pos,
+ const zft_volinfo *volume);
+extern inline __s64 zft_check_for_eof(const zft_volinfo *vtbl,
+ const zft_position *pos);
+
+/* this function decrements the zft_seg_pos counter if we are right
+ * at the beginning of a segment. This is to handel fsfm/bsfm -- we
+ * need to position before the eof mark. NOTE: zft_tape_pos is not
+ * changed
+ */
+extern inline void zft_position_before_eof(zft_position *pos,
+ const zft_volinfo *volume)
+{
+ TRACE_FUN(ft_t_flow);
+
+ if (pos->seg_pos == volume->end_seg + 1 && pos->seg_byte_pos == 0) {
+ pos->seg_pos --;
+ pos->seg_byte_pos = zft_get_seg_sz(pos->seg_pos);
+ }
+ TRACE_EXIT;
+}
+
+/* Mmmh. Is the position at the end of the last volume, that is right
+ * before the last EOF mark also logical an EOD condition?
+ */
+extern inline int zft_tape_at_eod(const zft_position *pos)
+{
+ TRACE_FUN(ft_t_any);
+
+ if (zft_qic_mode) {
+ TRACE_EXIT (pos->seg_pos >= zft_eom_vtbl->start_seg ||
+ zft_last_vtbl->open);
+ } else {
+ TRACE_EXIT pos->seg_pos > ft_last_data_segment;
+ }
+}
+
+extern inline int zft_tape_at_lbot(const zft_position *pos)
+{
+ if (zft_qic_mode) {
+ return (pos->seg_pos <= zft_first_vtbl->start_seg &&
+ pos->volume_pos == 0);
+ } else {
+ return (pos->seg_pos <= ft_first_data_segment &&
+ pos->volume_pos == 0);
+ }
+}
+
+/* This one checks for EOF. return remaing space (may be negative)
+ */
+extern inline __s64 zft_check_for_eof(const zft_volinfo *vtbl,
+ const zft_position *pos)
+{
+ return (__s64)(vtbl->size - pos->volume_pos);
+}
+
+#endif /* _ZFTAPE_VTBL_H */
diff --git a/drivers/char/ftape/zftape/zftape-write.c b/drivers/char/ftape/zftape/zftape-write.c
new file mode 100644
index 000000000..46f1ecc09
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-write.c
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 1996, 1997 Claus Heine
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-write.c,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/11/06 00:50:29 $
+ *
+ * This file contains the writing code
+ * for the QIC-117 floppy-tape driver for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+#include <linux/zftape.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6)
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#endif
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-vtbl.h"
+
+/* Global vars.
+ */
+
+/* Local vars.
+ */
+static int last_write_failed = 0;
+static int need_flush = 0;
+
+void zft_prevent_flush(void)
+{
+ need_flush = 0;
+}
+
+static int zft_write_header_segments(__u8* buffer)
+{
+ int header_1_ok = 0;
+ int header_2_ok = 0;
+ unsigned int time_stamp;
+ TRACE_FUN(ft_t_noise);
+
+ TRACE_CATCH(ftape_abort_operation(),);
+ ftape_seek_to_bot(); /* prevents extra rewind */
+ if (GET4(buffer, 0) != FT_HSEG_MAGIC) {
+ TRACE_ABORT(-EIO, ft_t_err,
+ "wrong header signature found, aborting");
+ }
+ /* Be optimistic: */
+ PUT4(buffer, FT_SEG_CNT,
+ zft_written_segments + GET4(buffer, FT_SEG_CNT) + 2);
+ if ((time_stamp = zft_get_time()) != 0) {
+ PUT4(buffer, FT_WR_DATE, time_stamp);
+ if (zft_label_changed) {
+ PUT4(buffer, FT_LABEL_DATE, time_stamp);
+ }
+ }
+ TRACE(ft_t_noise,
+ "writing first header segment %d", ft_header_segment_1);
+ header_1_ok = zft_verify_write_segments(ft_header_segment_1,
+ buffer, FT_SEGMENT_SIZE,
+ zft_deblock_buf) >= 0;
+ TRACE(ft_t_noise,
+ "writing second header segment %d", ft_header_segment_2);
+ header_2_ok = zft_verify_write_segments(ft_header_segment_2,
+ buffer, FT_SEGMENT_SIZE,
+ zft_deblock_buf) >= 0;
+ if (!header_1_ok) {
+ TRACE(ft_t_warn, "Warning: "
+ "update of first header segment failed");
+ }
+ if (!header_2_ok) {
+ TRACE(ft_t_warn, "Warning: "
+ "update of second header segment failed");
+ }
+ if (!header_1_ok && !header_2_ok) {
+ TRACE_ABORT(-EIO, ft_t_err, "Error: "
+ "update of both header segments failed.");
+ }
+ TRACE_EXIT 0;
+}
+
+int zft_update_header_segments(void)
+{
+ TRACE_FUN(ft_t_noise);
+
+ /* must NOT use zft_write_protected, as it also includes the
+ * file access mode. But we also want to update when soft
+ * write protection is enabled (O_RDONLY)
+ */
+ if (ft_write_protected || zft_old_ftape) {
+ TRACE_ABORT(0, ft_t_noise, "Tape set read-only: no update");
+ }
+ if (!zft_header_read) {
+ TRACE_ABORT(0, ft_t_noise, "Nothing to update");
+ }
+ if (!zft_header_changed) {
+ zft_header_changed = zft_written_segments > 0;
+ }
+ if (!zft_header_changed && !zft_volume_table_changed) {
+ TRACE_ABORT(0, ft_t_noise, "Nothing to update");
+ }
+ TRACE(ft_t_noise, "Updating header segments");
+ if (ftape_get_status()->fti_state == writing) {
+ TRACE_CATCH(ftape_loop_until_writes_done(),);
+ }
+ TRACE_CATCH(ftape_abort_operation(),);
+
+ zft_deblock_segment = -1; /* invalidate the cache */
+ if (zft_header_changed) {
+ TRACE_CATCH(zft_write_header_segments(zft_hseg_buf),);
+ }
+ if (zft_volume_table_changed) {
+ TRACE_CATCH(zft_update_volume_table(ft_first_data_segment),);
+ }
+ zft_header_changed =
+ zft_volume_table_changed =
+ zft_label_changed =
+ zft_written_segments = 0;
+ TRACE_CATCH(ftape_abort_operation(),);
+ ftape_seek_to_bot();
+ TRACE_EXIT 0;
+}
+
+static int read_merge_buffer(int seg_pos, __u8 *buffer, int offset, int seg_sz)
+{
+ int result = 0;
+ const ft_trace_t old_tracing = TRACE_LEVEL;
+ TRACE_FUN(ft_t_flow);
+
+ if (zft_qic_mode) {
+ /* writing in the middle of a volume is NOT allowed
+ *
+ */
+ TRACE(ft_t_noise, "No need to read a segment");
+ memset(buffer + offset, 0, seg_sz - offset);
+ TRACE_EXIT 0;
+ }
+ TRACE(ft_t_any, "waiting");
+ ftape_start_writing(FT_WR_MULTI);
+ TRACE_CATCH(ftape_loop_until_writes_done(),);
+
+ TRACE(ft_t_noise, "trying to read segment %d from offset %d",
+ seg_pos, offset);
+ SET_TRACE_LEVEL(ft_t_bug);
+ result = zft_fetch_segment_fraction(seg_pos, buffer,
+ FT_RD_SINGLE,
+ offset, seg_sz - offset);
+ SET_TRACE_LEVEL(old_tracing);
+ if (result != (seg_sz - offset)) {
+ TRACE(ft_t_noise, "Ignore error: read_segment() result: %d",
+ result);
+ memset(buffer + offset, 0, seg_sz - offset);
+ }
+ TRACE_EXIT 0;
+}
+
+/* flush the write buffer to tape and write an eof-marker at the
+ * current position if not in raw mode. This function always
+ * positions the tape before the eof-marker. _ftape_close() should
+ * then advance to the next segment.
+ *
+ * the parameter "finish_volume" describes whether to position before
+ * or after the possibly created file-mark. We always position after
+ * the file-mark when called from ftape_close() and a flush was needed
+ * (that is ftape_write() was the last tape operation before calling
+ * ftape_flush) But we always position before the file-mark when this
+ * function get's called from outside ftape_close()
+ */
+int zft_flush_buffers(void)
+{
+ int result;
+ int data_remaining;
+ int this_segs_size;
+ TRACE_FUN(ft_t_flow);
+
+ TRACE(ft_t_data_flow,
+ "entered, ftape_state = %d", ftape_get_status()->fti_state);
+ if (ftape_get_status()->fti_state != writing && !need_flush) {
+ TRACE_ABORT(0, ft_t_noise, "no need for flush");
+ }
+ zft_io_state = zft_idle; /* triggers some initializations for the
+ * read and write routines
+ */
+ if (last_write_failed) {
+ ftape_abort_operation();
+ TRACE_EXIT -EIO;
+ }
+ TRACE(ft_t_noise, "flushing write buffers");
+ this_segs_size = zft_get_seg_sz(zft_pos.seg_pos);
+ if (this_segs_size == zft_pos.seg_byte_pos) {
+ zft_pos.seg_pos ++;
+ data_remaining = zft_pos.seg_byte_pos = 0;
+ } else {
+ data_remaining = zft_pos.seg_byte_pos;
+ }
+ /* If there is any data not written to tape yet, append zero's
+ * up to the end of the sector (if using compression) or merge
+ * it with the data existing on the tape Then write the
+ * segment(s) to tape.
+ */
+ TRACE(ft_t_noise, "Position:\n"
+ KERN_INFO "seg_pos : %d\n"
+ KERN_INFO "byte pos : %d\n"
+ KERN_INFO "remaining: %d",
+ zft_pos.seg_pos, zft_pos.seg_byte_pos, data_remaining);
+ if (data_remaining > 0) {
+ do {
+ this_segs_size = zft_get_seg_sz(zft_pos.seg_pos);
+ if (this_segs_size > data_remaining) {
+ TRACE_CATCH(read_merge_buffer(zft_pos.seg_pos,
+ zft_deblock_buf,
+ data_remaining,
+ this_segs_size),
+ last_write_failed = 1);
+ }
+ result = ftape_write_segment(zft_pos.seg_pos,
+ zft_deblock_buf,
+ FT_WR_MULTI);
+ if (result != this_segs_size) {
+ TRACE(ft_t_err, "flush buffers failed");
+ zft_pos.tape_pos -= zft_pos.seg_byte_pos;
+ zft_pos.seg_byte_pos = 0;
+
+ last_write_failed = 1;
+ TRACE_EXIT result;
+ }
+ zft_written_segments ++;
+ TRACE(ft_t_data_flow,
+ "flush, moved out buffer: %d", result);
+ /* need next segment for more data (empty segments?)
+ */
+ if (result < data_remaining) {
+ if (result > 0) {
+ /* move remainder to buffer beginning
+ */
+ memmove(zft_deblock_buf,
+ zft_deblock_buf + result,
+ FT_SEGMENT_SIZE - result);
+ }
+ }
+ data_remaining -= result;
+ zft_pos.seg_pos ++;
+ } while (data_remaining > 0);
+ TRACE(ft_t_any, "result: %d", result);
+ zft_deblock_segment = --zft_pos.seg_pos;
+ if (data_remaining == 0) { /* first byte next segment */
+ zft_pos.seg_byte_pos = this_segs_size;
+ } else { /* after data previous segment, data_remaining < 0 */
+ zft_pos.seg_byte_pos = data_remaining + result;
+ }
+ } else {
+ TRACE(ft_t_noise, "zft_deblock_buf empty");
+ zft_pos.seg_pos --;
+ zft_pos.seg_byte_pos = zft_get_seg_sz (zft_pos.seg_pos);
+ ftape_start_writing(FT_WR_MULTI);
+ }
+ TRACE(ft_t_any, "waiting");
+ if ((result = ftape_loop_until_writes_done()) < 0) {
+ /* that's really bad. What to to with zft_tape_pos?
+ */
+ TRACE(ft_t_err, "flush buffers failed");
+ }
+ TRACE(ft_t_any, "zft_seg_pos: %d, zft_seg_byte_pos: %d",
+ zft_pos.seg_pos, zft_pos.seg_byte_pos);
+ last_write_failed =
+ need_flush = 0;
+ TRACE_EXIT result;
+}
+
+/* return-value: the number of bytes removed from the user-buffer
+ *
+ * out:
+ * int *write_cnt: how much actually has been moved to the
+ * zft_deblock_buf
+ * int req_len : MUST NOT BE CHANGED, except at EOT, in
+ * which case it may be adjusted
+ * in :
+ * char *buff : the user buffer
+ * int buf_pos_write : copy of buf_len_wr int
+ * this_segs_size : the size in bytes of the actual segment
+ * char
+ * *zft_deblock_buf : zft_deblock_buf
+ */
+static int zft_simple_write(int *cnt,
+ __u8 *dst_buf, const int seg_sz,
+ const __u8 *src_buf, const int req_len,
+ const zft_position *pos,const zft_volinfo *volume)
+{
+ int space_left;
+ TRACE_FUN(ft_t_flow);
+
+ /* volume->size holds the tape capacity while volume is open */
+ if (pos->tape_pos + volume->blk_sz > volume->size) {
+ TRACE_EXIT -ENOSPC;
+ }
+ /* remaining space in this segment, NOT zft_deblock_buf
+ */
+ space_left = seg_sz - pos->seg_byte_pos;
+ *cnt = req_len < space_left ? req_len : space_left;
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+ if (copy_from_user(dst_buf + pos->seg_byte_pos, src_buf, *cnt) != 0) {
+ TRACE_EXIT -EFAULT;
+ }
+#else
+ TRACE_CATCH(verify_area(VERIFY_READ, src_buf, *cnt),);
+ memcpy_fromfs(dst_buf + pos->seg_byte_pos, src_buf, *cnt);
+#endif
+ TRACE_EXIT *cnt;
+}
+
+static int check_write_access(int req_len,
+ const zft_volinfo **volume,
+ zft_position *pos,
+ const unsigned int blk_sz)
+{
+ int result;
+ TRACE_FUN(ft_t_flow);
+
+ if ((req_len % zft_blk_sz) != 0) {
+ TRACE_ABORT(-EINVAL, ft_t_info,
+ "write-count %d must be multiple of block-size %d",
+ req_len, blk_sz);
+ }
+ if (zft_io_state == zft_writing) {
+ /* all other error conditions have been checked earlier
+ */
+ TRACE_EXIT 0;
+ }
+ zft_io_state = zft_idle;
+ TRACE_CATCH(zft_check_write_access(pos),);
+ /* If we haven't read the header segment yet, do it now.
+ * This will verify the configuration, get the bad sector
+ * table and read the volume table segment
+ */
+ if (!zft_header_read) {
+ TRACE_CATCH(zft_read_header_segments(),);
+ }
+ /* fine. Now the tape is either at BOT or at EOD,
+ * Write start of volume now
+ */
+ TRACE_CATCH(zft_open_volume(pos, blk_sz, zft_use_compression),);
+ *volume = zft_find_volume(pos->seg_pos);
+ DUMP_VOLINFO(ft_t_noise, "", *volume);
+ zft_just_before_eof = 0;
+ /* now merge with old data if neccessary */
+ if (!zft_qic_mode && pos->seg_byte_pos != 0){
+ result = zft_fetch_segment(pos->seg_pos,
+ zft_deblock_buf,
+ FT_RD_SINGLE);
+ if (result < 0) {
+ if (result == -EINTR || result == -ENOSPC) {
+ TRACE_EXIT result;
+ }
+ TRACE(ft_t_noise,
+ "ftape_read_segment() result: %d. "
+ "This might be normal when using "
+ "a newly\nformatted tape", result);
+ memset(zft_deblock_buf, '\0', pos->seg_byte_pos);
+ }
+ }
+ zft_io_state = zft_writing;
+ TRACE_EXIT 0;
+}
+
+static int fill_deblock_buf(__u8 *dst_buf, const int seg_sz,
+ zft_position *pos, const zft_volinfo *volume,
+ const char *usr_buf, const int req_len)
+{
+ int cnt = 0;
+ int result = 0;
+ TRACE_FUN(ft_t_flow);
+
+ if (seg_sz == 0) {
+ TRACE_ABORT(0, ft_t_data_flow, "empty segment");
+ }
+ TRACE(ft_t_data_flow, "\n"
+ KERN_INFO "remaining req_len: %d\n"
+ KERN_INFO " buf_pos: %d",
+ req_len, pos->seg_byte_pos);
+ /* zft_deblock_buf will not contain a valid segment any longer */
+ zft_deblock_segment = -1;
+ if (zft_use_compression) {
+ TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),);
+ TRACE_CATCH(result= (*zft_cmpr_ops->write)(&cnt,
+ dst_buf, seg_sz,
+ usr_buf, req_len,
+ pos, volume),);
+ } else {
+ TRACE_CATCH(result= zft_simple_write(&cnt,
+ dst_buf, seg_sz,
+ usr_buf, req_len,
+ pos, volume),);
+ }
+ pos->volume_pos += result;
+ pos->seg_byte_pos += cnt;
+ pos->tape_pos += cnt;
+ TRACE(ft_t_data_flow, "\n"
+ KERN_INFO "removed from user-buffer : %d bytes.\n"
+ KERN_INFO "copied to zft_deblock_buf: %d bytes.\n"
+ KERN_INFO "zft_tape_pos : " LL_X " bytes.",
+ result, cnt, LL(pos->tape_pos));
+ TRACE_EXIT result;
+}
+
+
+/* called by the kernel-interface routine "zft_write()"
+ */
+int _zft_write(const char* buff, int req_len)
+{
+ int result = 0;
+ int written = 0;
+ int write_cnt;
+ int seg_sz;
+ static const zft_volinfo *volume = NULL;
+ TRACE_FUN(ft_t_flow);
+
+ zft_resid = req_len;
+ last_write_failed = 1; /* reset to 0 when successful */
+ /* check if write is allowed
+ */
+ TRACE_CATCH(check_write_access(req_len, &volume,&zft_pos,zft_blk_sz),);
+ while (req_len > 0) {
+ /* Allow us to escape from this loop with a signal !
+ */
+ FT_SIGNAL_EXIT(_DONT_BLOCK);
+ seg_sz = zft_get_seg_sz(zft_pos.seg_pos);
+ if ((write_cnt = fill_deblock_buf(zft_deblock_buf,
+ seg_sz,
+ &zft_pos,
+ volume,
+ buff,
+ req_len)) < 0) {
+ zft_resid -= written;
+ if (write_cnt == -ENOSPC) {
+ /* leave the remainder to flush_buffers()
+ */
+ TRACE(ft_t_info, "No space left on device");
+ last_write_failed = 0;
+ if (!need_flush) {
+ need_flush = written > 0;
+ }
+ TRACE_EXIT written > 0 ? written : -ENOSPC;
+ } else {
+ TRACE_EXIT result;
+ }
+ }
+ if (zft_pos.seg_byte_pos == seg_sz) {
+ TRACE_CATCH(ftape_write_segment(zft_pos.seg_pos,
+ zft_deblock_buf,
+ FT_WR_ASYNC),
+ zft_resid -= written);
+ zft_written_segments ++;
+ zft_pos.seg_byte_pos = 0;
+ zft_deblock_segment = zft_pos.seg_pos;
+ ++zft_pos.seg_pos;
+ }
+ written += write_cnt;
+ buff += write_cnt;
+ req_len -= write_cnt;
+ } /* while (req_len > 0) */
+ TRACE(ft_t_data_flow, "remaining in blocking buffer: %d",
+ zft_pos.seg_byte_pos);
+ TRACE(ft_t_data_flow, "just written bytes: %d", written);
+ last_write_failed = 0;
+ zft_resid -= written;
+ need_flush = need_flush || written > 0;
+ TRACE_EXIT written; /* bytes written */
+}
diff --git a/drivers/char/ftape/zftape/zftape-write.h b/drivers/char/ftape/zftape/zftape-write.h
new file mode 100644
index 000000000..4a8d47687
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape-write.h
@@ -0,0 +1,38 @@
+#ifndef _ZFTAPE_WRITE_H
+#define _ZFTAPE_WRITE_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-write.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:13 $
+ *
+ * This file contains the definitions for the write functions
+ * for the zftape driver for Linux.
+ *
+ */
+
+extern int zft_flush_buffers(void);
+extern int zft_update_header_segments(void);
+extern void zft_prevent_flush(void);
+
+/* hook for the VFS interface
+ */
+extern int _zft_write(const char *buff, int req_len);
+#endif /* _ZFTAPE_WRITE_H */
diff --git a/drivers/char/ftape/zftape/zftape_syms.c b/drivers/char/ftape/zftape/zftape_syms.c
new file mode 100644
index 000000000..71b3175a5
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape_syms.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape_syms.c,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/10/05 19:19:14 $
+ *
+ * This file contains the the symbols that the zftape frontend to
+ * the ftape floppy tape driver exports
+ */
+
+#include <linux/config.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/zftape.h>
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-buffers.h"
+#include "../zftape/zftape-ctl.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+# define FT_KSYM(sym) EXPORT_SYMBOL(##sym);
+#else
+# define FT_KSYM(sym) X(##sym),
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+struct symbol_table zft_symbol_table = {
+#include <linux/symtab_begin.h>
+#endif
+/* zftape-init.c */
+FT_KSYM(zft_cmpr_register)
+FT_KSYM(zft_cmpr_unregister)
+/* zftape-read.c */
+FT_KSYM(zft_fetch_segment_fraction)
+/* zftape-buffers.c */
+FT_KSYM(zft_vmalloc_once)
+FT_KSYM(zft_vmalloc_always)
+FT_KSYM(zft_vfree)
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+#include <linux/symtab_end.h>
+};
+#endif
diff --git a/drivers/char/ftape/zftape/zftape_syms.h b/drivers/char/ftape/zftape/zftape_syms.h
new file mode 100644
index 000000000..26f87565d
--- /dev/null
+++ b/drivers/char/ftape/zftape/zftape_syms.h
@@ -0,0 +1,39 @@
+#ifndef _ZFTAPE_SYMS_H
+#define _ZFTAPE_SYMS_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute 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.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape_syms.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:14 $
+ *
+ * This file contains the definitions needed for the symbols
+ * ftape exports
+ *
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+#include <linux/module.h>
+/* ftape-vfs.c defined global vars.
+ */
+
+extern struct symbol_table zft_symbol_table;
+#endif
+
+#endif
diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c
index f18d25d9a..25647ab2f 100644
--- a/drivers/char/istallion.c
+++ b/drivers/char/istallion.c
@@ -813,7 +813,7 @@ static int stli_open(struct tty_struct *tty, struct file *filp)
portp->refcount++;
while (test_bit(ST_INITIALIZING, &portp->state)) {
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return(-ERESTARTSYS);
interruptible_sleep_on(&portp->raw_wait);
}
@@ -1048,7 +1048,7 @@ static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, i
* memory, so we must wait until it is complete.
*/
while (test_bit(ST_CLOSING, &portp->state)) {
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
restore_flags(flags);
return(-ERESTARTSYS);
}
@@ -1081,7 +1081,7 @@ static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, i
rc = 0;
set_bit(ST_OPENING, &portp->state);
while (test_bit(ST_OPENING, &portp->state)) {
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
rc = -ERESTARTSYS;
break;
}
@@ -1123,7 +1123,7 @@ static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg,
*/
if (wait) {
while (test_bit(ST_CLOSING, &portp->state)) {
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
restore_flags(flags);
return(-ERESTARTSYS);
}
@@ -1155,7 +1155,7 @@ static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg,
*/
rc = 0;
while (test_bit(ST_CLOSING, &portp->state)) {
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
rc = -ERESTARTSYS;
break;
}
@@ -1188,7 +1188,7 @@ static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, v
save_flags(flags);
cli();
while (test_bit(ST_CMDING, &portp->state)) {
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
restore_flags(flags);
return(-ERESTARTSYS);
}
@@ -1198,7 +1198,7 @@ static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, v
stli_sendcmd(brdp, portp, cmd, arg, size, copyback);
while (test_bit(ST_CMDING, &portp->state)) {
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
restore_flags(flags);
return(-ERESTARTSYS);
}
@@ -1312,7 +1312,7 @@ static int stli_waitcarrier(stlibrd_t *brdp, stliport_t *portp, struct file *fil
(doclocal || (portp->sigs & TIOCM_CD))) {
break;
}
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
rc = -ERESTARTSYS;
break;
}
diff --git a/drivers/char/joystick.c b/drivers/char/joystick.c
index e85d36a98..c8c44d77a 100644
--- a/drivers/char/joystick.c
+++ b/drivers/char/joystick.c
@@ -1,376 +1,837 @@
/*
+ * $Id: joystick.c,v 1.2 1997/10/31 19:11:48 mj Exp $
+ *
+ * Copyright (C) 1997 Vojtech Pavlik
+ */
- linux/drivers/char/joystick.c
- Copyright (C) 1992, 1993 Arthur C. Smith
- Joystick driver for Linux running on an IBM compatible computer.
-
-VERSION INFO:
-01/08/93 ACS 0.1: Works but needs multi-joystick support
-01/13/93 ACS 0.2: Added multi-joystick support (minor 0 and 1)
- Added delay between measuring joystick axis
- Added scaling ioctl
-02/16/93 ACS 0.3: Modified scaling to use ints to prevent kernel
- panics 8-)
-02/28/93 ACS 0.4: Linux99.6 and fixed race condition in js_read.
- After looking at a schematic of a joystick card
- it became apparent that any write to the joystick
- port started ALL the joystick one shots. If the
- one that we are reading is short enough and the
- first one to be read, the second one will return
- bad data if it's one shot has not expired when
- the joystick port is written for the second time.
- Thus solves the mystery delay problem in 0.2!
-05/05/93 ACS/Eyal 0.5: Upgraded the driver to the 99.9 kernel, added
- joystick support to the make config options,
- updated the driver to return the buttons as
- positive logic, and read both axis at once
- (thanks Eyal!), and added some new ioctls.
-02/12/94 Jeff Tranter 0.6: Made necessary changes to work with 0.99pl15
- kernel (and hopefully 1.0). Also did some
- cleanup: indented code, fixed some typos, wrote
- man page, etc...
-05/17/95 Dan Fandrich 0.7.3: Added I/O port registration, cleaned up code
-04/03/96 Matt Rhoten 0.8: many minor changes:
- new read loop from Hal Maney <maney@norden.com>
- cleaned up #includes to allow #include of
- joystick.h with gcc -Wall and from g++
- made js_init fail if it finds zero joysticks
- general source/comment cleanup
- use of MOD_(INC|DEC)_USE_COUNT
- changes from Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE>
- to compile correctly under 1.3 in kernel or as module
-06/30/97 Alan Cox 0.9: Ported to 2.1.x
- Reformatted to resemble Linux coding standard
- Removed semaphore bug (we can dump the lot I think)
- Fixed xntp timer adjust during joystick timer0 bug
- Changed variable names to lower case. Kept binary
- compatibility.
- Better ioctl names. Kept binary compatibility.
- Removed 'save_busy'. Just set busy to 1.
-*/
+/*
+ * This is joystick driver for Linux. It supports up to two analog joysticks
+ * on a PC compatible machine. See Documentation/joystick.txt for changelog
+ * and credits.
+ */
+#include <linux/config.h>
+#include <linux/init.h>
#include <linux/module.h>
-#include <linux/joystick.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
#include <linux/mm.h>
+#include <linux/ptrace.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/poll.h>
#include <linux/major.h>
-#include <linux/ioport.h>
+#include <linux/joystick.h>
+
#include <asm/io.h>
+#include <asm/ptrace.h>
#include <asm/uaccess.h>
+#include <asm/param.h>
-static struct js_config js_data[JS_MAX]; /* misc data */
-static int js_exist; /* which joysticks' axis exist? */
-static int js_read_semaphore; /* to prevent two processes from trying
- to read different joysticks at the
- same time */
+#define PIT_HZ 1193180L /* PIT clock is 1.19318 MHz */
-/*
- * get_timer0():
- * returns the current value of timer 0. This is a 16 bit counter that starts
- * at LATCH and counts down to 0
+#define JS_MAXTIME PIT_HZ/250 /* timeout for read (4 ms) */
+
+#define JS_BUTTON_PERIOD HZ/50 /* button valid time (20 ms) */
+#define JS_AXIS_MIN_PERIOD HZ/25 /* axis min valid time (40 ms) */
+#define JS_AXIS_MAX_PERIOD HZ/25*2 /* axis max valid time (80 ms) */
+
+#define JS_FIFO_SIZE 16 /* number of FIFO entries */
+#define JS_BUFF_SIZE 32 /* output buffer size */
+#define JS_RETRIES 4 /* number of retries */
+#define JS_DEF_PREC 8 /* initial precision for all axes */
+
+#define JS_NUM 2 /* number of joysticks */
+
+#define JS_AXES 0x0f /* bit mask for all axes */
+#define JS_BUTTONS 0xf0 /* bit mask for all buttons */
+
+#define PIT_MODE 0x43 /* timer mode port */
+#define PIT_DATA 0x40 /* timer 0 data port */
+#define JS_PORT 0x201 /* joystick port */
+
+#define JS_TRIGGER 0xff /* triggers one-shots */
+#define PIT_READ_TIMER 0x00 /* to read timer 0 */
+
+#define DELTA(X,Y,Z) ((X)-(Y)+(((X)>=(Y))?0:Z)) /* cyclic delta */
+#define DELTA_T(X,Y) DELTA((X),(Y),(PIT_HZ/HZ)) /* for time measurement */
+#define DELTA_TX(X,Y,Z) DELTA_T((X),((Y)&0xFF)|(((Z)&0xFF)<<8))
+#define ROT(A,B,C) ((((A)<(C))&&(((B)>(A))&&((B)<(C))))||(((A)>(C))&&(((B)>(A))||((B)<(C)))))
+#define GOF(X) (((X)==JS_BUFF_SIZE-1)?0:(X)+1)
+#define GOFF(X) (((X)==JS_FIFO_SIZE-1)?0:(X)+1)
+#define GOB(X) ((X)?(X)-1:JS_BUFF_SIZE-1)
+
+struct js_data {
+ int ahead;
+ int bhead;
+ int tail;
+ struct js_event buff[JS_BUFF_SIZE];
+ struct js_list *list;
+ struct wait_queue *wait;
+ unsigned int exist;
+};
+
+struct js_axis {
+ int value;
+ struct js_corr corr;
+};
+
+struct js_list {
+ struct js_list *next; /* next-in-list pointer */
+ unsigned long time; /* when the device was open */
+ int tail; /* a tail for js_buff */
+ char startup;
+};
+
+struct js_fifo {
+ unsigned long time;
+ unsigned long event;
+};
+
+static struct js_data jsd[JS_NUM]; /* joystick data */
+static struct timer_list js_timer; /* joystick timer */
+
+static unsigned char js_fifo_head = 0; /* head of the fifo */
+static unsigned char js_fifo_tail = JS_FIFO_SIZE - 1; /* tail of the fifo */
+static struct js_fifo js_fifo[JS_FIFO_SIZE]; /* the fifo */
+
+static unsigned char js_last_buttons = 0; /* last read button state */
+static unsigned long js_axis_time = 0; /* last read axis time */
+static unsigned long js_mark_time = 0;
+
+static unsigned char js_axes_exist; /* all axes that exist */
+static unsigned char js_buttons_exist; /* all buttons that exist */
+
+static struct js_axis js_axis[4];
+static unsigned int js_buttons = 0;
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@atrey.karlin.mff.cuni.cz>");
+MODULE_SUPPORTED_DEVICE("js");
+MODULE_PARM(js, "0-1b");
+
+static char js[] = {0, 0};
+
+/*
+ * get_pit() returns the immediate state of PIT0. Must be run
+ * with interrupts disabled.
*/
-
-extern inline int get_timer0(void)
+
+static inline int get_pit(void)
{
- unsigned long flags;
- int t0, t1;
+ int t, flags;
+
save_flags(flags);
cli();
- outb (0, PIT_MODE);
- t0 = (int) inb (PIT_COUNTER_0);
- t1 = ((int) inb (PIT_COUNTER_0) << 8) + t0;
+ outb(PIT_READ_TIMER, PIT_MODE);
+ t = inb(PIT_DATA);
+ t |= (int) inb(PIT_DATA) << 8;
restore_flags(flags);
- return (t1);
+ return t;
}
/*
- * find_axes():
- *
- * returns which axes are hooked up, in a bitfield. 2^n is set if
- * axis n is hooked up, for 0 <= n < 4.
- *
- * REVIEW: should update this to handle eight-axis (four-stick) game port
- * cards. anyone have one of these to test on? mattrh 3/23/96
+ * count_bits() counts set bits in a byte.
*/
-
-extern inline int find_axes(void)
+
+static int count_bits(unsigned char c)
{
- int j;
- outb (0xff, JS_PORT); /* trigger oneshots */
- /* and see what happens */
- for (j = JS_DEF_TIMEOUT; (0x0f & inb (JS_PORT)) && j; j--);
- /* do nothing; wait for the timeout */
- js_exist = inb (JS_PORT) & 0x0f; /* get joystick status byte */
- js_exist = (~js_exist) & 0x0f;
-/* printk("find_axes: js_exist is %d (0x%04X)\n", js_exist, js_exist);*/
- return js_exist;
+ int i, t = 0;
+ for (i = 0; i < 8; i++)
+ if (c & (1 << i)) t++;
+ return t;
}
-static int js_ioctl (struct inode *inode,
- struct file *file,
- unsigned int cmd,
- unsigned long arg)
+/*
+ * js_correct() performs correction of raw joystick data.
+ */
+
+static int js_correct(int value, struct js_corr *corr)
{
- unsigned int minor = MINOR (inode->i_rdev);
- if (minor >= JS_MAX)
- return -ENODEV;
-
- if ((((inb (JS_PORT) & 0x0f) >> (minor << 1)) & 0x03) == 0x03) /*js minor exists?*/
- return -ENODEV;
- switch (cmd)
- {
-
- case JSIOCSCAL: /*from struct *arg to js_data[minor]*/
- if(copy_from_user(&js_data[minor].js_corr,
- (void *)arg, sizeof(struct js_status)))
- return -EFAULT;
- break;
- case JSIOCGCAL: /*to struct *arg from js_data[minor]*/
- if(copy_to_user((void *) arg, &js_data[minor].js_corr,
- sizeof(struct js_status)))
- return -EFAULT;
- break;
- case JSIOCSTIMEOUT:
- if(copy_from_user(&js_data[minor].js_timeout,
- (void *)arg, sizeof(js_data[0].js_timeout)))
- return -EFAULT;
- break;
- case JSIOCGTIMEOUT:
- if(copy_to_user((void *)arg, &js_data[minor].js_timeout,
- sizeof(js_data[0].js_timeout)))
- return -EFAULT;
- break;
- case JSIOCSTIMELIMIT:
- if(copy_from_user(&js_data[minor].js_timelimit,
- (void *)arg, sizeof(js_data[0].js_timelimit)))
- return -EFAULT;
- break;
- case JSIOCGTIMELIMIT:
- if(copy_to_user((void *)arg, &js_data[minor].js_timelimit,
- sizeof(js_data[0].js_timelimit)))
- return -EFAULT;
- break;
- case JSIOCGCONFIG:
- if(copy_to_user((void *)arg, &js_data[minor],
- sizeof(struct js_config)))
- return -EFAULT;
- break;
- case JSIOCSCONFIG:
- if(copy_from_user(&js_data[minor], (void *)arg,
- sizeof(struct js_config)))
- return -EFAULT;
- /* Must be busy to do this ioctl! */
- js_data[minor].busy = 1;
- break;
- default:
- return -EINVAL;
+ int t;
+
+ if (corr->type == JS_CORR_NONE) return value;
+ t = value > corr->coef[0] ? (value < corr->coef[1] ? corr->coef[0] : value - corr->coef[1] + corr->coef[0]) : value;
+ if (t == corr->coef[0]) return 32768;
+
+ switch (corr->type) {
+ case JS_CORR_BROKEN:
+ t = t < corr->coef[0] ? ((corr->coef[2] * t) >> 14) + corr->coef[3] :
+ ((corr->coef[4] * t) >> 14) + corr->coef[5];
+ break;
+ default:
+ return 0;
}
- return 0;
+
+ if (t < 0) return 0;
+ if (t > 65535) return 65535;
+
+ return t;
}
/*
- * js_open():
- * device open routine. increments module usage count, initializes
- * data for that joystick.
- *
- * returns: 0 or
- * -ENODEV: asked for joystick other than #0 or #1
- * -ENODEV: asked for joystick on axis where there is none
- * -EBUSY: attempt to open joystick already open
+ * js_compare() compares two close axis values and decides
+ * whether they are "same".
*/
-
-static int js_open (struct inode *inode, struct file *file)
+
+static int js_compare(int x, int y, int prec)
{
- unsigned int minor = MINOR (inode->i_rdev);
- int j;
-
- if (minor >= JS_MAX)
- return -ENODEV; /*check for joysticks*/
-
- for (j = JS_DEF_TIMEOUT; (js_exist & inb (JS_PORT)) && j; j--);
- cli(); /*block js_read while js_exist is being modified*/
- /*js minor exists?*/
- if ((((js_exist = inb (JS_PORT)) >> (minor << 1)) & 0x03) == 0x03) {
- js_exist = (~js_exist) & 0x0f;
- sti();
- return -ENODEV;
+ return (x < y + prec) && (y < x + prec);
+}
+
+/*
+ * js_probe() probes for joysticks
+ */
+
+inline int js_probe(void)
+{
+ int t;
+
+ outb(JS_TRIGGER, JS_PORT);
+ t = get_pit();
+ while (DELTA_T(t, get_pit()) < JS_MAXTIME);
+ t = inb(JS_PORT);
+
+ if (js[0] || js[1]) {
+ jsd[0].exist = js[0] & ~(t & JS_AXES);
+ jsd[1].exist = js[1] & ~(t & JS_AXES);
+ } else
+ switch (t & JS_AXES) {
+ case 0x0c: jsd[0].exist = 0x33; jsd[1].exist = 0x00; break; /* joystick 0 connected */
+ case 0x03: jsd[0].exist = 0x00; jsd[1].exist = 0xcc; break; /* joystick 1 connected */
+ case 0x04: jsd[0].exist = 0xfb; jsd[1].exist = 0x00; break; /* 3-axis joystick connected */
+ case 0x00: jsd[0].exist = 0x33; jsd[1].exist = 0xcc; break; /* joysticks 0 and 1 connected */
+ default: jsd[0].exist = 0x00; jsd[1].exist = 0x00; return -1; /* no joysticks */
}
- js_exist = (~js_exist) & 0x0f;
- sti();
- if (js_data[minor].busy)
- return -EBUSY;
- js_data[minor].busy = JS_TRUE;
- js_data[minor].js_corr.x = JS_DEF_CORR; /*default scale*/
- js_data[minor].js_corr.y = JS_DEF_CORR;
- js_data[minor].js_timeout = JS_DEF_TIMEOUT;
- js_data[minor].js_timelimit = JS_DEF_TIMELIMIT;
- js_data[minor].js_expiretime = jiffies;
+ js_axes_exist = (jsd[0].exist | jsd[1].exist) & JS_AXES;
+ js_buttons_exist = (jsd[0].exist | jsd[1].exist) & JS_BUTTONS;
- MOD_INC_USE_COUNT;
return 0;
}
-static int js_release (struct inode *inode, struct file *file)
+/*
+ * js_do_timer() controls the action by adding entries to the event
+ * fifo each time a button changes its state or axis valid time
+ * expires.
+ */
+
+static void js_do_timer(unsigned long data)
{
- unsigned int minor = MINOR (inode->i_rdev);
- inode->i_atime = CURRENT_TIME;
- js_data[minor].busy = JS_FALSE;
- MOD_DEC_USE_COUNT;
- return 0;
+ int t = ~inb(JS_PORT) & js_buttons_exist;
+ if ((js_last_buttons != t) && (js_fifo_head != js_fifo_tail)) {
+ js_fifo[js_fifo_head].event = js_last_buttons = t;
+ js_fifo[js_fifo_head].time = jiffies;
+ js_fifo_head++;
+ if (js_fifo_head == JS_FIFO_SIZE) js_fifo_head = 0;
+ if (!js_mark_time) {
+ js_mark_time = jiffies;
+ mark_bh(JS_BH);
+ }
+ }
+ else
+ if ((jiffies > js_axis_time + JS_AXIS_MAX_PERIOD) && !js_mark_time) {
+ js_mark_time = jiffies;
+ mark_bh(JS_BH);
+ }
+ js_timer.expires = jiffies + JS_BUTTON_PERIOD;
+ add_timer(&js_timer);
}
/*
- * js_read() reads the buttons x, and y axis from both joysticks if a
- * given interval has expired since the last read or is equal to
- * -1l. The buttons are in port 0x201 in the high nibble. The axis are
- * read by writing to 0x201 and then measuring the time it takes the
- * one shots to clear.
+ * js_do_bh() does the main processing and adds events to output buffers.
*/
-static long js_read (struct inode *inode, struct file *file, char *buf, unsigned long count)
+static void js_do_bh(void)
{
- int j, chk, jsmask;
- int t0, t_x0, t_y0, t_x1, t_y1;
- unsigned int minor, minor2;
- int buttons;
- if (count != JS_RETURN)
- return -EINVAL;
- minor = MINOR (inode->i_rdev);
- inode->i_atime = CURRENT_TIME;
- if (jiffies >= js_data[minor].js_expiretime)
- {
- minor2 = minor << 1;
- j = js_data[minor].js_timeout;
- for (; (js_exist & inb (JS_PORT)) && j; j--);
- if (j == 0)
- return -ENODEV; /*no joystick here*/
- /*Make sure no other proc is using port*/
+ int i, j, k;
+ unsigned int t;
+
+ if (jiffies > js_axis_time + JS_AXIS_MIN_PERIOD) {
+
+ unsigned int old_axis[4];
+ unsigned int t_low, t_high;
+ unsigned int flags, joy_state;
+ unsigned int t1l, t1h, jsm;
+ unsigned char jss;
+ unsigned char again;
+ unsigned char retries = 0;
+
+ for (i = 0; i < 4; i++)
+ old_axis[i] = js_axis[i].value;
+
+ do {
+ i = 0;
+ again = 0;
+ t_low = 0;
+ t_high = 0;
+ joy_state = JS_AXES;
+
+/*
+ * Measure the axes.
+ */
+
+ save_flags(flags);
+ cli(); /* no interrupts */
+ outb(JS_TRIGGER, JS_PORT); /* trigger one-shots */
+ outb(PIT_READ_TIMER, PIT_MODE); /* read timer */
+ t = (t1l = inb(PIT_DATA)) |
+ (t1h = inb(PIT_DATA)) << 8;
+ restore_flags(flags);
+
+ do {
+ jss = inb(JS_PORT);
+ if ((jss ^ joy_state) & js_axes_exist) {
+ t_low = (t_low << 8) | t1l;
+ t_high = (t_high << 8) | t1h;
+ joy_state = (joy_state << 8) | jss;
+ i++;
+ }
+
+ cli();
+ outb(PIT_READ_TIMER, PIT_MODE);
+ t1l = inb(PIT_DATA);
+ t1h = inb(PIT_DATA);
+ restore_flags(flags);
+
+ } while ((jss & js_axes_exist) && (DELTA_TX(t, t1l, t1h) < JS_MAXTIME));
+
+/*
+ * Process the gathered axis data in joy_state.
+ */
+
+ joy_state ^= ((joy_state >> 8) | 0xff000000L); /* More magic */
+
+ for (; i > 0; i--) {
+ for (j = 0; j < 4; j++)
+ if (joy_state & js_axes_exist & (1 << j)) {
+ jsm = js_correct(DELTA_TX(t, t_low, t_high), &js_axis[j].corr);
+ if (!js_compare(jsm, js_axis[j].value, js_axis[j].corr.prec)) {
+ if (jsm < js_axis[j].value || !retries)
+ js_axis[j].value = jsm;
+ again = 1;
+ }
+ }
+ joy_state = joy_state >> 8;
+ t_low = t_low >> 8;
+ t_high = t_high >> 8;
+ }
+
+ } while (retries++ < JS_RETRIES && again);
+
+/*
+ * Check if joystick lost.
+ */
+
+ for (i = 0; i < JS_NUM; i++) {
+
+ if (jsd[i].exist && ((jss & jsd[i].exist & JS_AXES) == (jsd[i].exist & JS_AXES))) {
+ printk(KERN_WARNING "js%d: joystick lost.\n", i);
+ js_buttons_exist &= ~jsd[i].exist;
+ js_axes_exist &= ~jsd[i].exist;
+ jsd[i].exist = 0;
+ wake_up_interruptible(&jsd[i].wait);
+ }
+
+ if ((jss & jsd[i].exist & JS_AXES)) {
+ printk(KERN_WARNING "js%d: joystick broken. Check cables.\n", i);
+ }
+
+ }
+
+/*
+ * Put changed axes into output buffer.
+ */
+
+ if (retries > 1)
+ for (i = 0; i < JS_NUM; i++)
+ if (jsd[i].list) {
+ k = 0;
+ for (j = 0; j < 4; j++)
+ if ((1 << j) & jsd[i].exist) {
+ if (!js_compare(js_axis[j].value, old_axis[j], js_axis[j].corr.prec)) {
+ jsd[i].buff[jsd[i].ahead].time = js_mark_time;
+ jsd[i].buff[jsd[i].ahead].type = JS_EVENT_AXIS;
+ jsd[i].buff[jsd[i].ahead].number = k;
+ jsd[i].buff[jsd[i].ahead].value = js_axis[j].value;
+ jsd[i].ahead++;
+ if (jsd[i].ahead == JS_BUFF_SIZE) jsd[i].ahead = 0;
+ }
+ k++;
+ }
+ }
+ js_axis_time = jiffies;
+ }
+ js_mark_time = 0;
+
+/*
+ * And now process the button fifo.
+ */
+
+ while (js_fifo_head != (t = GOFF(js_fifo_tail))) {
+ for (i = 0; i < JS_NUM; i++)
+ if (jsd[i].list) {
+ k = 0;
+ for (j = 4; j < 8; j++)
+ if ((1 << j) & jsd[i].exist) {
+ if ((1 << j) & (js_buttons ^ js_fifo[t].event)) {
+ jsd[i].buff[jsd[i].ahead].time = js_fifo[t].time;
+ jsd[i].buff[jsd[i].ahead].type = JS_EVENT_BUTTON;
+ jsd[i].buff[jsd[i].ahead].number = k;
+ jsd[i].buff[jsd[i].ahead].value = (js_fifo[t].event >> j) & 1;
+ jsd[i].ahead++;
+ if (jsd[i].ahead == JS_BUFF_SIZE) jsd[i].ahead = 0;
+ }
+ k++;
+ }
+ }
+ js_buttons = js_fifo[js_fifo_tail = t].event;
+ }
+
+/*
+ * Sync ahead with bhead and cut too long tails.
+ */
- cli();
- js_read_semaphore++;
- sti();
-
- buttons = ~(inb (JS_PORT) >> 4);
- js_data[0].js_save.buttons = buttons & 0x03;
- js_data[1].js_save.buttons = (buttons >> 2) & 0x03;
- j = js_data[minor].js_timeout;
- jsmask = 0;
-
- cli(); /*no interrupts!*/
- outb (0xff, JS_PORT); /*trigger one-shots*/
- /*get init timestamp*/
- t_x0 = t_y0 = t_x1 = t_y1 = t0 = get_timer0 ();
- /*wait for an axis' bit to clear or timeout*/
- while (j-- && (chk = (inb (JS_PORT) & js_exist ) | jsmask))
- {
- if (!(chk & JS_X_0)) {
- t_x0 = get_timer0();
- jsmask |= JS_X_0;
+ for (i = 0; i < JS_NUM; i++)
+ if (jsd[i].list)
+ if (jsd[i].bhead != jsd[i].ahead) {
+ if (ROT(jsd[i].bhead, jsd[i].tail, jsd[i].ahead) || (jsd[i].tail == jsd[i].bhead)) {
+ struct js_list *curl;
+ curl = jsd[i].list;
+ while (curl) {
+ if (ROT(jsd[i].bhead, curl->tail, jsd[i].ahead) || (curl->tail == jsd[i].bhead)) {
+ curl->tail = jsd[i].ahead;
+ curl->startup = jsd[i].exist;
+ }
+ curl = curl->next;
+ }
+ jsd[i].tail = jsd[i].ahead;
+ }
+ jsd[i].bhead = jsd[i].ahead;
+ wake_up_interruptible(&jsd[i].wait);
+ }
+
+}
+
+/*
+ * js_lseek() just returns with error.
+ */
+
+static loff_t js_lseek(struct file *file, loff_t offset, int origin)
+{
+ return -ESPIPE;
+}
+
+/*
+ * js_read() copies one or more entries from jsd[].buff to user
+ * space.
+ */
+
+static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+ unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+ struct wait_queue wait = { current, NULL };
+ struct js_list *curl = file->private_data;
+ struct js_event *buff = (void *) buf;
+ unsigned long blocks = count / sizeof(struct js_event);
+ unsigned long i = 0, j;
+ int t, u = curl->tail;
+ int retval = 0;
+
+/*
+ * Check user data.
+ */
+
+ if (MAJOR(file->f_dentry->d_inode->i_rdev) != JOYSTICK_MAJOR)
+ return -EINVAL;
+ if (file->f_pos < 0)
+ return -EINVAL;
+ if (!blocks)
+ return -EINVAL;
+ if (!curl)
+ return -EINVAL;
+
+ if (minor > JS_NUM)
+ return -ENODEV;
+ if (!jsd[minor].exist)
+ return -ENODEV;
+
+/*
+ * Handle (non)blocking i/o.
+ */
+
+ if (count != sizeof(struct JS_DATA_TYPE)) {
+
+ if ((GOF(curl->tail) == jsd[minor].ahead && !curl->startup) || (curl->startup && !js_axis_time)) {
+ add_wait_queue(&jsd[minor].wait, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+ while ((GOF(curl->tail) == jsd[minor].ahead && !curl->startup) || (curl->startup && !js_axis_time)) {
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
+ if (current->signal & ~current->blocked) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ if (!jsd[minor].exist) {
+ retval = -ENODEV;
+ break;
+ }
}
- if (!(chk & JS_Y_0)) {
- t_y0 = get_timer0();
- jsmask |= JS_Y_0;
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&jsd[minor].wait, &wait);
+ }
+
+ if (retval) return retval;
+
+/*
+ * Do the i/o.
+ */
+
+ if (curl->startup) {
+ struct js_event tmpevent;
+
+ t = 0;
+ for (j = 0; j < 4 && (i < blocks) && !retval; j++)
+ if (jsd[minor].exist & (1 << j)) {
+ if (curl->startup & (1 << j)) {
+ tmpevent.type = JS_EVENT_AXIS | JS_EVENT_INIT;
+ tmpevent.number = t;
+ tmpevent.value = js_axis[j].value;
+ if (copy_to_user(&buff[i], &tmpevent, sizeof(struct js_event)))
+ retval = -EFAULT;
+ if (put_user((__u32)((jiffies - curl->time) * (1000/HZ)), &buff[i].time))
+ retval = -EFAULT;
+ curl->startup &= ~(1 << j);
+ i++;
+ }
+ t++;
}
- if (!(chk & JS_X_1)) {
- t_x1 = get_timer0();
- jsmask |= JS_X_1;
+
+ t = 0;
+ for (j = 4; j < 8 && (i < blocks) && !retval; j++)
+ if (jsd[minor].exist & (1 << j)) {
+ if (curl->startup & (1 << j)) {
+ tmpevent.type = JS_EVENT_BUTTON | JS_EVENT_INIT;
+ tmpevent.number = t;
+ tmpevent.value = (js_buttons >> j) & 1;
+ if (copy_to_user(&buff[i], &tmpevent, sizeof(struct js_event)))
+ retval = -EFAULT;
+ if (put_user((__u32)((jiffies - curl->time) * (1000/HZ)), &buff[i].time))
+ retval = -EFAULT;
+ curl->startup &= ~(1 << j);
+ i++;
+ }
+ t++;
}
- if (!(chk & JS_Y_1)) {
- t_y1 = get_timer0();
- jsmask |= JS_Y_1;
+ }
+
+
+ while ((jsd[minor].ahead != (t = GOF(curl->tail))) && (i < blocks) && !retval) {
+ if (copy_to_user(&buff[i], &jsd[minor].buff[t], sizeof(struct js_event)))
+ retval = -EFAULT;
+ if (put_user((__u32)((jsd[minor].buff[t].time - curl->time) * (1000/HZ)), &buff[i].time))
+ retval = -EFAULT;
+ curl->tail = t;
+ i++;
+ }
+
+ }
+
+ else
+
+/*
+ * Handle version 0.x compatibility.
+ */
+
+ {
+ struct JS_DATA_TYPE *bufo = (void *) buf;
+ int buttons = 0;
+
+ while (~jsd[minor].exist & (1<<i)) i++;
+ copy_to_user(&bufo->x, &js_axis[i].value, sizeof(int));
+
+ i++;
+ while (~jsd[minor].exist & (1<<i)) i++;
+ copy_to_user(&bufo->y, &js_axis[i].value, sizeof(int));
+
+ i = 0;
+ for (j = 4; j < 8; j++)
+ if ((1 << j) & jsd[minor].exist)
+ buttons |= (!!(js_last_buttons & (1 << j))) << (i++);
+ copy_to_user(&bufo->buttons, &buttons, sizeof(int));
+
+ curl->tail = GOB(jsd[minor].ahead);
+ retval = sizeof(struct JS_DATA_TYPE);
+ }
+
+/*
+ * Check main tail and move it.
+ */
+
+ if (u == jsd[minor].tail) {
+ t = curl->tail;
+ curl = jsd[minor].list;
+ while (curl && curl->tail != jsd[minor].tail) {
+ if (ROT(jsd[minor].ahead, t, curl->tail) ||
+ (jsd[minor].ahead == curl->tail)) t = curl->tail;
+ curl = curl->next;
+ }
+ if (!curl) jsd[minor].tail = t;
+ }
+
+ return retval ? retval : i*sizeof(struct js_event);
+}
+
+/*
+ * js_poll() does select() support.
+ */
+
+static unsigned int js_poll(struct file *file, poll_table *wait)
+{
+ struct js_list *curl;
+ unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+ curl = file->private_data;
+
+ poll_wait(&jsd[minor].wait, wait);
+ if (GOF(curl->tail) != jsd[minor].ahead)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+/*
+ * js_ioctl handles misc ioctl calls.
+ */
+
+static int js_ioctl(struct inode *inode,
+ struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ int i, j;
+
+ if (MAJOR(inode->i_rdev) != JOYSTICK_MAJOR)
+ return -EINVAL;
+ if (minor > JS_NUM)
+ return -ENODEV;
+ if (!jsd[minor].exist)
+ return -ENODEV;
+
+ switch (cmd) {
+ case JSIOCGVERSION:
+ if(put_user(JS_VERSION, (__u32 *) arg)) return -EFAULT;
+ break;
+ case JSIOCGAXES:
+ if(put_user(count_bits(jsd[minor].exist & JS_AXES), (__u8 *) arg)) return -EFAULT;
+ break;
+ case JSIOCGBUTTONS:
+ if(put_user(count_bits(jsd[minor].exist & JS_BUTTONS), (__u8 *) arg)) return -EFAULT;
+ break;
+ case JSIOCSCORR:
+ j = 0;
+ for (i = 0; i < 4; i++)
+ if ((1 << i) & jsd[minor].exist) {
+ if (copy_from_user(&js_axis[i].corr, (void *) arg + j * sizeof(struct js_corr),
+ sizeof(struct js_corr))) return -EFAULT;
+ j++;
+ }
+ js_axis_time = 0;
+ break;
+ case JSIOCGCORR:
+ j = 0;
+ for (i = 0; i < 4; i++)
+ if ((1 << i) & jsd[minor].exist) {
+ if (copy_to_user((void *) arg + j * sizeof(struct js_corr), &js_axis[i].corr,
+ sizeof(struct js_corr))) return -EFAULT;
+ j++;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * js_open() performs necessary initialization and adds
+ * an entry to the linked list.
+ */
+
+static int js_open(struct inode *inode, struct file *file)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ struct js_list *curl;
+ int t;
+
+ if (MAJOR(inode->i_rdev) != JOYSTICK_MAJOR)
+ return -EINVAL;
+ if (minor > JS_NUM)
+ return -ENODEV;
+ if (!jsd[minor].exist) {
+ js_probe();
+ if (jsd[minor].exist) printk(KERN_INFO "js%d: %d-axis joystick at %#x\n",
+ minor, count_bits(jsd[minor].exist & JS_AXES), JS_PORT);
+ else return -ENODEV;
+ }
+
+ MOD_INC_USE_COUNT;
+
+ if (!jsd[0].list && !jsd[1].list) {
+ js_timer.expires = jiffies + JS_BUTTON_PERIOD;
+ add_timer(&js_timer);
+ }
+
+ curl = jsd[minor].list;
+ jsd[minor].list = kmalloc(sizeof(struct js_list), GFP_KERNEL);
+ jsd[minor].list->next = curl;
+ jsd[minor].list->startup = jsd[minor].exist;
+ jsd[minor].list->tail = t = GOB(jsd[minor].ahead);
+ jsd[minor].list->time = jiffies;
+
+ file->private_data = jsd[minor].list;
+
+ return 0;
+}
+
+/*
+ * js_release() removes an entry from list and deallocates memory
+ * used by it.
+ */
+
+static int js_release(struct inode *inode, struct file *file)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ struct js_list **curp, *curl;
+ int t;
+
+ curp = &jsd[minor].list;
+ curl = file->private_data;
+
+ while (*curp && (*curp != curl)) curp = &((*curp)->next);
+ *curp = (*curp)->next;
+
+ if (jsd[minor].list) {
+ if (curl->tail == jsd[minor].tail) {
+ curl = jsd[minor].list;
+ t = curl->tail;
+ while (curl && curl->tail != jsd[minor].tail) {
+ if (ROT(jsd[minor].ahead, t, curl->tail) ||
+ (jsd[minor].ahead == curl->tail)) t = curl->tail;
+ curl = curl->next;
}
+ if (!curl) jsd[minor].tail = t;
}
- sti(); /* allow interrupts */
-
- js_read_semaphore = 0; /* allow other reads to progress */
- if (j == 0)
- return -ENODEV; /*read timed out*/
- js_data[0].js_expiretime = jiffies +
- js_data[0].js_timelimit; /*update data*/
- js_data[1].js_expiretime = jiffies +
- js_data[1].js_timelimit;
- js_data[0].js_save.x = DELTA_TIME (t0, t_x0) >>
- js_data[0].js_corr.x;
- js_data[0].js_save.y = DELTA_TIME (t0, t_y0) >>
- js_data[0].js_corr.y;
- js_data[1].js_save.x = DELTA_TIME (t0, t_x1) >>
- js_data[1].js_corr.x;
- js_data[1].js_save.y = DELTA_TIME (t0, t_y1) >>
- js_data[1].js_corr.y;
}
- if(copy_to_user(buf, &js_data[minor].js_save, JS_RETURN))
- return -EFAULT;
- return JS_RETURN;
+ kfree(file->private_data);
+ if (!jsd[0].list && !jsd[1].list) del_timer(&js_timer);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
}
+/*
+ * The operations structure.
+ */
static struct file_operations js_fops =
{
- NULL, /* js_lseek*/
+ js_lseek, /* js_lseek */
js_read, /* js_read */
- NULL, /* js_write*/
- NULL, /* js_readaddr*/
- NULL, /* js_select */
- js_ioctl, /* js_ioctl*/
+ NULL, /* js_write */
+ NULL, /* js_readdir */
+ js_poll, /* js_poll */
+ js_ioctl, /* js_ioctl */
NULL, /* js_mmap */
- js_open, /* js_open*/
- js_release, /* js_release*/
- NULL /* js_fsync */
+ js_open, /* js_open */
+ js_release, /* js_release */
+ NULL /* js_sync */
};
-#ifdef MODULE
+/*
+ * js_setup() parses kernel command line parametres.
+ */
-#define joystick_init init_module
+#ifndef MODULE
+__initfunc(void js_setup(char *str, int *ints))
-void cleanup_module (void)
{
- if (unregister_chrdev (JOYSTICK_MAJOR, "joystick"))
- printk ("joystick: cleanup_module failed\n");
- release_region(JS_PORT, 1);
+ js[0] = ((ints[0] > 0) ? ints[1] : 0 );
+ js[1] = ((ints[0] > 1) ? ints[2] : 0 );
}
+#endif
-#endif /* MODULE */
+/*
+ * js_init() registres the driver and calls the probe function.
+ * also initializes some crucial variables.
+ */
-int joystick_init(void)
+#ifdef MODULE
+int init_module(void)
+#else
+__initfunc(int js_init(void))
+#endif
{
- int js_num;
- int js_count;
+ int i;
if (check_region(JS_PORT, 1)) {
- printk("js_init: port already in use\n");
+ printk(KERN_ERR "js: port %#x already in use\n", JS_PORT);
return -EBUSY;
}
- js_num = find_axes();
- js_count = !!(js_num & 0x3) + !!(js_num & 0xC);
-
-
- if (js_count == 0)
- {
- printk("No joysticks found.\n");
+ if (js_probe() < 0) {
+ printk(KERN_INFO "js: no joysticks found\n");
return -ENODEV;
- /* if the user boots the machine, which runs insmod, and THEN
- decides to hook up the joystick, well, then we do the wrong
- thing. But it's a good idea to avoid giving out a false sense
- of security by letting the module load otherwise. */
}
- if (register_chrdev (JOYSTICK_MAJOR, "joystick", &js_fops)) {
- printk ("Unable to get major=%d for joystick\n",
- JOYSTICK_MAJOR);
+ if (register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) {
+ printk(KERN_ERR "js: unable to get major %d for joystick\n", JOYSTICK_MAJOR);
return -EBUSY;
}
- request_region(JS_PORT, 1, "joystick");
-
- for (js_num = 0; js_num < JS_MAX; js_num++)
- js_data[js_num].busy = JS_FALSE;
- js_read_semaphore = 0;
- printk (KERN_INFO "Found %d joystick%c.\n",
- js_count,
- (js_num == 1) ? ' ' : 's');
+ for (i = 0; i < JS_NUM; i++) {
+ if (jsd[i].exist) printk(KERN_INFO "js%d: %d-axis joystick at %#x\n",
+ i, count_bits(jsd[i].exist & JS_AXES), JS_PORT);
+ jsd[i].ahead = jsd[i].bhead = 0;
+ jsd[i].tail = JS_BUFF_SIZE - 1;
+ jsd[i].list = NULL;
+ jsd[i].wait = NULL;
+ memset(jsd[i].buff, 0, JS_BUFF_SIZE * sizeof(struct js_event));
+ }
+
+ for (i = 0; i < 4; i++) {
+ js_axis[i].corr.type = JS_CORR_NONE;
+ js_axis[i].corr.prec = JS_DEF_PREC;
+ }
+
+ request_region(JS_PORT, 1, "js");
+ init_bh(JS_BH, &js_do_bh);
+ enable_bh(JS_BH);
+ init_timer(&js_timer);
+ js_timer.function = js_do_timer;
+
return 0;
}
+/*
+ * cleanup_module() handles module removal.
+ */
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ if (MOD_IN_USE)
+ printk(KERN_NOTICE "js: device busy, remove delayed\n");
+ else {
+ del_timer(&js_timer);
+ disable_bh(JS_BH);
+ if (unregister_chrdev(JOYSTICK_MAJOR, "js"))
+ printk(KERN_ERR "js: module cleanup failed\n");
+ release_region(JS_PORT, 1);
+ }
+}
+#endif
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index 49a6c65db..fdfb961c7 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -11,6 +11,9 @@
* lp_read (Status readback) support added by Carsten Gross,
* carsten@sol.wohnheim.uni-ulm.de
* Support for parport by Philip Blundell <Philip.Blundell@pobox.com>
+ * Reverted interrupt to polling at runtime if more than one device is parport
+ * registered and joined the interrupt and polling code.
+ * by Andrea Arcangeli <arcangeli@mbox.queen.it>
*/
/* This driver is about due for a rewrite. */
@@ -251,7 +254,7 @@ static inline int lp_write_buf(unsigned int minor, const char *buf, int count)
sti();
}
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
if (total_bytes_written + bytes_written)
return total_bytes_written + bytes_written;
else
@@ -269,9 +272,9 @@ static inline int lp_write_buf(unsigned int minor, const char *buf, int count)
return total_bytes_written;
}
-static long lp_write(struct inode * inode, struct file * file,
- const char * buf, unsigned long count)
+static ssize_t lp_write(struct file * file, const char * buf, size_t count, loff_t *ppos)
{
+ struct inode *inode = file->f_dentry->d_inode;
unsigned int minor = MINOR(inode->i_rdev);
int retv;
@@ -312,9 +315,9 @@ static void lp_select_in_high(int minor) {
}
/* Status readback confirming to ieee1284 */
-static long lp_read(struct inode * inode, struct file * file,
- char * buf, unsigned long count)
+static ssize_t lp_read(struct file * file, char * buf, size_t count, loff_t *ppos)
{
+ struct inode *inode = file->f_dentry->d_inode;
unsigned char z=0, Byte=0, status;
char *temp;
int retval;
@@ -376,7 +379,7 @@ static long lp_read(struct inode * inode, struct file * file,
#ifdef LP_READ_DEBUG
printk(KERN_DEBUG "lp_read: (Autofeed low) timeout\n");
#endif
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
lp_select_in_high(minor);
parport_release(lp_table[minor].dev);
if (temp !=buf)
diff --git a/drivers/char/lp_m68k.c b/drivers/char/lp_m68k.c
index bdb3405ca..a36903c7a 100644
--- a/drivers/char/lp_m68k.c
+++ b/drivers/char/lp_m68k.c
@@ -222,7 +222,7 @@ static long lp_write_interrupt(struct inode *inode, struct file *file,
lp_table[dev]->do_print = 0;
rc = total_bytes_written + lp_table[dev]->bytes_written;
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
if (rc)
return rc;
else
@@ -320,7 +320,7 @@ static long lp_write_polled(struct inode *inode, struct file *file,
}
/* check for signals before going to sleep */
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
if (temp != buf)
return temp-buf;
else
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 94c207e10..177862c2a 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -36,11 +36,10 @@ void isdn_init(void);
void pcwatchdog_init(void);
#endif
-static long do_write_mem(struct file * file,
- void *p, unsigned long realp,
- const char * buf, unsigned long count)
+static ssize_t do_write_mem(struct file * file, void *p, unsigned long realp,
+ const char * buf, size_t count, loff_t *ppos)
{
- unsigned long written;
+ ssize_t written;
written = 0;
#if defined(__sparc__) || defined(__mc68000__)
@@ -58,8 +57,8 @@ static long do_write_mem(struct file * file,
if (copy_from_user(p, buf, count) < 0)
return -EFAULT;
written += count;
- file->f_pos += written;
- return count;
+ *ppos += written;
+ return written;
}
@@ -67,12 +66,12 @@ static long do_write_mem(struct file * file,
* This funcion reads the *physical* memory. The f_pos points directly to the
* memory location.
*/
-static long read_mem(struct inode * inode, struct file * file,
- char * buf, unsigned long count)
+static ssize_t read_mem(struct file * file, char * buf,
+ size_t count, loff_t *ppos)
{
- unsigned long p = file->f_pos;
+ unsigned long p = *ppos;
unsigned long end_mem;
- unsigned long read;
+ ssize_t read;
end_mem = __pa(high_memory);
if (p >= end_mem)
@@ -99,14 +98,14 @@ static long read_mem(struct inode * inode, struct file * file,
if (copy_to_user(buf, __va(p), count) < 0)
return -EFAULT;
read += count;
- file->f_pos += read;
+ *ppos += read;
return read;
}
-static long write_mem(struct inode * inode, struct file * file,
- const char * buf, unsigned long count)
+static ssize_t write_mem(struct file * file, const char * buf,
+ size_t count, loff_t *ppos)
{
- unsigned long p = file->f_pos;
+ unsigned long p = *ppos;
unsigned long end_mem;
end_mem = __pa(high_memory);
@@ -114,7 +113,7 @@ static long write_mem(struct inode * inode, struct file * file,
return 0;
if (count > end_mem - p)
count = end_mem - p;
- return do_write_mem(file,__va(p),p,buf,count);
+ return do_write_mem(file, __va(p), p, buf, count, ppos);
}
static int mmap_mem(struct file * file, struct vm_area_struct * vma)
@@ -135,9 +134,10 @@ static int mmap_mem(struct file * file, struct vm_area_struct * vma)
#endif
#ifdef __powerpc__
if (offset >= __pa(high_memory))
- pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE | _PAGE_GUARDED;
+ pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE|_PAGE_GUARDED;
#endif
- if (remap_page_range(vma->vm_start, offset, vma->vm_end - vma->vm_start, vma->vm_page_prot))
+ if (remap_page_range(vma->vm_start, offset, vma->vm_end-vma->vm_start,
+ vma->vm_page_prot))
return -EAGAIN;
vma->vm_dentry = dget(file->f_dentry);
return 0;
@@ -146,61 +146,63 @@ static int mmap_mem(struct file * file, struct vm_area_struct * vma)
/*
* This function reads the *virtual* memory as seen by the kernel.
*/
-static long read_kmem(struct inode *inode, struct file *file,
- char *buf, unsigned long count)
+static ssize_t read_kmem(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
{
- unsigned long p = file->f_pos;
- unsigned long read = 0;
- long virtr;
+ unsigned long p = *ppos;
+ ssize_t read = 0;
+ ssize_t virtr;
if (p < (unsigned long) high_memory) {
- unsigned long tmp;
-
+ read = count;
if (count > (unsigned long) high_memory - p)
- tmp = (unsigned long) high_memory - p;
- else
- tmp = count;
- read = tmp;
+ read = (unsigned long) high_memory - p;
+
#if defined(__sparc__) || defined(__mc68000__)
/* we don't have page 0 mapped on sparc and m68k.. */
- while (p < PAGE_SIZE && tmp > 0) {
- put_user(0,buf);
- buf++;
- p++;
- tmp--;
+ if (p < PAGE_SIZE && read > 0) {
+ size_t tmp = PAGE_SIZE - p;
+ if (tmp > read) tmp = read;
+ clear_user(buf, tmp);
+ buf += tmp;
+ p += tmp;
+ read -= tmp;
+ count -= tmp;
}
#endif
- copy_to_user(buf, (char *) p, tmp);
- buf += tmp;
+ copy_to_user(buf, (char *)p, read);
+ p += read;
+ buf += read;
+ count -= read;
}
- virtr = vread(buf, (char *) (unsigned long) file->f_pos, count - read);
+ virtr = vread(buf, (char *)p, count);
if (virtr < 0)
return virtr;
- file->f_pos += virtr + read;
+ *ppos += p + virtr;
return virtr + read;
}
/*
* This function writes to the *virtual* memory as seen by the kernel.
*/
-static long write_kmem(struct inode * inode, struct file * file,
- const char * buf, unsigned long count)
+static ssize_t write_kmem(struct file * file, const char * buf,
+ size_t count, loff_t *ppos)
{
- unsigned long p = file->f_pos;
+ unsigned long p = *ppos;
if (p >= (unsigned long) high_memory)
return 0;
if (count > (unsigned long) high_memory - p)
count = (unsigned long) high_memory - p;
- return do_write_mem(file,(void*)p,p,buf,count);
+ return do_write_mem(file, (void*)p, p, buf, count, ppos);
}
-static long read_port(struct inode * inode, struct file * file,
- char * buf, unsigned long count)
+static ssize_t read_port(struct file * file, char * buf,
+ size_t count, loff_t *ppos)
{
- unsigned int i = file->f_pos;
- char * tmp = buf;
+ unsigned long i = *ppos;
+ char *tmp = buf;
if (verify_area(VERIFY_WRITE,buf,count))
return -EFAULT;
@@ -210,14 +212,14 @@ static long read_port(struct inode * inode, struct file * file,
i++;
tmp++;
}
- file->f_pos = i;
+ *ppos = i;
return tmp-buf;
}
-static long write_port(struct inode * inode, struct file * file,
- const char * buf, unsigned long count)
+static ssize_t write_port(struct file * file, const char * buf,
+ size_t count, loff_t *ppos)
{
- unsigned int i = file->f_pos;
+ unsigned long i = *ppos;
const char * tmp = buf;
if (verify_area(VERIFY_READ,buf,count))
@@ -230,18 +232,18 @@ static long write_port(struct inode * inode, struct file * file,
i++;
tmp++;
}
- file->f_pos = i;
+ *ppos = i;
return tmp-buf;
}
-static long read_null(struct inode * node, struct file * file,
- char * buf, unsigned long count)
+static ssize_t read_null(struct file * file, char * buf,
+ size_t count, loff_t *ppos)
{
return 0;
}
-static long write_null(struct inode * inode, struct file * file,
- const char * buf, unsigned long count)
+static ssize_t write_null(struct file * file, const char * buf,
+ size_t count, loff_t *ppos)
{
return count;
}
@@ -249,7 +251,7 @@ static long write_null(struct inode * inode, struct file * file,
/*
* For fun, we are using the MMU for this.
*/
-static inline unsigned long read_zero_pagealigned(char * buf, unsigned long size)
+static inline size_t read_zero_pagealigned(char * buf, size_t size)
{
struct vm_area_struct * vma;
unsigned long addr=(unsigned long)buf;
@@ -292,8 +294,8 @@ static inline unsigned long read_zero_pagealigned(char * buf, unsigned long size
return size;
}
-static long read_zero(struct inode * node, struct file * file,
- char * buf, unsigned long count)
+static ssize_t read_zero(struct file * file, char * buf,
+ size_t count, loff_t *ppos)
{
unsigned long left, unwritten, written = 0;
@@ -339,21 +341,23 @@ static int mmap_zero(struct file * file, struct vm_area_struct * vma)
return 0;
}
-static long write_full(struct inode * inode, struct file * file,
- const char * buf, unsigned long count)
+static ssize_t write_full(struct file * file, const char * buf,
+ size_t count, loff_t *ppos)
{
return -ENOSPC;
}
/*
- * Special lseek() function for /dev/null and /dev/zero. Most notably, you can fopen()
- * both devices with "a" now. This was previously impossible. SRB.
+ * Special lseek() function for /dev/null and /dev/zero. Most notably, you
+ * can fopen() both devices with "a" now. This was previously impossible.
+ * -- SRB.
*/
-static long long null_lseek(struct file * file, long long offset, int orig)
+static loff_t null_lseek(struct file * file, loff_t offset, int orig)
{
- return file->f_pos=0;
+ return file->f_pos = 0;
}
+
/*
* The memory devices use the full 32/64 bits of the offset, and so we cannot
* check against negative addresses: they are ok. The return value is weird,
@@ -362,7 +366,7 @@ static long long null_lseek(struct file * file, long long offset, int orig)
* also note that seeking relative to the "end of file" isn't supported:
* it has no meaning, so it returns -EINVAL.
*/
-static long long memory_lseek(struct file * file, long long offset, int orig)
+static loff_t memory_lseek(struct file * file, loff_t offset, int orig)
{
switch (orig) {
case 0:
diff --git a/drivers/char/misc.c b/drivers/char/misc.c
index 826863ec0..e1819d9f2 100644
--- a/drivers/char/misc.c
+++ b/drivers/char/misc.c
@@ -78,6 +78,7 @@ extern void pcwatchdog_init(void);
extern int rtc_init(void);
extern int dsp56k_init(void);
extern int nvram_init(void);
+extern int radio_init(void);
extern void hfmodem_init(void);
#ifdef CONFIG_PROC_FS
@@ -117,7 +118,7 @@ static int misc_open(struct inode * inode, struct file * file)
return -ENODEV;
}
- if ((file->f_op = c->fops))
+ if ((file->f_op = c->fops) && file->f_op->open)
return file->f_op->open(inode,file);
else
return -ENODEV;
@@ -259,6 +260,9 @@ __initfunc(int misc_init(void))
#ifdef CONFIG_NVRAM
nvram_init();
#endif
+#ifdef CONFIG_MISC_RADIO
+ radio_init();
+#endif
#ifdef CONFIG_HFMODEM
hfmodem_init();
#endif
diff --git a/drivers/char/msbusmouse.c b/drivers/char/msbusmouse.c
index a8c32ce6d..75b7e7f32 100644
--- a/drivers/char/msbusmouse.c
+++ b/drivers/char/msbusmouse.c
@@ -129,15 +129,14 @@ static int open_mouse(struct inode * inode, struct file * file)
return 0;
}
-
-static long write_mouse(struct inode * inode, struct file * file,
- const char * buffer, unsigned long count)
+static ssize_t write_mouse(struct file * file,
+ const char * buffer, size_t count, loff_t *ppos)
{
return -EINVAL;
}
-static long read_mouse(struct inode * inode, struct file * file,
- char * buffer, unsigned long count)
+static ssize_t read_mouse(struct file * file,
+ char * buffer, size_t count, loff_t *ppos)
{
int i, dx, dy;
diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c
index 32dc3a51d..3581b81c3 100644
--- a/drivers/char/n_tty.c
+++ b/drivers/char/n_tty.c
@@ -88,9 +88,8 @@ void n_tty_flush_buffer(struct tty_struct * tty)
/*
* Return number of characters buffered to be delivered to user
- *
*/
-int n_tty_chars_in_buffer(struct tty_struct *tty)
+ssize_t n_tty_chars_in_buffer(struct tty_struct *tty)
{
if (tty->icanon) {
if (!tty->canon_data) return 0;
@@ -817,10 +816,10 @@ static inline int input_available_p(struct tty_struct *tty, int amt)
*/
static inline void copy_from_read_buf(struct tty_struct *tty,
unsigned char **b,
- unsigned int *nr)
+ size_t *nr)
{
- int n;
+ ssize_t n;
n = MIN(*nr, MIN(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail));
if (!n)
@@ -832,15 +831,15 @@ static inline void copy_from_read_buf(struct tty_struct *tty,
*nr -= n;
}
-static int read_chan(struct tty_struct *tty, struct file *file,
- unsigned char *buf, unsigned int nr)
+static ssize_t read_chan(struct tty_struct *tty, struct file *file,
+ unsigned char *buf, size_t nr)
{
struct wait_queue wait = { current, NULL };
int c;
unsigned char *b = buf;
int minimum, time;
- int retval = 0;
- int size;
+ ssize_t retval = 0;
+ ssize_t size;
do_it_again:
@@ -924,7 +923,7 @@ do_it_again:
retval = -EAGAIN;
break;
}
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
@@ -1004,13 +1003,13 @@ do_it_again:
return (size ? size : retval);
}
-static int write_chan(struct tty_struct * tty, struct file * file,
- const unsigned char * buf, unsigned int nr)
+static ssize_t write_chan(struct tty_struct * tty, struct file * file,
+ const unsigned char * buf, size_t nr)
{
struct wait_queue wait = { current, NULL };
- int c, num;
+ int c;
const unsigned char *b = buf;
- int retval = 0;
+ ssize_t retval = 0, num;
/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
if (L_TOSTOP(tty) && file->f_dentry->d_inode->i_rdev != CONSOLE_DEV) {
@@ -1022,7 +1021,7 @@ static int write_chan(struct tty_struct * tty, struct file * file,
add_wait_queue(&tty->write_wait, &wait);
while (1) {
current->state = TASK_INTERRUPTIBLE;
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c
index 402981749..329ac7b32 100644
--- a/drivers/char/nvram.c
+++ b/drivers/char/nvram.c
@@ -227,11 +227,11 @@ static long long nvram_llseek(struct file *file,loff_t offset, int origin )
return( (offset >= 0) ? (file->f_pos = offset) : -EINVAL );
}
-static long nvram_read( struct inode * inode, struct file * file,
- char * buf, unsigned long count )
+static ssize_t nvram_read( struct file * file,
+ char * buf, size_t count, loff_t *ppos )
{
unsigned long flags;
- unsigned i = file->f_pos;
+ unsigned i = *ppos;
char *tmp = buf;
save_flags(flags);
@@ -244,17 +244,16 @@ static long nvram_read( struct inode * inode, struct file * file,
for( ; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp )
put_user( nvram_read_int(i), tmp );
- file->f_pos = i;
+ *ppos = i;
restore_flags(flags);
return( tmp - buf );
}
-static long nvram_write( struct inode * inode, struct file * file,
- const char * buf, unsigned long count )
+static ssize_t nvram_write( struct file * file, const char * buf, size_t count, loff_t *ppos )
{
unsigned long flags;
- unsigned i = file->f_pos;
+ unsigned i = *ppos;
const char *tmp = buf;
char c;
@@ -271,7 +270,7 @@ static long nvram_write( struct inode * inode, struct file * file,
nvram_write_int( c, i );
}
nvram_set_checksum_int();
- file->f_pos = i;
+ *ppos = i;
restore_flags(flags);
return( tmp - buf );
diff --git a/drivers/char/pc110pad.c b/drivers/char/pc110pad.c
index 71b75deb2..c2ed5482a 100644
--- a/drivers/char/pc110pad.c
+++ b/drivers/char/pc110pad.c
@@ -13,6 +13,7 @@
* 0.1 1997-05-19 Robin O'Leary <robin@acm.org> - PS/2 emulation
* 0.2 1997-06-03 Robin O'Leary <robin@acm.org> - tap gesture
* 0.3 1997-06-27 Alan Cox <alan@cymru.net> - 2.1 commit
+ * 0.4 1997-11-09 Alan Cox <alan@cymru.net> - Single Unix VFS API changes
*/
#include <linux/config.h>
@@ -532,7 +533,7 @@ static int open_pad(struct inode * inode, struct file * file)
/*
* writes are disallowed
*/
-static long write_pad(struct inode * inode, struct file * file, const char * buffer, unsigned long count)
+static ssize_t write_pad(struct file * file, const char * buffer, size_t count, loff_t *ppos)
{
return -EINVAL;
}
@@ -553,7 +554,7 @@ void new_sample(int d[3])
/*
* Read pad data. Currently never blocks.
*/
-static long read_pad(struct inode * inode, struct file * file, char * buffer, unsigned long count)
+static ssize_t read_pad(struct file * file, char * buffer, size_t count, loff_t *ppos)
{
int r;
diff --git a/drivers/char/pcwd.c b/drivers/char/pcwd.c
index 2f8a5a75b..213306349 100644
--- a/drivers/char/pcwd.c
+++ b/drivers/char/pcwd.c
@@ -381,13 +381,12 @@ static int pcwd_open(struct inode *ino, struct file *filep)
return(0);
}
-static long pcwd_read(struct inode *inode, struct file *file, char *buf,
- unsigned long count)
+static ssize_t pcwd_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
unsigned short c = inb(current_readport);
unsigned char cp;
- switch(MINOR(inode->i_rdev))
+ switch(MINOR(file->f_dentry->d_inode->i_rdev))
{
case TEMP_MINOR:
cp = c;
diff --git a/drivers/char/pcxx.c b/drivers/char/pcxx.c
index 9a68bb472..eecdaee5a 100644
--- a/drivers/char/pcxx.c
+++ b/drivers/char/pcxx.c
@@ -368,7 +368,7 @@ static int pcxx_waitcarrier(struct tty_struct *tty,struct file *filp,struct chan
(info->asyncflags & ASYNC_CLOSING) == 0 &&
(do_clocal || (info->imodem & info->dcd)))
break;
- if(current->signal & ~current->blocked) {
+ if(signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
diff --git a/drivers/char/pms.c b/drivers/char/pms.c
new file mode 100644
index 000000000..78682a353
--- /dev/null
+++ b/drivers/char/pms.c
@@ -0,0 +1,1069 @@
+/*
+ * Media Vision Pro Movie Studio
+ * or
+ * "all you need is an I2C bus some RAM and a prayer"
+ *
+ * This draws heavily on code
+ *
+ * (c) Wolfgang Koehler, wolf@first.gmd.de, Dec. 1994
+ * Kiefernring 15
+ * 14478 Potsdam, Germany
+ *
+ * Most of this code is directly derived from his userspace driver.
+ * His driver works so send any reports to alan@cymru.net unless the
+ * userspace driver also doesnt work for you...
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <linux/sched.h>
+#include <linux/videodev.h>
+#include <linux/version.h>
+#include <asm/uaccess.h>
+
+
+#define MOTOROLA 1
+#define PHILIPS2 2
+#define PHILIPS1 3
+#define MVVMEMORYWIDTH 0x40 /* 512 bytes */
+
+struct pms_device
+{
+ struct video_device v;
+ struct video_picture picture;
+ int height;
+ int width;
+};
+
+struct i2c_info
+{
+ u8 slave;
+ u8 sub;
+ u8 data;
+ u8 hits;
+};
+
+static int i2c_count = 0;
+static struct i2c_info i2cinfo[64];
+
+static int decoder = PHILIPS2;
+static int standard = 0; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
+
+/*
+ * I/O ports and Shared Memory
+ */
+
+static int io_port = 0x250;
+static int data_port = 0x251;
+static int mem_base = 0xC8000;
+
+
+
+extern __inline__ void mvv_write(u8 index, u8 value)
+{
+ outw(index|(value<<8), io_port);
+}
+
+extern __inline__ u8 mvv_read(u8 index)
+{
+ outb(index, io_port);
+ return inb(data_port);
+}
+
+extern int i2c_stat(u8 slave)
+{
+ int counter;
+ int i;
+
+ outb(0x28, io_port);
+
+ counter=0;
+ while((inb(data_port)&0x01)==0)
+ if(counter++==256)
+ break;
+
+ while((inb(data_port)&0x01)!=0)
+ if(counter++==256)
+ break;
+
+ outb(slave, io_port);
+
+ counter=0;
+ while((inb(data_port)&0x01)==0)
+ if(counter++==256)
+ break;
+
+ while((inb(data_port)&0x01)!=0)
+ if(counter++==256)
+ break;
+
+ for(i=0;i<12;i++)
+ {
+ char st=inb(data_port);
+ if((st&2)!=0)
+ return -1;
+ if((st&1)==0)
+ break;
+ }
+ outb(0x29, io_port);
+ return inb(data_port);
+}
+
+int i2c_write(u16 slave, u16 sub, u16 data)
+{
+ int skip=0;
+ int count;
+ int i;
+
+ for(i=0;i<i2c_count;i++)
+ {
+ if((i2cinfo[i].slave==slave) &&
+ (i2cinfo[i].sub == sub))
+ {
+ if(i2cinfo[i].data==data)
+ skip=1;
+ i2cinfo[i].data=data;
+ i=i2c_count+1;
+ }
+ }
+
+ if(i==i2c_count && i2c_count<64)
+ {
+ i2cinfo[i2c_count].slave=slave;
+ i2cinfo[i2c_count].sub=sub;
+ i2cinfo[i2c_count].data=data;
+ i2c_count++;
+ }
+
+ if(skip)
+ return 0;
+
+ mvv_write(0x29, sub);
+ mvv_write(0x2A, data);
+ mvv_write(0x28, slave);
+
+ outb(0x28, io_port);
+
+ count=0;
+ while((inb(data_port)&1)==0)
+ if(count>255)
+ break;
+ while((inb(data_port)&1)!=0)
+ if(count>255)
+ break;
+
+ count=inb(data_port);
+
+ if(count&2)
+ return -1;
+ return count;
+}
+
+int i2c_read(int slave, int sub)
+{
+ int i=0;
+ for(i=0;i<i2c_count;i++)
+ {
+ if(i2cinfo[i].slave==slave && i2cinfo[i].sub==sub)
+ return i2cinfo[i].data;
+ }
+ return 0;
+}
+
+
+void i2c_andor(int slave, int sub, int and, int or)
+{
+ u8 tmp;
+
+ tmp=i2c_read(slave, sub);
+ tmp = (tmp&and)|or;
+ i2c_write(slave, sub, tmp);
+}
+
+/*
+ * Control functions
+ */
+
+
+static void pms_videosource(short source)
+{
+ mvv_write(0x2E, source?0x31:0x30);
+}
+
+static void pms_hue(short hue)
+{
+ switch(decoder)
+ {
+ case MOTOROLA:
+ i2c_write(0x8A, 0x00, hue);
+ break;
+ case PHILIPS2:
+ i2c_write(0x8A, 0x07, hue);
+ break;
+ case PHILIPS1:
+ i2c_write(0x42, 0x07, hue);
+ break;
+ }
+}
+
+static void pms_colour(short colour)
+{
+ switch(decoder)
+ {
+ case MOTOROLA:
+ i2c_write(0x8A, 0x00, colour);
+ break;
+ case PHILIPS1:
+ i2c_write(0x42, 012, colour);
+ break;
+ }
+}
+
+
+static void pms_contrast(short contrast)
+{
+ switch(decoder)
+ {
+ case MOTOROLA:
+ i2c_write(0x8A, 0x00, contrast);
+ break;
+ case PHILIPS1:
+ i2c_write(0x42, 0x13, contrast);
+ break;
+ }
+}
+
+static void pms_brightness(short brightness)
+{
+ switch(decoder)
+ {
+ case MOTOROLA:
+ i2c_write(0x8A, 0x00, brightness);
+ i2c_write(0x8A, 0x00, brightness);
+ i2c_write(0x8A, 0x00, brightness);
+ break;
+ case PHILIPS1:
+ i2c_write(0x42, 0x19, brightness);
+ break;
+ }
+}
+
+static void pms_hstart(short start)
+{
+ switch(decoder)
+ {
+ case PHILIPS1:
+ i2c_write(0x8A, 0x05, start);
+ i2c_write(0x8A, 0x18, start);
+ break;
+ case PHILIPS2:
+ i2c_write(0x42, 0x05, start);
+ i2c_write(0x42, 0x18, start);
+ break;
+ }
+}
+
+static void pms_format(short format)
+{
+ int target;
+ standard = format;
+
+ if(decoder==PHILIPS1)
+ target=0x42;
+ else if(decoder==PHILIPS2)
+ target=0x8A;
+ else
+ return;
+
+ switch(format)
+ {
+ case 0: /* Auto */
+ i2c_andor(target, 0x0D, 0xFE,0x00);
+ i2c_andor(target, 0x0F, 0x3F,0x80);
+ break;
+ case 1: /* NTSC */
+ i2c_andor(target, 0x0D, 0xFE, 0x00);
+ i2c_andor(target, 0x0F, 0x3F, 0x40);
+ break;
+ case 2: /* PAL */
+ i2c_andor(target, 0x0D, 0xFE, 0x00);
+ i2c_andor(target, 0x0F, 0x3F, 0x00);
+ break;
+ case 3: /* SECAM */
+ i2c_andor(target, 0x0D, 0xFE, 0x01);
+ i2c_andor(target, 0x0F, 0x3F, 0x00);
+ break;
+ }
+}
+
+/*
+ * Bandpass filters
+ */
+
+static void pms_bandpass(short pass)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0x8A, 0x06, 0xCF, (pass&0x03)<<4);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x06, 0xCF, (pass&0x03)<<4);
+}
+
+static void pms_antisnow(short snow)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0x8A, 0x06, 0xF3, (snow&0x03)<<2);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x06, 0xF3, (snow&0x03)<<2);
+}
+
+static void pms_sharpness(short sharp)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0x8A, 0x06, 0xFC, sharp&0x03);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x06, 0xFC, sharp&0x03);
+}
+
+static void pms_chromaagc(short agc)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0x8A, 0x0C, 0x9F, (agc&0x03)<<5);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x0C, 0x9F, (agc&0x03)<<5);
+}
+
+static void pms_vertnoise(short noise)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0x8A, 0x10, 0xFC, noise&3);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x10, 0xFC, noise&3);
+}
+
+static void pms_secamcross(short cross)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0x8A, 0x0F, 0xDF, (cross&1)<<5);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x0F, 0xDF, (cross&1)<<5);
+}
+
+static void pms_forcecolour(short colour)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0x8A, 0x0C, 0x7F, (colour&1)<<7);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x0C, 0x7, (colour&1)<<7);
+}
+
+static void pms_antigamma(short gamma)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0xB8, 0x00, 0x7F, (gamma&1)<<7);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x20, 0x7, (gamma&1)<<7);
+}
+
+static void pms_prefilter(short filter)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0x8A, 0x06, 0xBF, (filter&1)<<6);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x06, 0xBF, (filter&1)<<6);
+}
+
+static void pms_hfilter(short filter)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0xB8, 0x04, 0x1F, (filter&7)<<5);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x24, 0x1F, (filter&7)<<5);
+}
+
+static void pms_vfilter(short filter)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0xB8, 0x08, 0x9F, (filter&3)<<5);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42, 0x28, 0x9F, (filter&3)<<5);
+}
+
+static void pms_killcolour(short colour)
+{
+ if(decoder==PHILIPS2)
+ {
+ i2c_andor(0x8A, 0x08, 0x07, (colour&0x1F)<<3);
+ i2c_andor(0x8A, 0x09, 0x07, (colour&0x1F)<<3);
+ }
+ else if(decoder==PHILIPS1)
+ {
+ i2c_andor(0x42, 0x08, 0x07, (colour&0x1F)<<3);
+ i2c_andor(0x42, 0x09, 0x07, (colour&0x1F)<<3);
+ }
+}
+
+static void pms_swsense(short sense)
+{
+ if(decoder==PHILIPS2)
+ {
+ i2c_write(0x8A, 0x0A, sense);
+ i2c_write(0x8A, 0x0B, sense);
+ }
+ else if(decoder==PHILIPS1)
+ {
+ i2c_write(0x42, 0x08, sense);
+ i2c_write(0x42, 0x09, sense);
+ }
+}
+
+static void pms_chromagain(short chroma)
+{
+ if(decoder==PHILIPS2)
+ {
+ i2c_write(0x8A, 0x0A, chroma);
+ i2c_write(0x8A, 0x0B, chroma);
+ }
+ else if(decoder==PHILIPS1)
+ {
+ i2c_write(0x42, 0x08, chroma);
+ i2c_write(0x42, 0x09, chroma);
+ }
+}
+
+
+static void pms_spacialcompl(short data)
+{
+ mvv_write(0x3B, data);
+}
+
+static void pms_spacialcomph(short data)
+{
+ mvv_write(0x3A, data);
+}
+
+static void pms_framerate(short frr)
+{
+ int fps=(standard==1)?30:25;
+ if(frr==0)
+ return;
+ fps=fps/frr;
+ mvv_write(0x14,0x80|fps);
+ mvv_write(0x15,1);
+}
+
+static void pms_vert(u8 deciden, u8 decinum)
+{
+ mvv_write(0x1C, deciden); /* Denominator */
+ mvv_write(0x1D, decinum); /* Numerator */
+}
+
+/*
+ * Turn 16bit ratios into best small ratio the chipset can grok
+ */
+
+static void pms_vertdeci(unsigned short decinum, unsigned short deciden)
+{
+ /* Knock it down by /5 once */
+ if(decinum%5==0)
+ {
+ deciden/=5;
+ decinum/=5;
+ }
+ /*
+ * 3's
+ */
+ while(decinum%3==0 && deciden%3==0)
+ {
+ deciden/=3;
+ decinum/=3;
+ }
+ /*
+ * 2's
+ */
+ while(decinum%2==0 && deciden%2==0)
+ {
+ decinum/=2;
+ deciden/=2;
+ }
+ /*
+ * Fudgyify
+ */
+ while(deciden>32)
+ {
+ deciden/=2;
+ decinum=(decinum+1)/2;
+ }
+ if(deciden==32)
+ deciden--;
+ pms_vert(deciden,decinum);
+}
+
+static void pms_horzdeci(short decinum, short deciden)
+{
+ if(decinum<=512)
+ {
+ if(decinum%5==0)
+ {
+ decinum/=5;
+ deciden/=5;
+ }
+ }
+ else
+ {
+ decinum=512;
+ deciden=640; /* 768 would be ideal */
+ }
+
+ while(decinum%2==0 && deciden%2==0)
+ {
+ decinum/=2;
+ deciden/=2;
+ }
+ while(deciden>32)
+ {
+ deciden/=2;
+ decinum=(decinum+1)/2;
+ }
+ if(deciden==32)
+ deciden--;
+
+ mvv_write(0x24, 0x80|deciden);
+ mvv_write(0x25, deciden);
+}
+
+static void pms_resolution(short width, short height)
+{
+ int fg_height;
+
+ fg_height=height;
+ if(fg_height>280)
+ fg_height=280;
+
+ mvv_write(0x18, fg_height);
+ mvv_write(0x19, fg_height>>8);
+
+ if(standard==1)
+ {
+ mvv_write(0x1A, 0xFC);
+ mvv_write(0x1B, 0x00);
+ if(height>width)
+ pms_vertdeci(240,240);
+ else
+ pms_vertdeci(fg_height,240);
+ }
+ else
+ {
+ mvv_write(0x1A, 0x1A);
+ mvv_write(0x1B, 0x01);
+ if(fg_height>256)
+ pms_vertdeci(270,270);
+ else
+ pms_vertdeci(fg_height, 270);
+ }
+ mvv_write(0x12,0);
+ mvv_write(0x13, MVVMEMORYWIDTH);
+ mvv_write(0x42, 0x00);
+ mvv_write(0x43, 0x00);
+ mvv_write(0x44, MVVMEMORYWIDTH);
+
+ mvv_write(0x22, width+8);
+ mvv_write(0x23, (width+8)>> 8);
+
+ if(standard==1)
+ pms_horzdeci(width,640);
+ else
+ pms_horzdeci(width+8, 768);
+
+ mvv_write(0x30, mvv_read(0x30)&0xFE);
+ mvv_write(0x08, mvv_read(0x08)|0x01);
+ mvv_write(0x01, mvv_read(0x01)&0xFD);
+ mvv_write(0x32, 0x00);
+ mvv_write(0x33, MVVMEMORYWIDTH);
+}
+
+static void pms_vstart(short start)
+{
+ mvv_write(0x16, start);
+ mvv_write(0x17, (start>>8)&0x01);
+}
+
+/*
+ * Set Input
+ */
+
+static void pms_vcrinput(short input)
+{
+ if(decoder==PHILIPS2)
+ i2c_andor(0x8A,0x0D,0x7F,(input&1)<<7);
+ else if(decoder==PHILIPS1)
+ i2c_andor(0x42,0x0D,0x7F,(input&1)<<7);
+}
+
+
+static int pms_capture(struct pms_device *dev, char *buf, int rgb555, int count)
+{
+ char dump[16];
+ int y;
+ int ww= dev->width, wh= dev->height;
+ int dinc, zz;
+ int dw=2*ww, loops;
+ unsigned char r8;
+ int len=0;
+
+ short *dst=(short *)buf;
+ short *src=(short *)bus_to_virt((void *)mem_base);
+
+ if(wh>256)
+ {
+ dinc=2*ww;
+ zz=wh/2;
+ loops=2;
+ }
+ else
+ {
+ dinc=ww;
+ zz=wh;
+ loops=1;
+ }
+
+
+ r8=0x5;
+ if(rgb555)
+ r8|=0x20;
+
+field_cap:
+
+ mvv_write(0x08, r8); /* Capture mode, Enable DRAM, PC enable */
+
+ for(y=0;y<zz;y++)
+ {
+ int n;
+
+ len+=16;
+
+ /*
+ * Avoid overrunning the given buffer
+ */
+
+ n=((char *)dst)-buf; /* Bytes left in frame */
+ n=count-n;
+ if(n>dw) /* But only as many as needed */
+ n=dw;
+ memcpy(dump, src, 16); /* Junk */
+ if(n)
+ copy_to_user(dst, src, n); /* Data */
+ dst+=dinc;
+ *src=0; /* Synchronization */
+ }
+
+ if(--loops>0)
+ {
+ mvv_write(0x14, mvv_read(0x14)|0xC0); /* Other frame */
+ dst=(short *)buf+ww;
+ goto field_cap;
+ }
+
+ /*
+ * Set back to capture even frames
+ */
+
+ if(wh>256)
+ mvv_write(0x14, (mvv_read(0x14)|0x80)&~0x40);
+ return len;
+}
+
+
+/*
+ * Video4linux interfacing
+ */
+
+static int pms_open(struct video_device *dev, int flags)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void pms_close(struct video_device *dev)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+static int pms_init_done(struct video_device *dev)
+{
+ return 0;
+}
+
+static long pms_write(struct video_device *v, const char *buf, unsigned long count, int noblock)
+{
+ return -EINVAL;
+}
+
+static int pms_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+ struct pms_device *pd=(struct pms_device *)dev;
+
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability b;
+ strcpy(b.name, "Mediavision PMS");
+ b.type = VID_TYPE_CAPTURE|VID_TYPE_SCALES;
+ b.channels = 4;
+ b.audios = 0;
+ b.maxwidth = 640;
+ b.maxheight = 480;
+ b.minwidth = 16;
+ b.minheight = 16;
+ if(copy_to_user(arg, &b,sizeof(b)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCGCHAN:
+ {
+ struct video_channel v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if(v.channel<0 || v.channel>3)
+ return -EINVAL;
+ v.flags=0;
+ v.tuners=1;
+ /* Good question.. its composite or SVHS so.. */
+ v.type = VIDEO_TYPE_CAMERA;
+ if(copy_to_user(arg, &v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSCHAN:
+ {
+ int v;
+ if(copy_from_user(&v, arg,sizeof(v)))
+ return -EFAULT;
+ if(v<0 && v>3)
+ return -EINVAL;
+ pms_videosource(v&1);
+ pms_vcrinput(v>>1);
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner v;
+ if(copy_from_user(&v, arg, sizeof(v))!=0)
+ return -EFAULT;
+ if(v.tuner)
+ return -EINVAL;
+ strcpy(v.name, "Format");
+ v.rangelow=0;
+ v.rangehigh=0;
+ v.flags= VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
+ switch(standard)
+ {
+ case 0:
+ v.mode = VIDEO_MODE_AUTO;
+ break;
+ case 1:
+ v.mode = VIDEO_MODE_NTSC;
+ break;
+ case 2:
+ v.mode = VIDEO_MODE_PAL;
+ break;
+ case 3:
+ v.mode = VIDEO_MODE_SECAM;
+ break;
+ }
+ if(copy_to_user(arg,&v,sizeof(v))!=0)
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner v;
+ if(copy_from_user(&v, arg, sizeof(v))!=0)
+ return -EFAULT;
+ if(v.tuner)
+ return -EINVAL;
+ switch(v.mode)
+ {
+ case VIDEO_MODE_AUTO:
+ pms_framerate(25);
+ pms_secamcross(0);
+ pms_format(0);
+ break;
+ case VIDEO_MODE_NTSC:
+ pms_framerate(30);
+ pms_secamcross(0);
+ pms_format(1);
+ break;
+ case VIDEO_MODE_PAL:
+ pms_framerate(25);
+ pms_secamcross(0);
+ pms_format(2);
+ break;
+ case VIDEO_MODE_SECAM:
+ pms_framerate(25);
+ pms_secamcross(1);
+ pms_format(2);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+ }
+ case VIDIOCGPICT:
+ {
+ struct video_picture p=pd->picture;
+ if(copy_to_user(arg, &p, sizeof(p)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSPICT:
+ {
+ struct video_picture p;
+ if(copy_from_user(&p, arg, sizeof(p)))
+ return -EFAULT;
+ if(!((p.palette==VIDEO_PALETTE_RGB565 && p.depth==16)
+ ||(p.palette==VIDEO_PALETTE_RGB555 && p.depth==15)))
+ return -EINVAL;
+ pd->picture=p;
+
+ /*
+ * Now load the card.
+ */
+
+ pms_brightness(p.brightness);
+ pms_hue(p.hue);
+ pms_colour(p.colour);
+ pms_contrast(p.contrast);
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window vw;
+ if(copy_from_user(&vw, arg,sizeof(vw)))
+ return -EFAULT;
+ if(vw.flags)
+ return -EINVAL;
+ if(vw.clipcount)
+ return -EINVAL;
+ if(vw.height<16||vw.height>480)
+ return -EINVAL;
+ if(vw.width<16||vw.width>640)
+ return -EINVAL;
+ pd->width=vw.width;
+ pd->height=vw.height;
+ pms_resolution(pd->width, pd->height);
+ /* Ok we figured out what to use from our wide choice */
+ return 0;
+ }
+ case VIDIOCGWIN:
+ {
+ struct video_window vw;
+ vw.x=0;
+ vw.y=0;
+ vw.width=pd->width;
+ vw.height=pd->height;
+ vw.chromakey=0;
+ vw.flags=0;
+ if(copy_to_user(arg, &vw, sizeof(vw)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCCAPTURE:
+ return -EINVAL;
+ case VIDIOCGFBUF:
+ return -EINVAL;
+ case VIDIOCSFBUF:
+ return -EINVAL;
+ case VIDIOCKEY:
+ return 0;
+ case VIDIOCGFREQ:
+ return -EINVAL;
+ case VIDIOCSFREQ:
+ return -EINVAL;
+ case VIDIOCGAUDIO:
+ return -EINVAL;
+ case VIDIOCSAUDIO:
+ return -EINVAL;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static long pms_read(struct video_device *v, char *buf, unsigned long count, int noblock)
+{
+ struct pms_device *pd=(struct pms_device *)v;
+ int len;
+
+ /* FIXME: semaphore this */
+ len=pms_capture(pd, buf, (pd->picture.depth==16)?0:1,count);
+ return len;
+}
+
+
+struct video_device pms_template=
+{
+ "Mediavision PMS",
+ VID_TYPE_CAPTURE,
+ VID_HARDWARE_PMS,
+ pms_open,
+ pms_close,
+ pms_read,
+ pms_write,
+ pms_ioctl,
+ NULL,
+ pms_init_done,
+ NULL,
+ 0,
+ 0
+};
+
+struct pms_device pms_device;
+
+
+/*
+ * Probe for and initialise the Mediavision PMS
+ */
+
+static int init_mediavision(void)
+{
+ int id;
+ int idec, decst;
+ int i;
+
+ unsigned char i2c_defs[]={
+ 0x4C,0x30,0x00,0xE8,
+ 0xB6,0xE2,0x00,0x00,
+ 0xFF,0xFF,0x00,0x00,
+ 0x00,0x00,0x78,0x98,
+ 0x00,0x00,0x00,0x00,
+ 0x34,0x0A,0xF4,0xCE,
+ 0xE4
+ };
+
+ if(check_region(0x9A01,1))
+ {
+ printk(KERN_WARNING "mediavision: unable to detect: 0x9A01 in use.\n");
+ return -EBUSY;
+ }
+ if(check_region(io_port,3))
+ {
+ printk(KERN_WARNING "mediavision: I/O port %d in use.\n", io_port);
+ return -EBUSY;
+ }
+ outb(0xB8, 0x9A01); /* Unlock */
+ outb(io_port>>4, 0x9A01); /* Set IO port */
+
+
+ id=mvv_read(3);
+ decst=i2c_stat(0x43);
+
+ if(decst!=-1)
+ idec=2;
+ else if(i2c_stat(0xb9)!=-1)
+ idec=3;
+ else if(i2c_stat(0x8b)!=-1)
+ idec=1;
+ else
+ idec=0;
+
+ if(idec==0)
+ return -ENODEV;
+
+ /*
+ * Ok we have a PMS of some sort
+ */
+
+ request_region(io_port,3, "Mediavision PMS");
+ request_region(0x9A01, 1, "Mediavision PMS config");
+
+ mvv_write(0x04, mem_base>>12); /* Set the memory area */
+
+ /* Ok now load the defaults */
+
+ for(i=0;i<0x19;i++)
+ {
+ if(i2c_defs[i]==0xFF)
+ i2c_andor(0x8A, i, 0x07,0x00);
+ else
+ i2c_write(0x8A, i, i2c_defs[i]);
+ }
+
+ i2c_write(0xB8,0x00,0x12);
+ i2c_write(0xB8,0x04,0x00);
+ i2c_write(0xB8,0x07,0x00);
+ i2c_write(0xB8,0x08,0x00);
+ i2c_write(0xB8,0x09,0xFF);
+ i2c_write(0xB8,0x0A,0x00);
+ i2c_write(0xB8,0x0B,0x10);
+ i2c_write(0xB8,0x10,0x03);
+
+ mvv_write(0x01, 0x00);
+ mvv_write(0x05, 0xA0);
+ mvv_write(0x08, 0x25);
+ mvv_write(0x09, 0x00);
+ mvv_write(0x0A, 0x20|MVVMEMORYWIDTH);
+
+ mvv_write(0x10, 0x02);
+ mvv_write(0x1E, 0x0C);
+ mvv_write(0x1F, 0x03);
+ mvv_write(0x26, 0x06);
+
+ mvv_write(0x2B, 0x00);
+ mvv_write(0x2C, 0x20);
+ mvv_write(0x2D, 0x00);
+ mvv_write(0x2F, 0x70);
+ mvv_write(0x32, 0x00);
+ mvv_write(0x33, MVVMEMORYWIDTH);
+ mvv_write(0x34, 0x00);
+ mvv_write(0x35, 0x00);
+ mvv_write(0x3A, 0x80);
+ mvv_write(0x3B, 0x10);
+ mvv_write(0x20, 0x00);
+ mvv_write(0x21, 0x00);
+ mvv_write(0x30, 0x22);
+ return 0;
+}
+
+static void shutdown_mediavision(void)
+{
+ release_region(io_port,3);
+ release_region(0x9A01, 1);
+}
+
+/*
+ * Module stuff
+ */
+
+#ifdef MODULE
+
+int init_module(void)
+{
+ printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.01\n");
+ if(init_mediavision())
+ {
+ printk(KERN_INFO "Board not found.\n");
+ return -ENODEV;
+ }
+ memcpy(&pms_device, &pms_template, sizeof(pms_template));
+ pms_device.height=240;
+ pms_device.width=320;
+ pms_resolution(320,240);
+ return video_register_device((struct video_device *)&pms_device);
+}
+
+void cleanup_module(void)
+{
+ shutdown_mediavision();
+ video_unregister_device((struct video_device *)&pms_device);
+}
+
+#endif
diff --git a/drivers/char/psaux.c b/drivers/char/psaux.c
index d297670a8..771af9a51 100644
--- a/drivers/char/psaux.c
+++ b/drivers/char/psaux.c
@@ -321,13 +321,13 @@ static int open_aux(struct inode * inode, struct file * file)
* Write to the aux device.
*/
-static long write_aux(struct inode * inode, struct file * file,
- const char * buffer, unsigned long count)
+static ssize_t write_aux(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos)
{
- int retval = 0;
+ ssize_t retval = 0;
if (count) {
- int written = 0;
+ ssize_t written = 0;
aux_start_atomic();
do {
@@ -345,7 +345,7 @@ static long write_aux(struct inode * inode, struct file * file,
retval = -EIO;
if (written) {
retval = written;
- inode->i_mtime = CURRENT_TIME;
+ file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
}
}
@@ -468,10 +468,10 @@ static int open_qp(struct inode * inode, struct file * file)
* Write to the 82C710 mouse device.
*/
-static long write_qp(struct inode * inode, struct file * file,
- const char * buffer, unsigned long count)
+static ssize_t write_qp(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos)
{
- int i = count;
+ ssize_t i = count;
while (i--) {
char c;
@@ -480,7 +480,7 @@ static long write_qp(struct inode * inode, struct file * file,
get_user(c, buffer++);
outb_p(c, qp_data);
}
- inode->i_mtime = CURRENT_TIME;
+ file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
return count;
}
@@ -546,11 +546,11 @@ __initfunc(static int probe_qp(void))
* Put bytes from input queue to buffer.
*/
-static long read_aux(struct inode * inode, struct file * file,
- char * buffer, unsigned long count)
+static ssize_t read_aux(struct file * file, char * buffer,
+ size_t count, loff_t *ppos)
{
struct wait_queue wait = { current, NULL };
- int i = count;
+ ssize_t i = count;
unsigned char c;
if (queue_empty()) {
@@ -559,7 +559,7 @@ static long read_aux(struct inode * inode, struct file * file,
add_wait_queue(&queue->proc_list, &wait);
repeat:
current->state = TASK_INTERRUPTIBLE;
- if (queue_empty() && !(current->signal & ~current->blocked)) {
+ if (queue_empty() && !signal_pending(current)) {
schedule();
goto repeat;
}
@@ -573,10 +573,10 @@ repeat:
}
aux_ready = !queue_empty();
if (count-i) {
- inode->i_atime = CURRENT_TIME;
+ file->f_dentry->d_inode->i_atime = CURRENT_TIME;
return count-i;
}
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -ERESTARTSYS;
return 0;
}
diff --git a/drivers/char/radio.c b/drivers/char/radio.c
new file mode 100644
index 000000000..2b79e9872
--- /dev/null
+++ b/drivers/char/radio.c
@@ -0,0 +1,234 @@
+/*
+ * Radio Card Device Driver for Linux
+ *
+ * (c) 1997 Matthew Kirkwood <weejock@ferret.lmh.ox.ac.uk>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+
+#include <linux/miscdevice.h>
+
+#include <asm/uaccess.h>
+
+#include <linux/config.h>
+
+#include <linux/radio.h>
+#ifdef CONFIG_RADIO_RTRACK
+#include "rtrack.h"
+#endif
+#ifdef CONFIG_RADIO_WINRADIO
+#include "winradio.h"
+#endif
+
+int radio_open(struct inode *inode, struct file *file);
+int radio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+
+/* /dev/radio interface */
+static struct file_operations radio_fops = {
+ NULL, /* seek */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ &radio_ioctl, /* ioctl */
+ NULL, /* mmap */
+ &radio_open, /* we're not allowed NULL, it seems... */
+ NULL /* release */
+};
+
+static struct miscdevice radio_miscdevice = {
+ RADIO_MINOR, /* minor device number */
+ "radio", /* title */
+ &radio_fops, /* file operations */
+ NULL, NULL /* previous and next (not our business) */
+};
+
+
+static struct radio_device *firstdevice, *lastdevice;
+static int numdevices;
+
+__initfunc(void radio_init(void))
+{
+ /* register the handler for the device number... */
+ if(misc_register(&radio_miscdevice)) {
+ printk("radio: couldn't register misc device\n");
+ return;
+ }
+
+ /* do some general initialisation stuff */
+ lastdevice = firstdevice = NULL;
+ numdevices = 0;
+
+#ifdef CONFIG_RADIO_RTRACK
+ radiotrack_init();
+#endif
+#ifdef CONFIG_RADIO_WINRADIO
+ printk("oooops. no winradio support yet... :-(\n");
+#endif
+/* etc.... */
+
+ printk("radio: registered %d devices\n", numdevices);
+}
+
+
+/* according to drivers/char/misc.c, the "open" call must not be NULL.
+ * I'm not sure if I approve, but I didn't write it, so...
+ */
+int radio_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+
+/* append a device to the linked list... */
+int radio_add_device(struct radio_device *newdev)
+{
+ if(firstdevice == NULL) {
+ firstdevice = newdev;
+ } else {
+ lastdevice->next = newdev;
+ }
+ lastdevice = newdev; numdevices++;
+ newdev->cap->dev_num=numdevices;
+ newdev->next = NULL; /* don't need, but... */
+ return(numdevices);
+}
+
+struct radio_device *getdev(int index)
+{
+struct radio_device *retval;
+
+ if(index > numdevices)
+ return NULL; /* let's have a bit less of that */
+
+ retval = firstdevice;
+ for(;index;index--)
+ retval = retval->next;
+
+ return retval;
+}
+
+
+/* interface routine */
+int radio_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+struct radio_device *dev;
+struct radio_ctl *ctl_arg = (struct radio_ctl*)arg;
+int nowrite;
+int val, ret;
+
+ if((void*)arg == NULL)
+ return -EFAULT; /* XXX - check errnos are OK */
+
+
+ switch(cmd) {
+ case RADIO_NUMDEVS:
+ return (put_user(numdevices,(int*)arg) ? -EFAULT : 0);
+
+
+ case RADIO_GETCAPS:
+ /* p'raps I should verify for read then write ?? */
+ if(verify_area(VERIFY_WRITE, (void*)arg, sizeof(struct radio_cap)))
+ return -EFAULT;
+ if((dev = getdev(((struct radio_cap*)arg)->dev_num)) == NULL)
+ return -EINVAL;
+ copy_to_user((void*)arg, dev->cap, sizeof(struct radio_cap));
+ return 0;
+
+
+ case RADIO_GETBNDCAP:
+ if(verify_area(VERIFY_WRITE, (void*)arg, sizeof(struct radio_band)))
+ return -EFAULT;
+
+ if((dev = getdev(((struct radio_band*)arg)->dev_num)) == NULL)
+ return -EINVAL;
+
+ val = ((struct radio_band*)arg)->index;
+ if(val >= dev->cap->num_bwidths)
+ return -EINVAL; /* XXX errno */
+
+ copy_to_user((void*)arg, (dev->bands)+(val*sizeof(struct radio_band)),
+ sizeof(struct radio_band));
+ return 0;
+ }
+
+
+/* now, we know that arg points to a struct radio_ctl */
+ /* get the requested device */
+ if(verify_area(VERIFY_READ, ctl_arg, sizeof(struct radio_ctl)))
+ return -EFAULT;
+
+ if((dev = getdev(ctl_arg->dev_num)) == NULL)
+ return -EINVAL;
+
+ nowrite = verify_area(VERIFY_WRITE, ctl_arg, sizeof(struct radio_ctl));
+
+ val = ctl_arg->value;
+
+ switch(cmd) {
+ case RADIO_SETVOL:
+ if((val < dev->cap->volmin) || (val > dev->cap->volmax))
+ return -EINVAL;
+ if((ret = (*(dev->setvol))(dev, val)))
+ return ret;
+ dev->curvol = val;
+ return 0;
+
+ case RADIO_GETVOL:
+ if(nowrite)
+ return -EFAULT;
+ ctl_arg->value = dev->curvol;
+ return 0;
+
+
+ case RADIO_SETBAND:
+ if(val >= dev->cap->num_bwidths)
+ return -EINVAL;
+ if((ret = (*(dev->setband))(dev, val)))
+ return ret;
+ dev->curband = val;
+ return 0;
+
+ case RADIO_GETBAND:
+ if(nowrite)
+ return -EFAULT;
+ ctl_arg->value = dev->curband;
+ return 0;
+
+
+ case RADIO_SETFREQ: {
+ struct radio_band *bp;
+
+ bp = (dev->bands) + ((dev->curband) * sizeof(struct radio_band));
+ if((val < bp->freqmin) || (val > bp->freqmax))
+ return -EINVAL;
+ if((ret = (*(dev->setfreq))(dev, val)))
+ return ret;
+ dev->curfreq = val;
+ return 0;
+ }
+
+ case RADIO_GETFREQ:
+ if(nowrite)
+ return -EFAULT;
+ ctl_arg->value = dev->curfreq;
+ return 0;
+
+
+ case RADIO_GETSIGSTR:
+ if(nowrite)
+ return -EFAULT;
+ ctl_arg->value = (*(dev->getsigstr))(dev);
+ return 0;
+
+
+ default:
+ return -ENOSYS;
+ }
+}
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 527ac8609..6b8a2f12b 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -346,13 +346,13 @@ static struct timer_rand_state *blkdev_timer_state[MAX_BLKDEV];
static struct wait_queue *random_read_wait;
static struct wait_queue *random_write_wait;
-static long random_read(struct inode * inode, struct file * file,
- char * buf, unsigned long nbytes);
-static long random_read_unlimited(struct inode * inode, struct file * file,
- char * buf, unsigned long nbytes);
+static ssize_t random_read(struct file * file, char * buf,
+ size_t nbytes, loff_t *ppos);
+static ssize_t random_read_unlimited(struct file * file, char * buf,
+ size_t nbytes, loff_t *ppos);
static unsigned int random_poll(struct file *file, poll_table * wait);
-static long random_write(struct inode * inode, struct file * file,
- const char * buffer, unsigned long count);
+static ssize_t random_write(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos);
static int random_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg);
@@ -983,10 +983,10 @@ static void MD5Transform(__u32 buf[4],
* bits of entropy are left in the pool, but it does not restrict the
* number of bytes that are actually obtained.
*/
-static int extract_entropy(struct random_bucket *r, char * buf,
- int nbytes, int to_user)
+static ssize_t extract_entropy(struct random_bucket *r, char * buf,
+ size_t nbytes, int to_user)
{
- int ret, i;
+ ssize_t ret, i;
__u32 tmp[HASH_BUFFER_SIZE];
char *cp,*dp;
@@ -1075,13 +1075,11 @@ void get_random_bytes(void *buf, int nbytes)
extract_entropy(&random_state, (char *) buf, nbytes, 0);
}
-static long
-random_read(struct inode * inode, struct file * file, char * buf, unsigned long nbytes)
+static ssize_t
+random_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
{
struct wait_queue wait = { current, NULL };
- int n;
- int retval = 0;
- int count = 0;
+ ssize_t n, retval = 0, count = 0;
if (nbytes == 0)
return 0;
@@ -1098,7 +1096,7 @@ random_read(struct inode * inode, struct file * file, char * buf, unsigned long
retval = -EAGAIN;
break;
}
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
@@ -1121,19 +1119,18 @@ random_read(struct inode * inode, struct file * file, char * buf, unsigned long
remove_wait_queue(&random_read_wait, &wait);
/*
- * If we gave the user some bytes and we have an inode pointer,
- * update the access time.
+ * If we gave the user some bytes, update the access time.
*/
- if (inode && count != 0) {
- UPDATE_ATIME(inode);
+ if (count != 0) {
+ UPDATE_ATIME(file->f_dentry->d_inode);
}
return (count ? count : retval);
}
-static long
-random_read_unlimited(struct inode * inode, struct file * file,
- char * buf, unsigned long nbytes)
+static ssize_t
+random_read_unlimited(struct file * file, char * buf,
+ size_t nbytes, loff_t *ppos)
{
return extract_entropy(&random_state, buf, nbytes, 1);
}
@@ -1153,14 +1150,14 @@ random_poll(struct file *file, poll_table * wait)
return mask;
}
-static long
-random_write(struct inode * inode, struct file * file,
- const char * buffer, unsigned long count)
+static ssize_t
+random_write(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos)
{
- int i, bytes, ret = 0;
+ ssize_t i, bytes, ret = 0;
__u32 buf[16];
const char *p = buffer;
- int c = count;
+ ssize_t c = count;
while (c > 0) {
bytes = MIN(c, sizeof(buf));
@@ -1179,9 +1176,9 @@ random_write(struct inode * inode, struct file * file,
while (i--)
add_entropy_word(&random_state, buf[i]);
}
- if ((ret > 0) && inode) {
- inode->i_mtime = CURRENT_TIME;
- mark_inode_dirty(inode);
+ if (ret > 0) {
+ file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(file->f_dentry->d_inode);
}
return ret;
}
@@ -1265,7 +1262,8 @@ random_ioctl(struct inode * inode, struct file * file,
retval = verify_area(VERIFY_READ, (void *) p, size);
if (retval)
return retval;
- retval = random_write(0, file, (const char *) p, size);
+ retval = random_write(file, (const char *) p,
+ size, &file->f_pos);
if (retval < 0)
return retval;
/*
diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c
index 806caf370..ef6b347ab 100644
--- a/drivers/char/riscom8.c
+++ b/drivers/char/riscom8.c
@@ -1040,7 +1040,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
!(port->flags & ASYNC_CLOSING) &&
(do_clocal || CD))
break;
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c
index 59d2666ab..ef9af462b 100644
--- a/drivers/char/rocket.c
+++ b/drivers/char/rocket.c
@@ -622,10 +622,12 @@ static void init_r_port(int board, int aiop, int chan)
rp_table[line] = info;
}
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
static int baud_table[] = {
0, 50, 75, 110, 134, 150, 200, 300,
600, 1200, 1800, 2400, 4800, 9600, 19200,
38400, 57600, 115200, 230400, 460800, 0 };
+#endif
/*
* This routine configures a rocketport port so according to its
@@ -671,6 +673,7 @@ static void configure_r_port(struct r_port *info)
}
/* baud rate */
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
i = cflag & CBAUD;
if (i & CBAUDEX) {
i &= ~CBAUDEX;
@@ -690,6 +693,11 @@ static void configure_r_port(struct r_port *info)
i += 4;
}
baud = baud_table[i] ? baud_table[i] : 9600;
+#else
+ baud = tty_get_baud_rate(info->tty);
+ if (!baud)
+ baud = 9600;
+#endif
info->cps = baud / bits;
sSetBaud(cp, (rp_baud_base/baud) - 1);
@@ -836,7 +844,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
(do_clocal || (sGetChanStatusLo(&info->channel) &
CD_ACT)))
break;
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
@@ -1182,7 +1190,7 @@ static void rp_set_termios(struct tty_struct *tty, struct termios *old_termios)
/*
* Here are the routines used by rp_ioctl
*/
-
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
static void send_break( struct r_port * info, int duration)
{
current->state = TASK_INTERRUPTIBLE;
@@ -1193,6 +1201,24 @@ static void send_break( struct r_port * info, int duration)
sClrBreak(&info->channel);
sti();
}
+#else
+static void rp_break(struct tty_struct *tty, int break_state)
+{
+ struct r_port * info = (struct r_port *)tty->driver_data;
+ unsigned long flags;
+
+ if (rocket_paranoia_check(info, tty->device, "rp_break"))
+ return;
+
+ save_flags(flags); cli();
+ if (break_state == -1) {
+ sSendBreak(&info->channel);
+ } else {
+ sClrBreak(&info->channel);
+ }
+ restore_flags(flags);
+}
+#endif
static int get_modem_info(struct r_port * info, unsigned int *value)
{
@@ -1289,8 +1315,19 @@ static int set_config(struct r_port * info, struct rocket_config * new_info)
(new_serial.flags & ROCKET_FLAGS));
info->close_delay = new_serial.close_delay;
info->closing_wait = new_serial.closing_wait;
- configure_r_port(info);
+
+#if (LINUX_VERSION_CODE >= 131393) /* Linux 2.1.65 */
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI)
+ info->tty->alt_speed = 57600;
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI)
+ info->tty->alt_speed = 115200;
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI)
+ info->tty->alt_speed = 230400;
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP)
+ info->tty->alt_speed = 460800;
+#endif
+ configure_r_port(info);
return 0;
}
@@ -1319,25 +1356,27 @@ static int get_ports(struct r_port * info, struct rocket_ports * retports)
static int rp_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
{
- int tmp;
struct r_port * info = (struct r_port *)tty->driver_data;
- int retval;
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
+ int retval, tmp;
+#endif
if (cmd != RCKP_GET_PORTS &&
rocket_paranoia_check(info, tty->device, "rp_ioctl"))
return -ENODEV;
switch (cmd) {
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
case TCSBRK: /* SVID version: non-zero arg --> no break */
retval = tty_check_change(tty);
if (retval)
return retval;
tty_wait_until_sent(tty, 0);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -EINTR;
if (!arg) {
send_break(info, HZ/4); /* 1/4 second */
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -EINTR;
}
return 0;
@@ -1346,10 +1385,10 @@ static int rp_ioctl(struct tty_struct *tty, struct file * file,
if (retval)
return retval;
tty_wait_until_sent(tty, 0);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -EINTR;
send_break(info, arg ? arg*(HZ/10) : HZ/4);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -EINTR;
return 0;
case TIOCGSOFTCAR:
@@ -1365,6 +1404,7 @@ static int rp_ioctl(struct tty_struct *tty, struct file * file,
((tty->termios->c_cflag & ~CLOCAL) |
(tmp ? CLOCAL : 0));
return 0;
+#endif
case TIOCMGET:
return get_modem_info(info, (unsigned int *) arg);
case TIOCMBIS:
@@ -1548,7 +1588,7 @@ static void rp_wait_until_sent(struct tty_struct *tty, int timeout)
current->counter = 0; /* make us low-priority */
current->timeout = jiffies + check_time;
schedule();
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
break;
}
current->state = TASK_RUNNING;
@@ -2093,6 +2133,9 @@ __initfunc(int rp_init(void))
rocket_driver.stop = rp_stop;
rocket_driver.start = rp_start;
rocket_driver.hangup = rp_hangup;
+#if (LINUX_VERSION_CODE >= 131393) /* Linux 2.1.65 */
+ rocket_driver.break_ctl = rp_break;
+#endif
#if (LINUX_VERSION_CODE >= 131343)
rocket_driver.send_xchar = rp_send_xchar;
rocket_driver.wait_until_sent = rp_wait_until_sent;
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index b150dd7e1..26cfa4768 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -78,8 +78,8 @@ static struct timer_list rtc_irq_timer;
static long long rtc_llseek(struct file *file, loff_t offset, int origin);
-static long rtc_read(struct inode *inode, struct file *file,
- char *buf, unsigned long count);
+static ssize_t rtc_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos);
static int rtc_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
@@ -146,11 +146,12 @@ static long long rtc_llseek(struct file *file, loff_t offset, int origin)
return -ESPIPE;
}
-static long rtc_read(struct inode *inode, struct file *file, char *buf,
- unsigned long count)
+static ssize_t rtc_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
{
struct wait_queue wait = { current, NULL };
- int retval = 0;
+ unsigned long data;
+ ssize_t retval;
if (count < sizeof(unsigned long))
return -EINVAL;
@@ -159,30 +160,22 @@ static long rtc_read(struct inode *inode, struct file *file, char *buf,
current->state = TASK_INTERRUPTIBLE;
- while (rtc_irq_data == 0) {
+ while ((data = xchg(&rtc_irq_data, 0)) == 0) {
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
- break;
+ goto out;
}
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
- break;
+ goto out;
}
schedule();
}
- if (retval == 0) {
- unsigned long data, flags;
- save_flags(flags);
- cli();
- data = rtc_irq_data;
- rtc_irq_data = 0;
- restore_flags(flags);
- retval = put_user(data, (unsigned long *)buf);
- if (!retval)
- retval = sizeof(unsigned long);
- }
-
+ retval = put_user(data, (unsigned long *)buf);
+ if (!retval)
+ retval = sizeof(unsigned long);
+out:
current->state = TASK_RUNNING;
remove_wait_queue(&rtc_wait, &wait);
diff --git a/drivers/char/rtrack.c b/drivers/char/rtrack.c
new file mode 100644
index 000000000..efe01046a
--- /dev/null
+++ b/drivers/char/rtrack.c
@@ -0,0 +1,213 @@
+/* radiotrack (radioreveal) driver for Linux radio support
+ * (c) 1997 M. Kirkwood
+ */
+/* TODO: Allow for more than one of these foolish entities :-) */
+
+/* Notes on the hardware (reverse engineered from other peoples'
+ * reverse engineering of AIMS' code :-)
+ *
+ * Frequency control is done digitally -- ie out(port,encodefreq(95.8));
+ *
+ * The signal strength query is unsurprisingly inaccurate. And it seems
+ * to indicate that (on my card, at least) the frequency setting isn't
+ * too great. (I have to tune up .025MHz from what the freq should be
+ * to get a report that the thing is tuned.)
+ *
+ * Volume control is (ugh) analogue:
+ * out(port, start_increasing_volume);
+ * wait(a_wee_while);
+ * out(port, stop_changing_the_volume);
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/radio.h>
+#include <linux/ioport.h>
+
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include "rtrack.h"
+
+/* Let's just be a bit careful here, shall we? */
+#if (CONFIG_RADIO_RTRACK_PORT != 0x20f) && (CONFIG_RADIO_RTRACK_PORT != 0x30f)
+#error Invalid port specified for RadioTrack
+#endif
+
+/* local prototypes */
+void outbits(int bits, int data, int port);
+void decvol(int port);
+void incvol(int port);
+void mute(int port);
+void unmute(int port);
+
+/* public structurey-type things */
+int rt_port = CONFIG_RADIO_RTRACK_PORT;
+
+struct radio_cap rt_cap = {
+ 0, /* device index (not dealt with here) */
+ RADIO_TYPE_RTRACK, /* type */
+ 1, /* number of bandwidths */
+ 0, 10 /* volmin, volmax */
+};
+
+/* we only get one band/protocol with radiotrack */
+struct radio_band rt_band = {
+ 0, /* device index */
+ 0, /* bandwidth "index" */
+ RADIO_PROTOCOL_FM,
+ RADIO_BAND_FM_STD,
+ RADIO_FM_FRTOINT(88.0), /* freq range */
+ RADIO_FM_FRTOINT(108.0),
+ 0,1 /* sig strength range */
+};
+
+/* p'raps these should become struct radio_ops and struct radio_status? */
+struct radio_device rt_dev = {
+ &rt_cap,
+ &rt_band,
+ &rt_setvol,
+ 0, /* curvol */
+ &rt_setband,
+ 0, /* curband */
+ &rt_setfreq,
+ 0, /* curfreq */
+ &rt_getsigstr,
+ NULL, /* next (to be filled in later) */
+ &rt_port /* misc */
+};
+
+
+void radiotrack_init()
+{
+int dev_num, i;
+
+/* XXX - probe here?? - XXX */
+
+/* try to grab the i/o port */
+ if(check_region(rt_port,2)) {
+ printk("rtrack.c: port 0x%x already used\n", rt_port);
+ return;
+ }
+
+ request_region(rt_port,2,"rtrack");
+
+ dev_num = radio_add_device(&rt_dev);
+/* initialise the card */
+ /* set the volume very low */
+ for(i=rt_cap.volmax; i>rt_cap.volmin; i--)
+ decvol(rt_port);
+ rt_dev.curvol = rt_cap.volmin;
+}
+
+
+int rt_setvol(struct radio_device *dev, int vol)
+{
+int port, i;
+
+ if(vol == dev->curvol)
+ return 0;
+
+ port = *(int*)(dev->misc);
+ if(vol == 0)
+ mute(port);
+
+ if(vol > dev->curvol)
+ for(i = dev->curvol; i < vol; i++)
+ incvol(port);
+ else
+ for(i = dev->curvol; i > vol; i--)
+ decvol(port);
+
+ if(dev->curvol == 0)
+ unmute(port);
+
+ return 0;
+}
+
+
+int rt_setband(struct radio_device *dev, int band)
+{
+/* we know in advance that we only have one band, and
+ * the wrapper checks the validity of all the inputs
+ */
+ return 0;
+}
+
+int rt_setfreq(struct radio_device *dev, int freq)
+{
+int myport = *(int*)(dev->misc);
+
+ outbits(16, RTRACK_ENCODE(freq), myport);
+ outbits(8, 0xa0, myport);
+/* XXX - get rid of this once setvol is implemented properly - XXX */
+/* these insist on turning the thing on. not sure I approve... */
+ udelay(1000);
+ outb(0, myport);
+ outb(0xc8, myport);
+
+ return 0;
+}
+
+int rt_getsigstr(struct radio_device *dev)
+{
+int res;
+int myport = *(int*)(dev->misc);
+
+ outb(0xf8, myport);
+ udelay(200000);
+ res = (int)inb(myport);
+ udelay(10000);
+ outb(0xe8, myport);
+ if(res == 0xfd)
+ return 1;
+ else
+ return 0;
+}
+
+
+/* local things */
+void outbits(int bits, int data, int port)
+{
+ while(bits--) {
+ if(data & 1) {
+ outw(5, port);
+ outw(5, port);
+ outw(7, port);
+ outw(7, port);
+ } else {
+ outw(1, port);
+ outw(1, port);
+ outw(3, port);
+ outw(3, port);
+ }
+ data>>=1;
+ }
+}
+
+void decvol(int port)
+{
+ outb(0x48, port);
+ udelay(100000);
+ outb(0xc8, port);
+}
+
+void incvol(int port)
+{
+ outb(0x88, port);
+ udelay(100000);
+ outb(0xc8, port);
+}
+
+void mute(int port)
+{
+ outb(0, port);
+ outb(0xc0, port);
+}
+
+void unmute(int port)
+{
+ outb(0, port);
+ outb(0xc8, port);
+}
diff --git a/drivers/char/rtrack.h b/drivers/char/rtrack.h
new file mode 100644
index 000000000..b5a9bc124
--- /dev/null
+++ b/drivers/char/rtrack.h
@@ -0,0 +1,25 @@
+/* RadioTrack (RadioReveal) include file.
+ * (c) 1997 M. Kirkwood
+ *
+ * Not in include/linux/ because there's no need for anyone
+ * to know about these details, I reckon.
+ */
+
+#ifndef __RTRACK_H
+#define __RTRACK_H
+
+#include <linux/radio.h>
+
+void radiotrack_init(void);
+int rt_setvol(struct radio_device *dev, int vol);
+int rt_setband(struct radio_device *dev, int vol);
+int rt_setfreq(struct radio_device *dev, int vol);
+int rt_getsigstr(struct radio_device *dev);
+
+/* frequency encoding stuff... */
+/* we have to careful not to introduce fp stuff here */
+#define RTRACK_ENCODE(x) (((((x)*2)/5)-(40*88))+0xf6c)
+#define RTRACK_DECODE(x) (((((x)-0xf6c)+(40*88))*5)/2)
+/* we shouldn't actually need the decode macro (or the excessive bracketing :-) */
+
+#endif /* __RTRACK_H */
diff --git a/drivers/char/serial.c b/drivers/char/serial.c
index d9a8d5cea..7d21673aa 100644
--- a/drivers/char/serial.c
+++ b/drivers/char/serial.c
@@ -24,6 +24,9 @@
* 1/97: Extended dumb serial ports are a config option now.
* Saves 4k. Michael A. Griffith <grif@acm.org>
*
+ * 8/97: Fix bug in rs_set_termios with RTS
+ * Stanislav V. Voronyi <stas@uanet.kharkov.ua>
+ *
* This module exports the following rs232 io functions:
*
* int rs_init(void);
@@ -392,13 +395,6 @@ static inline int serial_paranoia_check(struct async_struct *info,
return 0;
}
-/*
- * This is used to figure out the divisor speeds and the timeouts
- */
-static int baud_table[] = {
- 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
- 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };
-
static inline unsigned int serial_in(struct async_struct *info, int offset)
{
#ifdef CONFIG_HUB6
@@ -634,7 +630,7 @@ static _INLINE_ void receive_chars(struct async_struct *info,
ignore_char:
*status = serial_inp(info, UART_LSR);
} while (*status & UART_LSR_DR);
- queue_task(&tty->flip.tqueue, &tq_timer);
+ tty_flip_buffer_push(tty);
}
static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
@@ -717,10 +713,10 @@ static _INLINE_ void check_modem_status(struct async_struct *info)
else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
(info->flags & ASYNC_CALLOUT_NOHUP))) {
#ifdef SERIAL_DEBUG_OPEN
- printk("scheduling hangup...");
+ printk("doing serial hangup...");
#endif
- queue_task(&info->tqueue_hangup,
- &tq_scheduler);
+ if (info->tty)
+ tty_hangup(info->tty);
}
}
if (info->flags & ASYNC_CTS_FLOW) {
@@ -1001,28 +997,6 @@ static void do_softint(void *private_)
}
/*
- * This routine is called from the scheduler tqueue when the interrupt
- * routine has signalled that a hangup has occurred. The path of
- * hangup processing is:
- *
- * serial interrupt routine -> (scheduler tqueue) ->
- * do_serial_hangup() -> tty->hangup() -> rs_hangup()
- *
- */
-static void do_serial_hangup(void *private_)
-{
- struct async_struct *info = (struct async_struct *) private_;
- struct tty_struct *tty;
-
- tty = info->tty;
- if (!tty)
- return;
-
- tty_hangup(tty);
-}
-
-
-/*
* This subroutine is called when the RS_TIMER goes off. It is used
* by the serial driver to handle ports that do not have an interrupt
* (irq=0). This doesn't work very well for 16450's, but gives barely
@@ -1155,7 +1129,6 @@ static int startup(struct async_struct * info)
unsigned short ICP;
#endif
-
page = get_free_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
@@ -1458,9 +1431,9 @@ static void shutdown(struct async_struct * info)
static void change_speed(struct async_struct *info)
{
unsigned short port;
- int quot = 0, baud_base;
+ int quot = 0, baud_base, baud;
unsigned cflag, cval, fcr = 0;
- int i, bits;
+ int bits;
unsigned long flags;
if (!info->tty || !info->tty->termios)
@@ -1494,37 +1467,20 @@ static void change_speed(struct async_struct *info)
#endif
/* Determine divisor based on baud rate */
- i = cflag & CBAUD;
- if (i & CBAUDEX) {
- i &= ~CBAUDEX;
- if (i < 1 || i > 4)
- info->tty->termios->c_cflag &= ~CBAUDEX;
- else
- i += 15;
- }
- if (i == 15) {
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- i += 1;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- i += 2;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
- i += 3;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
- i += 4;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
- quot = info->state->custom_divisor;
- }
+ baud = tty_get_baud_rate(info->tty);
baud_base = info->state->baud_base;
- if (!quot) {
- if (baud_table[i] == 134)
+ if (baud == 38400)
+ quot = info->state->custom_divisor;
+ else {
+ if (baud == 134)
/* Special case since 134 is really 134.5 */
quot = (2*baud_base / 269);
- else if (baud_table[i])
- quot = baud_base / baud_table[i];
- /* If the quotient is ever zero, default to 9600 bps */
- if (!quot)
- quot = baud_base / 9600;
+ else if (baud)
+ quot = baud_base / baud;
}
+ /* If the quotient is ever zero, default to 9600 bps */
+ if (!quot)
+ quot = baud_base / 9600;
info->quot = quot;
info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
info->timeout += HZ/50; /* Add .02 seconds of slop */
@@ -1936,8 +1892,17 @@ check_and_exit:
if (state->flags & ASYNC_INITIALIZED) {
if (((old_state.flags & ASYNC_SPD_MASK) !=
(state->flags & ASYNC_SPD_MASK)) ||
- (old_state.custom_divisor != state->custom_divisor))
+ (old_state.custom_divisor != state->custom_divisor)) {
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ info->tty->alt_speed = 57600;
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ info->tty->alt_speed = 115200;
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+ info->tty->alt_speed = 230400;
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+ info->tty->alt_speed = 460800;
change_speed(info);
+ }
} else
retval = startup(info);
return retval;
@@ -2068,51 +2033,27 @@ static int do_autoconfig(struct async_struct * info)
return 0;
}
-
-/*
- * This routine sends a break character out the serial port.
- */
-static void send_break( struct async_struct * info, int duration)
-{
- if (!info->port)
- return;
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + duration;
-#ifdef SERIAL_DEBUG_SEND_BREAK
- printk("rs_send_break(%d) jiff=%lu...", duration, jiffies);
-#endif
- cli();
- serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
- schedule();
- serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
- sti();
-#ifdef SERIAL_DEBUG_SEND_BREAK
- printk("done jiffies=%lu\n", jiffies);
-#endif
-}
-
/*
- * This routine sets the break condition on the serial port.
+ * rs_break() --- routine which turns the break handling on or off
*/
-static void begin_break(struct async_struct * info)
+static void rs_break(struct tty_struct *tty, int break_state)
{
- if (!info->port)
+ struct async_struct * info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_break"))
return;
- cli();
- serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
- sti();
-}
-/*
- * This routine clears the break condition on the serial port.
- */
-static void end_break(struct async_struct * info)
-{
if (!info->port)
return;
- cli();
- serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
- sti();
+ save_flags(flags); cli();
+ if (break_state == -1)
+ serial_out(info, UART_LCR,
+ serial_inp(info, UART_LCR) | UART_LCR_SBC);
+ else
+ serial_out(info, UART_LCR,
+ serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
+ restore_flags(flags);
}
/*
@@ -2279,7 +2220,6 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
{
int error;
struct async_struct * info = (struct async_struct *)tty->driver_data;
- int retval;
struct async_icount cprev, cnow; /* kernel counter temps */
struct serial_icounter_struct *p_cuser; /* user space */
@@ -2295,53 +2235,6 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
}
switch (cmd) {
- case TCSBRK: /* SVID version: non-zero arg --> no break */
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- if (current->signal & ~current->blocked)
- return -EINTR;
- if (!arg) {
- send_break(info, HZ/4); /* 1/4 second */
- if (current->signal & ~current->blocked)
- return -EINTR;
- }
- return 0;
- case TCSBRKP: /* support for POSIX tcsendbreak() */
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- if (current->signal & ~current->blocked)
- return -EINTR;
- send_break(info, arg ? arg*(HZ/10) : HZ/4);
- if (current->signal & ~current->blocked)
- return -EINTR;
- return 0;
- case TIOCSBRK:
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- begin_break(info);
- return 0;
- case TIOCCBRK:
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- end_break(info);
- return 0;
- case TIOCGSOFTCAR:
- return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
- case TIOCSSOFTCAR:
- error = get_user(arg, (unsigned int *) arg);
- if (error)
- return error;
- tty->termios->c_cflag =
- ((tty->termios->c_cflag & ~CLOCAL) |
- (arg ? CLOCAL : 0));
- return 0;
case TIOCMGET:
return get_modem_info(info, (unsigned int *) arg);
case TIOCMBIS:
@@ -2403,7 +2296,7 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
while (1) {
interruptible_sleep_on(&info->delta_msr_wait);
/* see if a signal did it */
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -ERESTARTSYS;
cli();
cnow = info->state->icount; /* atomic copy */
@@ -2472,8 +2365,8 @@ static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
if (!(old_termios->c_cflag & CBAUD) &&
(tty->termios->c_cflag & CBAUD)) {
info->MCR |= UART_MCR_DTR;
- if (!tty->hw_stopped ||
- !(tty->termios->c_cflag & CRTSCTS)) {
+ if (!(tty->termios->c_cflag & CRTSCTS) ||
+ !test_bit(TTY_THROTTLED, &tty->flags)) {
info->MCR |= UART_MCR_RTS;
}
cli();
@@ -2655,7 +2548,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
current->counter = 0; /* make us low-priority */
current->timeout = jiffies + char_time;
schedule();
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
break;
if (timeout && ((orig_jiffies + timeout) < jiffies))
break;
@@ -2710,10 +2603,8 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
if (info->flags & ASYNC_CLOSING)
interruptible_sleep_on(&info->close_wait);
#ifdef SERIAL_DO_RESTART
- if (info->flags & ASYNC_HUP_NOTIFY)
- return -EAGAIN;
- else
- return -ERESTARTSYS;
+ return ((info->flags & ASYNC_HUP_NOTIFY) ?
+ -EAGAIN : -ERESTARTSYS);
#else
return -EAGAIN;
#endif
@@ -2802,7 +2693,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
(do_clocal || (serial_in(info, UART_MSR) &
UART_MSR_DCD)))
break;
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
@@ -2851,8 +2742,6 @@ static int get_async_struct(int line, struct async_struct **ret_info)
info->line = line;
info->tqueue.routine = do_softint;
info->tqueue.data = info;
- info->tqueue_hangup.routine = do_serial_hangup;
- info->tqueue_hangup.data = info;
info->state = sstate;
if (sstate->info) {
kfree_s(info, sizeof(struct async_struct));
@@ -2900,7 +2789,22 @@ static int rs_open(struct tty_struct *tty, struct file * filp)
else
tmp_buf = (unsigned char *) page;
}
-
+
+ /*
+ * If the port is the middle of closing, bail out now
+ */
+ if (tty_hung_up_p(filp) ||
+ (info->flags & ASYNC_CLOSING)) {
+ if (info->flags & ASYNC_CLOSING)
+ interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+ return ((info->flags & ASYNC_HUP_NOTIFY) ?
+ -EAGAIN : -ERESTARTSYS);
+#else
+ return -EAGAIN;
+#endif
+ }
+
/*
* Start up serial port
*/
@@ -3294,7 +3198,6 @@ static void autoconfig(struct serial_state * state)
scratch = serial_in(info, UART_IIR) >> 5;
if (scratch == 7) {
serial_outp(info, UART_LCR, 0);
- serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
scratch = serial_in(info, UART_IIR) >> 5;
if (scratch == 6)
state->type = PORT_16750;
@@ -3424,6 +3327,7 @@ __initfunc(int rs_init(void))
serial_driver.stop = rs_stop;
serial_driver.start = rs_start;
serial_driver.hangup = rs_hangup;
+ serial_driver.break_ctl = rs_break;
serial_driver.wait_until_sent = rs_wait_until_sent;
serial_driver.read_proc = rs_read_proc;
diff --git a/drivers/char/softdog.c b/drivers/char/softdog.c
index 2eda6ef10..be63f08b4 100644
--- a/drivers/char/softdog.c
+++ b/drivers/char/softdog.c
@@ -113,7 +113,7 @@ static void softdog_ping(void)
return;
}
-static long softdog_write(struct inode *inode, struct file *file, const char *data, unsigned long len)
+static ssize_t softdog_write(struct file *file, const char *data, size_t len, loff_t *ppos)
{
/*
* Refresh the timer.
diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c
index 1ec03230b..8e378215f 100644
--- a/drivers/char/stallion.c
+++ b/drivers/char/stallion.c
@@ -889,7 +889,7 @@ static int stl_waitcarrier(stlport_t *portp, struct file *filp)
(doclocal || (portp->sigs & TIOCM_CD))) {
break;
}
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
rc = -ERESTARTSYS;
break;
}
diff --git a/drivers/char/tpqic02.c b/drivers/char/tpqic02.c
index e3032bb85..df4dff23f 100644
--- a/drivers/char/tpqic02.c
+++ b/drivers/char/tpqic02.c
@@ -1750,11 +1750,10 @@ static long long qic02_tape_lseek(struct file * file, long long offset, int orig
* request would return the EOF flag for the previous file.
*/
-static long qic02_tape_read(struct inode * inode, struct file * filp,
- char * buf, unsigned long count)
+static ssize_t qic02_tape_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
{
int err;
- kdev_t dev = inode->i_rdev;
+ kdev_t dev = filp->f_dentry->d_inode->i_rdev;
unsigned short flags = filp->f_flags;
unsigned long bytes_todo, bytes_done, total_bytes_done = 0;
int stat;
@@ -1925,7 +1924,7 @@ static long qic02_tape_read(struct inode * inode, struct file * filp,
{
status_bytes_rd = YES;
buf += bytes_done;
- filp->f_pos += bytes_done;
+ *ppos += bytes_done;
total_bytes_done += bytes_done;
count -= bytes_done;
}
@@ -1964,11 +1963,11 @@ static long qic02_tape_read(struct inode * inode, struct file * filp,
* tape device again. The driver will detect an exception status in (No Cartridge)
* and force a rewind. After that tar may continue writing.
*/
-static long qic02_tape_write(struct inode * inode, struct file * filp,
- const char * buf, unsigned long count)
+static ssize_t qic02_tape_write( struct file * filp, const char * buf,
+ size_t count, loff_t *ppos)
{
int err;
- kdev_t dev = inode->i_rdev;
+ kdev_t dev = filp->f_dentry->d_inode->i_rdev;
unsigned short flags = filp->f_flags;
unsigned long bytes_todo, bytes_done, total_bytes_done = 0;
@@ -2120,7 +2119,7 @@ static long qic02_tape_write(struct inode * inode, struct file * filp,
{
status_bytes_wr = YES;
buf += bytes_done;
- filp->f_pos += bytes_done;
+ *ppos += bytes_done;
total_bytes_done += bytes_done;
count -= bytes_done;
}
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 2148a06a6..a2f092b74 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -108,8 +108,8 @@ struct tty_struct * redirect = NULL;
static void initialize_tty_struct(struct tty_struct *tty);
-static long tty_read(struct inode *, struct file *, char *, unsigned long);
-static long tty_write(struct inode *, struct file *, const char *, unsigned long);
+static ssize_t tty_read(struct file *, char *, size_t, loff_t *);
+static ssize_t tty_write(struct file *, const char *, size_t, loff_t *);
static unsigned int tty_poll(struct file *, poll_table *);
static int tty_open(struct inode *, struct file *);
static int tty_release(struct inode *, struct file *);
@@ -304,15 +304,21 @@ int tty_check_change(struct tty_struct * tty)
return -ERESTARTSYS;
}
-static long hung_up_tty_read(struct inode * inode, struct file * file,
- char * buf, unsigned long count)
+static ssize_t hung_up_tty_read(struct file * file, char * buf,
+ size_t count, loff_t *ppos)
{
+ /* Can't seek (pread) on ttys. */
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
return 0;
}
-static long hung_up_tty_write(struct inode * inode,
- struct file * file, const char * buf, unsigned long count)
+static ssize_t hung_up_tty_write(struct file * file, const char * buf,
+ size_t count, loff_t *ppos)
{
+ /* Can't seek (pwrite) on ttys. */
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
return -EIO;
}
@@ -360,14 +366,18 @@ static struct file_operations hung_up_tty_fops = {
NULL /* hung_up_tty_fasync */
};
-void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
+void do_tty_hangup(void *data)
{
-
+ struct tty_struct *tty = (struct tty_struct *) data;
struct file * filp;
struct task_struct *p;
+ unsigned long flags;
if (!tty)
return;
+
+ save_flags(flags); cli();
+
check_tty_count(tty, "do_tty_hangup");
for (filp = inuse_filps; filp; filp = filp->f_next) {
if (filp->private_data != tty)
@@ -381,7 +391,7 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
if (filp->f_op != &tty_fops)
continue;
tty_fasync(filp, 0);
- filp->f_op = fops;
+ filp->f_op = &hung_up_tty_fops;
}
if (tty->ldisc.flush_buffer)
@@ -398,6 +408,8 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
* Shutdown the current line discipline, and reset it to
* N_TTY.
*/
+ if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS)
+ *tty->termios = tty->driver.init_termios;
if (tty->ldisc.num != ldiscs[N_TTY].num) {
if (tty->ldisc.close)
(tty->ldisc.close)(tty);
@@ -429,10 +441,9 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
tty->session = 0;
tty->pgrp = -1;
tty->ctrl_status = 0;
- if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS)
- *tty->termios = tty->driver.init_termios;
if (tty->driver.hangup)
(tty->driver.hangup)(tty);
+ restore_flags(flags);
}
void tty_hangup(struct tty_struct * tty)
@@ -440,7 +451,7 @@ void tty_hangup(struct tty_struct * tty)
#ifdef TTY_DEBUG_HANGUP
printk("%s hangup...\n", tty_name(tty));
#endif
- do_tty_hangup(tty, &hung_up_tty_fops);
+ queue_task(&tty->tq_hangup, &tq_timer);
}
void tty_vhangup(struct tty_struct * tty)
@@ -448,7 +459,7 @@ void tty_vhangup(struct tty_struct * tty)
#ifdef TTY_DEBUG_HANGUP
printk("%s vhangup...\n", tty_name(tty));
#endif
- do_tty_hangup(tty, &hung_up_tty_fops);
+ do_tty_hangup((void *) tty);
}
int tty_hung_up_p(struct file * filp)
@@ -543,13 +554,19 @@ void start_tty(struct tty_struct *tty)
wake_up_interruptible(&tty->write_wait);
}
-static long tty_read(struct inode * inode, struct file * file,
- char * buf, unsigned long count)
+static ssize_t tty_read(struct file * file, char * buf, size_t count,
+ loff_t *ppos)
{
int i;
struct tty_struct * tty;
+ struct inode *inode;
+
+ /* Can't seek (pread) on ttys. */
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
tty = (struct tty_struct *)file->private_data;
+ inode = file->f_dentry->d_inode;
if (tty_paranoia_check(tty, inode->i_rdev, "tty_read"))
return -EIO;
if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
@@ -584,15 +601,14 @@ static long tty_read(struct inode * inode, struct file * file,
* Split writes up in sane blocksizes to avoid
* denial-of-service type attacks
*/
-static inline int do_tty_write(
- int (*write)(struct tty_struct *, struct file *, const unsigned char *, unsigned int),
- struct inode *inode,
+static inline ssize_t do_tty_write(
+ ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
struct tty_struct *tty,
struct file *file,
const unsigned char *buf,
- unsigned int count)
+ size_t count)
{
- int ret = 0, written = 0;
+ ssize_t ret = 0, written = 0;
for (;;) {
unsigned long size = PAGE_SIZE*2;
@@ -607,25 +623,31 @@ static inline int do_tty_write(
if (!count)
break;
ret = -ERESTARTSYS;
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
break;
if (need_resched)
schedule();
}
if (written) {
- inode->i_mtime = CURRENT_TIME;
+ file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
ret = written;
}
return ret;
}
-static long tty_write(struct inode * inode, struct file * file,
- const char * buf, unsigned long count)
+static ssize_t tty_write(struct file * file, const char * buf, size_t count,
+ loff_t *ppos)
{
int is_console;
struct tty_struct * tty;
+ struct inode *inode;
+ /* Can't seek (pwrite) on ttys. */
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
+
+ inode = file->f_dentry->d_inode;
is_console = (inode->i_rdev == CONSOLE_DEV);
if (is_console && redirect)
@@ -649,22 +671,23 @@ static long tty_write(struct inode * inode, struct file * file,
#endif
if (!tty->ldisc.write)
return -EIO;
- return do_tty_write(tty->ldisc.write,
- inode, tty, file,
- (const unsigned char *)buf,
- (unsigned int)count);
+ return do_tty_write(tty->ldisc.write, tty, file,
+ (const unsigned char *)buf, count);
}
/* Semaphore to protect creating and releasing a tty */
static struct semaphore tty_sem = MUTEX;
+
static void down_tty_sem(int index)
{
down(&tty_sem);
}
+
static void up_tty_sem(int index)
{
up(&tty_sem);
}
+
static void release_mem(struct tty_struct *tty, int idx);
/*
@@ -1117,27 +1140,9 @@ static void release_dev(struct file * filp)
}
/*
- * Make sure that the tty's task queue isn't activated. If it
- * is, take it out of the linked list. The tqueue isn't used by
- * pty's, so skip the test for them.
+ * Make sure that the tty's task queue isn't activated.
*/
- if (tty->driver.type != TTY_DRIVER_TYPE_PTY) {
- spin_lock_irq(&tqueue_lock);
- if (tty->flip.tqueue.sync) {
- struct tq_struct *tq, *prev;
-
- for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
- if (tq == &tty->flip.tqueue) {
- if (prev)
- prev->next = tq->next;
- else
- tq_timer = tq->next;
- break;
- }
- }
- }
- spin_unlock_irq(&tqueue_lock);
- }
+ run_task_queue(&tq_timer);
/*
* The release_mem function takes care of the details of clearing
@@ -1217,7 +1222,7 @@ retry_open:
release_dev(filp);
if (retval != -ERESTARTSYS)
return retval;
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return retval;
schedule();
/*
@@ -1472,15 +1477,26 @@ static int tiocsetd(struct tty_struct *tty, int *arg)
{
int retval, ldisc;
- retval = tty_check_change(tty);
- if (retval)
- return retval;
retval = get_user(ldisc, arg);
if (retval)
return retval;
return tty_set_ldisc(tty, ldisc);
}
+static int send_break(struct tty_struct *tty, int duration)
+{
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + duration;
+
+ tty->driver.break_ctl(tty, -1);
+ if (!signal_pending(current))
+ schedule();
+ tty->driver.break_ctl(tty, 0);
+ if (signal_pending(current))
+ return -EINTR;
+ return 0;
+}
+
/*
* Split this up, as gcc can choke on it otherwise..
*/
@@ -1488,6 +1504,7 @@ static int tty_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
struct tty_struct *tty, *real_tty;
+ int retval;
tty = (struct tty_struct *)file->private_data;
if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl"))
@@ -1498,6 +1515,46 @@ static int tty_ioctl(struct inode * inode, struct file * file,
tty->driver.subtype == PTY_TYPE_MASTER)
real_tty = tty->link;
+ /*
+ * Break handling by driver
+ */
+ if (!tty->driver.break_ctl) {
+ switch(cmd) {
+ case TIOCSBRK:
+ case TIOCCBRK:
+ return tty->driver.ioctl(tty, file, cmd, arg);
+
+ /* These two ioctl's always return success; even if */
+ /* the driver doesn't support them. */
+ case TCSBRK:
+ case TCSBRKP:
+ retval = tty->driver.ioctl(tty, file, cmd, arg);
+ if (retval == -ENOIOCTLCMD)
+ retval = 0;
+ return retval;
+ }
+ }
+
+ /*
+ * Factor out some common prep work
+ */
+ switch (cmd) {
+ case TIOCSETD:
+ case TIOCSBRK:
+ case TIOCCBRK:
+ case TCSBRK:
+ case TCSBRKP:
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ if (cmd != TIOCCBRK) {
+ tty_wait_until_sent(tty, 0);
+ if (signal_pending(current))
+ return -EINTR;
+ }
+ break;
+ }
+
switch (cmd) {
case TIOCSTI:
return tiocsti(tty, (char *)arg);
@@ -1538,6 +1595,28 @@ static int tty_ioctl(struct inode * inode, struct file * file,
#endif
case TIOCTTYGSTRUCT:
return tiocttygstruct(tty, (struct tty_struct *) arg);
+
+ /*
+ * Break handling
+ */
+ case TIOCSBRK: /* Turn break on, unconditionally */
+ tty->driver.break_ctl(tty, -1);
+ return 0;
+
+ case TIOCCBRK: /* Turn break off, unconditionally */
+ tty->driver.break_ctl(tty, 0);
+ return 0;
+ case TCSBRK: /* SVID version: non-zero arg --> no break */
+ /*
+ * XXX is the above comment correct, or the
+ * code below correct? Is this ioctl used at
+ * all by anyone?
+ */
+ if (!arg)
+ return send_break(tty, HZ/4);
+ return 0;
+ case TCSBRKP: /* support for POSIX tcsendbreak() */
+ return send_break(tty, arg ? arg*(HZ/10) : HZ/4);
}
if (tty->driver.ioctl) {
int retval = (tty->driver.ioctl)(tty, file, cmd, arg);
@@ -1612,13 +1691,14 @@ static void flush_to_ldisc(void *private_)
unsigned char *cp;
char *fp;
int count;
+ unsigned long flags;
if (tty->flip.buf_num) {
cp = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
fp = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
tty->flip.buf_num = 0;
- cli();
+ save_flags(flags); cli();
tty->flip.char_buf_ptr = tty->flip.char_buf;
tty->flip.flag_buf_ptr = tty->flip.flag_buf;
} else {
@@ -1626,22 +1706,57 @@ static void flush_to_ldisc(void *private_)
fp = tty->flip.flag_buf;
tty->flip.buf_num = 1;
- cli();
+ save_flags(flags); cli();
tty->flip.char_buf_ptr = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
tty->flip.flag_buf_ptr = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
}
count = tty->flip.count;
tty->flip.count = 0;
- sti();
+ restore_flags(flags);
-#if 0
- if (count > tty->max_flip_cnt)
- tty->max_flip_cnt = count;
-#endif
tty->ldisc.receive_buf(tty, cp, fp, count);
}
/*
+ * Routine which returns the baud rate of the tty
+ */
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int baud_table[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+ 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };
+
+int tty_get_baud_rate(struct tty_struct *tty)
+{
+ unsigned int cflag, i;
+
+ cflag = tty->termios->c_cflag;
+
+ i = cflag & CBAUD;
+ if (i & CBAUDEX) {
+ i &= ~CBAUDEX;
+ if (i < 1 || i > 4)
+ tty->termios->c_cflag &= ~CBAUDEX;
+ else
+ i += 15;
+ }
+ if (i==15 && tty->alt_speed)
+ return(tty->alt_speed);
+
+ return baud_table[i];
+}
+
+void tty_flip_buffer_push(struct tty_struct *tty)
+{
+ if (tty->low_latency)
+ flush_to_ldisc((void *) tty);
+ else
+ queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+/*
* This subroutine initializes a tty structure.
*/
static void initialize_tty_struct(struct tty_struct *tty)
@@ -1655,6 +1770,8 @@ static void initialize_tty_struct(struct tty_struct *tty)
tty->flip.tqueue.routine = flush_to_ldisc;
tty->flip.tqueue.data = tty;
tty->flip.pty_sem = MUTEX;
+ tty->tq_hangup.routine = do_tty_hangup;
+ tty->tq_hangup.data = tty;
}
/*
diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c
index 02f42ec87..21c09384e 100644
--- a/drivers/char/tty_ioctl.c
+++ b/drivers/char/tty_ioctl.c
@@ -60,7 +60,7 @@ void tty_wait_until_sent(struct tty_struct * tty, int timeout)
printk("waiting %s...(%d)\n", tty_name(tty), tty->driver.chars_in_buffer(tty));
#endif
current->state = TASK_INTERRUPTIBLE;
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
goto stop_waiting;
if (!tty->driver.chars_in_buffer(tty))
break;
@@ -170,7 +170,7 @@ static int set_termios(struct tty_struct * tty, unsigned long arg, int opt)
if (opt & TERMIOS_WAIT) {
tty_wait_until_sent(tty, 0);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -EINTR;
}
@@ -509,18 +509,15 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file,
tty->packet = 0;
return 0;
}
- /* These two ioctl's always return success; even if */
- /* the driver doesn't support them. */
- case TCSBRK: case TCSBRKP:
- retval = tty_check_change(tty);
+ case TIOCGSOFTCAR:
+ return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
+ case TIOCSSOFTCAR:
+ retval = get_user(arg, (unsigned int *) arg);
if (retval)
return retval;
- tty_wait_until_sent(tty, 0);
- if (current->signal & ~current->blocked)
- return -EINTR;
- if (!tty->driver.ioctl)
- return 0;
- tty->driver.ioctl(tty, file, cmd, arg);
+ tty->termios->c_cflag =
+ ((tty->termios->c_cflag & ~CLOCAL) |
+ (arg ? CLOCAL : 0));
return 0;
default:
return -ENOIOCTLCMD;
diff --git a/drivers/char/tuner.h b/drivers/char/tuner.h
new file mode 100644
index 000000000..5b3cf012f
--- /dev/null
+++ b/drivers/char/tuner.h
@@ -0,0 +1,59 @@
+/*
+ tuner.h - definition for different tuners
+
+ Copyright (C) 1997 Markus Schroeder (schroedm@uni-duesseldorf.de)
+ minor modifications by Ralph Metzler (rjkm@thp.uni-koeln.de)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _TUNER_H
+#define _TUNER_H
+
+#define TUNER_TEMIC_PAL 0 /* Miro Gpio Coding -1 */
+#define TUNER_PHILIPS_PAL_I 1
+#define TUNER_PHILIPS_NTSC 2
+#define TUNER_PHILIPS_SECAM 3
+#define TUNER_ABSENT 4
+#define TUNER_PHILIPS_PAL 5
+#define TUNER_TEMIC_NTSC 6
+#define TUNER_TEMIC_PAL_I 7
+
+#define NOTUNER 0
+#define PAL 1
+#define PAL_I 2
+#define NTSC 3
+#define SECAM 4
+
+#define NoTuner 0
+#define Philips 1
+#define TEMIC 2
+#define Sony 3
+
+struct tunertype {
+ char *name;
+ unchar Vendor;
+ unchar Type;
+
+ ushort thresh1; /* frequency Range for UHF,VHF-L, VHF_H */
+ ushort thresh2;
+ unchar VHF_L;
+ unchar VHF_H;
+ unchar UHF;
+ unchar config;
+ unchar I2C;
+};
+#endif
+
diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c
index 9d2ecda3a..77fb908fe 100644
--- a/drivers/char/vc_screen.c
+++ b/drivers/char/vc_screen.c
@@ -103,12 +103,13 @@ static long long vcs_lseek(struct file *file, long long offset, int orig)
}
#define RETURN( x ) { enable_bh( CONSOLE_BH ); return x; }
-static long
-vcs_read(struct inode *inode, struct file *file, char *buf, unsigned long count)
+static ssize_t
+vcs_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
- int p = file->f_pos;
+ struct inode *inode = file->f_dentry->d_inode;
unsigned int currcons = MINOR(inode->i_rdev);
- int viewed, attr, size, read;
+ long p = *ppos;
+ long viewed, attr, size, read;
char *buf0;
unsigned short *org = NULL;
@@ -160,16 +161,17 @@ vcs_read(struct inode *inode, struct file *file, char *buf, unsigned long count)
put_user(func_scr_readw(org) & 0xff, buf++);
}
read = buf - buf0;
- file->f_pos += read;
+ *ppos += read;
RETURN( read );
}
-static long
-vcs_write(struct inode *inode, struct file *file, const char *buf, unsigned long count)
+static ssize_t
+vcs_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
- int p = file->f_pos;
+ struct inode *inode = file->f_dentry->d_inode;
unsigned int currcons = MINOR(inode->i_rdev);
- int viewed, attr, size, written;
+ long p = *ppos;
+ long viewed, attr, size, written;
const char *buf0;
unsigned short *org = NULL;
@@ -242,7 +244,7 @@ vcs_write(struct inode *inode, struct file *file, const char *buf, unsigned long
update_screen(currcons);
#endif
written = buf - buf0;
- file->f_pos += written;
+ *ppos += written;
RETURN( written );
}
diff --git a/drivers/char/videodev.c b/drivers/char/videodev.c
new file mode 100644
index 000000000..71e88f04c
--- /dev/null
+++ b/drivers/char/videodev.c
@@ -0,0 +1,264 @@
+/*
+ * Video capture interface for Linux
+ *
+ * A generic video device interface for the LINUX operating system
+ * using a set of device structures/vectors for low level operations.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Author: Alan Cox, <alan@cymru.net>
+ *
+ * Fixes:
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/videodev.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+
+#define VIDEO_NUM_DEVICES 256
+
+/*
+ * Active devices
+ */
+
+static struct video_device *video_device[VIDEO_NUM_DEVICES];
+
+/*
+ * Initialiser list
+ */
+
+struct video_init
+{
+ char *name;
+ int (*init)(struct video_init *);
+};
+
+extern int init_bttv_cards(struct video_init *);
+
+static struct video_init video_init_list[]={
+#ifdef CONFIG_VIDEO_BT848
+ {"bttv", init_bttv_cards},
+#endif
+#ifdef CONFIG_VIDEO_BWQCAM
+ {"bttv", init_bw_qcams},
+#endif
+#ifdef CONFIG_VIDEO_PMS
+ {"bttv", init_pms_cards},
+#endif
+ {"end", NULL}
+};
+
+
+/*
+ * Read will do some smarts later on. Buffer pin etc.
+ */
+
+static ssize_t video_read(struct file *file,
+ char *buf, size_t count, loff_t *ppos)
+{
+ int err;
+ struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)];
+ return vfl->read(vfl, buf, count, file->f_flags&O_NONBLOCK);
+}
+
+/*
+ * Write for now does nothing. No reason it shouldnt do overlay setting
+ * for some boards I guess..
+ */
+
+static ssize_t video_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ int err;
+ struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)];
+ return vfl->write(vfl, buf, count, file->f_flags&O_NONBLOCK);
+}
+
+/*
+ * Open a video device.
+ */
+
+static int video_open(struct inode *inode, struct file *file)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ int err;
+ struct video_device *vfl;
+
+ if(minor>=VIDEO_NUM_DEVICES)
+ return -ENODEV;
+
+ vfl=video_device[minor];
+ if(vfl==NULL)
+ return -ENODEV;
+ if(vfl->busy)
+ return -EBUSY;
+ vfl->busy=1; /* In case vfl->open sleeps */
+
+ if(vfl->open)
+ {
+ err=vfl->open(vfl,0); /* Tell the device it is open */
+ if(err)
+ {
+ vfl->busy=0;
+ return err;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Last close of a video for Linux device
+ */
+
+static int video_release(struct inode *inode, struct file *file)
+{
+ struct video_device *vfl=video_device[MINOR(inode->i_rdev)];
+ if(vfl->close)
+ vfl->close(vfl);
+ vfl->busy=0;
+ return 0;
+}
+
+/*
+ * Question: Should we be able to capture and then seek around the
+ * image ?
+ */
+
+static long long video_lseek(struct file * file,
+ long long offset, int origin)
+{
+ return -ESPIPE;
+}
+
+
+static int video_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vfl=video_device[MINOR(inode->i_rdev)];
+ int err=vfl->ioctl(vfl, cmd, (void *)arg);
+
+ if(err!=-ENOIOCTLCMD)
+ return err;
+
+ switch(cmd)
+ {
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * We need to do MMAP support
+ */
+
+/*
+ * Video For Linux device drivers request registration here.
+ */
+
+int video_register_device(struct video_device *vfd)
+{
+ int i=0;
+ int base=0;
+ int err;
+
+ for(i=base;i<base+VIDEO_NUM_DEVICES;i++)
+ {
+ if(video_device[i]==NULL)
+ {
+ video_device[i]=vfd;
+ vfd->minor=i;
+ /* The init call may sleep so we book the slot out
+ then call */
+ MOD_INC_USE_COUNT;
+ err=vfd->initialize(vfd);
+ if(err<0)
+ {
+ video_device[i]=NULL;
+ MOD_DEC_USE_COUNT;
+ return err;
+ }
+ return 0;
+ }
+ }
+ return -ENFILE;
+}
+
+/*
+ * Unregister an unused video for linux device
+ */
+
+void video_unregister_device(struct video_device *vfd)
+{
+ if(video_device[vfd->minor]!=vfd)
+ panic("vfd: bad unregister");
+ video_device[vfd->minor]=NULL;
+ MOD_DEC_USE_COUNT;
+}
+
+
+static struct file_operations video_fops=
+{
+ video_lseek,
+ video_read,
+ video_write,
+ NULL, /* readdir */
+ NULL, /* poll */
+ video_ioctl,
+ NULL, /* mmap */
+ video_open,
+ video_release
+};
+
+/*
+ * Initialise video for linux
+ */
+
+int videodev_init(void)
+{
+ struct video_init *vfli = video_init_list;
+
+ printk(KERN_INFO "Linux video capture interface: v0.01 ALPHA\n");
+ if(register_chrdev(VIDEO_MAJOR,"video_capture", &video_fops))
+ {
+ printk("video_dev: unable to get major %d\n", VIDEO_MAJOR);
+ return -EIO;
+ }
+
+ /*
+ * Init kernel installed video drivers
+ */
+
+ while(vfli->init!=NULL)
+ {
+ vfli->init(vfli);
+ vfli++;
+ }
+ return 0;
+}
+
+
+int init_module(void)
+{
+ return videodev_init();
+}
+
+void cleanup_module(void)
+{
+ unregister_chrdev(VIDEO_MAJOR, "video_capture");
+}
+
+EXPORT_SYMBOL(video_register_device);
+EXPORT_SYMBOL(video_unregister_device);
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index 971426bf1..6ba0c687d 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -1094,7 +1094,7 @@ int vt_waitactive(int vt)
if (vt == fg_console)
break;
retval = -EINTR;
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
break;
schedule();
}
diff --git a/drivers/char/wdt.c b/drivers/char/wdt.c
index cbe763975..37f444e29 100644
--- a/drivers/char/wdt.c
+++ b/drivers/char/wdt.c
@@ -169,7 +169,7 @@ static void wdt_ping(void)
outb_p(0, WDT_DC);
}
-static long wdt_write(struct inode *inode, struct file *file, const char *buf, unsigned long count)
+static ssize_t wdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
if(count)
{
@@ -183,13 +183,13 @@ static long wdt_write(struct inode *inode, struct file *file, const char *buf, u
* Read reports the temperature in farenheit
*/
-static long wdt_read(struct inode *inode, struct file *file, char *buf, unsigned long count)
+static ssize_t wdt_read(struct file *file, char *buf, size_t count, loff_t *ptr)
{
unsigned short c=inb_p(WDT_RT);
unsigned char cp;
int err;
- switch(MINOR(inode->i_rdev))
+ switch(MINOR(file->f_dentry->d_inode->i_rdev))
{
case TEMP_MINOR:
err=verify_area(VERIFY_WRITE, buf, 1);
diff --git a/drivers/isdn/avmb1/b1capi.c b/drivers/isdn/avmb1/b1capi.c
index b379d8f7d..a9f06c880 100644
--- a/drivers/isdn/avmb1/b1capi.c
+++ b/drivers/isdn/avmb1/b1capi.c
@@ -760,7 +760,7 @@ static int capi_manufacturer(unsigned int cmd, void *data)
current->state = TASK_INTERRUPTIBLE;
schedule();
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -EINTR;
}
return 0;
diff --git a/drivers/isdn/avmb1/capi.c b/drivers/isdn/avmb1/capi.c
index b21a9f868..aed5ca0f5 100644
--- a/drivers/isdn/avmb1/capi.c
+++ b/drivers/isdn/avmb1/capi.c
@@ -45,12 +45,11 @@
#include <linux/timer.h>
#include <linux/wait.h>
#include <linux/skbuff.h>
-#if (LINUX_VERSION_CODE >= 0x020117)
-#include <asm/poll.h>
-#endif
#include <linux/capi.h>
#include <linux/kernelcapi.h>
+#include <asm/poll.h>
+
#include "compat.h"
#include "capiutil.h"
#include "capicmd.h"
@@ -96,29 +95,16 @@ static void capi_signal(__u16 applid, __u32 minor)
/* -------- file_operations ----------------------------------------- */
-#if LINUX_VERSION_CODE < 0x020100
-static int capi_lseek(struct inode *inode, struct file *file,
- off_t offset, int origin)
+static loff_t capi_llseek(struct file *file, loff_t offset, int origin)
{
return -ESPIPE;
}
-#else
-static long long capi_llseek(struct inode *inode, struct file *file,
- long long offset, int origin)
-{
- return -ESPIPE;
-}
-#endif
-#if LINUX_VERSION_CODE < 0x020100
-static int capi_read(struct inode *inode, struct file *file,
- char *buf, int count)
-#else
-static long capi_read(struct inode *inode, struct file *file,
- char *buf, unsigned long count)
-#endif
+static ssize_t capi_read(struct file *file,
+ char *buf, size_t count,
+ loff_t *off)
{
- unsigned int minor = MINOR(inode->i_rdev);
+ unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
struct capidev *cdev;
struct sk_buff *skb;
int retval;
@@ -138,7 +124,7 @@ static long capi_read(struct inode *inode, struct file *file,
interruptible_sleep_on(&cdev->recv_wait);
if ((skb = skb_dequeue(&cdev->recv_queue)) != 0)
break;
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
break;
}
if (skb == 0)
@@ -164,15 +150,11 @@ static long capi_read(struct inode *inode, struct file *file,
return copied;
}
-#if LINUX_VERSION_CODE < 0x020100
-static int capi_write(struct inode *inode, struct file *file,
- const char *buf, int count)
-#else
-static long capi_write(struct inode *inode, struct file *file,
- const char *buf, unsigned long count)
-#endif
+static ssize_t capi_write(struct file *file,
+ const char *buf, size_t count,
+ loff_t *off)
{
- unsigned int minor = MINOR(inode->i_rdev);
+ unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
struct capidev *cdev;
struct sk_buff *skb;
int retval;
@@ -215,40 +197,6 @@ static long capi_write(struct inode *inode, struct file *file,
return count;
}
-#if (LINUX_VERSION_CODE < 0x020117)
-static int capi_select(struct inode *inode, struct file *file,
- int sel_type, select_table * wait)
-{
- unsigned int minor = MINOR(inode->i_rdev);
- struct capidev *cdev;
-
- if (!minor || minor > CAPI_MAXMINOR || !capidevs[minor].is_registered)
- return -ENODEV;
-
- cdev = &capidevs[minor];
-
- switch (sel_type) {
- case SEL_IN:
- if (!skb_queue_empty(&cdev->recv_queue))
- return 1;
- /* fall througth */
- case SEL_EX:
- /* error conditions ? */
-
- select_wait(&cdev->recv_wait, wait);
- return 0;
- case SEL_OUT:
- /*
- if (!queue_full())
- return 1;
- select_wait(&cdev->send_wait, wait);
- return 0;
- */
- return 1;
- }
- return 1;
-}
-#else
static unsigned int
capi_poll(struct file *file, poll_table * wait)
{
@@ -266,7 +214,6 @@ capi_poll(struct file *file, poll_table * wait)
mask |= POLLIN | POLLRDNORM;
return mask;
}
-#endif
static int capi_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
@@ -480,19 +427,11 @@ capi_release(struct inode *inode, struct file *file)
static struct file_operations capi_fops =
{
-#if LINUX_VERSION_CODE < 0x020100
- capi_lseek,
-#else
capi_llseek,
-#endif
capi_read,
capi_write,
NULL, /* capi_readdir */
-#if (LINUX_VERSION_CODE < 0x020117)
- capi_select,
-#else
capi_poll,
-#endif
capi_ioctl,
NULL, /* capi_mmap */
capi_open,
diff --git a/drivers/isdn/hisax/avm_a1.c b/drivers/isdn/hisax/avm_a1.c
index a17549904..8b5bea45d 100644
--- a/drivers/isdn/hisax/avm_a1.c
+++ b/drivers/isdn/hisax/avm_a1.c
@@ -558,7 +558,7 @@ avm_a1_interrupt(int intno, void *dev_id, struct pt_regs *regs)
u_char val, sval, stat = 0;
char tmp[32];
- sp = (struct IsdnCardState *) irq2dev_map[intno];
+ sp = (struct IsdnCardState *) dev_id;
if (!sp) {
printk(KERN_WARNING "AVM A1: Spurious interrupt!\n");
@@ -798,8 +798,7 @@ initavm_a1(struct IsdnCardState *sp)
printk(KERN_WARNING
"AVM A1: IRQ(%d) getting no interrupts during init\n",
sp->irq);
- irq2dev_map[sp->irq] = NULL;
- free_irq(sp->irq, NULL);
+ free_irq(sp->irq, sp);
return (0);
}
}
diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c
index 9c402f492..28db33235 100644
--- a/drivers/isdn/hisax/elsa.c
+++ b/drivers/isdn/hisax/elsa.c
@@ -887,7 +887,7 @@ elsa_interrupt(int intno, void *dev_id, struct pt_regs *regs)
struct IsdnCardState *sp;
u_char val;
- sp = (struct IsdnCardState *) irq2dev_map[intno];
+ sp = (struct IsdnCardState *) dev_id;
if (!sp) {
printk(KERN_WARNING "Elsa: Spurious interrupt!\n");
@@ -1219,8 +1219,7 @@ initelsa(struct IsdnCardState *sp)
"Elsa: IRQ(%d) getting no interrupts during init %d\n",
sp->irq, 4 - cnt);
if (cnt == 1) {
- irq2dev_map[sp->irq] = NULL;
- free_irq(sp->irq, NULL);
+ free_irq(sp->irq, sp);
return (0);
} else {
reset_elsa(sp);
diff --git a/drivers/isdn/hisax/isdnl1.c b/drivers/isdn/hisax/isdnl1.c
index 0dc0bb520..291f846ae 100644
--- a/drivers/isdn/hisax/isdnl1.c
+++ b/drivers/isdn/hisax/isdnl1.c
@@ -766,13 +766,12 @@ get_irq(int cardnr, void *routine)
save_flags(flags);
cli();
if (request_irq(card->sp->irq, routine,
- I4L_IRQ_FLAG, "HiSax", NULL)) {
+ I4L_IRQ_FLAG, "HiSax", card->sp)) {
printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n",
card->sp->irq);
restore_flags(flags);
return (0);
}
- irq2dev_map[card->sp->irq] = (void *) card->sp;
restore_flags(flags);
return (1);
}
@@ -782,8 +781,7 @@ release_irq(int cardnr)
{
struct IsdnCard *card = cards + cardnr;
- irq2dev_map[card->sp->irq] = NULL;
- free_irq(card->sp->irq, NULL);
+ free_irq(card->sp->irq, card->sp);
}
void
diff --git a/drivers/isdn/hisax/ix1_micro.c b/drivers/isdn/hisax/ix1_micro.c
index ac98a4b4f..707635261 100644
--- a/drivers/isdn/hisax/ix1_micro.c
+++ b/drivers/isdn/hisax/ix1_micro.c
@@ -644,7 +644,7 @@ ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs)
struct IsdnCardState *sp;
u_char val, stat = 0;
- sp = (struct IsdnCardState *) irq2dev_map[intno];
+ sp = (struct IsdnCardState *) dev_id;
if (!sp) {
printk(KERN_WARNING "Teles: Spurious interrupt!\n");
@@ -867,8 +867,7 @@ initix1micro(struct IsdnCardState *sp)
printk(KERN_WARNING
"ix1-Micro: IRQ(%d) getting no interrupts during init\n",
sp->irq);
- irq2dev_map[sp->irq] = NULL;
- free_irq(sp->irq, NULL);
+ free_irq(sp->irq, sp);
return (0);
}
}
diff --git a/drivers/isdn/hisax/teles0.c b/drivers/isdn/hisax/teles0.c
index 0c21e5228..0560eff88 100644
--- a/drivers/isdn/hisax/teles0.c
+++ b/drivers/isdn/hisax/teles0.c
@@ -605,7 +605,7 @@ telesS0_interrupt(int intno, void *dev_id, struct pt_regs *regs)
struct IsdnCardState *sp;
u_char val, stat = 0;
- sp = (struct IsdnCardState *) irq2dev_map[intno];
+ sp = (struct IsdnCardState *) dev_id;
if (!sp) {
printk(KERN_WARNING "Teles0: Spurious interrupt!\n");
@@ -828,8 +828,7 @@ initteles0(struct IsdnCardState *sp)
printk(KERN_WARNING
"Teles0: IRQ(%d) getting no interrupts during init\n",
sp->irq);
- irq2dev_map[sp->irq] = NULL;
- free_irq(sp->irq, NULL);
+ free_irq(sp->irq, sp);
return (0);
}
}
diff --git a/drivers/isdn/hisax/teles3.c b/drivers/isdn/hisax/teles3.c
index 357f21e7e..c83a00958 100644
--- a/drivers/isdn/hisax/teles3.c
+++ b/drivers/isdn/hisax/teles3.c
@@ -585,7 +585,7 @@ teles3_interrupt(int intno, void *dev_id, struct pt_regs *regs)
u_char val, stat = 0;
int count = 0;
- sp = (struct IsdnCardState *) irq2dev_map[intno];
+ sp = (struct IsdnCardState *) dev_id;
if (!sp) {
printk(KERN_WARNING "Teles: Spurious interrupt!\n");
@@ -829,8 +829,7 @@ initteles3(struct IsdnCardState *sp)
printk(KERN_WARNING
"Teles3: IRQ(%d) getting no interrupts during init\n",
sp->irq);
- irq2dev_map[sp->irq] = NULL;
- free_irq(sp->irq, NULL);
+ free_irq(sp->irq, sp);
return (0);
}
}
diff --git a/drivers/isdn/isdn_common.c b/drivers/isdn/isdn_common.c
index 89271da9c..5402c5b8d 100644
--- a/drivers/isdn/isdn_common.c
+++ b/drivers/isdn/isdn_common.c
@@ -849,9 +849,9 @@ isdn_info_update(void)
}
static RWTYPE
-isdn_read(struct inode *inode, struct file *file, char *buf, RWARG count)
+isdn_read(struct file *file, char *buf, RWARG count, loff_t *off)
{
- uint minor = MINOR(inode->i_rdev);
+ uint minor = MINOR(file->f_dentry->d_inode->i_rdev);
int len = 0;
ulong flags;
int drvidx;
@@ -869,7 +869,7 @@ isdn_read(struct inode *inode, struct file *file, char *buf, RWARG count)
if ((len = strlen(p)) <= count) {
if (copy_to_user(buf, p, len))
return -EFAULT;
- file->f_pos += len;
+ *off += len;
return len;
}
return 0;
@@ -886,7 +886,7 @@ isdn_read(struct inode *inode, struct file *file, char *buf, RWARG count)
save_flags(flags);
cli();
len = isdn_readbchan(drvidx, chidx, buf, 0, count, 1);
- file->f_pos += len;
+ *off += len;
restore_flags(flags);
return len;
}
@@ -912,7 +912,7 @@ isdn_read(struct inode *inode, struct file *file, char *buf, RWARG count)
else
dev->drv[drvidx]->stavail = 0;
restore_flags(flags);
- file->f_pos += len;
+ *off += len;
return len;
}
#ifdef CONFIG_ISDN_PPP
@@ -928,9 +928,9 @@ static LSTYPE isdn_lseek(struct file *file, LSARG offset, int orig)
}
static RWTYPE
-isdn_write(struct inode *inode, struct file *file, const char *buf, RWARG count)
+isdn_write(struct file *file, const char *buf, RWARG count, loff_t * off)
{
- uint minor = MINOR(inode->i_rdev);
+ uint minor = MINOR(file->f_dentry->d_inode->i_rdev);
int drvidx;
int chidx;
diff --git a/drivers/isdn/isdn_tty.c b/drivers/isdn/isdn_tty.c
index a5f31eaf3..4b21f83f9 100644
--- a/drivers/isdn/isdn_tty.c
+++ b/drivers/isdn/isdn_tty.c
@@ -1576,7 +1576,7 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info *
(do_clocal || (info->msr & UART_MSR_DCD))) {
break;
}
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c
index 6e1f5fadf..b6aad25ab 100644
--- a/drivers/macintosh/adb.c
+++ b/drivers/macintosh/adb.c
@@ -36,7 +36,7 @@ static int adb_wait_reply(struct adbdev_state *state, struct file *file)
ret = -EAGAIN;
break;
}
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
diff --git a/drivers/macintosh/macserial.c b/drivers/macintosh/macserial.c
index 724950cfe..967f13d86 100644
--- a/drivers/macintosh/macserial.c
+++ b/drivers/macintosh/macserial.c
@@ -1562,7 +1562,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
!(info->flags & ZILOG_CLOSING) &&
(do_clocal || (read_zsreg(info->zs_channel, 0) & DCD)))
break;
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
diff --git a/drivers/misc/TODO-parport b/drivers/misc/TODO-parport
index eea6a01fd..5ffe406fa 100644
--- a/drivers/misc/TODO-parport
+++ b/drivers/misc/TODO-parport
@@ -6,7 +6,7 @@ Things to be done.
2. Overhaul lp.c:
- a) It's a mess, and there is a lot of code duplication.
+ a) It's a _mess_
b) ECP support would be nice. This can only work if both the port and
the printer support it.
diff --git a/drivers/misc/parport_pc.c b/drivers/misc/parport_pc.c
index 477b350e3..c5a7a2a11 100644
--- a/drivers/misc/parport_pc.c
+++ b/drivers/misc/parport_pc.c
@@ -889,9 +889,9 @@ int parport_pc_init(int *io, int *irq, int *dma)
}
#ifdef MODULE
-static int io[PC_MAX_PORTS+1] = { 0, };
-static int dma[PC_MAX_PORTS] = { PARPORT_DMA_AUTO, };
-static int irq[PC_MAX_PORTS] = { PARPORT_IRQ_AUTO, };
+static int io[PC_MAX_PORTS+1] = { [0 ... PC_MAX_PORTS] = 0 };
+static int dma[PC_MAX_PORTS] = { [0 ... PC_MAX_PORTS-1] = PARPORT_DMA_AUTO };
+static int irq[PC_MAX_PORTS] = { [0 ... PC_MAX_PORTS-1] = PARPORT_IRQ_AUTO };
MODULE_PARM(io, "1-" __MODULE_STRING(PC_MAX_PORTS) "i");
MODULE_PARM(irq, "1-" __MODULE_STRING(PC_MAX_PORTS) "i");
MODULE_PARM(dma, "1-" __MODULE_STRING(PC_MAX_PORTS) "i");
diff --git a/drivers/net/3c501.c b/drivers/net/3c501.c
index 77a5e302d..d5cb133d6 100644
--- a/drivers/net/3c501.c
+++ b/drivers/net/3c501.c
@@ -357,10 +357,9 @@ static int el_open(struct device *dev)
if (el_debug > 2)
printk("%s: Doing el_open()...", dev->name);
- if (request_irq(dev->irq, &el_interrupt, 0, "3c501", NULL))
+ if (request_irq(dev->irq, &el_interrupt, 0, "3c501", dev))
return -EAGAIN;
- irq2dev_map[dev->irq] = dev;
el_reset(dev);
dev->start = 1;
@@ -483,7 +482,7 @@ load_it_again_sam:
static void el_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct device *dev = dev_id;
struct net_local *lp;
int ioaddr;
int axsr; /* Aux. status reg. */
@@ -759,9 +758,8 @@ static int el1_close(struct device *dev)
* Free and disable the IRQ.
*/
- free_irq(dev->irq, NULL);
+ free_irq(dev->irq, dev);
outb(AX_RESET, AX_CMD); /* Reset the chip */
- irq2dev_map[dev->irq] = 0;
MOD_DEC_USE_COUNT;
return 0;
diff --git a/drivers/net/3c503.c b/drivers/net/3c503.c
index aa69a74e6..228404fc6 100644
--- a/drivers/net/3c503.c
+++ b/drivers/net/3c503.c
@@ -340,13 +340,13 @@ el2_open(struct device *dev)
outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM and interrupts. */
do {
- if (request_irq (*irqp, NULL, 0, "bogus", NULL) != -EBUSY) {
+ if (request_irq (*irqp, NULL, 0, "bogus", dev) != -EBUSY) {
/* Twinkle the interrupt, and check if it's seen. */
autoirq_setup(0);
outb_p(0x04 << ((*irqp == 9) ? 2 : *irqp), E33G_IDCFR);
outb_p(0x00, E33G_IDCFR);
if (*irqp == autoirq_report(0) /* It's a good IRQ line! */
- && request_irq (dev->irq = *irqp, &ei_interrupt, 0, ei_status.name, NULL) == 0)
+ && request_irq (dev->irq = *irqp, &ei_interrupt, 0, ei_status.name, dev) == 0)
break;
}
} while (*++irqp);
@@ -355,7 +355,7 @@ el2_open(struct device *dev)
return -EAGAIN;
}
} else {
- if (request_irq(dev->irq, &ei_interrupt, 0, ei_status.name, NULL)) {
+ if (request_irq(dev->irq, &ei_interrupt, 0, ei_status.name, dev)) {
return -EAGAIN;
}
}
@@ -369,9 +369,8 @@ el2_open(struct device *dev)
static int
el2_close(struct device *dev)
{
- free_irq(dev->irq, NULL);
+ free_irq(dev->irq, dev);
dev->irq = ei_status.saved_irq;
- irq2dev_map[dev->irq] = NULL;
outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */
ei_close(dev);
@@ -675,7 +674,7 @@ cleanup_module(void)
for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) {
struct device *dev = &dev_el2[this_dev];
if (dev->priv != NULL) {
- /* NB: el2_close() handles free_irq + irq2dev map */
+ /* NB: el2_close() handles free_irq */
kfree(dev->priv);
dev->priv = NULL;
release_region(dev->base_addr, EL2_IO_EXTENT);
diff --git a/drivers/net/3c505.c b/drivers/net/3c505.c
index d8a7b33ce..ae1f06c99 100644
--- a/drivers/net/3c505.c
+++ b/drivers/net/3c505.c
@@ -669,7 +669,7 @@ static void elp_interrupt(int irq, void *dev_id, struct pt_regs *reg_ptr)
printk("elp_interrupt(): illegal IRQ number found in interrupt routine (%i)\n", irq);
return;
}
- dev = irq2dev_map[irq];
+ dev = dev_id;
if (dev == NULL) {
printk("elp_interrupt(): irq %d for unknown device.\n", irq);
@@ -926,15 +926,9 @@ static int elp_open(struct device *dev)
adapter->rx_backlog.out = 0;
/*
- * make sure we can find the device header given the interrupt number
- */
- irq2dev_map[dev->irq] = dev;
-
- /*
* install our interrupt service routine
*/
- if (request_irq(dev->irq, &elp_interrupt, 0, "3c505", NULL)) {
- irq2dev_map[dev->irq] = NULL;
+ if (request_irq(dev->irq, &elp_interrupt, 0, "3c505", dev)) {
return -EAGAIN;
}
if (request_dma(dev->dma, "3c505")) {
@@ -1222,12 +1216,7 @@ static int elp_close(struct device *dev)
/*
* release the IRQ
*/
- free_irq(dev->irq, NULL);
-
- /*
- * and we no longer have to map irq to dev either
- */
- irq2dev_map[dev->irq] = 0;
+ free_irq(dev->irq, dev);
free_dma(dev->dma);
free_pages((unsigned long) adapter->dma_buffer, __get_order(DMA_BUFFER_SIZE));
diff --git a/drivers/net/3c507.c b/drivers/net/3c507.c
index c12d6ee98..a331f72d6 100644
--- a/drivers/net/3c507.c
+++ b/drivers/net/3c507.c
@@ -362,7 +362,7 @@ __initfunc(int el16_probe1(struct device *dev, int ioaddr))
irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
- irqval = request_irq(irq, &el16_interrupt, 0, "3c507", NULL);
+ irqval = request_irq(irq, &el16_interrupt, 0, "3c507", dev);
if (irqval) {
printk ("unable to get IRQ %d (irqval=%d).\n", irq, irqval);
return EAGAIN;
@@ -431,8 +431,6 @@ __initfunc(int el16_probe1(struct device *dev, int ioaddr))
static int el16_open(struct device *dev)
{
- irq2dev_map[dev->irq] = dev;
-
/* Initialize the 82586 memory and start it. */
init_82586_mem(dev);
@@ -449,7 +447,7 @@ static int el16_send_packet(struct sk_buff *skb, struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
- short *shmem = (short*)dev->mem_start;
+ unsigned long shmem = dev->mem_start;
if (dev->tbusy)
{
@@ -460,7 +458,7 @@ static int el16_send_packet(struct sk_buff *skb, struct device *dev)
return 1;
if (net_debug > 1)
printk("%s: transmit timed out, %s? ", dev->name,
- shmem[iSCB_STATUS>>1] & 0x8000 ? "IRQ conflict" :
+ readw(shmem+iSCB_STATUS) & 0x8000 ? "IRQ conflict" :
"network cable problem");
/* Try to restart the adaptor. */
if (lp->last_restart == lp->stats.tx_packets) {
@@ -470,7 +468,7 @@ static int el16_send_packet(struct sk_buff *skb, struct device *dev)
} else {
/* Issue the channel attention signal and hope it "gets better". */
if (net_debug > 1) printk("Kicking board.\n");
- shmem[iSCB_CMD>>1] = 0xf000|CUC_START|RX_START;
+ writew(0xf000|CUC_START|RX_START,shmem+iSCB_CMD);
outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */
lp->last_restart = lp->stats.tx_packets;
}
@@ -506,11 +504,11 @@ static int el16_send_packet(struct sk_buff *skb, struct device *dev)
Handle the network interface interrupts. */
static void el16_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct device *dev = dev_id;
struct net_local *lp;
int ioaddr, status, boguscount = 0;
ushort ack_cmd = 0;
- ushort *shmem;
+ unsigned long shmem;
if (dev == NULL) {
printk ("net_interrupt(): irq %d for unknown device.\n", irq);
@@ -520,9 +518,9 @@ static void el16_interrupt(int irq, void *dev_id, struct pt_regs *regs)
ioaddr = dev->base_addr;
lp = (struct net_local *)dev->priv;
- shmem = ((ushort*)dev->mem_start);
+ shmem = dev->mem_start;
- status = shmem[iSCB_STATUS>>1];
+ status = readw(shmem+iSCB_STATUS);
if (net_debug > 4) {
printk("%s: 3c507 interrupt, status %4.4x.\n", dev->name, status);
@@ -533,7 +531,7 @@ static void el16_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/* Reap the Tx packet buffers. */
while (lp->tx_reap != lp->tx_head) {
- unsigned short tx_status = shmem[lp->tx_reap>>1];
+ unsigned short tx_status = readw(shmem+lp->tx_reap);
if (tx_status == 0) {
if (net_debug > 5) printk("Couldn't reap %#x.\n", lp->tx_reap);
@@ -588,11 +586,11 @@ static void el16_interrupt(int irq, void *dev_id, struct pt_regs *regs)
printk("%s: Rx unit stopped, status %04x, restarting.\n",
dev->name, status);
init_rx_bufs(dev);
- shmem[iSCB_RFA >> 1] = RX_BUF_START;
+ writew(RX_BUF_START,shmem+iSCB_RFA);
ack_cmd |= RX_START;
}
- shmem[iSCB_CMD>>1] = ack_cmd;
+ writew(ack_cmd,shmem+iSCB_CMD);
outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */
/* Clear the latched interrupt. */
@@ -607,22 +605,19 @@ static void el16_interrupt(int irq, void *dev_id, struct pt_regs *regs)
static int el16_close(struct device *dev)
{
int ioaddr = dev->base_addr;
- ushort *shmem = (short*)dev->mem_start;
+ unsigned long shmem = dev->mem_start;
dev->tbusy = 1;
dev->start = 0;
/* Flush the Tx and disable Rx. */
- shmem[iSCB_CMD >> 1] = RX_SUSPEND | CUC_SUSPEND;
+ writew(RX_SUSPEND | CUC_SUSPEND,shmem+iSCB_CMD);
outb(0, ioaddr + SIGNAL_CA);
/* Disable the 82586's input to the interrupt line. */
outb(0x80, ioaddr + MISC_CTRL);
- /* We always physically use the IRQ line, so we don't do free_irq().
- We do remove ourselves from the map. */
-
- irq2dev_map[dev->irq] = 0;
+ /* We always physically use the IRQ line, so we don't do free_irq(). */
/* Update the statistics here. */
@@ -646,7 +641,7 @@ static struct net_device_stats *el16_get_stats(struct device *dev)
static void init_rx_bufs(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
- unsigned short *write_ptr;
+ unsigned long write_ptr;
unsigned short SCB_base = SCB_BASE;
int cur_rxbuf = lp->rx_head = RX_BUF_START;
@@ -654,26 +649,26 @@ static void init_rx_bufs(struct device *dev)
/* Initialize each Rx frame + data buffer. */
do { /* While there is room for one more. */
- write_ptr = (unsigned short *)(dev->mem_start + cur_rxbuf);
-
- *write_ptr++ = 0x0000; /* Status */
- *write_ptr++ = 0x0000; /* Command */
- *write_ptr++ = cur_rxbuf + RX_BUF_SIZE; /* Link */
- *write_ptr++ = cur_rxbuf + 22; /* Buffer offset */
- *write_ptr++ = 0x0000; /* Pad for dest addr. */
- *write_ptr++ = 0x0000;
- *write_ptr++ = 0x0000;
- *write_ptr++ = 0x0000; /* Pad for source addr. */
- *write_ptr++ = 0x0000;
- *write_ptr++ = 0x0000;
- *write_ptr++ = 0x0000; /* Pad for protocol. */
-
- *write_ptr++ = 0x0000; /* Buffer: Actual count */
- *write_ptr++ = -1; /* Buffer: Next (none). */
- *write_ptr++ = cur_rxbuf + 0x20 + SCB_base; /* Buffer: Address low */
- *write_ptr++ = 0x0000;
+ write_ptr = dev->mem_start + cur_rxbuf;
+
+ writew(0x0000,write_ptr); /* Status */
+ writew(0x0000,write_ptr+=2); /* Command */
+ writew(cur_rxbuf + RX_BUF_SIZE,write_ptr+=2); /* Link */
+ writew(cur_rxbuf + 22,write_ptr+=2); /* Buffer offset */
+ writew(0x0000,write_ptr+=2); /* Pad for dest addr. */
+ writew(0x0000,write_ptr+=2);
+ writew(0x0000,write_ptr+=2);
+ writew(0x0000,write_ptr+=2); /* Pad for source addr. */
+ writew(0x0000,write_ptr+=2);
+ writew(0x0000,write_ptr+=2);
+ writew(0x0000,write_ptr+=2); /* Pad for protocol. */
+
+ writew(0x0000,write_ptr+=2); /* Buffer: Actual count */
+ writew(-1,write_ptr+=2); /* Buffer: Next (none). */
+ writew(cur_rxbuf + 0x20 + SCB_base,write_ptr+=2);/* Buffer: Address low */
+ writew(0x0000,write_ptr+=2);
/* Finally, the number of bytes in the buffer. */
- *write_ptr++ = 0x8000 + RX_BUF_SIZE-0x20;
+ writew(0x8000 + RX_BUF_SIZE-0x20,write_ptr+=2);
lp->rx_tail = cur_rxbuf;
cur_rxbuf += RX_BUF_SIZE;
@@ -681,18 +676,16 @@ static void init_rx_bufs(struct device *dev)
/* Terminate the list by setting the EOL bit, and wrap the pointer to make
the list a ring. */
- write_ptr = (unsigned short *)
- (dev->mem_start + lp->rx_tail + 2);
- *write_ptr++ = 0xC000; /* Command, mark as last. */
- *write_ptr++ = lp->rx_head; /* Link */
-
+ write_ptr = dev->mem_start + lp->rx_tail + 2;
+ writew(0xC000,write_ptr); /* Command, mark as last. */
+ writew(lp->rx_head,write_ptr+2); /* Link */
}
static void init_82586_mem(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
short ioaddr = dev->base_addr;
- ushort *shmem = (short*)dev->mem_start;
+ unsigned long shmem = dev->mem_start;
/* Enable loopback to protect the wire while starting up,
and hold the 586 in reset during the memory initialization. */
@@ -703,13 +696,13 @@ static void init_82586_mem(struct device *dev)
init_words[7] = SCB_BASE;
/* Write the words at 0xfff6 (address-aliased to 0xfffff6). */
- memcpy((void*)dev->mem_end-10, init_words, 10);
+ memcpy_toio(dev->mem_end-10, init_words, 10);
/* Write the words at 0x0000. */
- memcpy((char*)dev->mem_start, init_words + 5, sizeof(init_words) - 10);
+ memcpy_toio(dev->mem_start, init_words + 5, sizeof(init_words) - 10);
/* Fill in the station address. */
- memcpy((char*)dev->mem_start+SA_OFFSET, dev->dev_addr,
+ memcpy_toio(dev->mem_start+SA_OFFSET, dev->dev_addr,
sizeof(dev->dev_addr));
/* The Tx-block list is written as needed. We just set up the values. */
@@ -727,11 +720,11 @@ static void init_82586_mem(struct device *dev)
{
int boguscnt = 50;
- while (shmem[iSCB_STATUS>>1] == 0)
+ while (readw(shmem+iSCB_STATUS) == 0)
if (--boguscnt == 0) {
printk("%s: i82586 initialization timed out with status %04x,"
"cmd %04x.\n", dev->name,
- shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
+ readw(shmem+iSCB_STATUS), readw(shmem+iSCB_CMD));
break;
}
/* Issue channel-attn -- the 82586 won't start. */
@@ -742,7 +735,7 @@ static void init_82586_mem(struct device *dev)
outb(0x84, ioaddr + MISC_CTRL);
if (net_debug > 4)
printk("%s: Initialized 82586, status %04x.\n", dev->name,
- shmem[iSCB_STATUS>>1]);
+ readw(shmem+iSCB_STATUS));
return;
}
@@ -751,30 +744,30 @@ static void hardware_send_packet(struct device *dev, void *buf, short length)
struct net_local *lp = (struct net_local *)dev->priv;
short ioaddr = dev->base_addr;
ushort tx_block = lp->tx_head;
- ushort *write_ptr = (ushort *)(dev->mem_start + tx_block);
+ unsigned long write_ptr = dev->mem_start + tx_block;
/* Set the write pointer to the Tx block, and put out the header. */
- *write_ptr++ = 0x0000; /* Tx status */
- *write_ptr++ = CMD_INTR|CmdTx; /* Tx command */
- *write_ptr++ = tx_block+16; /* Next command is a NoOp. */
- *write_ptr++ = tx_block+8; /* Data Buffer offset. */
+ writew(0x0000,write_ptr); /* Tx status */
+ writew(CMD_INTR|CmdTx,write_ptr+=2); /* Tx command */
+ writew(tx_block+16,write_ptr+=2); /* Next command is a NoOp. */
+ writew(tx_block+8,write_ptr+=2); /* Data Buffer offset. */
/* Output the data buffer descriptor. */
- *write_ptr++ = length | 0x8000; /* Byte count parameter. */
- *write_ptr++ = -1; /* No next data buffer. */
- *write_ptr++ = tx_block+22+SCB_BASE;/* Buffer follows the NoOp command. */
- *write_ptr++ = 0x0000; /* Buffer address high bits (always zero). */
+ writew(length | 0x8000,write_ptr+=2); /* Byte count parameter. */
+ writew(-1,write_ptr+=2); /* No next data buffer. */
+ writew(tx_block+22+SCB_BASE,write_ptr+=2); /* Buffer follows the NoOp command. */
+ writew(0x0000,write_ptr+=2); /* Buffer address high bits (always zero). */
/* Output the Loop-back NoOp command. */
- *write_ptr++ = 0x0000; /* Tx status */
- *write_ptr++ = CmdNOp; /* Tx command */
- *write_ptr++ = tx_block+16; /* Next is myself. */
+ writew(0x0000,write_ptr+=2); /* Tx status */
+ writew(CmdNOp,write_ptr+=2); /* Tx command */
+ writew(tx_block+16,write_ptr+=2); /* Next is myself. */
/* Output the packet at the write pointer. */
- memcpy(write_ptr, buf, length);
+ memcpy_toio(write_ptr+2, buf, length);
/* Set the old command link pointing to this send packet. */
- *(ushort*)(dev->mem_start + lp->tx_cmd_link) = tx_block;
+ writew(tx_block,dev->mem_start + lp->tx_cmd_link);
lp->tx_cmd_link = tx_block + 20;
/* Set the next free tx region. */
@@ -794,19 +787,19 @@ static void hardware_send_packet(struct device *dev, void *buf, short length)
static void el16_rx(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
- short *shmem = (short*)dev->mem_start;
+ unsigned long shmem = dev->mem_start;
ushort rx_head = lp->rx_head;
ushort rx_tail = lp->rx_tail;
ushort boguscount = 10;
short frame_status;
- while ((frame_status = shmem[rx_head>>1]) < 0) { /* Command complete */
- ushort *read_frame = (short *)(dev->mem_start + rx_head);
- ushort rfd_cmd = read_frame[1];
- ushort next_rx_frame = read_frame[2];
- ushort data_buffer_addr = read_frame[3];
- ushort *data_frame = (short *)(dev->mem_start + data_buffer_addr);
- ushort pkt_len = data_frame[0];
+ while ((frame_status = readw(shmem+rx_head)) < 0) { /* Command complete */
+ unsigned long read_frame = dev->mem_start + rx_head;
+ ushort rfd_cmd = readw(read_frame+2);
+ ushort next_rx_frame = readw(read_frame+4);
+ ushort data_buffer_addr = readw(read_frame+6);
+ unsigned long data_frame = dev->mem_start + data_buffer_addr;
+ ushort pkt_len = readw(data_frame);
if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22
|| (pkt_len & 0xC000) != 0xC000) {
@@ -838,7 +831,7 @@ static void el16_rx(struct device *dev)
skb->dev = dev;
/* 'skb->data' points to the start of sk_buff data area. */
- memcpy(skb_put(skb,pkt_len), data_frame + 5, pkt_len);
+ memcpy_fromio(skb_put(skb,pkt_len), data_frame + 10, pkt_len);
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
@@ -846,10 +839,10 @@ static void el16_rx(struct device *dev)
}
/* Clear the status word and set End-of-List on the rx frame. */
- read_frame[0] = 0;
- read_frame[1] = 0xC000;
+ writew(0,read_frame);
+ writew(0xC000,read_frame+2);
/* Clear the end-of-list on the prev. RFD. */
- *(short*)(dev->mem_start + rx_tail + 2) = 0x0000;
+ writew(0x0000,dev->mem_start + rx_tail + 2);
rx_tail = rx_head;
rx_head = next_rx_frame;
@@ -895,7 +888,7 @@ cleanup_module(void)
dev_3c507.priv = NULL;
/* If we don't do this, we can't re-insmod it later. */
- free_irq(dev_3c507.irq, NULL);
+ free_irq(dev_3c507.irq, &dev_3c507);
release_region(dev_3c507.base_addr, EL16_IO_EXTENT);
}
#endif /* MODULE */
diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c
index 676561504..febe4b514 100644
--- a/drivers/net/3c509.c
+++ b/drivers/net/3c509.c
@@ -418,7 +418,6 @@ el3_open(struct device *dev)
outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
if (request_irq(dev->irq, &el3_interrupt, 0, "3c509", dev)) {
- irq2dev_map[dev->irq] = NULL;
return -EAGAIN;
}
@@ -867,8 +866,7 @@ cleanup_module(void)
if (dev->priv != NULL) {
kfree_s(dev->priv,sizeof(struct el3_private));
dev->priv = NULL;
- free_irq(dev->irq, NULL);
- irq2dev_map[dev->irq] = NULL;
+ free_irq(dev->irq, dev);
release_region(dev->base_addr, EL3_IO_EXTENT);
unregister_netdev(dev);
}
diff --git a/drivers/net/8390.c b/drivers/net/8390.c
index 4623128a2..597566c24 100644
--- a/drivers/net/8390.c
+++ b/drivers/net/8390.c
@@ -121,7 +121,6 @@ int ei_open(struct device *dev)
return -ENXIO;
}
- irq2dev_map[dev->irq] = dev;
NS8390_init(dev, 1);
dev->start = 1;
ei_local->irqlock = 0;
@@ -284,7 +283,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct device *dev)
Handle the ether interface interrupts. */
void ei_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
- struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct device *dev = dev_id;
int e8390_base;
int interrupts, nr_serviced = 0;
struct ei_device *ei_local;
@@ -296,12 +295,14 @@ void ei_interrupt(int irq, void *dev_id, struct pt_regs * regs)
e8390_base = dev->base_addr;
ei_local = (struct ei_device *) dev->priv;
if (dev->interrupt || ei_local->irqlock) {
+#if 1 /* This might just be an interrupt for a PCI device sharing this line */
/* The "irqlock" check is only for testing. */
printk(ei_local->irqlock
? "%s: Interrupted while interrupts are masked! isr=%#2x imr=%#2x.\n"
: "%s: Reentering the interrupt handler! isr=%#2x imr=%#2x.\n",
dev->name, inb_p(e8390_base + EN0_ISR),
inb_p(e8390_base + EN0_IMR));
+#endif
return;
}
diff --git a/drivers/net/8390.h b/drivers/net/8390.h
index 770dbcdab..5032e9b40 100644
--- a/drivers/net/8390.h
+++ b/drivers/net/8390.h
@@ -45,9 +45,8 @@ extern void ei_interrupt(int irq, void *dev_id, struct pt_regs *regs);
#ifndef HAVE_AUTOIRQ
/* From auto_irq.c */
-extern struct device *irq2dev_map[16];
-extern int autoirq_setup(int waittime);
-extern int autoirq_report(int waittime);
+extern void autoirq_setup(int waittime);
+extern unsigned long autoirq_report(int waittime);
#endif
/* Most of these entries should be in 'struct device' (or most of the
diff --git a/drivers/net/Config.in b/drivers/net/Config.in
index f0e760d5a..55f010e93 100644
--- a/drivers/net/Config.in
+++ b/drivers/net/Config.in
@@ -141,9 +141,9 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
fi
fi
-if [ ! "$CONFIG_PARPORT" = "n" ]; then
- dep_tristate 'PLIP (parallel port) support' CONFIG_PLIP $CONFIG_PARPORT
-fi
+#if [ ! "$CONFIG_PARPORT" = "n" ]; then
+# dep_tristate 'PLIP (parallel port) support' CONFIG_PLIP $CONFIG_PARPORT
+#fi
tristate 'PPP (point-to-point) support' CONFIG_PPP
if [ ! "$CONFIG_PPP" = "n" ]; then
@@ -182,7 +182,7 @@ if [ "$CONFIG_NET_RADIO" != "n" ]; then
bool 'HFmodem support for WSS and Crystal cards' CONFIG_HFMODEM_WSS
fi
fi
- tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP
+# tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP
tristate 'AT&T WaveLAN & DEC RoamAbout DS support' CONFIG_WAVELAN
fi
diff --git a/drivers/net/README.DLINK b/drivers/net/README.DLINK
deleted file mode 100644
index dba5dbcc5..000000000
--- a/drivers/net/README.DLINK
+++ /dev/null
@@ -1,205 +0,0 @@
-Released 1994-06-13
-
-
- CONTENTS:
-
- 1. Introduction.
- 2. License.
- 3. Files in this release.
- 4. Installation.
- 5. Problems and tuning.
- 6. Using the drivers with earlier releases.
- 7. Acknowledgments.
-
-
- 1. INTRODUCTION.
-
- This is a set of Ethernet drivers for the D-Link DE-600/DE-620
- pocket adapters, for the parallel port on a Linux based machine.
- Some adapter "clones" will also work. Xircom is _not_ a clone...
- These drivers _can_ be used as loadable modules,
- and were developed for use on Linux v1.1.13 and above.
- For use on Linux v1.0.X, or earlier releases, see below.
-
- I have used these drivers for NFS, ftp, telnet and X-clients on
- remote machines. Transmissions with ftp seems to work as
- good as can be expected (i.e. > 80k bytes/sec) from a
- parallel port...:-) Receive speeds will be about 60-80% of this.
- Depending on your machine, somewhat higher speeds can be achieved.
-
- All comments/fixes to Bjorn Ekwall (bj0rn@blox.se).
-
-
- 2. LICENSE.
-
- This program is free software; you can redistribute 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.
-
-
- 3. FILES IN THIS RELEASE.
-
- README.DLINK This file.
- de600.c The Source (,may it be with You :-) for the DE-600
- de620.c ditto for the DE-620
- de620.h Macros for de620.c
-
- If you are upgrading from the d-link tar release, there will
- also be a "dlink-patches" file that will patch Linux v1.1.18:
- linux/drivers/net/Makefile
- linux/drivers/net/CONFIG
- linux/drivers/net/MODULES
- linux/drivers/net/Space.c
- linux/config.in
- Apply the patch by:
- "cd /usr/src; patch -p0 < linux/drivers/net/dlink-patches"
- The old source, "linux/drivers/net/d_link.c", can be removed.
-
-
- 4. INSTALLATION.
-
- o Get the latest net binaries, according to current net.wisdom.
-
- o Read the NET-2 and Ethernet HOWTO's and modify your setup.
-
- o If your parallel port has a strange address or irq,
- modify "linux/drivers/net/CONFIG" accordingly, or adjust
- the parameters in the "tuning" section in the sources.
-
- If you are going to use the drivers a loadable modules, do _not_
- enable them while doing "make config", but instead make sure that
- the drivers are included in "linux/drivers/net/MODULES".
-
- If you are _not_ going to use the driver(s) as loadable modules,
- but instead have them included in the kernel, remember to enable
- the drivers while doing "make config".
-
- o To include networking and DE600/DE620 support in your kernel:
- # cd /linux
- (as modules:)
- # make config (answer yes on CONFIG_NET and CONFIG_INET)
- (else included in the kernel:)
- # make config (answer yes on CONFIG _NET, _INET and _DE600 or _DE620)
- # make clean
- # make depend
- # make zImage (or whatever magic you usually do)
-
- o I use lilo to boot multiple kernels, so that I at least
- can have one working kernel :-). If you do too, append
- these lines to /etc/lilo/config:
-
- image = /linux/zImage
- label = newlinux
- root = /dev/hda2 (or whatever YOU have...)
-
- # /etc/lilo/install
-
- o Do "sync" and reboot the new kernel with a D-Link
- DE-600/DE-620 pocket adapter connected.
-
- o The adapter can be configured with ifconfig eth?
- where the actual number is decided by the kernel
- when the drivers are initialized.
-
-
- 5. "PROBLEMS" AND TUNING,
-
- o If you see error messages from the driver, and if the traffic
- stops on the adapter, try to do "ifconfig" and "route" once
- more, just as in "rc.inet1". This should take care of most
- problems, including effects from power loss, or adapters that
- aren't connected to the printer port in some way or another.
- You can somewhat change the behaviour by enabling/disabling
- the macro SHUTDOWN_WHEN_LOST in the "tuning" section.
- For the DE-600 there is another macro, CHECK_LOST_DE600,
- that you might want to read about in the "tuning" section.
-
- o Some machines have trouble handling the parallel port and
- the adapter at high speed. If you experience problems:
-
- DE-600:
- - The adapter is not recognized at boot, i.e. an Ethernet
- address of 00:80:c8:... is not shown, try to add another
- "; SLOW_DOWN_IO"
- at DE600_SLOW_DOWN in the "tuning" section. As a last resort,
- uncomment: "#define REALLY_SLOW_IO" (see <asm/io.h> for hints).
-
- - You experience "timeout" messages: first try to add another
- "; SLOW_DOWN_IO"
- at DE600_SLOW_DOWN in the "tuning" section, _then_ try to
- increase the value (original value: 5) at
- "if (tickssofar < 5)" near line 422.
-
- DE-620:
- - Your parallel port might be "sluggish". To cater for
- this, there are the macros LOWSPEED and READ_DELAY/WRITE_DELAY
- in the "tuning" section. Your first step should be to enable
- LOWSPEED, and after that you can "tune" the XXX_DELAY values.
-
- o If the adapter _is_ recognized at boot but you get messages
- about "Network Unreachable", then the problem is probably
- _not_ with the driver. Check your net configuration instead
- (ifconfig and route) in "rc.inet1".
-
- o There is some rudimentary support for debugging, look at
- the source. Use "-DDE600_DEBUG=3" or "-DDE620_DEBUG=3"
- when compiling, or include it in "linux/drivers/net/CONFIG".
- IF YOU HAVE PROBLEMS YOU CAN'T SOLVE: PLEASE COMPILE THE DRIVER
- WITH DEBUGGING ENABLED, AND SEND ME THE RESULTING OUTPUT!
-
-
- 6. USING THE DRIVERS WITH EARLIER RELEASES.
-
- The later v1.1.X releases of the Linux kernel include some
- changes in the networking layer (a.k.a. NET3). This affects
- these drivers in a few places. The hints that follow are
- _not_ tested by me, since I don't have the diskspace to keep
- all releases on-line.
- Known needed changes to date:
- - release patchfile: some patches will fail, but they should
- be easy to apply "by hand", since they are trivial.
- (Space.c: d_link_init() is now called de600_probe())
- - de600.c: change "mark_bh(NET_BH)" to "mark_bh(INET_BH)".
- - de620.c: (maybe) change the code around "netif_rx(skb);" to be
- similar to the code around "dev_rint(...)" in de600.c
-
-
- 7. ACKNOWLEDGMENTS.
-
- These drivers wouldn't have been done without the base
- (and support) from Ross Biro <bir7@leland.stanford.edu>,
- and D-Link Systems Inc. The driver relies upon GPL-ed
- source from D-Link Systems Inc. and from Russel Nelson at
- Crynwr Software <nelson@crynwr.com>.
-
- Additional input also from:
- Donald Becker <becker@super.org>, Alan Cox <A.Cox@swansea.ac.uk>
- and Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
-
- DE-600 alpha release primary victim^H^H^H^H^H^Htester:
- - Erik Proper <erikp@cs.kun.nl>.
- Good input also from several users, most notably
- - Mark Burton <markb@ordern.demon.co.uk>.
-
- DE-620 alpha release victims^H^H^H^H^H^H^Htesters:
- - J. Joshua Kopper <kopper@rtsg.mot.com>
- - Olav Kvittem <Olav.Kvittem@uninett.no>
- - Germano Caronni <caronni@nessie.cs.id.ethz.ch>
- - Jeremy Fitzhardinge <jeremy@suite.sw.oz.au>
-
-
- Happy hacking!
-
- Bjorn Ekwall == bj0rn@blox.se
diff --git a/drivers/net/README.de4x5 b/drivers/net/README.de4x5
deleted file mode 100644
index 118437723..000000000
--- a/drivers/net/README.de4x5
+++ /dev/null
@@ -1,42 +0,0 @@
-This driver has been upgraded to include generic DECchip support through the
-use of the on-board SROM that is found on all DECchip cards except for the
-DC21040. The driver will work with the following set of cards and probably
-more:
-
- KINGSTON
- Linksys
- ZNYX342
- SMC8432
- SMC9332 (w/new SROM)
- ZNYX31[45]
- DIGITAL EtherWORKS PCI/EISA (DE425, DE434, DE435, DE450, DE500)
-
-Auto media detection is provided so that the media choice isn't compiled in
-and is flexible enough to be able to reconfigure on-the-fly.
-
-The ability to load this driver as a loadable module has been included and
-will now load (and unload) as many DECchip cards as it can find and
-configure with just one invocation of 'insmod'.
-
-The performance we've achieved so far has been measured through the 'ttcp'
-tool at 1.06MB/s for TCP and 1.17MB/s for UDP. This measures the total
-stack performance which includes the card, so don't expect to get much
-nearer the 1.25MB/s theoretical ethernet rate.
-
- TCP UDP
- TX RX TX RX
- DE425 1030k 997k 1170k 1128k (EISA on a Dell 433DE)
- DE434 1063k 995k 1170k 1125k (PCI: DECpc XL 466d2)
- DE435 1063k 995k 1170k 1125k (PCI: DECpc XL 466d2)
- DE500 1063k 998k 1170k 1125k in 10Mb/s mode (PCI: DECpc XL 466d2)
-
-All values are typical (in kBytes/sec) from a sample of 4 for each
-measurement. Their error is approx +/-20k on a quiet (private) network and
-also depend on what load the CPU has, CPU speed etc.
-
-I've had reports that Alphas can get 80+Mb/s when using 100BASE-TX and
-similar figures for 133MHz Pentium Pros.
-
-Enjoy!
-
-Dave
diff --git a/drivers/net/README.dgrs b/drivers/net/README.dgrs
deleted file mode 100644
index 58af33a08..000000000
--- a/drivers/net/README.dgrs
+++ /dev/null
@@ -1,52 +0,0 @@
- The Digi Intl. RightSwitch SE-X (dgrs) Device Driver
-
-This is a Linux driver for the Digi International RightSwitch SE-X
-EISA and PCI boards. These are 4 (EISA) or 6 (PCI) port ethernet
-switches and a NIC combined into a single board. This driver can
-be compiled into the kernel statically or as a loadable module.
-
-There is also a companion management tool, called "xrightswitch".
-The management tool lets you watch the performance graphically,
-as well as set the SNMP agent IP and IPX addresses, IEEE Spanning
-Tree, and Aging time. These can also be set from the command line
-when the driver is loaded. The driver command line options are:
-
- debug=NNN Debug printing level
- dma=0/1 Disable/Enable DMA on PCI card
- spantree=0/1 Disable/Enable IEEE spanning tree
- hashexpire=NNN Change address aging time (default 300 seconds)
- ipaddr=A,B,C,D Set SNMP agent IP address i.e. 199,86,8,221
- iptrap=A,B,C,D Set SNMP agent IP trap address i.e. 199,86,8,221
- ipxnet=NNN Set SNMP agent IPX network number
- nicmode=0/1 Disable/Enable multiple NIC mode
-
-There is also a tool for setting up input and output packet filters
-on each port, called "dgrsfilt".
-
-Both the management tool and the filtering tool are available
-separately from the following FTP site:
-
- ftp://ftp.dgii.com/drivers/rightswitch/linux/
-
-When nicmode=1, the board and driver operate as 4 or 6 individual
-NIC ports (eth0...eth5) instead of as a switch. All switching
-functions are disabled. In the future, the board firmware may include
-a routing cache when in this mode.
-
-Copyright 1995-1996 Digi International Inc.
-
-This software may be used and distributed according to the terms
-of the GNU General Public License, incorporated herein by reference.
-
-For information on purchasing a RightSwitch SE-4 or SE-6
-board, please contact Digi's sales department at 1-612-912-3444
-or 1-800-DIGIBRD. Outside the U.S., please check our Web page at:
-
- http://www.dgii.com
-
-for sales offices worldwide. Tech support is also available through
-the channels listed on the Web site, although as long as I am
-employed on networking products at Digi I will be happy to provide
-any bug fixes that may be needed.
-
--Rick Richardson, rick@dgii.com
diff --git a/drivers/net/README.eql b/drivers/net/README.eql
deleted file mode 100644
index 08cded598..000000000
--- a/drivers/net/README.eql
+++ /dev/null
@@ -1,528 +0,0 @@
- EQL Driver: Serial IP Load Balancing HOWTO
- Simon "Guru Aleph-Null" Janes, simon@ncm.com
- v1.1, February 27, 1995
-
- This is the manual for the EQL device driver. EQL is a software device
- that lets you load-balance IP serial links (SLIP or uncompressed PPP)
- to increase your bandwidth. It will not reduce your latency (i.e. ping
- times) except in the case where you already have lots of traffic on
- your link, in which it will help them out. This driver has been tested
- with the 1.1.75 kernel, and is known to have patched cleanly with
- 1.1.86. Some testing with 1.1.92 has been done with the v1.1 patch
- which was only created to patch cleanly in the very latest kernel
- source trees. (Yes, it worked fine.)
-
- 1. Introduction
-
- Which is worse? A huge fee for a 56K leased line or two phone lines?
- Its probably the former. If you find yourself craving more bandwidth,
- and have a ISP that is flexible, it is now possible to bind modems
- together to work as one point-to-point link to increase your
- bandwidth. All without having to have a special black box on either
- side.
-
-
- The eql driver has only been tested with the Livingston PortMaster-2e
- terminal server. I do not know if other terminal servers support load-
- balancing, but I do know that the PortMaster does it, and does it
- almost as well as the eql driver seems to do it (-- Unfortunately, in
- my testing so far, the Livingston PortMaster 2e's load-balancing is a
- good 1 to 2 KB/s slower than the test machine working with a 28.8 Kbps
- and 14.4 Kbps connection. However, I am not sure that it really is
- the PortMaster, or if it's Linux's TCP drivers. I'm told that Linux's
- TCP implementation is pretty fast though.--)
-
-
- I suggest to ISP's out there that it would probably be fair to charge
- a load-balancing client 75% of the cost of the second line and 50% of
- the cost of the third line etc...
-
-
- Hey, we can all dream you know...
-
-
- 2. Kernel Configuration
-
- Here I describe the general steps of getting a kernel up and working
- with the eql driver. From patching, building, to installing.
-
-
- 2.1. Patching The Kernel
-
- If you do not have or cannot get a copy of the kernel with the eql
- driver folded into it, get your copy of the driver from
- ftp://slaughter.ncm.com/pub/Linux/LOAD_BALANCING/eql-1.1.tar.gz.
- Unpack this archive someplace obvious like /usr/local/src/. It will
- create the following files:
-
-
-
- ______________________________________________________________________
- -rw-r--r-- guru/ncm 198 Jan 19 18:53 1995 eql-1.1/NO-WARRANTY
- -rw-r--r-- guru/ncm 30620 Feb 27 21:40 1995 eql-1.1/eql-1.1.patch
- -rwxr-xr-x guru/ncm 16111 Jan 12 22:29 1995 eql-1.1/eql_enslave
- -rw-r--r-- guru/ncm 2195 Jan 10 21:48 1995 eql-1.1/eql_enslave.c
- ______________________________________________________________________
-
- Unpack a recent kernel (something after 1.1.92) Someplace convenient
- like say /usr/src/linux-1.1.92.eql. Use symbolic links to point
- /usr/src/linux to this development directory.
-
-
- Apply the patch by running the commands:
-
-
- ______________________________________________________________________
- cd /usr/src
- patch </usr/local/src/eql-1.1/eql-1.1.patch
- ______________________________________________________________________
-
-
-
-
-
- 2.2. Building The Kernel
-
- After patching the kernel, run make config and configure the kernel
- for your hardware.
-
-
- After configuration, make and install according to your habit.
-
-
- 3. Network Configuration
-
- So far, I have only used the eql device with the DSLIP SLIP connection
- manager by Matt Dillon (-- "The man who sold his soul to code so much
- so quickly."--) . How you configure it for other "connection"
- managers is up to you. Most other connection managers that I've seen
- don't do a very good job when it comes to handling more than one
- connection.
-
-
- 3.1. /etc/rc.d/rc.inet1
-
- In rc.inet1, ifconfig the eql device to the IP address you usually use
- for your machine, and the MTU you prefer for your SLIP lines. One
- could argue that MTU should be roughly half the usual size for two
- modems, one-third for three, one-fourth for four, etc... But going
- too far below 296 is probably overkill. Here is an example ifconfig
- command that sets up the eql device:
-
-
-
- ______________________________________________________________________
- ifconfig eql 198.67.33.239 mtu 1006
- ______________________________________________________________________
-
-
-
-
-
- Once the eql device is up and running, add a static default route to
- it in the routing table using the cool new route syntax that makes
- life so much easier:
-
-
-
- ______________________________________________________________________
- route add default eql
- ______________________________________________________________________
-
-
- 3.2. Enslaving Devices By Hand
-
- Enslaving devices by hand requires two utility programs: eql_enslave
- and eql_emancipate (-- eql_emancipate hasn't been written because when
- an enslaved device "dies", it is automatically taken out of the queue.
- I haven't found a good reason to write it yet... other than for
- completeness, but that isn't a good motivator is it?--)
-
-
- The syntax for enslaving a device is "eql_enslave <master-name>
- <slave-name> <estimated-bps>". Here are some example enslavings:
-
-
-
- ______________________________________________________________________
- eql_enslave eql sl0 28800
- eql_enslave eql ppp0 14400
- eql_enslave eql sl1 57600
- ______________________________________________________________________
-
-
-
-
-
- When you want to free a device from its life of slavery, you can
- either down the device with ifconfig (eql will automatically bury the
- dead slave and remove it from its queue) or use eql_emancipate to free
- it. (-- Or just ifconfig it down, and the eql driver will take it out
- for you.--)
-
-
-
- ______________________________________________________________________
- eql_emancipate eql sl0
- eql_emancipate eql ppp0
- eql_emancipate eql sl1
- ______________________________________________________________________
-
-
-
-
-
- 3.3. DSLIP Configuration for the eql Device
-
- The general idea is to bring up and keep up as many SLIP connections
- as you need, automatically.
-
-
- 3.3.1. /etc/slip/runslip.conf
-
- Here is an example runslip.conf:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ______________________________________________________________________
- name sl-line-1
- enabled
- baud 38400
- mtu 576
- ducmd -e /etc/slip/dialout/cua2-288.xp -t 9
- command eql_enslave eql $interface 28800
- address 198.67.33.239
- line /dev/cua2
-
- name sl-line-2
- enabled
- baud 38400
- mtu 576
- ducmd -e /etc/slip/dialout/cua3-288.xp -t 9
- command eql_enslave eql $interface 28800
- address 198.67.33.239
- line /dev/cua3
- ______________________________________________________________________
-
-
-
-
-
- 3.4. Using PPP and the eql Device
-
- I have not yet done any load-balancing testing for PPP devices, mainly
- because I don't have a PPP-connection manager like SLIP has with
- DSLIP. I did find a good tip from LinuxNET:Billy for PPP performance:
- make sure you have asyncmap set to something so that control
- characters are not escaped.
-
-
- I tried to fix up a PPP script/system for redialing lost PPP
- connections for use with the eql driver the weekend of Feb 25-26 '95
- (Hereafter known as the 8-hour PPP Hate Festival). Perhaps later this
- year.
-
-
- 4. About the Slave Scheduler Algorithm
-
- The slave scheduler probably could be replaced with a dozen other
- things and push traffic much faster. The formula in the current set
- up of the driver was tuned to handle slaves with wildly different
- bits-per-second "priorities".
-
-
- All testing I have done was with two 28.8 V.FC modems, one connecting
- at 28800 bps or slower, and the other connecting at 14400 bps all the
- time.
-
-
- One version of the scheduler was able to push 5.3 K/s through the
- 28800 and 14400 connections, but when the priorities on the links were
- very wide apart (57600 vs. 14400) The "faster" modem received all
- traffic and the "slower" modem starved.
-
-
- 5. Tester's Reports
-
- Some people have experimented with the eql device with newer kernels
- kernels (than 1.1.75). I have since updated the driver to patch
- cleanly in newer kernels because of the removal of the old "slave-
- balancing" driver config option.
-
-
- o icee from LinuxNET patched 1.1.86 without any rejects and was able
- to boot the kernel and enslave a couple of ISDN PPP links.
-
- 5.1. Randolph Bentson's Test Report
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- From bentson@grieg.seaslug.org Wed Feb 8 19:08:09 1995
- Date: Tue, 7 Feb 95 22:57 PST
- From: Randolph Bentson <bentson@grieg.seaslug.org>
- To: guru@ncm.com
- Subject: EQL driver tests
-
-
- I have been checking out your eql driver. (Nice work, that!)
- Although you may already done this performance testing, here
- are some data I've discovered.
-
- Randolph Bentson
- bentson@grieg.seaslug.org
-
- ---------------------------------------------------------
-
-
- A pseudo-device driver, EQL, written by Simon Janes, can be used
- to bundle multiple SLIP connections into what appears to be a
- single connection. This allows one to improve dial-up network
- connectivity gradually, without having to buy expensive DSU/CSU
- hardware and services.
-
- I have done some testing of this software, with two goals in
- mind: first, to ensure it actually works as described and
- second, as a method of exercising my device driver.
-
- The following performance measurements were derived from a set
- of SLIP connections run between two Linux systems (1.1.84) using
- a 486DX2/66 with a Cyclom-8Ys and a 486SLC/40 with a Cyclom-16Y.
- (Ports 0,1,2,3 were used. A later configuration will distribute
- port selection across the different Cirrus chips on the boards.)
- Once a link was established, I timed a binary ftp transfer of
- 289284 bytes of data. If there were no overhead (packet headers,
- inter-character and inter-packet delays, etc.) the transfers
- would take the following times:
-
- bits/sec seconds
- 345600 8.3
- 234600 12.3
- 172800 16.7
- 153600 18.8
- 76800 37.6
- 57600 50.2
- 38400 75.3
- 28800 100.4
- 19200 150.6
- 9600 301.3
-
- A single line running at the lower speeds and with large packets
- comes to within 2% of this. Performance is limited for the higher
- speeds (as predicted by the Cirrus databook) to an aggregate of
- about 160 kbits/sec. The next round of testing will distribute
- the load across two or more Cirrus chips.
-
- The good news is that one gets nearly the full advantage of the
- second, third, and fourth line's bandwidth. (The bad news is
- that the connection establishment seemed fragile for the higher
- speeds. Once established, the connection seemed robust enough.)
-
- #lines speed mtu seconds theory actual %of
- kbit/sec duration speed speed max
- 3 115200 900 _ 345600
- 3 115200 400 18.1 345600 159825 46
- 2 115200 900 _ 230400
- 2 115200 600 18.1 230400 159825 69
- 2 115200 400 19.3 230400 149888 65
- 4 57600 900 _ 234600
- 4 57600 600 _ 234600
- 4 57600 400 _ 234600
- 3 57600 600 20.9 172800 138413 80
- 3 57600 900 21.2 172800 136455 78
- 3 115200 600 21.7 345600 133311 38
- 3 57600 400 22.5 172800 128571 74
- 4 38400 900 25.2 153600 114795 74
- 4 38400 600 26.4 153600 109577 71
- 4 38400 400 27.3 153600 105965 68
- 2 57600 900 29.1 115200 99410.3 86
- 1 115200 900 30.7 115200 94229.3 81
- 2 57600 600 30.2 115200 95789.4 83
- 3 38400 900 30.3 115200 95473.3 82
- 3 38400 600 31.2 115200 92719.2 80
- 1 115200 600 31.3 115200 92423 80
- 2 57600 400 32.3 115200 89561.6 77
- 1 115200 400 32.8 115200 88196.3 76
- 3 38400 400 33.5 115200 86353.4 74
- 2 38400 900 43.7 76800 66197.7 86
- 2 38400 600 44 76800 65746.4 85
- 2 38400 400 47.2 76800 61289 79
- 4 19200 900 50.8 76800 56945.7 74
- 4 19200 400 53.2 76800 54376.7 70
- 4 19200 600 53.7 76800 53870.4 70
- 1 57600 900 54.6 57600 52982.4 91
- 1 57600 600 56.2 57600 51474 89
- 3 19200 900 60.5 57600 47815.5 83
- 1 57600 400 60.2 57600 48053.8 83
- 3 19200 600 62 57600 46658.7 81
- 3 19200 400 64.7 57600 44711.6 77
- 1 38400 900 79.4 38400 36433.8 94
- 1 38400 600 82.4 38400 35107.3 91
- 2 19200 900 84.4 38400 34275.4 89
- 1 38400 400 86.8 38400 33327.6 86
- 2 19200 600 87.6 38400 33023.3 85
- 2 19200 400 91.2 38400 31719.7 82
- 4 9600 900 94.7 38400 30547.4 79
- 4 9600 400 106 38400 27290.9 71
- 4 9600 600 110 38400 26298.5 68
- 3 9600 900 118 28800 24515.6 85
- 3 9600 600 120 28800 24107 83
- 3 9600 400 131 28800 22082.7 76
- 1 19200 900 155 19200 18663.5 97
- 1 19200 600 161 19200 17968 93
- 1 19200 400 170 19200 17016.7 88
- 2 9600 600 176 19200 16436.6 85
- 2 9600 900 180 19200 16071.3 83
- 2 9600 400 181 19200 15982.5 83
- 1 9600 900 305 9600 9484.72 98
- 1 9600 600 314 9600 9212.87 95
- 1 9600 400 332 9600 8713.37 90
-
-
-
-
-
- 5.2. Anthony Healy's Report
-
-
-
-
-
-
-
- Date: Mon, 13 Feb 1995 16:17:29 +1100 (EST)
- From: Antony Healey <ahealey@st.nepean.uws.edu.au>
- To: Simon Janes <guru@ncm.com>
- Subject: Re: Load Balancing
-
- Hi Simon,
- I've installed your patch and it works great. I have trialed
- it over twin SL/IP lines, just over null modems, but I was
- able to data at over 48Kb/s [ISDN link -Simon]. I managed a
- transfer of upto 7.5 Kbyte/s on one go, but averaged around
- 6.4 Kbyte/s, which I think is pretty cool. :)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/drivers/net/README.ewrk3 b/drivers/net/README.ewrk3
deleted file mode 100644
index bea97f4e2..000000000
--- a/drivers/net/README.ewrk3
+++ /dev/null
@@ -1,45 +0,0 @@
-The EtherWORKS 3 driver in this distribution is designed to work with all
-kernels > 1.1.33 (approx) and includes tools in the 'ewrk3tools'
-subdirectory to allow set up of the card, similar to the MSDOS
-'NICSETUP.EXE' tools provided on the DOS drivers disk (type 'make' in that
-subdirectory to make the tools).
-
-The supported cards are DE203, DE204 and DE205. All other cards are NOT
-supported - refer to 'depca.c' for running the LANCE based network cards and
-'de4x5.c' for the DIGITAL Semiconductor PCI chip based adapters from
-Digital.
-
-The ability to load this driver as a loadable module has been included and
-used extensively during the driver development (to save those long reboot
-sequences). To utilise this ability, you have to do 8 things:
-
- 0) have a copy of the loadable modules code installed on your system.
- 1) copy ewrk3.c from the /linux/drivers/net directory to your favourite
- temporary directory.
- 2) edit the source code near line 1898 to reflect the I/O address and
- IRQ you're using.
- 3) compile ewrk3.c, but include -DMODULE in the command line to ensure
- that the correct bits are compiled (see end of source code).
- 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a
- kernel with the ewrk3 configuration turned off and reboot.
- 5) insmod ewrk3.o
- [Alan Cox: Changed this so you can insmod ewrk3.o irq=x io=y]
- 6) run the net startup bits for your new eth?? interface manually
- (usually /etc/rc.inet[12] at boot time).
- 7) enjoy!
-
- Note that autoprobing is not allowed in loadable modules - the system is
- already up and running and you're messing with interrupts.
-
- To unload a module, turn off the associated interface
- 'ifconfig eth?? down' then 'rmmod ewrk3'.
-
-The performance we've achieved so far has been measured through the 'ttcp'
-tool at 975kB/s. This measures the total tcp stack performance which
-includes the card, so don't expect to get much nearer the 1.25MB/s
-theoretical ethernet rate.
-
-
-Enjoy!
-
-Dave
diff --git a/drivers/net/README.ltpc b/drivers/net/README.ltpc
deleted file mode 100644
index 477f9fc6b..000000000
--- a/drivers/net/README.ltpc
+++ /dev/null
@@ -1,98 +0,0 @@
-This is the ALPHA version of the ltpc driver.
-
-In order to use it, you will need at least version 1.3.3 of the
-netatalk package, and the Apple or Farallon Localtalk PC card.
-There are a number of different Localtalk cards for the PC; this
-driver applies only to the one with the 65c02 processor chip on it.
-
-To include it in the kernel, select the CONFIG_LTPC switch in the
-configuration dialog; at this time (kernel 2.1.23) compiling it as
-a module will not work.
-
-Before starting up the netatalk demons (perhaps in rc.local), you
-need to add a line such as:
-
-/sbin/ifconfig ltalk0 127.0.0.42
-
-
-The driver will autoprobe, and you should see a message like:
-"LocalTalk card found at 240, IR9, DMA1."
-at bootup.
-
-The appropriate netatalk configuration depends on whether you are
-attached to a network that includes appletalk routers or not. If,
-like me, you are simply connecting to your home Macintoshes and
-printers, you need to set up netatalk to "seed". The way I do this
-is to have the lines
-
-dummy -seed -phase 2 -net 2000 -addr 2000.26 -zone "1033"
-ltalk0 -seed -phase 1 -net 1033 -addr 1033.27 -zone "1033"
-
-in my atalkd.conf. What is going on here is that I need to fool
-netatalk into thinking that there are two appletalk interfaces
-present -- otherwise it refuses to seed. This is a hack, and a
-more permanent solution would be to alter the netatalk code.
-Note that the dummy driver needs to accept multicasts also -- earlier
-versions of dummy.c may need to be patched.
-
-
-If you are attached to an extended appletalk network, with routers on
-it, then you don't need to fool around with this -- the appropriate
-line in atalkd.conf is
-
-ltalk0 -phase 1
-
---------------------------------------
-
-Card Configuration:
-
-The interrupts and so forth are configured via the dipswitch on the
-board. Set the switches so as not to conflict with other hardware.
-
- Interrupts -- set at most one. If none are set, the driver uses
- polled mode. Because the card was developed in the XT era, the
- original documentation refers to IRQ2. Since you'll be running
- this on an AT (or later) class machine, that really means IRQ9.
-
- SW1 IRQ 4
- SW2 IRQ 3
- SW3 IRQ 9 (2 in original card documentation only applies to XT)
-
-
- DMA -- choose DMA 1 or 3, and set both corresponding switches.
-
- SW4 DMA 3
- SW5 DMA 1
- SW6 DMA 3
- SW7 DMA 1
-
-
- I/O address -- choose one.
-
- SW8 220 / 240
-
---------------------------------------
-
-IP:
- Many people are interested in this driver in order to use IP
-when Localtalk, but no Ethernet, is available. While the code to do
-this is not strictly speaking part of this driver, an experimental
-version is available which seems to work under kernel 2.0.xx. It is
-not yet functional in the 2.1.xx kernels.
-
---------------------------------------
-
-BUGS:
-
-2.0.xx:
-
-2.1.xx: The module support doesn't work yet.
-
-______________________________________
-
-THANKS:
- Thanks to Alan Cox for helpful discussions early on in this
-work, and to Denis Hainsworth for doing the bleeding-edge testing.
-
--- Bradford Johnson <bradford@math.umn.edu>
-
diff --git a/drivers/net/README.multicast b/drivers/net/README.multicast
deleted file mode 100644
index 8bc9397ec..000000000
--- a/drivers/net/README.multicast
+++ /dev/null
@@ -1,57 +0,0 @@
-Behaviour of cards under Multicast. This is how they currently
-behave not what the hardware can do. In particular all the 8390 based
-cards don't use the onboard hash filter, and the lance driver doesn't
-use its filter, even though the code for loading it is in the DEC
-lance based driver.
-
-The following multicast requirements are needed
------------------------------------------------
-Appletalk Multicast hardware filtering not important but
- avoid cards only doing promisc
-IP-Multicast Multicast hardware filters really help
-IP-MRoute AllMulti hardware filters are of no help
-
-
-Board Multicast AllMulti Promisc Filter
-------------------------------------------------------------------------
-3c501 YES YES YES Software
-3c503 YES YES YES Hardware
-3c505 YES NO YES Hardware
-3c507 NO NO NO N/A
-3c509 YES YES YES Software
-3c59x YES YES YES Software
-ac3200 YES YES YES Hardware
-apricot YES PROMISC YES Hardware
-arcnet NO NO NO N/A
-at1700 PROMISC PROMISC YES Software
-atp PROMISC PROMISC YES Software
-cs89x0 YES YES YES Software
-de4x5 YES NO YES Hardware
-de600 NO NO NO N/A
-de620 PROMISC PROMISC YES Software
-depca YES PROMISC YES Hardware
-e2100 YES YES YES Hardware
-eepro YES PROMISC YES Hardware
-eexpress NO NO NO N/A
-ewrk3 YES PROMISC YES Hardware
-hp-plus YES YES YES Hardware
-hp YES YES YES Hardware
-hp100 YES YES YES Hardware
-ibmtr NO NO NO N/A
-lance YES YES YES Software(#)
-ne YES YES YES Hardware
-ni52 <------------------ Buggy ------------------>
-ni65 YES YES YES Software(#)
-seeq NO NO NO N/A
-sk_g16 NO NO YES N/A
-smc-ultra YES YES YES Hardware
-sunlance YES YES YES Hardware
-tulip YES YES YES Hardware
-wavelan YES PROMISC YES Hardware
-wd YES YES YES Hardware
-znet YES YES YES Software
-
-
-PROMISC = This multicasts mode is in fact promiscuous mode. Avoid using
-cards who go PROMISC on any multicast in a multicast kernel.
-(#) = Hardware multicast support is not used yet.
diff --git a/drivers/net/README.pt b/drivers/net/README.pt
deleted file mode 100644
index 30e2588dc..000000000
--- a/drivers/net/README.pt
+++ /dev/null
@@ -1,62 +0,0 @@
-This is the README for the Gracilis Packetwin device driver, version 0.5
-ALPHA for Linux 1.3.43.
-
-These files will allow you to talk to the PackeTwin (now know as PT) and
-connect through it just like a pair of TNC's. To do this you will also
-require the AX.25 code in the kernel enabled.
-
-There are four files in this archive; this readme, a patch file, a .c file
-and finally a .h file. The two program files need to be put into the
-drivers/net directory in the Linux source tree, for me this is the
-directory /usr/src/linux/drivers/net. The patch file needs to be patched in
-at the top of the Linux source tree (/usr/src/linux in my case).
-
-You will most probably have to edit the pt.c file to suit your own setup,
-this should just involve changing some of the defines at the top of the file.
-Please note that if you run an external modem you must specify a speed of 0.
-
-The program is currently setup to run a 4800 baud external modem on port A
-and a Kantronics DE-9600 daughter board on port B so if you have this (or
-something similar) then you're right.
-
-To compile in the driver, put the files in the correct place and patch in
-the diff. You will have to re-configure the kernel again before you
-recompile it.
-
-The driver is not real good at the moment for finding the card. You can
-'help' it by changing the order of the potential addresses in the structure
-found in the pt_init() function so the address of where the card is is put
-first.
-
-After compiling, you have to get them going, they are pretty well like any
-other net device and just need ifconfig to get them going.
-As an example, here is my /etc/rc.net
---------------------------
-
-#
-# Configure the PackeTwin, port A.
-/sbin/ifconfig pt0a 44.136.8.87 hw ax25 vk2xlz mtu 512
-/sbin/ifconfig pt0a 44.136.8.87 broadcast 44.136.8.255 netmask 255.255.255.0
-/sbin/route add -net 44.136.8.0 netmask 255.255.255.0 dev pt0a
-/sbin/route add -net 44.0.0.0 netmask 255.0.0.0 gw 44.136.8.68 dev pt0a
-/sbin/route add -net 138.25.16.0 netmask 255.255.240.0 dev pt0a
-/sbin/route add -host 44.136.8.255 dev pt0a
-#
-# Configure the PackeTwin, port B.
-/sbin/ifconfig pt0b 44.136.8.87 hw ax25 vk2xlz-1 mtu 512
-/sbin/ifconfig pt0b 44.136.8.87 broadcast 44.255.255.255 netmask 255.0.0.0
-/sbin/route add -host 44.136.8.216 dev pt0b
-/sbin/route add -host 44.136.8.95 dev pt0b
-/sbin/route add -host 44.255.255.255 dev pt0b
-
-This version of the driver comes under the GNU GPL. If you have one on my
-previous (non-GPL) versions of the driver, please update to this one.
-
-I hope that this all works well for you. I would be pleased to hear how
-many people use the driver and if it does its job.
-
- - Craig vk2xlz
-
-INET: csmall@acacia.itd.uts.edu.au craig.small@eol.ieaust.org.au
-AMPR: vk2xlz@gonzo.vk2xlz.ampr.org
-AX25: vk2xlz@vk2gdm.nsw.aus.oc
diff --git a/drivers/net/README.scc b/drivers/net/README.scc
deleted file mode 100644
index b3eb3445c..000000000
--- a/drivers/net/README.scc
+++ /dev/null
@@ -1,23 +0,0 @@
-
-You will find subset of the documentation in
-
- linux/Documentation/networking/z8530drv.txt
-
-To use this driver you MUST have the full package from:
-
-Internet:
-=========
-
-1. db0bm.automation.fh-aachen.de/incoming/dl1bke/z8530drv-utils-3.0.tar.gz
-
-2. ftp.ucsd.edu:/hamradio/packet/tcpip/incoming/z8530drv-utils-3.0.tar.gz
- If you can't find it there, try .../tcpip/linux/z8530drv-utils-3.0.tar.gz
-
-and various mirrors (i.e. nic.switch.ch)
-
-The package includes the utilities necessary to initialize and
-control the driver.
-
-Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org
- AX-25 : DL1BKE @ DB0ACH.#NRW.DEU.EU
- Internet: jreuter@lykos.oche.de
diff --git a/drivers/net/README.smc9 b/drivers/net/README.smc9
deleted file mode 100644
index 243c7e7e7..000000000
--- a/drivers/net/README.smc9
+++ /dev/null
@@ -1,42 +0,0 @@
-
-SMC 9xxxx Driver
-Revision 0.12
-3/5/96
-Copyright 1996 Erik Stahlman
-Released under terms of the GNU public license.
-
-This file contains the instructions and caveats for my SMC9xxx driver. You
-should not be using the driver without reading this file.
-
-Things to note about installation:
-
- 1. The driver should work on all kernels from 1.2.13 until 1.3.71.
- (A kernel patch is supplied for 1.3.71 )
-
- 2. If you include this into the kernel, you might need to change some
- options, such as for forcing IRQ.
-
-
- 3. To compile as a module, run 'make' .
- Make will give you the appropriate options for various kernel support.
-
- 4. Loading the driver as a module :
-
- use: insmod smc9194.o
- optional parameters:
- io=xxxx : your base address
- irq=xx : your irq
- ifport=x : 0 for whatever is default
- 1 for twisted pair
- 2 for AUI ( or BNC on some cards )
-
-How to obtain the latest version?
-
-FTP:
- ftp://fenris.campus.vt.edu/smc9/smc9-12.tar.gz
- ftp://sfbox.vt.edu/filebox/F/fenris/smc9/smc9-12.tar.gz
-
-
-Contacting me:
- erik@mail.vt.edu
-
diff --git a/drivers/net/README.tunnel b/drivers/net/README.tunnel
deleted file mode 100644
index 407635b71..000000000
--- a/drivers/net/README.tunnel
+++ /dev/null
@@ -1,123 +0,0 @@
-
-This is the alpha version of my IP tunneling driver.
-
-Protocol Tunneling:
-
- A network tunneling driver encapsulates packets of one
-protocol type within packets of another protocol type. It sends
-them out over the network to a relay (or destination) where the
-packet is unwrapped and is forwarded to its ultimate destination.
-Packet tunneling is useful in situations where you want to route
-packets of a non-standard protocol type over the common network.
-A good example of this is 'IPX encapsulation', in which IPX packets
-from a DOS network are routed across an IP network by encapsulating
-them in IP packets.
-
- There are two parts to every protocol tunnel. There is
-the encapsulator, and the decapsulator. The encapsulator wraps
-the packets in the host protocol and sends them on their way,
-while the decapsulator takes wrapped packets at the other end
-and unwraps them and forwards them (or whatever else should be
-done with them.)
-
- IP tunneling is a specific case of protocol tunneling,
-in which the encapsulating protocol is IP, and the encapsulated
-protocol may be any other protocol, including Apple-Talk, IPX,
-or even IP within IP.
-
- For more information on the semantics and specifications
-of IP encapsulation, see RFC-1241, also included in this package.
-
-
-My Implementation:
-
- My implementation of IP tunneling for Linux consists
-of two loadable module drivers, one an encapsulator (tunnel.o)
-and the other a decapsulator (ipip.o). Both are used for
-setting up a working IP-in-IP tunnel. Currently, the drivers
-only support IP encapsulated in IP.
-
- The tunnel driver is implemented as a network device,
-based on the Linux loopback driver written (in part) by Ross Biro,
-Fred N. van Kempen, and Donald Becker. After the driver is
-loaded, it can be set up as any other network interface, using
-ifconfig. The tunnel device is given its own IP address, which
-can match that of the machine, and also is given a pointopoint
-address. This pointopoint address is the address of the machine
-providing the decapsulating endpoint for the IP tunnel. After
-the device is configured for use, the 'route' command can be used
-to route traffic through the IP tunnel. There must be a route to
-the decapsulating endpoint that does not go through the tunnel
-device, otherwise a looping tunnel is created, preventing the
-network traffic from leaving the local endpoint.
-
- The decapsulating endpoint must have loaded the ipip.o
-decapsulator module for it to understand IP-in-IP encapsulation.
-This module takes any IP-in-IP packet that is destined for the local
-machine, unwraps it, and sends it on its way, using standard
-routing rules. The current implementation of IP decapsulation does
-no checking on the packet, other than making sure wrapper is bound
-for the local machine.
-
- Note that the above setup only provides a one-way pipe.
-To provide a full two-way IP tunnel, the decapsulation host must
-set up an IP encapsulation driver, and the encapsulating host must
-load the IP decapsulation module, providing full duplex communication
-through the IP tunnel.
-
-An example setup might be as follows.
-
- Machine A has an ethernet interface with an IP address
-of 111.112.101.37, while machine B is on a different network, with
-an ethernet interface at IP address 111.112.100.86. For some
-reason, machine A needs to appear on machine B's network. It could
-do that by setting up an IP tunnel with machine B.
-
-First, the commands that would be run on machine A:
-(Assuming both machines are Linux hosts, running Linux 1.1.x)
-
-# insmod ipip.o ; insmod tunnel.o // Here the drivers are loaded.
-# ifconfig tunl 111.112.100.87 pointopoint 111.112.100.86
-# ifconfig tunl netmask 255.255.255.0 // Set a proper netmask.
-# route add 111.112.100.86 dev eth0 // Set a static route to B.
-# route add -net 111.112.100.0 dev tunl // Set up other routes.
-
-At this point, machine A is ready to route all traffic to the
-network that machine B resides on. But now, machine B needs to
-set up its half of the IP tunnel:
-
-# insmod ipip.o ; insmod tunnel.o // Here the drivers are loaded.
-# ifconfig tunl 111.112.100.86 pointopoint 111.112.101.37
-# ifconfig tunl netmask 255.255.255.0 // Set a proper netmask.
-# route add 111.112.100.87 dev eth0 // Set a static route to B.
-# arp -s 111.112.100.87 EE.EE.EE.EE.EE pub // Act as a proxy arp server.
-
-The extra step of "arp -s" is needed so that when machines on
-network B query to see if 111.112.100.87 (the "ghost" host)
-exists, machine B will respond, acting as an arp proxy for machine
-A. In the command line, EE.EE.EE.EE.EE should be replaced with
-the ethernet hardware address of machine B's ethernet card.
-
-Notice that machine B's setup is almost the inverse of machine A's
-setup. This is because IP tunneling is a peer-to-peer concept.
-There is no client and no server, there is no state to keep track
-of. The concept is simple. Every IP packet outbound through the
-tunnel interface is wrapped and sent to the pointopoint address
-and every incoming IP-in-IP packet bound for the local machine is
-unwrapped and re-routed normally.
-The only difference in the two machines setup shown above is that
-machine A set its tunnel address to one existing on machine B's
-network, while B set a route to machine A's tunnel device address
-through the tunnel. This is because machine A wants to have a new
-address on network B, and machine B is simply acting as a proxy
-for machine A. Machine A needs its tunnel address to be on network
-B so that when packets from machine B are unwrapped, the Linux
-routing system knows that the address is a local one. Due to a
-feature of Linux, any packets received locally, bound for another
-local address, are simply routed through the loopback interface.
-This means that the tunnel device should never receive packets. Even
-on machine B, it is the ethernet interface that is receiving wrapped
-packets, and once they are unwrapped they go back out the ethernet
-interface. This could cause Linux to generate ICMP redirect messages
-if this special routing case isn't caught (see /linux/net/inet/ip.c)
-
diff --git a/drivers/net/README.wanpipe b/drivers/net/README.wanpipe
deleted file mode 100644
index 9650edb73..000000000
--- a/drivers/net/README.wanpipe
+++ /dev/null
@@ -1,148 +0,0 @@
-------------------------------------------------------------------------------
-WANPIPE(tm) Multiprotocol WAN Driver for Linux WAN Router
-------------------------------------------------------------------------------
-Release 3.1.0
-January 30, 1997
-Author: Gene Kozin <genek@compuserve.com>
-Copyright (c) 1995-1997 Sangoma Technologies Inc.
-------------------------------------------------------------------------------
-
-INTRODUCTION
-
-WANPIPE(tm) is a family of intelligent muliprotocol WAN communication adapters
-for personal computers (ISA bus) designed to provide PC connectivity to
-various communication links, such as leased lines and public data networks, at
-speeds up to T1/E1 using variety of synchronous communications protocols,
-including frame relay, PPP, X.25, SDLC, etc.
-
-WANPIPE driver together with Linux WAN Router module allows you to build
-relatively inexpensive, yet high-prformance multiprotocol WAN router. For
-more information about Linux WAN Router please read file
-Documentation/networking/wan-router.txt. You must also obtain WAN Tools
-package to be able to use Linux WAN Router and WANPIPE driver. The package
-is available via the Internet from Sangoma Technologies' anonymous FTP server:
-
- ftp.sangoma.com/pub/linux/wantools-X.Y.Z.tgz
-
-For technical questions and/or comments please e-mail to genek@compuserve.com.
-For general inquiries please contact Sangoma Technologies Inc. by
-
- Hotline: 1-800-388-2475 (USA and Canada, toll free)
- Phone: (905) 474-1990
- Fax: (905) 474-9223
- E-mail: dm@sangoma.com (David Mandelstam)
- WWW: http://www.sangoma.com
-
-
-
-COPYRIGHT AND LICENSING INFORMATION
-
-This program is free software; you can redistribute 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.
-
-
-
-NEW IN THIS RELEASE
-
- o Implemented as WAN Link Driver compliant with Linux WAN Router interface
- o Added support for X.25 protocol
- o Miscellaneous bug fixes and performance improvements
-
-
-
-FILE LIST
-
-drivers/net:
- README.wanpipe This file
- sdladrv.c SDLA support module source code
- wpmain.c WANPIPE driver module main source code
- wpx.c WANPIPE driver module X.25 source code
- wpf.c WANPIPE driver module frame relay source code
- wpp.c WANPIPE driver module PPP source code
- sdla_x25.h SDLA X.25 firmware API definitions
- sdla_fr.h SDLA frame relay firmware API definitions
- sdla_ppp.h SDLA PPP firmware API definitions
-
-include/linux:
- wanpipe.h WANPIPE API definitions
- sdladrv.h SDLA support module API definitions
- sdlasfm.h SDLA firmware module definitions
-
-
-
-REVISION HISTORY
-
-3.1.0 January 30, 1997
-
- o Implemented IOCTL for executing adapter commands.
- o Fixed a bug in frame relay code causing driver configured as a FR
- switch to be stuck in WAN_DISCONNECTED mode.
-
-3.0.0 December 31, 1996
-
- o Uses Linux WAN Router interface
- o Added support for X.25 routing
- o Miscellaneous bug fixes and performance improvements
-
-2.4.1 December 18, 1996
-
- o Added support for LMI and Q.933 frame relay link management
-
-2.3.0 October 17, 1996
-
- o All shell scripts use meta-configuration file
- o Miscellaneous bug fixes
-
-2.2.0 July 16, 1996
-
- o Compatible with Linux 2.0
- o Added uninstall script
- o User's Manual is available in HTML format
-
-2.1.0 June 20, 1996
-
- o Added support for synchronous PPP
- o Added support for S503 adapter
- o Added API for executing adapter commands
- o Fixed a re-entrancy problem in frame relaty driver
- o Changed interface between SDLA driver and protocol support modules
- o Updated frame relay firmware
-
-2.0.0 May 1, 1996
-
- o Added interactive installation and configuration scripts
- o Added System V-style start-up script
- o Added dynamic memory window address selection in SDLA driver
- o Miscellaneous bug fixes in SDLA driver
- o Updated S508 frame relay firmware
- o Changed SFM file format
-
-1.0.0 February 12, 1996
-
- o Final release
- o Added support for Linux 1.3
- o Updated S508 frame relay firmware
-
-0.9.0 December 21, 1995
-
- o Added SNAP encapsulation for routed frames
- o Added support for the frame relay switch emulation mode
- o Added support for S508 adapter
- o Added capability to autodetect adapter type
- o Miscellaneous bug fixes in SDLA and frame relay drivers
-
-0.1.0 October 12, 1995
-
- o Initial version
-
->>>>>>> END OF README <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-
diff --git a/drivers/net/README.wavelan b/drivers/net/README.wavelan
deleted file mode 100644
index 5849f287b..000000000
--- a/drivers/net/README.wavelan
+++ /dev/null
@@ -1,33 +0,0 @@
-Sun Jul 2 01:38:33 EST 1995
-
-1. At present the driver autoprobes for a WaveLAN card only at I/O address 0x390.
- The version of the card that I use (NCR) supports four I/O addresses (selectable
- via a pair of DIP switches). If you want the driver to autoprobe a different
- subset of the four valid addresses then you will need to edit
- .../drivers/net/wavelan.c (near line 714) and change the initialisation of the
- `iobase[]' array. Normally, I use a LILO configuration file directive to
- obviate the need for autoprobing entirely, a course of action I heartily
- recommend.
-
-2. By default, the driver uses the Network ID (NWID) stored in the card's Parameter
- Storage Area (PSA). However, the PSA NWID can be overridden by a value passed
- explicitly as the third numeric argument to LILO's "ether=" directive, either
- at the LILO prompt at boot time or within LILO's configuration file.
- For example, the following line from such a LILO configuration file would
- auto-configure the IRQ value, set the I/O base to 0x390 and set the NWID to
- 0x4321, all on a WaveLAN card labelled "eth0":
-
- ..
- append ="ether=0,0x390,0x4321,eth0"
- ..
-
-3. The driver uses the IRQ stored in the card's PSA.
- To change this you will need to use the configuration/setup software that
- accompanies each WaveLAN device. Yes, the driver should use the value passed
- in via LILO and it will, just as soon as I can work out why that part of the
- code doesn't work :-(.
-
-4. If you encounter any problems send me some email.
-
-Good luck,
-Bruce Janson (bruce@cs.usyd.edu.au)
diff --git a/drivers/net/README1.PLIP b/drivers/net/README1.PLIP
deleted file mode 100644
index 9d33f79a4..000000000
--- a/drivers/net/README1.PLIP
+++ /dev/null
@@ -1,113 +0,0 @@
-\title{PLIP: The Parallel Line Internet Protocol Device}
-
-\author{ Donald Becker (becker@super.org)}
-\affiliation{I.D.A. Supercomputing Research Center, Bowie MD 20715}
-
-%% At some point T. Thorn will probably contribute text,
-%% \author{ Tommy Thorn (tthorn@daimi.aau.dk)}
-
-\section{PLIP Introduction}
-This document describes the parallel port packet pusher for Net/LGX.
-This device interface allows a point-to-point connection between two
-parallel ports to appear as a IP network interface.
-
-\chapter{PLIP hardware interconnection}
-PLIP uses several different data transfer methods. The first (and the
-only one implemented in the early version of the code) uses a standard
-printer "null" cable to transfers data four bits at a time using
-data bit outputs connected to status bit inputs.
-
-The second data transfer method relies on both machines having
-bi-directional parallel ports, rather than output-only ``printer''
-ports. This allows byte-wide transfers and avoids reconstructing
-nibbles into bytes, leading to much faster transfers.
-
-\section{Parallel Transfer Mode 0 Cable}
-The cable for the first transfer mode is a standard
-printer "null" cable which transfers data four bits at a time using
-data bit outputs of the first port (machine T) connected to the
-status bit inputs of the second port (machine R). There are five
-status inputs, and they are used as four data inputs and a clock (data
-strobe) input, arranged so that the data input bits appear as contiguous
-bits with standard status register implementation.
-
-A cable that implements this protocol is available commercially as a
-"Null Printer" or "Turbo Laplink" cable. It can be constructed with
-two DB-25 male connectors symmetrically connected as follows:
-
- STROBE output 1*
- D0->ERROR 2 - 15 15 - 2
- D1->SLCT 3 - 13 13 - 3
- D2->PAPOUT 4 - 12 12 - 4
- D3->ACK 5 - 10 10 - 5
- D4->BUSY 6 - 11 11 - 6
- D5,D6,D7 are 7*, 8*, 9*
- AUTOFD output 14*
- INIT output 16*
- SLCTIN 17 - 17
- extra grounds are 18*,19*,20*,21*,22*,23*,24*
- GROUND 25 - 25
-* Do not connect these pins on either end
-
-If the cable you are using has a metallic shield it should be
-connected to the metallic DB-25 shell at one end only.
-
-\section{Parallel Transfer Mode 1}
-The second data transfer method relies on both machines having
-bi-directional parallel ports, rather than output-only ``printer''
-ports. This allows byte-wide transfers, and avoids reconstructing
-nibbles into bytes. This cable should not be used on unidirectional
-``printer'' (as opposed to ``parallel'') ports or when the machine
-isn't configured for PLIP, as it will result in output driver
-conflicts and the (unlikely) possibility of damage.
-
-The cable for this transfer mode should be constructed as follows:
-
- STROBE->BUSY 1 - 11
- D0->D0 2 - 2
- D1->D1 3 - 3
- D2->D2 4 - 4
- D3->D3 5 - 5
- D4->D4 6 - 6
- D5->D5 7 - 7
- D6->D6 8 - 8
- D7->D7 9 - 9
- INIT -> ACK 16 - 10
- AUTOFD->PAPOUT 14 - 12
- SLCT->SLCTIN 13 - 17
- GND->ERROR 18 - 15
- extra grounds are 19*,20*,21*,22*,23*,24*
- GROUND 25 - 25
-* Do not connect these pins on either end
-
-Once again, if the cable you are using has a metallic shield it should
-be connected to the metallic DB-25 shell at one end only.
-
-\section{PLIP Mode 0 transfer protocol}
-The PLIP driver is compatible with the "Crynwr" parallel port transfer
-standard in Mode 0. That standard specifies the following protocol:
-
- send header nibble '8'
- count-low octet
- count-high octet
- ... data octets
- checksum octet
-
-Each octet is sent as
- <wait for rx. '1'> <send 0x10+(octet&0x0F)>
- <wait for rx. '0'> <send 0x00+((octet>>4)&0x0F)>
-
-To start a transfer the transmitting machine outputs a nibble 0x08.
-The raises the ACK line, triggering an interrupt in the receiving
-machine. The receiving machine disables
-
-Restated:
-
-(OUT is bit 0-4, OUT.j is bit j from OUT. IN likewise)
-Send_Byte:
- OUT := low nibble, OUT.4 := 1
- WAIT FOR IN.4 = 1
- OUT := high nibble, OUT.4 := 0
- WAIT FOR IN.4 = 0
-
-
diff --git a/drivers/net/README2.PLIP b/drivers/net/README2.PLIP
deleted file mode 100644
index c1d2a82ef..000000000
--- a/drivers/net/README2.PLIP
+++ /dev/null
@@ -1,78 +0,0 @@
-
-(2nd attempt. 1st bounced.)
-Hi again
-
-About my previous mail: I've looked into parallel.asm, and I'm
-rather confused. Looks like the code agrees with you, but not
-the protocol description preceding it?? I got to look more
-careful, but it wont be for a while (approx a week).
-
->From plip.c (v0.04):
-
->make one yourself. The wiring is:
-> INIT 16 - 16 SLCTIN 17 - 17
-> GROUND 25 - 25
-> D0->ERROR 2 - 15 15 - 2
-
-I saw you removed 1 and 14 from the cable description, but not
-16 and 17. Why is that?
-
-Have been successful in getting parallel.com working (the Messy-Loss
-software). Using the pksend on the sender and pkall/pkwatch/whatnot
-gives me a hung receiver. (The cable works, I've tried unet11, a DOS
-cheap-net prog.)
-
-Using PLIP v0.03 and trying to ping the other end gives
- 88 timeout 88 timeout....(more) 2386 bogus packet size, dropped
-on the receiver, and on the sender lots of timeout, but of
-course I don't know how much is supposed to work.
-
-The following to something I wrote when I should have gone to bed a
-long time ago. Use it for whatever you like, or dump it in the bin. ;^)
-
-/Tommy
------
-Becker [& Co] proudly presents PLIP
-
-What is PLIP?
-=============
-
-PLIP is Parallel Line IP, that is, the transportation of IP packages
-over a parallel port. In the case of a PC, the obvious choice is the
-printer port. PLIP is a non-standard, but [can use] uses the standard
-LapLink null-printer cable [can also work in turbo mode, with a PLIP
-cable]. [The protocol used to pack IP packages, is a simple one
-initiated by Crynwr.]
-
-Advantages of PLIP
-==================
-
-It's cheap, it's available everywhere, and it's easy.
-
-The PLIP cable is all that's needed to connect two Linux boxes, and it
-can be build for very bucks.
-
-Connecting two Linux boxes takes only a seconds decision and a few
-minutes work, no need to search for a [supported] netcard. This might
-even be especially important in the case of notebooks, where netcard
-are not easily available.
-
-Not requiring a netcard also means that apart from connecting the
-cables, everything else is software configuration [which in principle
-could be made very easy.]
-
-Disadvantages of PLIP
-=====================
-
-Doesn't work over a modem, like SLIP and PPP. Limited range, 15 m.
-Can only be used to connect three (?) Linux boxes. Doesn't connect to
-an exiting ethernet. Isn't standard (not even de facto standard, like
-SLIP).
-
-Performance
-==========
-
-PLIP easily outperforms ethernet cards....(ups, I was dreaming, but
-it *is* getting late. EOB)
-
-
diff --git a/drivers/net/Space.c b/drivers/net/Space.c
index b568c12bf..eedc48f98 100644
--- a/drivers/net/Space.c
+++ b/drivers/net/Space.c
@@ -32,8 +32,6 @@
#include <linux/errno.h>
#include <linux/init.h>
-#include <net/netlink.h>
-
#define NEXT_DEV NULL
@@ -100,6 +98,11 @@ extern int atp_init(struct device *);
extern int de600_probe(struct device *);
extern int de620_probe(struct device *);
+/* FDDI adapters */
+extern int dfx_probe(struct device *dev);
+extern int apfddi_init(struct device *dev);
+
+
__initfunc(static int ethif_probe(struct device *dev))
{
u_long base_addr = dev->base_addr;
@@ -280,6 +283,27 @@ __initfunc(static int ethif_probe(struct device *dev))
}
+#ifdef CONFIG_FDDI
+__initfunc(static int fddiif_probe(struct device *dev))
+{
+ unsigned long base_addr = dev->base_addr;
+
+ if ((base_addr == 0xffe0) || (base_addr == 1))
+ return 1; /* ENXIO */
+
+ if (1
+#ifdef CONFIG_DEFXX
+ && dfx_probe(dev)
+#endif
+#ifdef CONFIG_APFDDI
+ && apfddi_init(dev);
+#endif
+ && 1 ) {
+ return 1; /* -ENODEV or -EAGAIN would be more accurate. */
+ }
+ return 0;
+}
+#endif
#ifdef CONFIG_ETHERTAP
@@ -317,13 +341,11 @@ static struct device atp_dev = {
#if defined(CONFIG_COPS)
extern int cops_probe(struct device *);
- static struct device dev_cops = {
- "lt0",
- 0, 0, 0, 0,
- 0x0, 0,
- 0, 0, 0, NEXT_DEV, cops_probe };
+ static struct device cops2_dev = { "lt2", 0, 0, 0, 0, 0x0, 0, 0, 0, 0, NEXT_DEV, cops_probe };
+ static struct device cops1_dev = { "lt1", 0, 0, 0, 0, 0x0, 0, 0, 0, 0, &cops2_dev, cops_probe };
+ static struct device cops0_dev = { "lt0", 0, 0, 0, 0, 0x0, 0, 0, 0, 0, &cops1_dev, cops_probe };
# undef NEXT_DEV
-# define NEXT_DEV (&dev_cops)
+# define NEXT_DEV (&cops0_dev)
#endif /* COPS */
#if defined(CONFIG_IPDDP)
@@ -483,48 +505,26 @@ static struct device tr0_dev = {
#endif
-#ifdef CONFIG_NET_IPIP
- extern int tunnel_init(struct device *);
-
- static struct device tunnel_dev1 =
- {
- "tunl1", /* IPIP tunnel */
- 0x0, /* recv memory end */
- 0x0, /* recv memory start */
- 0x0, /* memory end */
- 0x0, /* memory start */
- 0x0, /* base I/O address */
- 0, /* IRQ */
- 0, 0, 0, /* flags */
- NEXT_DEV, /* next device */
- tunnel_init /* Fill in the details */
- };
-
- static struct device tunnel_dev0 =
- {
- "tunl0", /* IPIP tunnel */
- 0x0, /* recv memory end */
- 0x0, /* recv memory start */
- 0x0, /* memory end */
- 0x0, /* memory start */
- 0x0, /* base I/O address */
- 0, /* IRQ */
- 0, 0, 0, /* flags */
- &tunnel_dev1, /* next device */
- tunnel_init /* Fill in the details */
- };
-# undef NEXT_DEV
-# define NEXT_DEV (&tunnel_dev0)
-
-#endif
-
-#ifdef CONFIG_APFDDI
- extern int apfddi_init(struct device *dev);
- static struct device fddi_dev = {
- "fddi", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, apfddi_init };
-# undef NEXT_DEV
-# define NEXT_DEV (&fddi_dev)
-#endif
+#ifdef CONFIG_FDDI
+ static struct device fddi7_dev =
+ {"fddi7", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, fddiif_probe};
+ static struct device fddi6_dev =
+ {"fddi6", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi7_dev, fddiif_probe};
+ static struct device fddi5_dev =
+ {"fddi5", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi6_dev, fddiif_probe};
+ static struct device fddi4_dev =
+ {"fddi4", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi5_dev, fddiif_probe};
+ static struct device fddi3_dev =
+ {"fddi3", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi4_dev, fddiif_probe};
+ static struct device fddi2_dev =
+ {"fddi2", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi3_dev, fddiif_probe};
+ static struct device fddi1_dev =
+ {"fddi1", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi2_dev, fddiif_probe};
+ static struct device fddi0_dev =
+ {"fddi0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi1_dev, fddiif_probe};
+#undef NEXT_DEV
+#define NEXT_DEV (&fddi0_dev)
+#endif
#ifdef CONFIG_APBIF
extern int bif_init(struct device *dev);
diff --git a/drivers/net/a2065.c b/drivers/net/a2065.c
index 8de7f28b9..66e784b6f 100644
--- a/drivers/net/a2065.c
+++ b/drivers/net/a2065.c
@@ -129,7 +129,7 @@ struct lance_private {
unsigned short busmaster_regval;
#ifdef CONFIG_AMIGA
- int key;
+ unsigned int key;
#endif
#ifdef CONFIG_SUNLANCE
struct Linux_SBus_DMA *ledma; /* if set this points to ledma and arch=4m */
@@ -585,16 +585,6 @@ static int lance_start_xmit (struct sk_buff *skb, struct device *dev)
return status;
}
- if (skb == NULL) {
- dev_tint (dev);
- printk ("skb is NULL\n");
- return 0;
- }
-
- if (skb->len <= 0) {
- printk ("skb len is %d\n", skb->len);
- return 0;
- }
/* Block a timer-based transmit from overlapping. */
#ifdef OLD_METHOD
dev->tbusy = 1;
@@ -737,20 +727,20 @@ static void lance_set_multicast (struct device *dev)
__initfunc(int a2065_probe(struct device *dev))
{
- int key1, key2 = 0;
- struct ConfigDev *cd;
+ unsigned int key, is_cbm;
+ const struct ConfigDev *cd;
u_long board;
u_long sn;
struct lance_private *priv;
struct A2065Board *a2065;
- if ((key1 = zorro_find(MANUF_COMMODORE, PROD_A2065, 0, 0)) ||
- (key1 = zorro_find(MANUF_COMMODORE, PROD_A2065_2, 0, 0)) ||
- (key2 = zorro_find(MANUF_AMERISTAR, PROD_AMERISTAR2065, 0, 0))) {
- cd = zorro_get_board(key1 ? key1 : key2);
+ if ((key = is_cbm = zorro_find(ZORRO_PROD_CBM_A2065_1, 0, 0)) ||
+ (key = is_cbm = zorro_find(ZORRO_PROD_CBM_A2065_2, 0, 0)) ||
+ (key = zorro_find(ZORRO_PROD_AMERISTAR_A2065, 0, 0))) {
+ cd = zorro_get_board(key);
if ((board = (u_long)cd->cd_BoardAddr)) {
sn = cd->cd_Rom.er_SerialNumber;
- if (key1) { /* Commodore */
+ if (is_cbm) { /* Commodore */
dev->dev_addr[0] = 0x00;
dev->dev_addr[1] = 0x80;
dev->dev_addr[2] = 0x10;
@@ -783,7 +773,7 @@ __initfunc(int a2065_probe(struct device *dev))
priv->lance_init_block = (struct lance_init_block *)
offsetof(struct A2065Board, RAM);
priv->auto_select = 0;
- priv->key = key1 ? key1 : key2;
+ priv->key = key;
priv->busmaster_regval = LE_C3_BSWP;
priv->lance_log_rx_bufs = LANCE_LOG_RX_BUFFERS;
@@ -799,11 +789,11 @@ __initfunc(int a2065_probe(struct device *dev))
dev->dma = 0;
ether_setup(dev);
- zorro_config_board(key1 ? key1 : key2, 0);
+ zorro_config_board(key, 0);
return(0);
}
}
- return(ENODEV);
+ return(-ENODEV);
}
diff --git a/drivers/net/ac3200.c b/drivers/net/ac3200.c
index 63af6498b..524dc6475 100644
--- a/drivers/net/ac3200.c
+++ b/drivers/net/ac3200.c
@@ -165,7 +165,7 @@ __initfunc(static int ac_probe1(int ioaddr, struct device *dev))
else if (dev->irq == 2)
dev->irq = 9;
- if (request_irq(dev->irq, ei_interrupt, 0, "ac3200", NULL)) {
+ if (request_irq(dev->irq, ei_interrupt, 0, "ac3200", dev)) {
printk (" unable to get IRQ %d.\n", dev->irq);
return EAGAIN;
}
@@ -173,7 +173,7 @@ __initfunc(static int ac_probe1(int ioaddr, struct device *dev))
/* Allocate dev->priv and fill in 8390 specific dev fields. */
if (ethdev_init(dev)) {
printk (" unable to allocate memory for dev->priv.\n");
- free_irq(dev->irq, NULL);
+ free_irq(dev->irq, dev);
return -ENOMEM;
}
@@ -228,7 +228,7 @@ static int ac_open(struct device *dev)
/* Someday we may enable the IRQ and shared memory here. */
int ioaddr = dev->base_addr;
- if (request_irq(dev->irq, ei_interrupt, 0, "ac3200", NULL))
+ if (request_irq(dev->irq, ei_interrupt, 0, "ac3200", dev))
return -EAGAIN;
#endif
@@ -303,8 +303,7 @@ static int ac_close_card(struct device *dev)
#ifdef notyet
/* We should someday disable shared memory and interrupts. */
outb(0x00, ioaddr + 6); /* Disable interrupts. */
- free_irq(dev->irq, NULL);
- irq2dev_map[dev->irq] = 0;
+ free_irq(dev->irq, dev);
#endif
ei_close(dev);
@@ -369,9 +368,8 @@ cleanup_module(void)
if (dev->priv != NULL) {
kfree(dev->priv);
dev->priv = NULL;
- /* Someday free_irq + irq2dev may be in ac_close_card() */
- free_irq(dev->irq, NULL);
- irq2dev_map[dev->irq] = NULL;
+ /* Someday free_irq may be in ac_close_card() */
+ free_irq(dev->irq, dev);
release_region(dev->base_addr, AC_IO_EXTENT);
unregister_netdev(dev);
}
diff --git a/drivers/net/apricot.c b/drivers/net/apricot.c
index e15d77966..9ddc79ba7 100644
--- a/drivers/net/apricot.c
+++ b/drivers/net/apricot.c
@@ -537,11 +537,9 @@ i596_open(struct device *dev)
if (i596_debug > 1)
printk("%s: i596_open() irq %d.\n", dev->name, dev->irq);
- if (request_irq(dev->irq, &i596_interrupt, 0, "apricot", NULL))
+ if (request_irq(dev->irq, &i596_interrupt, 0, "apricot", dev))
return -EAGAIN;
- irq2dev_map[dev->irq] = dev;
-
i = init_rx_bufs(dev, RX_RING_SIZE);
if ((i = init_rx_bufs(dev, RX_RING_SIZE)) < RX_RING_SIZE)
@@ -549,8 +547,7 @@ i596_open(struct device *dev)
if (i < 4)
{
- free_irq(dev->irq, NULL);
- irq2dev_map[dev->irq] = 0;
+ free_irq(dev->irq, dev);
return -EAGAIN;
}
@@ -601,17 +598,6 @@ i596_start_xmit(struct sk_buff *skb, struct device *dev)
dev->trans_start = jiffies;
}
- /* If some higher level thinks we've misses a tx-done interrupt
- we are passed NULL. n.b. dev_tint handles the cli()/sti()
- itself. */
- if (skb == NULL) {
- dev_tint(dev);
- return 0;
- }
-
- /* shouldn't happen */
- if (skb->len <= 0) return 0;
-
if (i596_debug > 3) printk("%s: i596_start_xmit() called\n", dev->name);
/* Block a timer-based transmit from overlapping. This could better be
@@ -746,7 +732,7 @@ __initfunc(int apricot_probe(struct device *dev))
static void
i596_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct device *dev = dev_id;
struct i596_private *lp;
short ioaddr;
int boguscnt = 200;
@@ -933,8 +919,7 @@ i596_close(struct device *dev)
dev->name, lp->scb.status, lp->scb.command);
break;
}
- free_irq(dev->irq, NULL);
- irq2dev_map[dev->irq] = 0;
+ free_irq(dev->irq, dev);
remove_rx_bufs(dev);
MOD_DEC_USE_COUNT;
diff --git a/drivers/net/arc-rimi.c b/drivers/net/arc-rimi.c
index c5ab22584..c4ce91c4f 100644
--- a/drivers/net/arc-rimi.c
+++ b/drivers/net/arc-rimi.c
@@ -1,4 +1,4 @@
-/* $Id: arc-rimi.c,v 1.2 1997/09/05 08:57:51 mj Exp $
+/* $Id: arc-rimi.c,v 1.5 1997/11/09 11:04:57 mj Exp $
Derived from the original arcnet.c,
Written 1994-1996 by Avery Pennarun,
@@ -131,7 +131,7 @@ extern int arcnet_num_devs;
#define SETCONF writeb(lp->config,_CONFIG)
static const char *version =
-"arc-rimi.c: v2.92 97/09/02 Avery Pennarun <apenwarr@bond.net> et al.\n";
+"arc-rimi.c: v3.00 97/11/09 Avery Pennarun <apenwarr@bond.net> et al.\n";
/****************************************************************************
* *
@@ -178,12 +178,11 @@ __initfunc(int arcrimi_found(struct device *dev,int node,int airq, u_long shmem)
int mirror_size;
/* reserve the irq */
- if (request_irq(airq,&arcnet_interrupt,0,"arcnet (RIM I)",NULL))
+ if (request_irq(airq,&arcnet_interrupt,0,"arcnet (RIM I)",dev))
{
BUGMSG(D_NORMAL,"Can't get IRQ %d!\n",airq);
return -ENODEV;
}
- irq2dev_map[airq]=dev;
dev->irq=airq;
dev->base_addr=0;
@@ -221,8 +220,7 @@ __initfunc(int arcrimi_found(struct device *dev,int node,int airq, u_long shmem)
dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL);
if (dev->priv == NULL)
{
- irq2dev_map[airq] = NULL;
- free_irq(airq,NULL);
+ free_irq(airq,dev);
return -ENOMEM;
}
memset(dev->priv,0,sizeof(struct arcnet_local));
@@ -792,8 +790,7 @@ void cleanup_module(void)
if (dev->irq)
{
- irq2dev_map[dev->irq] = NULL;
- free_irq(dev->irq,NULL);
+ free_irq(dev->irq,dev);
}
unregister_netdev(dev);
diff --git a/drivers/net/arcnet.c b/drivers/net/arcnet.c
index 7669b59a8..c4c14d1cc 100644
--- a/drivers/net/arcnet.c
+++ b/drivers/net/arcnet.c
@@ -1,4 +1,4 @@
-/* $Id: arcnet.c,v 1.30 1997/09/05 08:57:46 mj Exp $
+/* $Id: arcnet.c,v 1.34 1997/11/09 11:04:55 mj Exp $
Written 1994-1996 by Avery Pennarun,
derived from skeleton.c by Donald Becker.
@@ -18,13 +18,21 @@
**********************
- v2.92 ALPHA (97/02/09)
+ v3.00 (97/11/09)
+ - Minor cleanup of debugging messages. [mj]
+
+ v2.93 ALPHA (97/11/06)
+ - irq2dev mapping removed.
+ - Interrupt handler now checks whether dev->priv is non-null in order
+ to avoid crashes in interrupts which come during card init. [mj]
+
+ v2.92 ALPHA (97/09/02)
- Code cleanup [Martin Mares <mj@atrey.karlin.mff.cuni.cz>]
- Better probing for the COM90xx chipset, although only as
a temporary solution until we implement adding of all found
devices at once. [mj]
- v2.91 ALPHA (97/19/08)
+ v2.91 ALPHA (97/08/19)
- Add counting of octets in/out.
v2.90 ALPHA (97/08/08)
@@ -162,7 +170,7 @@
*/
static const char *version =
- "arcnet.c: v2.92 97/09/02 Avery Pennarun <apenwarr@bond.net> et al.\n";
+ "arcnet.c: v3.00 97/11/09 Avery Pennarun <apenwarr@bond.net> et al.\n";
#include <linux/module.h>
#include <linux/config.h>
@@ -377,11 +385,6 @@ void arcnet_setup(struct device *dev)
/* New-style flags. */
dev->flags = IFF_BROADCAST;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = 4;
/* Put in this stuff here, so we don't have to export the symbols
* to the chipset drivers.
@@ -541,7 +544,6 @@ arcnet_close(struct device *dev)
#ifdef CONFIG_ARCNET_ETH
/* free the ethernet-encap protocol device */
lp->edev->priv=NULL;
- dev_close(lp->edev);
unregister_netdev(lp->edev);
kfree(lp->edev->name);
kfree(lp->edev);
@@ -551,7 +553,6 @@ arcnet_close(struct device *dev)
#ifdef CONFIG_ARCNET_1051
/* free the RFC1051-encap protocol device */
lp->sdev->priv=NULL;
- dev_close(lp->sdev);
unregister_netdev(lp->sdev);
kfree(lp->sdev->name);
kfree(lp->sdev);
@@ -919,19 +920,23 @@ int arcnet_go_tx(struct device *dev,int enable_irq)
void
arcnet_interrupt(int irq,void *dev_id,struct pt_regs *regs)
{
- struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct device *dev = dev_id;
struct arcnet_local *lp;
if (dev==NULL)
{
- BUGLVL(D_DURING)
- printk(KERN_DEBUG "arcnet: irq %d for unknown device.\n", irq);
+ BUGMSG(D_DURING, "arcnet: irq %d for unknown device.\n", irq);
return;
}
BUGMSG(D_DURING,"in arcnet_interrupt\n");
lp=(struct arcnet_local *)dev->priv;
+ if (!lp)
+ {
+ BUGMSG(D_DURING, "arcnet: irq ignored.\n");
+ return;
+ }
/* RESET flag was enabled - if !dev->start, we must clear it right
* away (but nothing else) since inthandler() is never called.
diff --git a/drivers/net/ariadne.c b/drivers/net/ariadne.c
index 884398c53..667bd75fc 100644
--- a/drivers/net/ariadne.c
+++ b/drivers/net/ariadne.c
@@ -6,7 +6,7 @@
* Peter De Schrijver
* (Peter.DeSchrijver@linux.cc.kuleuven.ac.be)
*
- * ----------------------------------------------------------------------------------
+ * ---------------------------------------------------------------------------
*
* This program is based on
*
@@ -20,13 +20,13 @@
* MC68230: Parallel Interface/Timer (PI/T)
* Motorola Semiconductors, December, 1983
*
- * ----------------------------------------------------------------------------------
+ * ---------------------------------------------------------------------------
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of the Linux
* distribution for more details.
*
- * ----------------------------------------------------------------------------------
+ * ---------------------------------------------------------------------------
*
* The Ariadne is a Zorro-II board made by Village Tronic. It contains:
*
@@ -107,7 +107,7 @@ struct ariadne_private {
struct net_device_stats stats;
char tx_full;
unsigned long lock;
- int key;
+ unsigned int key;
};
@@ -148,13 +148,13 @@ static void memcpyw(u_short *dest, u_short *src, int len)
__initfunc(int ariadne_probe(struct device *dev))
{
- int key;
- struct ConfigDev *cd;
+ unsigned int key;
+ const struct ConfigDev *cd;
u_long board;
struct ariadne_private *priv;
/* Ethernet is part 0, Parallel is part 1 */
- if ((key = zorro_find(MANUF_VILLAGE_TRONIC, PROD_ARIADNE, 0, 0))) {
+ if ((key = zorro_find(ZORRO_PROD_VILLAGE_TRONIC_ARIADNE, 0, 0))) {
cd = zorro_get_board(key);
if ((board = (u_long)cd->cd_BoardAddr)) {
dev->dev_addr[0] = 0x00;
diff --git a/drivers/net/at1700.c b/drivers/net/at1700.c
index c75274c01..cbe2d968a 100644
--- a/drivers/net/at1700.c
+++ b/drivers/net/at1700.c
@@ -193,7 +193,7 @@ __initfunc(int at1700_probe1(struct device *dev, short ioaddr))
| (read_eeprom(ioaddr, 0)>>14)];
/* Snarf the interrupt vector now. */
- if (request_irq(irq, &net_interrupt, 0, "at1700", NULL)) {
+ if (request_irq(irq, &net_interrupt, 0, "at1700", dev)) {
printk ("AT1700 found at %#3x, but it's unusable due to a conflict on"
"IRQ %d.\n", ioaddr, irq);
return EAGAIN;
@@ -212,7 +212,6 @@ __initfunc(int at1700_probe1(struct device *dev, short ioaddr))
dev->base_addr = ioaddr;
dev->irq = irq;
- irq2dev_map[irq] = dev;
for(i = 0; i < 3; i++) {
unsigned short eeprom_val = read_eeprom(ioaddr, 4+i);
@@ -435,7 +434,7 @@ net_send_packet(struct sk_buff *skb, struct device *dev)
static void
net_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct device *dev = dev_id;
struct net_local *lp;
int ioaddr, status;
@@ -655,8 +654,7 @@ cleanup_module(void)
dev_at1700.priv = NULL;
/* If we don't do this, we can't re-insmod it later. */
- free_irq(dev_at1700.irq, NULL);
- irq2dev_map[dev_at1700.irq] = NULL;
+ free_irq(dev_at1700.irq, &dev_at1700);
release_region(dev_at1700.base_addr, AT1700_IO_EXTENT);
}
#endif /* MODULE */
diff --git a/drivers/net/atarilance.c b/drivers/net/atarilance.c
index 764416d79..3d7b5d288 100644
--- a/drivers/net/atarilance.c
+++ b/drivers/net/atarilance.c
@@ -763,14 +763,6 @@ static int lance_start_xmit( struct sk_buff *skb, struct device *dev )
return( 0 );
}
- if (skb == NULL) {
- dev_tint( dev );
- return( 0 );
- }
-
- if (skb->len <= 0)
- return( 0 );
-
DPRINTK( 2, ( "%s: lance_start_xmit() called, csr0 %4.4x.\n",
dev->name, DREG ));
diff --git a/drivers/net/atp.c b/drivers/net/atp.c
index 997368ef4..578a5253a 100644
--- a/drivers/net/atp.c
+++ b/drivers/net/atp.c
@@ -327,9 +327,7 @@ static int net_open(struct device *dev)
/* The interrupt line is turned off (tri-stated) when the device isn't in
use. That's especially important for "attached" interfaces where the
port or interrupt may be shared. */
- if (irq2dev_map[dev->irq] != 0
- || (irq2dev_map[dev->irq] = dev) == 0
- || request_irq(dev->irq, &net_interrupt, 0, "ATP", NULL)) {
+ if (request_irq(dev->irq, &net_interrupt, 0, "ATP", dev)) {
return -EAGAIN;
}
@@ -479,7 +477,7 @@ net_send_packet(struct sk_buff *skb, struct device *dev)
static void
net_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
- struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct device *dev = dev_id;
struct net_local *lp;
int ioaddr, status, boguscount = 20;
static int num_tx_since_rx = 0;
@@ -729,8 +727,7 @@ net_close(struct device *dev)
/* Free the IRQ line. */
outb(0x00, ioaddr + PAR_CONTROL);
- free_irq(dev->irq, NULL);
- irq2dev_map[dev->irq] = 0;
+ free_irq(dev->irq, dev);
/* Leave the hardware in a reset state. */
write_reg_high(ioaddr, CMR1, CMR1h_RESET);
diff --git a/drivers/net/auto_irq.c b/drivers/net/auto_irq.c
index c91da9c52..efeaeb52c 100644
--- a/drivers/net/auto_irq.c
+++ b/drivers/net/auto_irq.c
@@ -39,76 +39,19 @@ static const char *version=
#include <asm/irq.h>
#include <linux/netdevice.h>
-struct device *irq2dev_map[16] = {0, 0, /* ... zeroed */};
+static unsigned long irqs;
-unsigned long irqs_busy = 0x2147; /* The set of fixed IRQs (keyboard, timer, etc) */
-unsigned long irqs_used = 0x0001; /* The set of fixed IRQs sometimes enabled. */
-unsigned long irqs_reserved = 0x0000; /* An advisory "reserved" table. */
-unsigned long irqs_shared = 0x0000; /* IRQ lines "shared" among conforming cards.*/
-
-static volatile unsigned long irq_bitmap; /* The irqs we actually found. */
-static unsigned long irq_handled; /* The irq lines we have a handler on. */
-static volatile int irq_number; /* The latest irq number we actually found. */
-
-static void autoirq_probe(int irq, void *dev_id, struct pt_regs * regs)
-{
- irq_number = irq;
- set_bit(irq, (void *)&irq_bitmap); /* irq_bitmap |= 1 << irq; */
- disable_irq(irq);
- return;
-}
-
-int autoirq_setup(int waittime)
+void autoirq_setup(int waittime)
{
- int i, mask;
- int timeout = jiffies + waittime;
- int boguscount = (waittime*loops_per_sec) / 100;
-
- irq_handled = 0;
- for (i = 0; i < 16; i++) {
- if (test_bit(i, &irqs_busy) == 0
- && request_irq(i, autoirq_probe, SA_INTERRUPT, "irq probe", NULL) == 0)
- set_bit(i, (void *)&irq_handled); /* irq_handled |= 1 << i;*/
- }
- /* Update our USED lists. */
- irqs_used |= ~irq_handled;
- irq_number = 0;
- irq_bitmap = 0;
-
- /* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */
- while (timeout > jiffies && --boguscount > 0)
- ;
-
- for (i = 0, mask = 0x01; i < 16; i++, mask <<= 1) {
- if (irq_bitmap & irq_handled & mask) {
- irq_handled &= ~mask;
-#ifdef notdef
- printk(" Spurious interrupt on IRQ %d\n", i);
-#endif
- free_irq(i, NULL);
- }
- }
- return irq_handled;
+ irqs = probe_irq_on();
}
+#define BUSY_LOOP_UNTIL(j) while ((long)(jiffies-(j)) < 0) ;
int autoirq_report(int waittime)
{
- int i;
- int timeout = jiffies+waittime;
- int boguscount = (waittime*loops_per_sec) / 100;
-
- /* Hang out at least <waittime> jiffies waiting for the IRQ. */
-
- while (timeout > jiffies && --boguscount > 0)
- if (irq_number)
- break;
-
- /* Retract the irq handlers that we installed. */
- for (i = 0; i < 16; i++) {
- if (test_bit(i, (void *)&irq_handled))
- free_irq(i, NULL);
- }
- return irq_number;
+ unsigned long delay = jiffies + waittime;
+ BUSY_LOOP_UNTIL(delay)
+ return probe_irq_off(irqs);
}
/*
diff --git a/drivers/net/bpqether.c b/drivers/net/bpqether.c
index 2f698a747..54d63121e 100644
--- a/drivers/net/bpqether.c
+++ b/drivers/net/bpqether.c
@@ -77,7 +77,6 @@
#include <linux/stat.h>
#include <linux/firewall.h>
#include <linux/module.h>
-#include <linux/net_alias.h>
#include <linux/init.h>
#include <net/ip.h>
@@ -159,9 +158,6 @@ static __inline__ int dev_is_ethdev(struct device *dev)
return (
dev->type == ARPHRD_ETHER
&& strncmp(dev->name, "dummy", 5)
-#ifdef CONFIG_NET_ALIAS
- && !net_alias_is(dev)
-#endif
);
}
@@ -552,17 +548,7 @@ static int bpq_new_device(struct device *dev)
memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
memcpy(dev->dev_addr, ax25_defaddr, AX25_ADDR_LEN);
- /* preset with reasonable values */
-
dev->flags = 0;
- dev->family = AF_INET;
-
-#ifdef CONFIG_INET
- dev->pa_addr = in_aton("192.168.0.1");
- dev->pa_brdaddr = in_aton("192.168.0.255");
- dev->pa_mask = in_aton("255.255.255.0");
- dev->pa_alen = 4;
-#endif
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
dev->hard_header = ax25_encapsulate;
diff --git a/drivers/net/bsd_comp.c b/drivers/net/bsd_comp.c
index 55dd95553..aab014bc1 100644
--- a/drivers/net/bsd_comp.c
+++ b/drivers/net/bsd_comp.c
@@ -39,17 +39,18 @@
/*
* This version is for use with contiguous buffers on Linux-derived systems.
*
- * ==FILEVERSION 4==
+ * ==FILEVERSION 970607==
*
* NOTE TO MAINTAINERS:
- * If you modify this file at all, increment the number above.
+ * If you modify this file at all, please set the number above to the
+ * date of the modification as YYMMDD (year month day).
* bsd_comp.c is shipped with a PPP distribution as well as with
* the kernel; if everyone increases the FILEVERSION number above,
* then scripts can do the right thing when deciding whether to
* install a new bsd_comp.c file. Don't change the format of that
* line otherwise, so the installation script can recognize it.
*
- * $Id: bsd_comp.c,v 1.1 1994/12/08 01:59:58 paulus Exp $
+ * From: bsd_comp.c,v 1.3 1994/12/08 01:59:58 paulus Exp
*/
#ifndef MODULE
@@ -57,7 +58,6 @@
#endif
#include <linux/module.h>
-
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
@@ -76,7 +76,6 @@
#include <asm/system.h>
#include <asm/bitops.h>
-#include <asm/uaccess.h>
#include <asm/byteorder.h>
#include <linux/if.h>
@@ -89,14 +88,6 @@
#include <linux/ppp_defs.h>
-#ifdef NEW_SKBUFF
-# /*nodep*/ include <linux/netprotocol.h>
-#endif
-
-#include <linux/ip.h>
-#include <linux/tcp.h>
-#include <linux/if_arp.h>
-
#undef PACKETPTR
#define PACKETPTR 1
#include <linux/ppp-comp.h>
@@ -142,14 +133,16 @@ struct bsd_dict {
union { /* hash value */
unsigned long fcode;
struct {
-#if defined(__LITTLE_ENDIAN) /* Little endian order */
+#if defined(__LITTLE_ENDIAN) /* Little endian order */
unsigned short prefix; /* preceding code */
unsigned char suffix; /* last character of new code */
unsigned char pad;
-#elif defined(__BIG_ENDIAN) /* Big endian order */
+#elif defined(__BIG_ENDIAN) /* Big endian order */
unsigned char pad;
unsigned char suffix; /* last character of new code */
unsigned short prefix; /* preceding code */
+#else
+#error Endianness not defined...
#endif
} hs;
} f;
@@ -250,7 +243,6 @@ bsd_clear(struct bsd_db *db)
db->n_bits = BSD_INIT_BITS;
db->bytes_out = 0;
db->in_count = 0;
- db->incomp_count = 0;
db->ratio = 0;
db->checkpoint = CHECK_GAP;
}
@@ -685,7 +677,7 @@ static int bsd_compress (void *state, unsigned char *rptr, unsigned char *obuf,
/* Skip the input header */
rptr += PPP_HDRLEN;
isize -= PPP_HDRLEN;
- ilen = ++isize; /* This is off by one, but that is what is in draft! */
+ ilen = ++isize; /* Low byte of protocol is counted as input */
while (--ilen > 0)
{
@@ -774,7 +766,7 @@ nomatch:
OUTPUT(ent); /* output the last code */
- db->bytes_out += olen; /* Do not count bytes from here */
+ db->bytes_out += olen - PPP_HDRLEN - BSD_OVHD;
db->uncomp_bytes += isize;
db->in_count += isize;
++db->uncomp_count;
diff --git a/drivers/net/com20020.c b/drivers/net/com20020.c
index 1fb3a0836..cd02f0035 100644
--- a/drivers/net/com20020.c
+++ b/drivers/net/com20020.c
@@ -1,4 +1,4 @@
-/* $Id: com20020.c,v 1.2 1997/09/05 08:57:50 mj Exp $
+/* $Id: com20020.c,v 1.6 1997/11/09 11:04:58 mj Exp $
Written 1997 by David Woodhouse <dwmw2@cam.ac.uk>
@@ -215,7 +215,7 @@ void put_whole_buffer (struct device *dev, unsigned offset, unsigned length, cha
static const char *version =
- "com20020.c: v2.92 97/09/02 Avery Pennarun <apenwarr@bond.net> et al.\n";
+ "com20020.c: v3.00 97/11/09 Avery Pennarun <apenwarr@bond.net> et al.\n";
/****************************************************************************
* *
@@ -336,12 +336,11 @@ __initfunc(int arc20020_found(struct device *dev,int ioaddr,int airq))
struct arcnet_local *lp;
/* reserve the irq */
- if (request_irq(airq,&arcnet_interrupt,0,"arcnet (COM20020)",NULL))
+ if (request_irq(airq,&arcnet_interrupt,0,"arcnet (COM20020)",dev))
{
BUGMSG(D_NORMAL,"Can't get IRQ %d!\n",airq);
return -ENODEV;
}
- irq2dev_map[airq]=dev;
dev->irq=airq;
/* reserve the I/O region - guaranteed to work by check_region */
@@ -355,8 +354,7 @@ __initfunc(int arc20020_found(struct device *dev,int ioaddr,int airq))
dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL);
if (dev->priv == NULL)
{
- irq2dev_map[airq] = NULL;
- free_irq(airq,NULL);
+ free_irq(airq,dev);
release_region(ioaddr,ARCNET_TOTAL_SIZE);
return -ENOMEM;
}
@@ -924,7 +922,8 @@ arc20020_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
* frame.
*/
- put_whole_buffer(dev, lp->txbuf*512+offset,4,"\0\0xff\0xff\0xff");
+ put_buffer_byte(dev, lp->txbuf*512+offset,hdr[0]);
+ put_whole_buffer(dev, lp->txbuf*512+offset+1,3,"\377\377\377");
offset+=4;
}
else /* "other" Exception packet */
@@ -1026,8 +1025,7 @@ void cleanup_module(void)
if (dev->irq)
{
- irq2dev_map[dev->irq] = NULL;
- free_irq(dev->irq,NULL);
+ free_irq(dev->irq,dev);
}
if (dev->base_addr) release_region(dev->base_addr,ARCNET_TOTAL_SIZE);
diff --git a/drivers/net/com90io.c b/drivers/net/com90io.c
index 9bd902151..bb88552b1 100644
--- a/drivers/net/com90io.c
+++ b/drivers/net/com90io.c
@@ -1,4 +1,4 @@
-/* $Id: com90io.c,v 1.2 1997/09/05 08:57:52 mj Exp $
+/* $Id: com90io.c,v 1.6 1997/11/09 11:04:59 mj Exp $
Written 1997 by David Woodhouse <dwmw2@cam.ac.uk>
@@ -184,7 +184,7 @@ void put_whole_buffer (struct device *dev, unsigned offset, unsigned length, cha
static const char *version =
- "com90io.c: v2.91 97/08/19 Avery Pennarun <apenwarr@bond.net> et al.\n";
+ "com90io.c: v3.00 97/11/09 Avery Pennarun <apenwarr@bond.net> et al.\n";
/****************************************************************************
@@ -296,12 +296,11 @@ __initfunc(int arc90io_found(struct device *dev,int ioaddr,int airq))
struct arcnet_local *lp;
/* reserve the irq */
- if (request_irq(airq,&arcnet_interrupt,0,"arcnet (COM90xx-IO)",NULL))
+ if (request_irq(airq,&arcnet_interrupt,0,"arcnet (COM90xx-IO)",dev))
{
BUGMSG(D_NORMAL,"Can't get IRQ %d!\n",airq);
return -ENODEV;
}
- irq2dev_map[airq]=dev;
dev->irq=airq;
/* reserve the I/O region - guaranteed to work by check_region */
@@ -315,8 +314,7 @@ __initfunc(int arc90io_found(struct device *dev,int ioaddr,int airq))
dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL);
if (dev->priv == NULL)
{
- irq2dev_map[airq] = NULL;
- free_irq(airq,NULL);
+ free_irq(airq,dev);
release_region(ioaddr,ARCNET_TOTAL_SIZE);
return -ENOMEM;
}
@@ -807,7 +805,8 @@ arc90io_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
* frame.
*/
- put_whole_buffer(dev, lp->txbuf*512+offset,4,"\0\0xff\0xff\0xff");
+ put_buffer_byte(dev, lp->txbuf*512+offset,hdr[0]);
+ put_whole_buffer(dev, lp->txbuf*512+offset+1,3,"\377\377\377");
offset+=4;
}
else /* "other" Exception packet */
@@ -905,8 +904,7 @@ void cleanup_module(void)
if (dev->irq)
{
- irq2dev_map[dev->irq] = NULL;
- free_irq(dev->irq,NULL);
+ free_irq(dev->irq,dev);
}
if (dev->base_addr) release_region(dev->base_addr,ARCNET_TOTAL_SIZE);
diff --git a/drivers/net/com90xx.c b/drivers/net/com90xx.c
index ec88b7354..4a5b0d70c 100644
--- a/drivers/net/com90xx.c
+++ b/drivers/net/com90xx.c
@@ -1,4 +1,4 @@
-/* $Id: com90xx.c,v 1.3 1997/09/05 18:27:23 mj Exp $
+/* $Id: com90xx.c,v 1.6 1997/11/09 11:05:01 mj Exp $
Derived from the original arcnet.c,
Written 1994-1996 by Avery Pennarun,
@@ -154,7 +154,7 @@ extern int arcnet_num_devs;
#define ARCRESET inb(_RESET)
static const char *version =
- "com90xx.c: v2.92 97/09/02 Avery Pennarun <apenwarr@bond.net> et al.\n";
+ "com90xx.c: v3.00 97/11/09 Avery Pennarun <apenwarr@bond.net> et al.\n";
/****************************************************************************
@@ -542,12 +542,11 @@ __initfunc(static int arc90xx_found(struct device *dev,int ioaddr,int airq, u_lo
int mirror_size;
/* reserve the irq */
- if (request_irq(airq,&arcnet_interrupt,0,"arcnet (90xx)",NULL))
+ if (request_irq(airq,&arcnet_interrupt,0,"arcnet (90xx)",dev))
{
BUGMSG(D_NORMAL,"Can't get IRQ %d!\n",airq);
return -ENODEV;
}
- irq2dev_map[airq]=dev;
dev->irq=airq;
/* reserve the I/O region - guaranteed to work by check_region */
@@ -585,8 +584,7 @@ __initfunc(static int arc90xx_found(struct device *dev,int ioaddr,int airq, u_lo
dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL);
if (dev->priv == NULL)
{
- irq2dev_map[airq] = NULL;
- free_irq(airq,NULL);
+ free_irq(airq,dev);
release_region(ioaddr,ARCNET_TOTAL_SIZE);
return -ENOMEM;
}
@@ -1203,8 +1201,7 @@ void cleanup_module(void)
if (dev->irq)
{
- irq2dev_map[dev->irq] = NULL;
- free_irq(dev->irq,NULL);
+ free_irq(dev->irq,dev);
}
if (dev->base_addr) release_region(dev->base_addr,ARCNET_TOTAL_SIZE);
diff --git a/drivers/net/cops.c b/drivers/net/cops.c
index 66e3c5fea..2560139b9 100644
--- a/drivers/net/cops.c
+++ b/drivers/net/cops.c
@@ -23,10 +23,11 @@
* Can set board type in insmod
* Hooks for cops_setup routine
* (not yet implemented).
+ * 19971101 Jay Schulist Fixes for multiple lt* devices.
*/
static const char *version =
- "cops.c:v0.01 3/17/97 Jay Schulist <Jay.Schulist@spacs.k12.wi.us>\n";
+ "cops.c:v0.02 3/17/97 Jay Schulist <Jay.Schulist@spacs.k12.wi.us>\n";
/*
* Sources:
* COPS Localtalk SDK. This provides almost all of the information
@@ -62,6 +63,7 @@ static const char *version =
#include <asm/io.h>
#include <asm/dma.h>
#include <linux/errno.h>
+#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -119,9 +121,9 @@ static int irq = 0; /* Default IRQ */
* TANGENT driver mode:
* Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200
* DAYNA driver mode:
- * Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95, Farallon PhoneNET PC III
+ * Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95,
+ * Farallon PhoneNET PC III, Farallon PhoneNET PC II
* Other cards possibly supported mode unkown though:
- * Farallon PhoneNET PC II
* Dayna DL2000 (Full length)
*
* Cards NOT supported by this driver but supported by the ltpc.c
@@ -165,29 +167,9 @@ struct cops_local
int board; /* Holds what board type is. */
int nodeid; /* Set to 1 once have nodeid. */
unsigned char node_acquire; /* Node ID when acquired. */
+ struct at_addr node_addr; /* Full node addres */
};
-/* Allocate a new device with the form of lt0, lt1, lt2, etc. */
-struct device *cops_dev_alloc(char *name)
-{
- int i=0;
- struct device *d=kmalloc(sizeof(struct device)+8, GFP_KERNEL);
-
- memset(d,0,sizeof(*d)); /* Clear the structure */
- if(d==NULL)
- return NULL;
- d->name=(char *)(d+1); /* Name string space */
-
- /* Get next free device name */
- for(i=0;i<100;i++)
- {
- sprintf(d->name,name,i);
- if(dev_get(d->name)==NULL)
- return d;
- }
- return NULL; /* Over 100 of the things .. bail out! */
-}
-
/* Index to functions, as function prototypes. */
extern int cops_probe (struct device *dev);
static int cops_probe1 (struct device *dev, int ioaddr);
@@ -218,29 +200,27 @@ static struct enet_statistics *cops_get_stats (struct device *dev);
* If dev->base_addr == 2, allocate space for the device and return success
* (detachable devices only).
*/
-int cops_probe(struct device *dev)
+__initfunc(int cops_probe(struct device *dev))
{
int i;
int base_addr = dev ? dev->base_addr : 0;
- if (base_addr == 0 && io)
+ if(base_addr == 0 && io)
base_addr=io;
- if (base_addr > 0x1ff) /* Check a single specified location. */
+ if(base_addr > 0x1ff) /* Check a single specified location. */
return cops_probe1(dev, base_addr);
- else if (base_addr != 0) /* Don't probe at all. */
+ else if(base_addr != 0) /* Don't probe at all. */
return -ENXIO;
- for (i=0; cops_portlist[i]; i++) {
+ for(i=0; cops_portlist[i]; i++) {
int ioaddr = cops_portlist[i];
- if (check_region(ioaddr, COPS_IO_EXTENT))
+ if(check_region(ioaddr, COPS_IO_EXTENT))
continue;
- if (cops_probe1(dev, ioaddr) == 0)
+ if(cops_probe1(dev, ioaddr) == 0)
return 0;
}
- /* No "lt" devices found. */
- printk(KERN_WARNING "%s: No COPS localtalk devices found!\n", dev->name);
return -ENODEV;
}
@@ -249,7 +229,7 @@ int cops_probe(struct device *dev)
* probes on the ISA bus. A good device probes avoids doing writes, and
* verifies that the correct device exists and functions.
*/
-static int cops_probe1(struct device *dev, int ioaddr)
+__initfunc(static int cops_probe1(struct device *dev, int ioaddr))
{
struct cops_local *lp;
static unsigned version_printed = 0;
@@ -258,17 +238,7 @@ static int cops_probe1(struct device *dev, int ioaddr)
int board = board_type;
-/* Defined here to save some trouble */
-
- /* Allocate a new 'dev' if needed. */
- if (dev == NULL)
- {
- dev=cops_dev_alloc(dev->name); /* New "lt" device; beyond lt0. */
- if(dev==NULL)
- return -ENOMEM;
- }
-
- if (cops_debug && version_printed++ == 0)
+ if(cops_debug && version_printed++ == 0)
printk("%s", version);
/* Fill in the 'dev' fields. */
@@ -280,21 +250,20 @@ static int cops_probe1(struct device *dev, int ioaddr)
* can use the interrupt, and this marks the irq as busy. Jumpered
* interrupts are typically not reported by the boards, and we must
* used AutoIRQ to find them.
- *
*/
- if (dev->irq < 2 && irq)
+ if(dev->irq < 2 && irq)
dev->irq = irq;
- if (dev->irq < 2)
+ if(dev->irq < 2)
{
irqaddr = cops_irq(ioaddr, board); /* COPS AutoIRQ routine */
- if (irqaddr == 0)
+ if(irqaddr == 0)
return -EAGAIN; /* No IRQ found on this port */
else
dev->irq = irqaddr;
}
- else if (dev->irq == 2)
+ else if(dev->irq == 2)
/*
* Fixup for users that don't know that IRQ 2 is really
* IRQ 9, or don't know which one to set.
@@ -302,18 +271,19 @@ static int cops_probe1(struct device *dev, int ioaddr)
dev->irq = 9;
/* Snarf the interrupt now. */
- irqval = request_irq(dev->irq, &cops_interrupt, 0, cardname, NULL);
- if (irqval)
- {
- printk(KERN_WARNING "%s: Unable to get IRQ %d (irqval=%d).\n", dev->name, dev->irq, irqval);
- return -EAGAIN;
- }
+ irqval = request_irq(dev->irq, &cops_interrupt, 0, cardname, dev);
+
+ /* If its in use set it to 0 and disallow open() calls.. users can still
+ ifconfig the irq one day */
+
+ if(irqval)
+ dev->irq = 0;
dev->hard_start_xmit = &cops_send_packet;
/* Initialize the device structure. */
dev->priv = kmalloc(sizeof(struct cops_local), GFP_KERNEL);
- if (dev->priv == NULL)
+ if(dev->priv == NULL)
return -ENOMEM;
lp = (struct cops_local *)dev->priv;
@@ -347,7 +317,7 @@ static int cops_probe1(struct device *dev, int ioaddr)
return 0;
}
-static int cops_irq (int ioaddr, int board)
+__initfunc(static int cops_irq (int ioaddr, int board))
{ /*
* This does not use the IRQ to determine where the IRQ is. We just
* assume that when we get a correct status response that is the IRQ then.
@@ -379,7 +349,7 @@ static int cops_irq (int ioaddr, int board)
if(board==DAYNA)
{
status = (inb(ioaddr+DAYNA_CARD_STATUS)&3);
- if (status == 1)
+ if(status == 1)
return irqaddr;
}
if(board==TANGENT)
@@ -398,7 +368,11 @@ static int cops_irq (int ioaddr, int board)
*/
static int cops_open(struct device *dev)
{
- irq2dev_map[dev->irq] = dev;
+ if(dev->irq==0)
+ {
+ printk(KERN_WARNING "%s: No irq line set.\n", dev->name);
+ return -EAGAIN;
+ }
cops_jumpstart(dev); /* Start the card up. */
@@ -510,7 +484,7 @@ static void cops_load (struct device *dev)
/* Get card's firmware code and do some checks on it. */
#ifdef CONFIG_COPS_DAYNA
- if (lp->board==DAYNA)
+ if(lp->board==DAYNA)
{
ltf->length=sizeof(ffdrv_code);
ltf->data=ffdrv_code;
@@ -518,7 +492,7 @@ static void cops_load (struct device *dev)
else
#endif
#ifdef CONFIG_COPS_TANGENT
- if (lp->board==TANGENT)
+ if(lp->board==TANGENT)
{
ltf->length=sizeof(ltdrv_code);
ltf->data=ltdrv_code;
@@ -597,7 +571,7 @@ static int cops_nodeid (struct device *dev, int nodeid)
struct cops_local *lp = (struct cops_local *) dev->priv;
int ioaddr = dev->base_addr;
- if (lp->board == DAYNA)
+ if(lp->board == DAYNA)
{
/* Empty any pending adapter responses. */
while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0)
@@ -614,7 +588,7 @@ static int cops_nodeid (struct device *dev, int nodeid)
outb(nodeid, ioaddr); /* Suggest node address. */
}
- if (lp->board == TANGENT)
+ if(lp->board == TANGENT)
{
/* Empty any pending adapter responses. */
while(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY)
@@ -666,12 +640,12 @@ static int cops_nodeid (struct device *dev, int nodeid)
*/
static void cops_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
- struct device *dev = (struct device *) irq2dev_map[irq];
+ struct device *dev = dev_id;
struct cops_local *lp;
int ioaddr, status;
int boguscount = 0;
- if (dev == NULL)
+ if(dev == NULL)
{
printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq);
return;
@@ -695,7 +669,7 @@ static void cops_interrupt(int irq, void *dev_id, struct pt_regs * regs)
else
{
status=inb(ioaddr+TANG_CARD_STATUS);
- if (status&TANG_RX_READY)
+ if(status&TANG_RX_READY)
cops_rx(dev);
}
@@ -749,7 +723,7 @@ static void cops_rx(struct device *dev)
/* Malloc up new buffer. */
skb = dev_alloc_skb(pkt_len);
- if (skb == NULL)
+ if(skb == NULL)
{
printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);
lp->stats.rx_dropped++;
@@ -769,7 +743,7 @@ static void cops_rx(struct device *dev)
sti(); /* Restore interrupts. */
/* Check for bad response length */
- if (pkt_len < 0 || pkt_len > MAX_LLAP_SIZE)
+ if(pkt_len < 0 || pkt_len > MAX_LLAP_SIZE)
{
printk(KERN_NOTICE "%s: Bad packet length of %d bytes.\n", dev->name, pkt_len);
lp->stats.tx_errors++;
@@ -816,14 +790,14 @@ static int cops_send_packet(struct sk_buff *skb, struct device *dev)
struct cops_local *lp = (struct cops_local *)dev->priv;
int ioaddr = dev->base_addr;
- if (dev->tbusy)
+ if(dev->tbusy)
{
/*
* If we get here, some higher level has decided we are broken.
* There should really be a "kick me" function call instead.
*/
int tickssofar = jiffies - dev->trans_start;
- if (tickssofar < 5)
+ if(tickssofar < 5)
return 1;
lp->stats.tx_errors++;
if(lp->board==TANGENT)
@@ -841,7 +815,7 @@ static int cops_send_packet(struct sk_buff *skb, struct device *dev)
* Block a timer-based transmit from overlapping. This could better be
* done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
*/
- if (test_and_set_bit(0, (void*) &dev->tbusy) != 0)
+ if(test_and_set_bit(0, (void*) &dev->tbusy) != 0)
printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name);
else
{
@@ -918,7 +892,7 @@ static int cops_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
{
struct cops_local *lp = (struct cops_local *)dev->priv;
struct sockaddr_at *sa=(struct sockaddr_at *)&ifr->ifr_addr;
- struct at_addr *aa=(struct at_addr *)&dev->pa_addr;
+ struct at_addr *aa=(struct at_addr *)&lp->node_addr;
switch(cmd)
{
@@ -954,7 +928,6 @@ static int cops_close(struct device *dev)
{
dev->tbusy = 1;
dev->start = 0;
- irq2dev_map[dev->irq] = 0;
#ifdef MODULE
MOD_DEC_USE_COUNT;
@@ -974,9 +947,11 @@ static struct enet_statistics *cops_get_stats(struct device *dev)
}
#ifdef MODULE
-static struct device dev_cops =
+static char lt_name[16];
+
+static struct device cops0_dev =
{
- "lt0", /* device name */
+ lt_name, /* device name */
0, 0, 0, 0,
0x0, 0, /* I/O address, IRQ */
0, 0, 0, NULL, cops_probe
@@ -989,16 +964,20 @@ MODULE_PARM(board_type, "i");
int init_module(void)
{
- int result;
+ int result, err;
- if (io == 0)
+ if(io == 0)
printk(KERN_WARNING "%s: You shouldn't use auto-probing with insmod!\n", cardname);
/* Copy the parameters from insmod into the device structure. */
- dev_cops.base_addr = io;
- dev_cops.irq = irq;
+ cops0_dev.base_addr = io;
+ cops0_dev.irq = irq;
+
+ err=dev_alloc_name(&cops0_dev, "lt%d");
+ if(err < 0)
+ return err;
- if ((result = register_netdev(&dev_cops)) != 0)
+ if((result = register_netdev(&cops0_dev)) != 0)
return result;
return 0;
@@ -1007,12 +986,10 @@ int init_module(void)
void cleanup_module(void)
{
/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
-
- free_irq(dev_cops.irq, NULL);
- release_region(dev_cops.base_addr, COPS_IO_EXTENT);
- unregister_netdev(&dev_cops);
-
- if (dev_cops.priv)
- kfree_s(dev_cops.priv, sizeof(struct cops_local));
+ unregister_netdev(&cops0_dev);
+ if(cops0_dev.priv)
+ kfree_s(cops0_dev.priv, sizeof(struct cops_local));
+ free_irq(cops0_dev.irq, &cops0_dev);
+ release_region(cops0_dev.base_addr, COPS_IO_EXTENT);
}
#endif /* MODULE */
diff --git a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c
index 66852e388..14224bff0 100644
--- a/drivers/net/cs89x0.c
+++ b/drivers/net/cs89x0.c
@@ -605,10 +605,10 @@ net_open(struct device *dev)
/* Allow interrupts to be generated by the chip */
writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
for (i = 2; i < CS8920_NO_INTS; i++) if ((1 << dev->irq) & lp->irq_map) {
- if (request_irq (i, NULL, 0, "cs8920", NULL) != -EBUSY) {
+ if (request_irq (i, NULL, 0, "cs8920", dev) != -EBUSY) {
write_irq(dev, lp->chip_type, i);
writereg(dev, PP_BufCFG, GENERATE_SW_INTERRUPT);
- if (request_irq (dev->irq = i, &net_interrupt, 0, "cs89x0", NULL) == 0)
+ if (request_irq (dev->irq = i, &net_interrupt, 0, "cs89x0", dev) == 0)
break;
}
}
@@ -626,13 +626,11 @@ net_open(struct device *dev)
}
writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
write_irq(dev, lp->chip_type, dev->irq);
- if (request_irq(dev->irq, &net_interrupt, 0, "cs89x0", NULL)) {
+ if (request_irq(dev->irq, &net_interrupt, 0, "cs89x0", dev)) {
return -EAGAIN;
}
}
- irq2dev_map[dev->irq] = dev;
-
/* set the Ethernet address */
for (i=0; i < ETH_ALEN/2; i++)
writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
@@ -657,8 +655,7 @@ net_open(struct device *dev)
printk("%s: EEPROM is configured for unavailable media\n", dev->name);
release_irq:
writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) & ~(SERIAL_TX_ON | SERIAL_RX_ON));
- free_irq(dev->irq, NULL);
- irq2dev_map[dev->irq] = 0;
+ free_irq(dev->irq, dev);
return -EAGAIN;
}
@@ -794,7 +791,7 @@ net_send_packet(struct sk_buff *skb, struct device *dev)
Handle the network interface interrupts. */
static void net_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
- struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct device *dev = dev_id;
struct net_local *lp;
int ioaddr, status;
@@ -921,9 +918,7 @@ net_close(struct device *dev)
dev->start = 0;
- free_irq(dev->irq, NULL);
-
- irq2dev_map[dev->irq] = 0;
+ free_irq(dev->irq, dev);
/* Update the statistics here. */
diff --git a/drivers/net/de4x5.c b/drivers/net/de4x5.c
index a199c7f8a..246355b2e 100644
--- a/drivers/net/de4x5.c
+++ b/drivers/net/de4x5.c
@@ -1,310 +1,337 @@
/* de4x5.c: A DIGITAL DC21x4x DECchip and DE425/DE434/DE435/DE450/DE500
- ethernet driver for Linux.
-
- Copyright 1994, 1995 Digital Equipment Corporation.
-
- Testing resources for this driver have been made available
- in part by NASA Ames Research Center (mjacob@nas.nasa.gov).
-
- The author may be reached at davies@maniac.ultranet.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 SOFTWARE IS PROVIDED ``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.
-
- You 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.
-
- Originally, this driver was written for the Digital Equipment
- Corporation series of EtherWORKS ethernet cards:
-
- DE425 TP/COAX EISA
- DE434 TP PCI
- DE435 TP/COAX/AUI PCI
- DE450 TP/COAX/AUI PCI
- DE500 10/100 PCI Fasternet
-
- but it will now attempt to support all cards which conform to the
- Digital Semiconductor SROM Specification. The driver currently
- recognises the following chips:
-
- DC21040 (no SROM)
- DC21041[A]
- DC21140[A]
-
- I plan to add DC2114[23] support ASAP, time permitting. So far the
- driver is known to work with the following cards:
-
- KINGSTON
- Linksys
- ZNYX342
- SMC8432
- SMC9332 (w/new SROM)
- ZNYX31[45]
- ZNYX346 10/100 4 port (can act as a 10/100 bridge!)
-
- The driver has been tested on a relatively busy network using the DE425,
- DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred
- 16M of data to a DECstation 5000/200 as follows:
-
- TCP UDP
- TX RX TX RX
- DE425 1030k 997k 1170k 1128k
- DE434 1063k 995k 1170k 1125k
- DE435 1063k 995k 1170k 1125k
- DE500 1063k 998k 1170k 1125k in 10Mb/s mode
-
- All values are typical (in kBytes/sec) from a sample of 4 for each
- measurement. Their error is +/-20k on a quiet (private) network and also
- depend on what load the CPU has.
-
- =========================================================================
- This driver has been written substantially from scratch, although its
- inheritance of style and stack interface from 'ewrk3.c' and in turn from
- Donald Becker's 'lance.c' should be obvious. With the module autoload of
- every usable DECchip board, I pinched Donald's 'next_module' field to
- link my modules together.
-
- Upto 15 EISA cards can be supported under this driver, limited primarily
- by the available IRQ lines. I have checked different configurations of
- multiple depca, EtherWORKS 3 cards and de4x5 cards and have not found a
- problem yet (provided you have at least depca.c v0.38) ...
-
- PCI support has been added to allow the driver to work with the DE434,
- DE435, DE450 and DE500 cards. The I/O accesses are a bit of a kludge due
- to the differences in the EISA and PCI CSR address offsets from the base
- address.
-
- The ability to load this driver as a loadable module has been included
- and used extensively during the driver development (to save those long
- reboot sequences). Loadable module support under PCI and EISA has been
- achieved by letting the driver autoprobe as if it were compiled into the
- kernel. Do make sure you're not sharing interrupts with anything that
- cannot accommodate interrupt sharing!
-
- To utilise this ability, you have to do 8 things:
-
- 0) have a copy of the loadable modules code installed on your system.
- 1) copy de4x5.c from the /linux/drivers/net directory to your favourite
- temporary directory.
- 2) for fixed autoprobes (not recommended), edit the source code near
- line 5005 to reflect the I/O address you're using, or assign these when
- loading by:
-
- insmod de4x5 io=0xghh where g = bus number
- hh = device number
-
- NB: autoprobing for modules is now supported by default. You may just
- use:
-
- insmod de4x5
-
- to load all available boards. For a specific board, still use
- the 'io=?' above.
- 3) compile de4x5.c, but include -DMODULE in the command line to ensure
- that the correct bits are compiled (see end of source code).
- 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a
- kernel with the de4x5 configuration turned off and reboot.
- 5) insmod de4x5 [io=0xghh]
- 6) run the net startup bits for your new eth?? interface(s) manually
- (usually /etc/rc.inet[12] at boot time).
- 7) enjoy!
-
- To unload a module, turn off the associated interface(s)
- 'ifconfig eth?? down' then 'rmmod de4x5'.
-
- Automedia detection is included so that in principal you can disconnect
- from, e.g. TP, reconnect to BNC and things will still work (after a
- pause whilst the driver figures out where its media went). My tests
- using ping showed that it appears to work....
-
- By default, the driver will now autodetect any DECchip based card.
- Should you have a need to restrict the driver to DIGITAL only cards, you
- can compile with a DEC_ONLY define, or if loading as a module, use the
- 'dec_only=1' parameter.
-
- I've changed the timing routines to use the kernel timer and scheduling
- functions so that the hangs and other assorted problems that occurred
- while autosensing the media should be gone. A bonus for the DC21040
- auto media sense algorithm is that it can now use one that is more in
- line with the rest (the DC21040 chip doesn't have a hardware timer).
- The downside is the 1 'jiffies' (10ms) resolution.
-
- IEEE 802.3u MII interface code has been added in anticipation that some
- products may use it in the future.
-
- The SMC9332 card has a non-compliant SROM which needs fixing - I have
- patched this driver to detect it because the SROM format used complies
- to a previous DEC-STD format.
-
- I have removed the buffer copies needed for receive on Intels. I cannot
- remove them for Alphas since the Tulip hardware only does longword
- aligned DMA transfers and the Alphas get alignment traps with non
- longword aligned data copies (which makes them really slow). No comment.
-
- I have added SROM decoding routines to make this driver work with any
- card that supports the Digital Semiconductor SROM spec. This will help
- all cards running the dc2114x series chips in particular. Cards using
- the dc2104x chips should run correctly with the basic driver. I'm in
- debt to <mjacob@feral.com> for the testing and feedback that helped get
- this feature working. So far we have tested KINGSTON, SMC8432, SMC9332
- (with the latest SROM complying with the SROM spec V3: their first was
- broken), ZNYX342 and LinkSys. ZYNX314 (dual 21041 MAC) and ZNYX 315
- (quad 21041 MAC) cards also appear to work despite their incorrectly
- wired IRQs.
-
- I have added a temporary fix for interrupt problems when some SCSI cards
- share the same interrupt as the DECchip based cards. The problem occurs
- because the SCSI card wants to grab the interrupt as a fast interrupt
- (runs the service routine with interrupts turned off) vs. this card
- which really needs to run the service routine with interrupts turned on.
- This driver will now add the interrupt service routine as a fast
- interrupt if it is bounced from the slow interrupt. THIS IS NOT A
- RECOMMENDED WAY TO RUN THE DRIVER and has been done for a limited time
- until people sort out their compatibility issues and the kernel
- interrupt service code is fixed. YOU SHOULD SEPARATE OUT THE FAST
- INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not
- run on the same interrupt. PCMCIA/CardBus is another can of worms...
-
- TO DO:
- ------
-
-
- Revision History
- ----------------
-
- Version Date Description
-
- 0.1 17-Nov-94 Initial writing. ALPHA code release.
- 0.2 13-Jan-95 Added PCI support for DE435's.
- 0.21 19-Jan-95 Added auto media detection.
- 0.22 10-Feb-95 Fix interrupt handler call <chris@cosy.sbg.ac.at>.
- Fix recognition bug reported by <bkm@star.rl.ac.uk>.
- Add request/release_region code.
- Add loadable modules support for PCI.
- Clean up loadable modules support.
- 0.23 28-Feb-95 Added DC21041 and DC21140 support.
- Fix missed frame counter value and initialisation.
- Fixed EISA probe.
- 0.24 11-Apr-95 Change delay routine to use <linux/udelay>.
- Change TX_BUFFS_AVAIL macro.
- Change media autodetection to allow manual setting.
- Completed DE500 (DC21140) support.
- 0.241 18-Apr-95 Interim release without DE500 Autosense Algorithm.
- 0.242 10-May-95 Minor changes.
- 0.30 12-Jun-95 Timer fix for DC21140.
- Portability changes.
- Add ALPHA changes from <jestabro@ant.tay1.dec.com>.
- Add DE500 semi automatic autosense.
- Add Link Fail interrupt TP failure detection.
- Add timer based link change detection.
- Plugged a memory leak in de4x5_queue_pkt().
- 0.31 13-Jun-95 Fixed PCI stuff for 1.3.1.
- 0.32 26-Jun-95 Added verify_area() calls in de4x5_ioctl() from a
- suggestion by <heiko@colossus.escape.de>.
- 0.33 8-Aug-95 Add shared interrupt support (not released yet).
- 0.331 21-Aug-95 Fix de4x5_open() with fast CPUs.
- Fix de4x5_interrupt().
- Fix dc21140_autoconf() mess.
- No shared interrupt support.
- 0.332 11-Sep-95 Added MII management interface routines.
- 0.40 5-Mar-96 Fix setup frame timeout <maartenb@hpkuipc.cern.ch>.
- Add kernel timer code (h/w is too flaky).
- Add MII based PHY autosense.
- Add new multicasting code.
- Add new autosense algorithms for media/mode
- selection using kernel scheduling/timing.
- Re-formatted.
- Made changes suggested by <jeff@router.patch.net>:
- Change driver to detect all DECchip based cards
- with DEC_ONLY restriction a special case.
- Changed driver to autoprobe as a module. No irq
- checking is done now - assume BIOS is good!
- Added SMC9332 detection <manabe@Roy.dsl.tutics.ac.jp>
- 0.41 21-Mar-96 Don't check for get_hw_addr checksum unless DEC card
- only <niles@axp745gsfc.nasa.gov>
- Fix for multiple PCI cards reported by <jos@xos.nl>
- Duh, put the SA_SHIRQ flag into request_interrupt().
- Fix SMC ethernet address in enet_det[].
- Print chip name instead of "UNKNOWN" during boot.
- 0.42 26-Apr-96 Fix MII write TA bit error.
- Fix bug in dc21040 and dc21041 autosense code.
- Remove buffer copies on receive for Intels.
- Change sk_buff handling during media disconnects to
- eliminate DUP packets.
- Add dynamic TX thresholding.
- Change all chips to use perfect multicast filtering.
- Fix alloc_device() bug <jari@markkus2.fimr.fi>
- 0.43 21-Jun-96 Fix unconnected media TX retry bug.
- Add Accton to the list of broken cards.
- Fix TX under-run bug for non DC21140 chips.
- Fix boot command probe bug in alloc_device() as
- reported by <koen.gadeyne@barco.com> and
- <orava@nether.tky.hut.fi>.
- Add cache locks to prevent a race condition as
- reported by <csd@microplex.com> and
- <baba@beckman.uiuc.edu>.
- Upgraded alloc_device() code.
- 0.431 28-Jun-96 Fix potential bug in queue_pkt() from discussion
- with <csd@microplex.com>
- 0.44 13-Aug-96 Fix RX overflow bug in 2114[023] chips.
- Fix EISA probe bugs reported by <os2@kpi.kharkov.ua>
- and <michael@compurex.com>.
- 0.441 9-Sep-96 Change dc21041_autoconf() to probe quiet BNC media
- with a loopback packet.
- 0.442 9-Sep-96 Include AUI in dc21041 media printout. Bug reported
- by <bhat@mundook.cs.mu.OZ.AU>
- 0.45 8-Dec-96 Include endian functions for PPC use, from work
- by <cort@cs.nmt.edu> and <g.thomas@opengroup.org>.
- 0.451 28-Dec-96 Added fix to allow autoprobe for modules after
- suggestion from <mjacob@feral.com>.
- 0.5 30-Jan-97 Added SROM decoding functions.
- Updated debug flags.
- Fix sleep/wakeup calls for PCI cards, bug reported
- by <cross@gweep.lkg.dec.com>.
- Added multi-MAC, one SROM feature from discussion
- with <mjacob@feral.com>.
- Added full module autoprobe capability.
- Added attempt to use an SMC9332 with broken SROM.
- Added fix for ZYNX multi-mac cards that didn't
- get their IRQs wired correctly.
- 0.51 13-Feb-97 Added endian fixes for the SROM accesses from
- <paubert@iram.es>
- Fix init_connection() to remove extra device reset.
- Fix MAC/PHY reset ordering in dc21140m_autoconf().
- Fix initialisation problem with lp->timeout in
- typeX_infoblock() from <paubert@iram.es>.
- Fix MII PHY reset problem from work done by
- <paubert@iram.es>.
- 0.52 26-Apr-97 Some changes may not credit the right people -
- a disk crash meant I lost some mail.
- Change RX interrupt routine to drop rather than
- defer packets to avoid hang reported by
- <g.thomas@opengroup.org>.
- Fix srom_exec() to return for COMPACT and type 1
- infoblocks.
- Added DC21142 and DC21143 functions.
- Added byte counters from <phil@tazenda.demon.co.uk>
- Added SA_INTERRUPT temporary fix from
- <mjacob@feral.com>.
-
- =========================================================================
-*/
+ ethernet driver for Linux.
+
+ Copyright 1994, 1995 Digital Equipment Corporation.
+
+ Testing resources for this driver have been made available
+ in part by NASA Ames Research Center (mjacob@nas.nasa.gov).
+
+ The author may be reached at davies@maniac.ultranet.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 SOFTWARE IS PROVIDED ``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.
+
+ You 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.
+
+ Originally, this driver was written for the Digital Equipment
+ Corporation series of EtherWORKS ethernet cards:
+
+ DE425 TP/COAX EISA
+ DE434 TP PCI
+ DE435 TP/COAX/AUI PCI
+ DE450 TP/COAX/AUI PCI
+ DE500 10/100 PCI Fasternet
+
+ but it will now attempt to support all cards which conform to the
+ Digital Semiconductor SROM Specification. The driver currently
+ recognises the following chips:
+
+ DC21040 (no SROM)
+ DC21041[A]
+ DC21140[A]
+ DC21142
+ DC21143
+
+ So far the driver is known to work with the following cards:
+
+ KINGSTON
+ Linksys
+ ZNYX342
+ SMC8432
+ SMC9332 (w/new SROM)
+ ZNYX31[45]
+ ZNYX346 10/100 4 port (can act as a 10/100 bridge!)
+
+ The driver has been tested on a relatively busy network using the DE425,
+ DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred
+ 16M of data to a DECstation 5000/200 as follows:
+
+ TCP UDP
+ TX RX TX RX
+ DE425 1030k 997k 1170k 1128k
+ DE434 1063k 995k 1170k 1125k
+ DE435 1063k 995k 1170k 1125k
+ DE500 1063k 998k 1170k 1125k in 10Mb/s mode
+
+ All values are typical (in kBytes/sec) from a sample of 4 for each
+ measurement. Their error is +/-20k on a quiet (private) network and also
+ depend on what load the CPU has.
+
+ =========================================================================
+ This driver has been written substantially from scratch, although its
+ inheritance of style and stack interface from 'ewrk3.c' and in turn from
+ Donald Becker's 'lance.c' should be obvious. With the module autoload of
+ every usable DECchip board, I pinched Donald's 'next_module' field to
+ link my modules together.
+
+ Upto 15 EISA cards can be supported under this driver, limited primarily
+ by the available IRQ lines. I have checked different configurations of
+ multiple depca, EtherWORKS 3 cards and de4x5 cards and have not found a
+ problem yet (provided you have at least depca.c v0.38) ...
+
+ PCI support has been added to allow the driver to work with the DE434,
+ DE435, DE450 and DE500 cards. The I/O accesses are a bit of a kludge due
+ to the differences in the EISA and PCI CSR address offsets from the base
+ address.
+
+ The ability to load this driver as a loadable module has been included
+ and used extensively during the driver development (to save those long
+ reboot sequences). Loadable module support under PCI and EISA has been
+ achieved by letting the driver autoprobe as if it were compiled into the
+ kernel. Do make sure you're not sharing interrupts with anything that
+ cannot accommodate interrupt sharing!
+
+ To utilise this ability, you have to do 8 things:
+
+ 0) have a copy of the loadable modules code installed on your system.
+ 1) copy de4x5.c from the /linux/drivers/net directory to your favourite
+ temporary directory.
+ 2) for fixed autoprobes (not recommended), edit the source code near
+ line 5539 to reflect the I/O address you're using, or assign these when
+ loading by:
+
+ insmod de4x5 io=0xghh where g = bus number
+ hh = device number
+
+ NB: autoprobing for modules is now supported by default. You may just
+ use:
+
+ insmod de4x5
+
+ to load all available boards. For a specific board, still use
+ the 'io=?' above.
+ 3) compile de4x5.c, but include -DMODULE in the command line to ensure
+ that the correct bits are compiled (see end of source code).
+ 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a
+ kernel with the de4x5 configuration turned off and reboot.
+ 5) insmod de4x5 [io=0xghh]
+ 6) run the net startup bits for your new eth?? interface(s) manually
+ (usually /etc/rc.inet[12] at boot time).
+ 7) enjoy!
+
+ To unload a module, turn off the associated interface(s)
+ 'ifconfig eth?? down' then 'rmmod de4x5'.
+
+ Automedia detection is included so that in principal you can disconnect
+ from, e.g. TP, reconnect to BNC and things will still work (after a
+ pause whilst the driver figures out where its media went). My tests
+ using ping showed that it appears to work....
+
+ By default, the driver will now autodetect any DECchip based card.
+ Should you have a need to restrict the driver to DIGITAL only cards, you
+ can compile with a DEC_ONLY define, or if loading as a module, use the
+ 'dec_only=1' parameter.
+
+ I've changed the timing routines to use the kernel timer and scheduling
+ functions so that the hangs and other assorted problems that occurred
+ while autosensing the media should be gone. A bonus for the DC21040
+ auto media sense algorithm is that it can now use one that is more in
+ line with the rest (the DC21040 chip doesn't have a hardware timer).
+ The downside is the 1 'jiffies' (10ms) resolution.
+
+ IEEE 802.3u MII interface code has been added in anticipation that some
+ products may use it in the future.
+
+ The SMC9332 card has a non-compliant SROM which needs fixing - I have
+ patched this driver to detect it because the SROM format used complies
+ to a previous DEC-STD format.
+
+ I have removed the buffer copies needed for receive on Intels. I cannot
+ remove them for Alphas since the Tulip hardware only does longword
+ aligned DMA transfers and the Alphas get alignment traps with non
+ longword aligned data copies (which makes them really slow). No comment.
+
+ I have added SROM decoding routines to make this driver work with any
+ card that supports the Digital Semiconductor SROM spec. This will help
+ all cards running the dc2114x series chips in particular. Cards using
+ the dc2104x chips should run correctly with the basic driver. I'm in
+ debt to <mjacob@feral.com> for the testing and feedback that helped get
+ this feature working. So far we have tested KINGSTON, SMC8432, SMC9332
+ (with the latest SROM complying with the SROM spec V3: their first was
+ broken), ZNYX342 and LinkSys. ZYNX314 (dual 21041 MAC) and ZNYX 315
+ (quad 21041 MAC) cards also appear to work despite their incorrectly
+ wired IRQs.
+
+ I have added a temporary fix for interrupt problems when some SCSI cards
+ share the same interrupt as the DECchip based cards. The problem occurs
+ because the SCSI card wants to grab the interrupt as a fast interrupt
+ (runs the service routine with interrupts turned off) vs. this card
+ which really needs to run the service routine with interrupts turned on.
+ This driver will now add the interrupt service routine as a fast
+ interrupt if it is bounced from the slow interrupt. THIS IS NOT A
+ RECOMMENDED WAY TO RUN THE DRIVER and has been done for a limited time
+ until people sort out their compatibility issues and the kernel
+ interrupt service code is fixed. YOU SHOULD SEPARATE OUT THE FAST
+ INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not
+ run on the same interrupt. PCMCIA/CardBus is another can of worms...
+
+ Finally, I think I have really fixed the module loading problem with
+ more than one DECchip based card. As a side effect, I don't mess with
+ the device structure any more which means that if more than 1 card in
+ 2.0.x is installed (4 in 2.1.x), the user will have to edit
+ linux/drivers/net/Space.c to make room for them. Hence, module loading
+ is the preferred way to use this driver, since it doesn't have this
+ limitation.
+
+ Where SROM media detection is used and full duplex is specified in the
+ SROM, the feature is ignored unless de4x5_full_duplex is set at compile
+ time OR during a module load (insmod de4x5 de4x5_full_duplex=1). This
+ is because there is no way to automatically detect full duplex links
+ except through autonegotiation. When I include the autonegotiation
+ feature in the SROM autoconf code, this detection will occur
+ automatically.
+
+ TO DO:
+ ------
+
+ o check what revision numbers the 21142 and 21143 have
+ o
+
+ Revision History
+ ----------------
+
+ Version Date Description
+
+ 0.1 17-Nov-94 Initial writing. ALPHA code release.
+ 0.2 13-Jan-95 Added PCI support for DE435's.
+ 0.21 19-Jan-95 Added auto media detection.
+ 0.22 10-Feb-95 Fix interrupt handler call <chris@cosy.sbg.ac.at>.
+ Fix recognition bug reported by <bkm@star.rl.ac.uk>.
+ Add request/release_region code.
+ Add loadable modules support for PCI.
+ Clean up loadable modules support.
+ 0.23 28-Feb-95 Added DC21041 and DC21140 support.
+ Fix missed frame counter value and initialisation.
+ Fixed EISA probe.
+ 0.24 11-Apr-95 Change delay routine to use <linux/udelay>.
+ Change TX_BUFFS_AVAIL macro.
+ Change media autodetection to allow manual setting.
+ Completed DE500 (DC21140) support.
+ 0.241 18-Apr-95 Interim release without DE500 Autosense Algorithm.
+ 0.242 10-May-95 Minor changes.
+ 0.30 12-Jun-95 Timer fix for DC21140.
+ Portability changes.
+ Add ALPHA changes from <jestabro@ant.tay1.dec.com>.
+ Add DE500 semi automatic autosense.
+ Add Link Fail interrupt TP failure detection.
+ Add timer based link change detection.
+ Plugged a memory leak in de4x5_queue_pkt().
+ 0.31 13-Jun-95 Fixed PCI stuff for 1.3.1.
+ 0.32 26-Jun-95 Added verify_area() calls in de4x5_ioctl() from a
+ suggestion by <heiko@colossus.escape.de>.
+ 0.33 8-Aug-95 Add shared interrupt support (not released yet).
+ 0.331 21-Aug-95 Fix de4x5_open() with fast CPUs.
+ Fix de4x5_interrupt().
+ Fix dc21140_autoconf() mess.
+ No shared interrupt support.
+ 0.332 11-Sep-95 Added MII management interface routines.
+ 0.40 5-Mar-96 Fix setup frame timeout <maartenb@hpkuipc.cern.ch>.
+ Add kernel timer code (h/w is too flaky).
+ Add MII based PHY autosense.
+ Add new multicasting code.
+ Add new autosense algorithms for media/mode
+ selection using kernel scheduling/timing.
+ Re-formatted.
+ Made changes suggested by <jeff@router.patch.net>:
+ Change driver to detect all DECchip based cards
+ with DEC_ONLY restriction a special case.
+ Changed driver to autoprobe as a module. No irq
+ checking is done now - assume BIOS is good!
+ Added SMC9332 detection <manabe@Roy.dsl.tutics.ac.jp>
+ 0.41 21-Mar-96 Don't check for get_hw_addr checksum unless DEC card
+ only <niles@axp745gsfc.nasa.gov>
+ Fix for multiple PCI cards reported by <jos@xos.nl>
+ Duh, put the SA_SHIRQ flag into request_interrupt().
+ Fix SMC ethernet address in enet_det[].
+ Print chip name instead of "UNKNOWN" during boot.
+ 0.42 26-Apr-96 Fix MII write TA bit error.
+ Fix bug in dc21040 and dc21041 autosense code.
+ Remove buffer copies on receive for Intels.
+ Change sk_buff handling during media disconnects to
+ eliminate DUP packets.
+ Add dynamic TX thresholding.
+ Change all chips to use perfect multicast filtering.
+ Fix alloc_device() bug <jari@markkus2.fimr.fi>
+ 0.43 21-Jun-96 Fix unconnected media TX retry bug.
+ Add Accton to the list of broken cards.
+ Fix TX under-run bug for non DC21140 chips.
+ Fix boot command probe bug in alloc_device() as
+ reported by <koen.gadeyne@barco.com> and
+ <orava@nether.tky.hut.fi>.
+ Add cache locks to prevent a race condition as
+ reported by <csd@microplex.com> and
+ <baba@beckman.uiuc.edu>.
+ Upgraded alloc_device() code.
+ 0.431 28-Jun-96 Fix potential bug in queue_pkt() from discussion
+ with <csd@microplex.com>
+ 0.44 13-Aug-96 Fix RX overflow bug in 2114[023] chips.
+ Fix EISA probe bugs reported by <os2@kpi.kharkov.ua>
+ and <michael@compurex.com>.
+ 0.441 9-Sep-96 Change dc21041_autoconf() to probe quiet BNC media
+ with a loopback packet.
+ 0.442 9-Sep-96 Include AUI in dc21041 media printout. Bug reported
+ by <bhat@mundook.cs.mu.OZ.AU>
+ 0.45 8-Dec-96 Include endian functions for PPC use, from work
+ by <cort@cs.nmt.edu> and <g.thomas@opengroup.org>.
+ 0.451 28-Dec-96 Added fix to allow autoprobe for modules after
+ suggestion from <mjacob@feral.com>.
+ 0.5 30-Jan-97 Added SROM decoding functions.
+ Updated debug flags.
+ Fix sleep/wakeup calls for PCI cards, bug reported
+ by <cross@gweep.lkg.dec.com>.
+ Added multi-MAC, one SROM feature from discussion
+ with <mjacob@feral.com>.
+ Added full module autoprobe capability.
+ Added attempt to use an SMC9332 with broken SROM.
+ Added fix for ZYNX multi-mac cards that didn't
+ get their IRQs wired correctly.
+ 0.51 13-Feb-97 Added endian fixes for the SROM accesses from
+ <paubert@iram.es>
+ Fix init_connection() to remove extra device reset.
+ Fix MAC/PHY reset ordering in dc21140m_autoconf().
+ Fix initialisation problem with lp->timeout in
+ typeX_infoblock() from <paubert@iram.es>.
+ Fix MII PHY reset problem from work done by
+ <paubert@iram.es>.
+ 0.52 26-Apr-97 Some changes may not credit the right people -
+ a disk crash meant I lost some mail.
+ Change RX interrupt routine to drop rather than
+ defer packets to avoid hang reported by
+ <g.thomas@opengroup.org>.
+ Fix srom_exec() to return for COMPACT and type 1
+ infoblocks.
+ Added DC21142 and DC21143 functions.
+ Added byte counters from <phil@tazenda.demon.co.uk>
+ Added SA_INTERRUPT temporary fix from
+ <mjacob@feral.com>.
+ 0.53 12-Nov-97 Fix the *_probe() to include 'eth??' name during
+ module load: bug reported by
+ <Piete.Brooks@cl.cam.ac.uk>
+ Fix multi-MAC, one SROM, to work with 2114x chips:
+ bug reported by <cmetz@inner.net>.
+ Make above search independent of BIOS device scan
+ direction.
+ Completed DC2114[23] autosense functions.
+
+ =========================================================================
+ */
-static const char *version = "de4x5.c:V0.52 1997/4/26 davies@maniac.ultranet.com\n";
+static const char *version = "de4x5.c:V0.53 1997/11/12 davies@maniac.ultranet.com\n";
#include <linux/module.h>
@@ -319,7 +346,6 @@ static const char *version = "de4x5.c:V0.52 1997/4/26 davies@maniac.ultranet.com
#include <linux/bios32.h>
#include <linux/pci.h>
#include <linux/delay.h>
-#include <linux/init.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
@@ -340,89 +366,99 @@ static const char *version = "de4x5.c:V0.52 1997/4/26 davies@maniac.ultranet.com
#define c_char const char
#include <linux/version.h>
-#if LINUX_VERSION_CODE < ((2 << 16) | (1 << 8))
-#define net_device_stats enet_statistics
-#define copy_to_user(a,b,c) memcpy_tofs(a,b,c)
-#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c)
-#define le16_to_cpu(a) cpu_to_le16(a)
-#define le32_to_cpu(a) cpu_to_le32(a)
-#ifdef __powerpc__
-#define cpu_to_le16(a) ((((a) & 0x00ffU) << 8) | (((a) & 0xff00U) >> 8))
-#define cpu_to_le32(a) ((((a) & 0x000000ffU) << 24) |\
- (((a) & 0x0000ff00U) << 8) |\
- (((a) & 0x00ff0000U) >> 8) |\
- (((a) & 0xff000000U) >> 24))
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0)
+# define __initfunc(__arginit) __arginit
+# define test_and_set_bit set_bit
+# define net_device_stats enet_statistics
+# define copy_to_user(a,b,c) memcpy_tofs(a,b,c)
+# define copy_from_user(a,b,c) memcpy_fromfs(a,b,c)
+# define le16_to_cpu(a) cpu_to_le16(a)
+# define le32_to_cpu(a) cpu_to_le32(a)
+# ifdef __powerpc__
+# define cpu_to_le16(a) ((((a) & 0x00ffU) << 8) | (((a) & 0xff00U) >> 8))
+# define cpu_to_le32(a) ((((a) & 0x000000ffU) << 24) |\
+ (((a) & 0x0000ff00U) << 8) |\
+ (((a) & 0x00ff0000U) >> 8) |\
+ (((a) & 0xff000000U) >> 24))
+# else
+# define cpu_to_le16(a) (a)
+# define cpu_to_le32(a) (a)
+# endif /* __powerpc__ */
+# include <asm/segment.h>
#else
-#define cpu_to_le16(a) (a)
-#define cpu_to_le32(a) (a)
-#endif /* __powerpc__ */
-#include <asm/segment.h>
-#else
-#include <asm/uaccess.h>
-#endif /* LINUX_VERSION_CODE */
+# include <asm/uaccess.h>
+# include <linux/init.h>
+#endif /* LINUX_VERSION_CODE */
#define TWIDDLE(a) (u_short)le16_to_cpu(get_unaligned((u_short *)(a)))
/*
** MII Information
*/
struct phy_table {
- int reset; /* Hard reset required? */
- int id; /* IEEE OUI */
- int ta; /* One cycle TA time - 802.3u is confusing here */
- struct { /* Non autonegotiation (parallel) speed det. */
- int reg;
- int mask;
- int value;
- } spd;
+ int reset; /* Hard reset required? */
+ int id; /* IEEE OUI */
+ int ta; /* One cycle TA time - 802.3u is confusing here */
+ struct { /* Non autonegotiation (parallel) speed det. */
+ int reg;
+ int mask;
+ int value;
+ } spd;
};
struct mii_phy {
- int reset; /* Hard reset required? */
- int id; /* IEEE OUI */
- int ta; /* One cycle TA time */
- struct { /* Non autonegotiation (parallel) speed det. */
- int reg;
- int mask;
- int value;
- } spd;
- int addr; /* MII address for the PHY */
- u_char *gep; /* Start of GEP sequence block in SROM */
- u_char *rst; /* Start of reset sequence in SROM */
- u_int mc; /* Media Capabilities */
- u_int ana; /* NWay Advertisement */
- u_int fdx; /* Full DupleX capabilites for each media */
- u_int ttm; /* Transmit Threshold Mode for each media */
+ int reset; /* Hard reset required? */
+ int id; /* IEEE OUI */
+ int ta; /* One cycle TA time */
+ struct { /* Non autonegotiation (parallel) speed det. */
+ int reg;
+ int mask;
+ int value;
+ } spd;
+ int addr; /* MII address for the PHY */
+ u_char *gep; /* Start of GEP sequence block in SROM */
+ u_char *rst; /* Start of reset sequence in SROM */
+ u_int mc; /* Media Capabilities */
+ u_int ana; /* NWay Advertisement */
+ u_int fdx; /* Full DupleX capabilites for each media */
+ u_int ttm; /* Transmit Threshold Mode for each media */
+ u_int mci; /* 21142 MII Connector Interrupt info */
};
-#define DE4X5_MAX_PHY 8 /* Allow upto 8 attached PHY devices per board */
+#define DE4X5_MAX_PHY 8 /* Allow upto 8 attached PHY devices per board */
struct sia_phy {
- u_char mc; /* Media Code */
- u_char ext; /* csr13-15 valid when set */
- int csr13; /* SIA Connectivity Register */
- int csr14; /* SIA TX/RX Register */
- int csr15; /* SIA General Register */
- int gepc; /* SIA GEP Control Information */
- int gep; /* SIA GEP Data */
+ u_char mc; /* Media Code */
+ u_char ext; /* csr13-15 valid when set */
+ int csr13; /* SIA Connectivity Register */
+ int csr14; /* SIA TX/RX Register */
+ int csr15; /* SIA General Register */
+ int gepc; /* SIA GEP Control Information */
+ int gep; /* SIA GEP Data */
};
/*
** Define the know universe of PHY devices that can be
** recognised by this driver
*/
-static struct phy_table phy_info[] = {
- {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}}, /* National TX */
- {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}}, /* Broadcom T4 */
- {0, SEEQ_T4 , 1, {0x12, 0x10, 0x10}}, /* SEEQ T4 */
- {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}} /* Cypress T4 */
+static struct phy_table phy_info[] =
+{
+ {0, NATIONAL_TX, 1,
+ {0x19, 0x40, 0x00}}, /* National TX */
+ {1, BROADCOM_T4, 1,
+ {0x10, 0x02, 0x02}}, /* Broadcom T4 */
+ {0, SEEQ_T4, 1,
+ {0x12, 0x10, 0x10}}, /* SEEQ T4 */
+ {0, CYPRESS_T4, 1,
+ {0x05, 0x20, 0x20}} /* Cypress T4 */
};
/*
** Define special SROM detection cases
*/
-static c_char enet_det[][ETH_ALEN] = {
- {0x00, 0x00, 0xc0, 0x00, 0x00, 0x00},
- {0x00, 0x00, 0xe8, 0x00, 0x00, 0x00}
+static c_char enet_det[][ETH_ALEN] =
+{
+ {0x00, 0x00, 0xc0, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0xe8, 0x00, 0x00, 0x00}
};
#define SMC 1
@@ -433,34 +469,35 @@ static c_char enet_det[][ETH_ALEN] = {
** use this information to help figure out what to do. This is a
** "stab in the dark" and so far for SMC9332's only.
*/
-static c_char srom_repair_info[][100] = {
- {0x00,0x1e,0x00,0x00,0x00,0x08, /* SMC9332 */
- 0x1f,0x01,0x8f,0x01,0x00,0x01,0x00,0x02,
- 0x01,0x00,0x00,0x78,0xe0,0x01,0x00,0x50,
- 0x00,0x18,}
+static c_char srom_repair_info[][100] =
+{
+ {0x00, 0x1e, 0x00, 0x00, 0x00, 0x08, /* SMC9332 */
+ 0x1f, 0x01, 0x8f, 0x01, 0x00, 0x01, 0x00, 0x02,
+ 0x01, 0x00, 0x00, 0x78, 0xe0, 0x01, 0x00, 0x50,
+ 0x00, 0x18,}
};
-
#ifdef DE4X5_DEBUG
static int de4x5_debug = DE4X5_DEBUG;
#else
-static int de4x5_debug = (0);
+/*static int de4x5_debug = (DEBUG_MII | DEBUG_SROM | DEBUG_PCICFG | DEBUG_MEDIA | DEBUG_VERSION);*/
+static int de4x5_debug = (DEBUG_MEDIA | DEBUG_VERSION);
#endif
-#ifdef DE4X5_AUTOSENSE /* Should be done on a per adapter basis */
+#ifdef DE4X5_AUTOSENSE /* Should be done on a per adapter basis */
static int de4x5_autosense = DE4X5_AUTOSENSE;
#else
-static int de4x5_autosense = AUTO; /* Do auto media/mode sensing */
+static int de4x5_autosense = AUTO; /* Do auto media/mode sensing */
#endif
-#define DE4X5_AUTOSENSE_MS 250 /* msec autosense tick (DE500) */
+#define DE4X5_AUTOSENSE_MS 250 /* msec autosense tick (DE500) */
-#ifdef DE4X5_FULL_DUPLEX /* Should be done on a per adapter basis */
+#ifdef DE4X5_FULL_DUPLEX /* Should be done on a per adapter basis */
static s32 de4x5_full_duplex = 1;
#else
static s32 de4x5_full_duplex = 0;
#endif
-#define DE4X5_NDA 0xffe0 /* No Device (I/O) Address */
+#define DE4X5_NDA 0xffe0 /* No Device (I/O) Address */
/*
** Ethernet PROM defines
@@ -471,24 +508,24 @@ static s32 de4x5_full_duplex = 0;
/*
** Ethernet Info
*/
-#define PKT_BUF_SZ 1536 /* Buffer size for each Tx/Rx buffer */
-#define IEEE802_3_SZ 1518 /* Packet + CRC */
-#define MAX_PKT_SZ 1514 /* Maximum ethernet packet length */
-#define MAX_DAT_SZ 1500 /* Maximum ethernet data length */
-#define MIN_DAT_SZ 1 /* Minimum ethernet data length */
-#define PKT_HDR_LEN 14 /* Addresses and data length info */
+#define PKT_BUF_SZ 1536 /* Buffer size for each Tx/Rx buffer */
+#define IEEE802_3_SZ 1518 /* Packet + CRC */
+#define MAX_PKT_SZ 1514 /* Maximum ethernet packet length */
+#define MAX_DAT_SZ 1500 /* Maximum ethernet data length */
+#define MIN_DAT_SZ 1 /* Minimum ethernet data length */
+#define PKT_HDR_LEN 14 /* Addresses and data length info */
#define FAKE_FRAME_LEN (MAX_PKT_SZ + 1)
-#define QUEUE_PKT_TIMEOUT (3*HZ) /* 3 second timeout */
+#define QUEUE_PKT_TIMEOUT (3*HZ) /* 3 second timeout */
-#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */
-#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */
+#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */
+#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */
/*
** EISA bus defines
*/
-#define DE4X5_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */
-#define DE4X5_EISA_TOTAL_SIZE 0x100 /* I/O address extent */
+#define DE4X5_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */
+#define DE4X5_EISA_TOTAL_SIZE 0x100 /* I/O address extent */
#define MAX_EISA_SLOTS 16
#define EISA_SLOT_INC 0x1000
@@ -501,8 +538,9 @@ static s32 de4x5_full_duplex = 0;
** PCI Bus defines
*/
#define PCI_MAX_BUS_NUM 8
-#define DE4X5_PCI_TOTAL_SIZE 0x80 /* I/O address extent */
-#define DE4X5_CLASS_CODE 0x00020000 /* Network controller, Ethernet */
+#define DE4X5_PCI_TOTAL_SIZE 0x80 /* I/O address extent */
+#define DE4X5_CLASS_CODE 0x00020000 /* Network controller, Ethernet */
+#define NO_MORE_PCI -2 /* PCI bus search all done */
/*
** Memory Alignment. Each descriptor is 4 longwords long. To force a
@@ -510,20 +548,20 @@ static s32 de4x5_full_duplex = 0;
** DESC_ALIGN. ALIGN aligns the start address of the private memory area
** and hence the RX descriptor ring's first entry.
*/
-#define ALIGN4 ((u_long)4 - 1) /* 1 longword align */
-#define ALIGN8 ((u_long)8 - 1) /* 2 longword align */
-#define ALIGN16 ((u_long)16 - 1) /* 4 longword align */
-#define ALIGN32 ((u_long)32 - 1) /* 8 longword align */
-#define ALIGN64 ((u_long)64 - 1) /* 16 longword align */
-#define ALIGN128 ((u_long)128 - 1) /* 32 longword align */
-
-#define ALIGN ALIGN32 /* Keep the DC21040 happy... */
+#define ALIGN4 ((u_long)4 - 1) /* 1 longword align */
+#define ALIGN8 ((u_long)8 - 1) /* 2 longword align */
+#define ALIGN16 ((u_long)16 - 1) /* 4 longword align */
+#define ALIGN32 ((u_long)32 - 1) /* 8 longword align */
+#define ALIGN64 ((u_long)64 - 1) /* 16 longword align */
+#define ALIGN128 ((u_long)128 - 1) /* 32 longword align */
+
+#define ALIGN ALIGN32 /* Keep the DC21040 happy... */
#define CACHE_ALIGN CAL_16LONG
-#define DESC_SKIP_LEN DSL_0 /* Must agree with DESC_ALIGN */
+#define DESC_SKIP_LEN DSL_0 /* Must agree with DESC_ALIGN */
/*#define DESC_ALIGN u32 dummy[4]; / * Must agree with DESC_SKIP_LEN */
#define DESC_ALIGN
-#ifndef DEC_ONLY /* See README.de4x5 for using this */
+#ifndef DEC_ONLY /* See README.de4x5 for using this */
static int dec_only = 0;
#else
static int dec_only = 1;
@@ -572,7 +610,7 @@ static int dec_only = 1;
/*
** DE4X5 SIA RESET
*/
-#define RESET_SIA outl(0, DE4X5_SICR); /* Reset SIA connectivity regs */
+#define RESET_SIA outl(0, DE4X5_SICR); /* Reset SIA connectivity regs */
/*
** DE500 AUTOSENSE TIMER INTERVAL (MILLISECS)
@@ -583,17 +621,18 @@ static int dec_only = 1;
** SROM Structure
*/
struct de4x5_srom {
- char sub_vendor_id[2];
- char sub_system_id[2];
- char reserved[12];
- char id_block_crc;
- char reserved2;
- char version;
- char num_controllers;
- char ieee_addr[6];
- char info[100];
- short chksum;
+ char sub_vendor_id[2];
+ char sub_system_id[2];
+ char reserved[12];
+ char id_block_crc;
+ char reserved2;
+ char version;
+ char num_controllers;
+ char ieee_addr[6];
+ char info[100];
+ short chksum;
};
+
#define SUB_VENDOR_ID 0x500a
/*
@@ -604,106 +643,106 @@ struct de4x5_srom {
** is possible. 1536 showed better 'ttcp' performance. Take your pick. 32 TX
** descriptors are needed for machines with an ALPHA CPU.
*/
-#define NUM_RX_DESC 8 /* Number of RX descriptors */
-#define NUM_TX_DESC 32 /* Number of TX descriptors */
-#define RX_BUFF_SZ 1536 /* Power of 2 for kmalloc and */
- /* Multiple of 4 for DC21040 */
- /* Allows 512 byte alignment */
+#define NUM_RX_DESC 8 /* Number of RX descriptors */
+#define NUM_TX_DESC 32 /* Number of TX descriptors */
+#define RX_BUFF_SZ 1536 /* Power of 2 for kmalloc and */
+ /* Multiple of 4 for DC21040 */
+ /* Allows 512 byte alignment */
struct de4x5_desc {
- volatile s32 status;
- u32 des1;
- u32 buf;
- u32 next;
- DESC_ALIGN
+ volatile s32 status;
+ u32 des1;
+ u32 buf;
+ u32 next;
+ DESC_ALIGN
};
/*
** The DE4X5 private structure
*/
#define DE4X5_PKT_STAT_SZ 16
-#define DE4X5_PKT_BIN_SZ 128 /* Should be >=100 unless you
- increase DE4X5_PKT_STAT_SZ */
+#define DE4X5_PKT_BIN_SZ 128 /* Should be >=100 unless you
+ increase DE4X5_PKT_STAT_SZ */
struct de4x5_private {
- char adapter_name[80]; /* Adapter name */
- struct de4x5_desc rx_ring[NUM_RX_DESC]; /* RX descriptor ring */
- struct de4x5_desc tx_ring[NUM_TX_DESC]; /* TX descriptor ring */
- struct sk_buff *tx_skb[NUM_TX_DESC]; /* TX skb for freeing when sent */
- struct sk_buff *rx_skb[NUM_RX_DESC]; /* RX skb's */
- int rx_new, rx_old; /* RX descriptor ring pointers */
- int tx_new, tx_old; /* TX descriptor ring pointers */
- char setup_frame[SETUP_FRAME_LEN]; /* Holds MCA and PA info. */
- char frame[64]; /* Min sized packet for loopback*/
- struct net_device_stats stats; /* Public stats */
- struct {
- u_int bins[DE4X5_PKT_STAT_SZ]; /* Private stats counters */
- u_int unicast;
- u_int multicast;
- u_int broadcast;
- u_int excessive_collisions;
- u_int tx_underruns;
- u_int excessive_underruns;
- u_int rx_runt_frames;
- u_int rx_collision;
- u_int rx_dribble;
- u_int rx_overflow;
- } pktStats;
- char rxRingSize;
- char txRingSize;
- int bus; /* EISA or PCI */
- int bus_num; /* PCI Bus number */
- int device; /* Device number on PCI bus */
- int state; /* Adapter OPENED or CLOSED */
- int chipset; /* DC21040, DC21041 or DC21140 */
- s32 irq_mask; /* Interrupt Mask (Enable) bits */
- s32 irq_en; /* Summary interrupt bits */
- int media; /* Media (eg TP), mode (eg 100B)*/
- int c_media; /* Remember the last media conn */
- int fdx; /* media full duplex flag */
- int linkOK; /* Link is OK */
- int autosense; /* Allow/disallow autosensing */
- int tx_enable; /* Enable descriptor polling */
- int setup_f; /* Setup frame filtering type */
- int local_state; /* State within a 'media' state */
- struct mii_phy phy[DE4X5_MAX_PHY]; /* List of attached PHY devices */
- struct sia_phy sia; /* SIA PHY Information */
- int active; /* Index to active PHY device */
- int mii_cnt; /* Number of attached PHY's */
- int timeout; /* Scheduling counter */
- struct timer_list timer; /* Timer info for kernel */
- int tmp; /* Temporary global per card */
- struct {
- void *priv; /* Original kmalloc'd mem addr */
- void *buf; /* Original kmalloc'd mem addr */
- int lock; /* Lock the cache accesses */
- s32 csr0; /* Saved Bus Mode Register */
- s32 csr6; /* Saved Operating Mode Reg. */
- s32 csr7; /* Saved IRQ Mask Register */
- s32 gep; /* Saved General Purpose Reg. */
- s32 gepc; /* Control info for GEP */
- s32 csr13; /* Saved SIA Connectivity Reg. */
- s32 csr14; /* Saved SIA TX/RX Register */
- s32 csr15; /* Saved SIA General Register */
- int save_cnt; /* Flag if state already saved */
- struct sk_buff *skb; /* Save the (re-ordered) skb's */
- } cache;
- struct de4x5_srom srom; /* A copy of the SROM */
- struct device *next_module; /* Link to the next module */
- int rx_ovf; /* Check for 'RX overflow' tag */
- int useSROM; /* For non-DEC card use SROM */
- int useMII; /* Infoblock using the MII */
- int asBitValid; /* Autosense bits in GEP? */
- int asPolarity; /* 0 => asserted high */
- int asBit; /* Autosense bit number in GEP */
- int defMedium; /* SROM default medium */
- int tcount; /* Last infoblock number */
- int infoblock_init; /* Initialised this infoblock? */
- int infoleaf_offset; /* SROM infoleaf for controller */
- s32 infoblock_csr6; /* csr6 value in SROM infoblock */
- int infoblock_media; /* infoblock media */
- int (*infoleaf_fn)(struct device *); /* Pointer to infoleaf function */
- u_char *rst; /* Pointer to Type 5 reset info */
- u_char ibn; /* Infoblock number */
+ char adapter_name[80]; /* Adapter name */
+ struct de4x5_desc rx_ring[NUM_RX_DESC]; /* RX descriptor ring */
+ struct de4x5_desc tx_ring[NUM_TX_DESC]; /* TX descriptor ring */
+ struct sk_buff *tx_skb[NUM_TX_DESC]; /* TX skb for freeing when sent */
+ struct sk_buff *rx_skb[NUM_RX_DESC]; /* RX skb's */
+ int rx_new, rx_old; /* RX descriptor ring pointers */
+ int tx_new, tx_old; /* TX descriptor ring pointers */
+ char setup_frame[SETUP_FRAME_LEN]; /* Holds MCA and PA info. */
+ char frame[64]; /* Min sized packet for loopback */
+ struct net_device_stats stats; /* Public stats */
+ struct {
+ u_int bins[DE4X5_PKT_STAT_SZ]; /* Private stats counters */
+ u_int unicast;
+ u_int multicast;
+ u_int broadcast;
+ u_int excessive_collisions;
+ u_int tx_underruns;
+ u_int excessive_underruns;
+ u_int rx_runt_frames;
+ u_int rx_collision;
+ u_int rx_dribble;
+ u_int rx_overflow;
+ } pktStats;
+ char rxRingSize;
+ char txRingSize;
+ int bus; /* EISA or PCI */
+ int bus_num; /* PCI Bus number */
+ int device; /* Device number on PCI bus */
+ int state; /* Adapter OPENED or CLOSED */
+ int chipset; /* DC21040, DC21041 or DC21140 */
+ s32 irq_mask; /* Interrupt Mask (Enable) bits */
+ s32 irq_en; /* Summary interrupt bits */
+ int media; /* Media (eg TP), mode (eg 100B) */
+ int c_media; /* Remember the last media conn */
+ int fdx; /* media full duplex flag */
+ int linkOK; /* Link is OK */
+ int autosense; /* Allow/disallow autosensing */
+ int tx_enable; /* Enable descriptor polling */
+ int setup_f; /* Setup frame filtering type */
+ int local_state; /* State within a 'media' state */
+ struct mii_phy phy[DE4X5_MAX_PHY]; /* List of attached PHY devices */
+ struct sia_phy sia; /* SIA PHY Information */
+ int active; /* Index to active PHY device */
+ int mii_cnt; /* Number of attached PHY's */
+ int timeout; /* Scheduling counter */
+ struct timer_list timer; /* Timer info for kernel */
+ int tmp; /* Temporary global per card */
+ struct {
+ void *priv; /* Original kmalloc'd mem addr */
+ void *buf; /* Original kmalloc'd mem addr */
+ unsigned long lock; /* Lock the cache accesses */
+ s32 csr0; /* Saved Bus Mode Register */
+ s32 csr6; /* Saved Operating Mode Reg. */
+ s32 csr7; /* Saved IRQ Mask Register */
+ s32 gep; /* Saved General Purpose Reg. */
+ s32 gepc; /* Control info for GEP */
+ s32 csr13; /* Saved SIA Connectivity Reg. */
+ s32 csr14; /* Saved SIA TX/RX Register */
+ s32 csr15; /* Saved SIA General Register */
+ int save_cnt; /* Flag if state already saved */
+ struct sk_buff *skb; /* Save the (re-ordered) skb's */
+ } cache;
+ struct de4x5_srom srom; /* A copy of the SROM */
+ struct device *next_module; /* Link to the next module */
+ int rx_ovf; /* Check for 'RX overflow' tag */
+ int useSROM; /* For non-DEC card use SROM */
+ int useMII; /* Infoblock using the MII */
+ int asBitValid; /* Autosense bits in GEP? */
+ int asPolarity; /* 0 => asserted high */
+ int asBit; /* Autosense bit number in GEP */
+ int defMedium; /* SROM default medium */
+ int tcount; /* Last infoblock number */
+ int infoblock_init; /* Initialised this infoblock? */
+ int infoleaf_offset; /* SROM infoleaf for controller */
+ s32 infoblock_csr6; /* csr6 value in SROM infoblock */
+ int infoblock_media; /* infoblock media */
+ int (*infoleaf_fn) (struct device *); /* Pointer to infoleaf function */
+ u_char *rst; /* Pointer to Type 5 reset info */
+ u_char ibn; /* Infoblock number */
};
/*
@@ -712,13 +751,13 @@ struct de4x5_private {
** PROM is accessed differently.
*/
static struct bus_type {
- int bus;
- int bus_num;
- int device;
- int chipset;
- struct de4x5_srom srom;
- int autosense;
- int useSROM;
+ int bus;
+ int bus_num;
+ int device;
+ int chipset;
+ struct de4x5_srom srom;
+ int autosense;
+ int useSROM;
} bus;
/*
@@ -734,11 +773,14 @@ static struct bus_type {
** can't follow the PCI to PCI Bridge Architecture spec.
*/
static struct {
- int chipset;
- int bus;
- int irq;
- u_char addr[ETH_ALEN];
-} last = {0,};
+ int chipset;
+ int bus;
+ int irq;
+ u_char addr[ETH_ALEN];
+} last = {
+
+ 0,
+};
/*
** The transmit ring full condition is described by the tx_old and tx_new
@@ -756,164 +798,180 @@ static struct {
/*
** Public Functions
*/
-static int de4x5_open(struct device *dev);
-static int de4x5_queue_pkt(struct sk_buff *skb, struct device *dev);
-static void de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static int de4x5_close(struct device *dev);
-static struct net_device_stats *de4x5_get_stats(struct device *dev);
-static void de4x5_local_stats(struct device *dev, char *buf, int pkt_len);
-static void set_multicast_list(struct device *dev);
-static int de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+static int de4x5_open(struct device *dev);
+static int de4x5_queue_pkt(struct sk_buff *skb, struct device *dev);
+static void de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static int de4x5_close(struct device *dev);
+static struct net_device_stats *de4x5_get_stats(struct device *dev);
+static void de4x5_local_stats(struct device *dev, char *buf, int pkt_len);
+static void set_multicast_list(struct device *dev);
+static int de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd);
/*
** Private functions
*/
-static int de4x5_hw_init(struct device *dev, u_long iobase);
-static int de4x5_init(struct device *dev);
-static int de4x5_sw_reset(struct device *dev);
-static int de4x5_rx(struct device *dev);
-static int de4x5_tx(struct device *dev);
-static int de4x5_ast(struct device *dev);
-static int de4x5_txur(struct device *dev);
-static int de4x5_rx_ovfc(struct device *dev);
-
-static int autoconf_media(struct device *dev);
-static void create_packet(struct device *dev, char *frame, int len);
-static void de4x5_us_delay(u32 usec);
-static void de4x5_ms_delay(u32 msec);
-static void load_packet(struct device *dev, char *buf, u32 flags, struct sk_buff *skb);
-static int dc21040_autoconf(struct device *dev);
-static int dc21041_autoconf(struct device *dev);
-static int dc21140m_autoconf(struct device *dev);
-static int srom_autoconf(struct device *dev);
-static int de4x5_suspect_state(struct device *dev, int timeout, int prev_state, int (*fn)(struct device *, int), int (*asfn)(struct device *));
-static int dc21040_state(struct device *dev, int csr13, int csr14, int csr15, int timeout, int next_state, int suspect_state, int (*fn)(struct device *, int));
-static int test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec);
-static int test_sym_link(struct device *dev, int msec);
-static int test_mii_reg(struct device *dev, int reg, int mask, int pol, long msec);
-static int is_spd_100(struct device *dev);
-static int is_100_up(struct device *dev);
-static int is_10_up(struct device *dev);
-static int is_anc_capable(struct device *dev);
-static int ping_media(struct device *dev, int msec);
+static int de4x5_hw_init(struct device *dev, u_long iobase);
+static int de4x5_init(struct device *dev);
+static int de4x5_sw_reset(struct device *dev);
+static int de4x5_rx(struct device *dev);
+static int de4x5_tx(struct device *dev);
+static int de4x5_ast(struct device *dev);
+static int de4x5_txur(struct device *dev);
+static int de4x5_rx_ovfc(struct device *dev);
+
+static int autoconf_media(struct device *dev);
+static void create_packet(struct device *dev, char *frame, int len);
+static void de4x5_us_delay(u32 usec);
+static void de4x5_ms_delay(u32 msec);
+static void load_packet(struct device *dev, char *buf, u32 flags, struct sk_buff *skb);
+static int dc21040_autoconf(struct device *dev);
+static int dc21041_autoconf(struct device *dev);
+static int dc21140m_autoconf(struct device *dev);
+static int dc2114x_autoconf(struct device *dev);
+static int srom_autoconf(struct device *dev);
+static int de4x5_suspect_state(struct device *dev, int timeout, int prev_state, int (*fn) (struct device *, int), int (*asfn) (struct device *));
+static int dc21040_state(struct device *dev, int csr13, int csr14, int csr15, int timeout, int next_state, int suspect_state, int (*fn) (struct device *, int));
+static int test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec);
+static int test_for_100Mb(struct device *dev, int msec);
+static int wait_for_link(struct device *dev);
+static int test_mii_reg(struct device *dev, int reg, int mask, int pol, long msec);
+static int is_spd_100(struct device *dev);
+static int is_100_up(struct device *dev);
+static int is_10_up(struct device *dev);
+static int is_anc_capable(struct device *dev);
+static int ping_media(struct device *dev, int msec);
static struct sk_buff *de4x5_alloc_rx_buff(struct device *dev, int index, int len);
-static void de4x5_free_rx_buffs(struct device *dev);
-static void de4x5_free_tx_buffs(struct device *dev);
-static void de4x5_save_skbs(struct device *dev);
-static void de4x5_restore_skbs(struct device *dev);
-static void de4x5_cache_state(struct device *dev, int flag);
-static void de4x5_put_cache(struct device *dev, struct sk_buff *skb);
-static void de4x5_putb_cache(struct device *dev, struct sk_buff *skb);
-static struct sk_buff *de4x5_get_cache(struct device *dev);
-static void de4x5_setup_intr(struct device *dev);
-static void de4x5_init_connection(struct device *dev);
-static int de4x5_reset_phy(struct device *dev);
-static void reset_init_sia(struct device *dev, s32 sicr, s32 strr, s32 sigr);
-static int test_ans(struct device *dev, s32 irqs, s32 irq_mask, s32 msec);
-static int test_tp(struct device *dev, s32 msec);
-static int EISA_signature(char *name, s32 eisa_id);
-static int PCI_signature(char *name, struct bus_type *lp);
-static void DevicePresent(u_long iobase);
-static int de4x5_bad_srom(struct bus_type *lp);
-static short srom_rd(u_long address, u_char offset);
-static void srom_latch(u_int command, u_long address);
-static void srom_command(u_int command, u_long address);
-static void srom_address(u_int command, u_long address, u_char offset);
-static short srom_data(u_int command, u_long address);
-/*static void srom_busy(u_int command, u_long address);*/
-static void sendto_srom(u_int command, u_long addr);
-static int getfrom_srom(u_long addr);
-static void srom_map_media(struct device *dev);
-static int srom_infoleaf_info(struct device *dev);
-static void srom_init(struct device *dev);
-static void srom_exec(struct device *dev, u_char *p);
-static int mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr);
-static void mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr);
-static int mii_rdata(u_long ioaddr);
-static void mii_wdata(int data, int len, u_long ioaddr);
-static void mii_ta(u_long rw, u_long ioaddr);
-static int mii_swap(int data, int len);
-static void mii_address(u_char addr, u_long ioaddr);
-static void sendto_mii(u32 command, int data, u_long ioaddr);
-static int getfrom_mii(u32 command, u_long ioaddr);
-static int mii_get_oui(u_char phyaddr, u_long ioaddr);
-static int mii_get_phy(struct device *dev);
-static void SetMulticastFilter(struct device *dev);
-static int get_hw_addr(struct device *dev);
-static void srom_repair(struct device *dev, int card);
-static int test_bad_enet(struct device *dev, int status);
-
-static void eisa_probe(struct device *dev, u_long iobase);
-static void pci_probe(struct device *dev, u_long iobase);
-static struct device *alloc_device(struct device *dev, u_long iobase);
-static struct device *insert_device(struct device *dev, u_long iobase,
- int (*init)(struct device *));
-static char *build_setup_frame(struct device *dev, int mode);
-static void disable_ast(struct device *dev);
-static void enable_ast(struct device *dev, u32 time_out);
-static long de4x5_switch_mac_port(struct device *dev);
-static void timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec);
-static void yawn(struct device *dev, int state);
-static int de4x5_dev_index(char *s);
-static void link_modules(struct device *dev, struct device *tmp);
-static void de4x5_dbg_open(struct device *dev);
-static void de4x5_dbg_mii(struct device *dev, int k);
-static void de4x5_dbg_media(struct device *dev);
-static void de4x5_dbg_srom(struct de4x5_srom *p);
-static void de4x5_dbg_rx(struct sk_buff *skb, int len);
-static int de4x5_strncmp(char *a, char *b, int n);
-static int dc21041_infoleaf(struct device *dev);
-static int dc21140_infoleaf(struct device *dev);
-static int dc21142_infoleaf(struct device *dev);
-static int dc21143_infoleaf(struct device *dev);
-static int type0_infoblock(struct device *dev, u_char count, u_char *p);
-static int type1_infoblock(struct device *dev, u_char count, u_char *p);
-static int type2_infoblock(struct device *dev, u_char count, u_char *p);
-static int type3_infoblock(struct device *dev, u_char count, u_char *p);
-static int type4_infoblock(struct device *dev, u_char count, u_char *p);
-static int type5_infoblock(struct device *dev, u_char count, u_char *p);
-static int compact_infoblock(struct device *dev, u_char count, u_char *p);
+static void de4x5_free_rx_buffs(struct device *dev);
+static void de4x5_free_tx_buffs(struct device *dev);
+static void de4x5_save_skbs(struct device *dev);
+static void de4x5_restore_skbs(struct device *dev);
+static void de4x5_cache_state(struct device *dev, int flag);
+static void de4x5_put_cache(struct device *dev, struct sk_buff *skb);
+static void de4x5_putb_cache(struct device *dev, struct sk_buff *skb);
+static struct sk_buff *de4x5_get_cache(struct device *dev);
+static void de4x5_setup_intr(struct device *dev);
+static void de4x5_init_connection(struct device *dev);
+static int de4x5_reset_phy(struct device *dev);
+static void reset_init_sia(struct device *dev, s32 sicr, s32 strr, s32 sigr);
+static int test_ans(struct device *dev, s32 irqs, s32 irq_mask, s32 msec);
+static int test_tp(struct device *dev, s32 msec);
+static int EISA_signature(char *name, s32 eisa_id);
+static int PCI_signature(char *name, struct bus_type *lp);
+static void DevicePresent(u_long iobase);
+static int de4x5_bad_srom(struct bus_type *lp);
+static short srom_rd(u_long address, u_char offset);
+static void srom_latch(u_int command, u_long address);
+static void srom_command(u_int command, u_long address);
+static void srom_address(u_int command, u_long address, u_char offset);
+static short srom_data(u_int command, u_long address);
+/*static void srom_busy(u_int command, u_long address); */
+static void sendto_srom(u_int command, u_long addr);
+static int getfrom_srom(u_long addr);
+static int srom_map_media(struct device *dev);
+static int srom_infoleaf_info(struct device *dev);
+static void srom_init(struct device *dev);
+static void srom_exec(struct device *dev, u_char * p);
+static int mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr);
+static void mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr);
+static int mii_rdata(u_long ioaddr);
+static void mii_wdata(int data, int len, u_long ioaddr);
+static void mii_ta(u_long rw, u_long ioaddr);
+static int mii_swap(int data, int len);
+static void mii_address(u_char addr, u_long ioaddr);
+static void sendto_mii(u32 command, int data, u_long ioaddr);
+static int getfrom_mii(u32 command, u_long ioaddr);
+static int mii_get_oui(u_char phyaddr, u_long ioaddr);
+static int mii_get_phy(struct device *dev);
+static void SetMulticastFilter(struct device *dev);
+static int get_hw_addr(struct device *dev);
+static void srom_repair(struct device *dev, int card);
+static int test_bad_enet(struct device *dev, int status);
+
+#ifndef __sparc_v9__
+static void eisa_probe(struct device *dev, u_long iobase);
+#endif
+static void pci_probe(struct device *dev, u_long iobase);
+static void srom_search(int index);
+static char *build_setup_frame(struct device *dev, int mode);
+static void disable_ast(struct device *dev);
+static void enable_ast(struct device *dev, u32 time_out);
+static long de4x5_switch_mac_port(struct device *dev);
+static int gep_rd(struct device *dev);
+static void gep_wr(s32 data, struct device *dev);
+static void timeout(struct device *dev, void (*fn) (u_long data), u_long data, u_long msec);
+static void yawn(struct device *dev, int state);
+static void link_modules(struct device *dev, struct device *tmp);
+static void de4x5_dbg_open(struct device *dev);
+static void de4x5_dbg_mii(struct device *dev, int k);
+static void de4x5_dbg_media(struct device *dev);
+static void de4x5_dbg_srom(struct de4x5_srom *p);
+static void de4x5_dbg_rx(struct sk_buff *skb, int len);
+static int de4x5_strncmp(char *a, char *b, int n);
+static int dc21041_infoleaf(struct device *dev);
+static int dc21140_infoleaf(struct device *dev);
+static int dc21142_infoleaf(struct device *dev);
+static int dc21143_infoleaf(struct device *dev);
+static int type0_infoblock(struct device *dev, u_char count, u_char * p);
+static int type1_infoblock(struct device *dev, u_char count, u_char * p);
+static int type2_infoblock(struct device *dev, u_char count, u_char * p);
+static int type3_infoblock(struct device *dev, u_char count, u_char * p);
+static int type4_infoblock(struct device *dev, u_char count, u_char * p);
+static int type5_infoblock(struct device *dev, u_char count, u_char * p);
+static int compact_infoblock(struct device *dev, u_char count, u_char * p);
#ifdef MODULE
-int init_module(void);
+int init_module(void);
void cleanup_module(void);
-static struct device *unlink_modules(struct device *p);
-static int autoprobed = 0, loading_module = 1;
-# else
-static int autoprobed = 0, loading_module = 0;
-#endif /* MODULE */
+static struct device *unlink_modules(struct device *p);
+static struct device *insert_device(struct device *dev, u_long iobase,
+ int (*init)(struct device *));
+static int count_adapters(void);
+static int loading_module = 1;
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+MODULE_PARM(de4x5_debug, "i");
+MODULE_PARM(de4x5_full_duplex, "i");
+MODULE_PARM(dec_only, "i");
+#endif /* LINUX_VERSION_CODE */
+#else
+static int loading_module = 0;
+#endif /* MODULE */
static char name[DE4X5_NAME_LENGTH + 1];
+#ifndef __sparc_v9__
static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST;
-static int num_de4x5s = 0, num_eth = 0;
+#endif
+static int num_de4x5s = 0;
static int cfrv = 0, useSROM = 0;
+static int lastEISA = 0, lastPCI = -1;
+static struct device *lastModule = NULL;
/*
** List the SROM infoleaf functions and chipsets
*/
struct InfoLeaf {
- int chipset;
- int (*fn)(struct device *);
+ int chipset;
+ int (*fn) (struct device *);
};
-static struct InfoLeaf infoleaf_array[] = {
- {DC21041, dc21041_infoleaf},
- {DC21140, dc21140_infoleaf},
- {DC21142, dc21142_infoleaf},
- {DC21143, dc21143_infoleaf}
+static struct InfoLeaf infoleaf_array[] =
+{
+ {DC21041, dc21041_infoleaf},
+ {DC21140, dc21140_infoleaf},
+ {DC21142, dc21142_infoleaf},
+ {DC21143, dc21143_infoleaf}
};
#define INFOLEAF_SIZE (sizeof(infoleaf_array)/(sizeof(int)+sizeof(int *)))
/*
** List the SROM info block functions
*/
-static int (*dc_infoblock[])(struct device *dev, u_char, u_char *) = {
- type0_infoblock,
- type1_infoblock,
- type2_infoblock,
- type3_infoblock,
- type4_infoblock,
- type5_infoblock,
- compact_infoblock
+static int (*dc_infoblock[]) (struct device * dev, u_char, u_char *) = {
+ type0_infoblock,
+ type1_infoblock,
+ type2_infoblock,
+ type3_infoblock,
+ type4_infoblock,
+ type5_infoblock,
+ compact_infoblock
};
#define COMPACT (sizeof(dc_infoblock)/sizeof(int *) - 1)
@@ -939,320 +997,300 @@ static int (*dc_infoblock[])(struct device *dev, u_char, u_char *) = {
outl(0x00, DE4X5_GEP);\
udelay(2000); /* Wait for 2ms */\
}
-
+
/*
** Autoprobing in modules is allowed here. See the top of the file for
-** more info. Until I fix (un)register_netdevice() we won't be able to use it
-** though.
+** more info.
*/
-__initfunc(int
-de4x5_probe(struct device *dev))
+__initfunc(int de4x5_probe(struct device *dev))
{
- int status = -ENODEV;
- u_long iobase = dev->base_addr;
+ u_long iobase = dev->base_addr;
- eisa_probe(dev, iobase);
- pci_probe(dev, iobase);
-
- /*
- ** Walk the device list to check that at least one device
- ** initialised OK
- */
- for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next);
-
- if (dev->priv) status = 0;
- if (iobase == 0) autoprobed = 1;
+#ifndef __sparc_v9__
+ eisa_probe(dev, iobase);
+#endif
+ pci_probe(dev, iobase);
- return status;
+ return (dev->priv ? 0 : -ENODEV);
}
__initfunc(static int
-de4x5_hw_init(struct device *dev, u_long iobase))
+ de4x5_hw_init(struct device *dev, u_long iobase))
{
- struct bus_type *lp = &bus;
- int i, status=0;
- char *tmp;
-
- /* Ensure we're not sleeping */
- if (lp->bus == EISA) {
- outb(WAKEUP, PCI_CFPM);
- } else {
- pcibios_write_config_byte(lp->bus_num, lp->device << 3,
- PCI_CFDA_PSM, WAKEUP);
- }
- de4x5_ms_delay(10);
-
- RESET_DE4X5;
-
- if ((inl(DE4X5_STS) & (STS_TS | STS_RS)) != 0) {
- return -ENXIO; /* Hardware could not reset */
- }
-
- /*
- ** Now find out what kind of DC21040/DC21041/DC21140 board we have.
- */
- useSROM = FALSE;
- if (lp->bus == PCI) {
- PCI_signature(name, lp);
- } else {
- EISA_signature(name, EISA_ID0);
- }
-
- if (*name == '\0') { /* Not found a board signature */
- return -ENXIO;
- }
-
- dev->base_addr = iobase;
- if (lp->bus == EISA) {
- printk("%s: %s at 0x%04lx (EISA slot %ld)",
- dev->name, name, iobase, ((iobase>>12)&0x0f));
- } else { /* PCI port address */
- printk("%s: %s at 0x%04lx (PCI bus %d, device %d)", dev->name, name,
- iobase, lp->bus_num, lp->device);
- }
-
- printk(", h/w address ");
- status = get_hw_addr(dev);
- for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet addr. */
- printk("%2.2x:", dev->dev_addr[i]);
- }
- printk("%2.2x,\n", dev->dev_addr[i]);
-
- if (status != 0) {
- printk(" which has an Ethernet PROM CRC error.\n");
- return -ENXIO;
- } else {
- struct de4x5_private *lp;
-
- /*
- ** Reserve a section of kernel memory for the adapter
- ** private area and the TX/RX descriptor rings.
- */
- dev->priv = (void *) kmalloc(sizeof(struct de4x5_private) + ALIGN,
- GFP_KERNEL);
- if (dev->priv == NULL) {
- return -ENOMEM;
- }
-
- /*
- ** Align to a longword boundary
- */
- tmp = dev->priv;
- dev->priv = (void *)(((u_long)dev->priv + ALIGN) & ~ALIGN);
- lp = (struct de4x5_private *)dev->priv;
- memset(dev->priv, 0, sizeof(struct de4x5_private));
- lp->bus = bus.bus;
- lp->bus_num = bus.bus_num;
- lp->device = bus.device;
- lp->chipset = bus.chipset;
- lp->cache.priv = tmp;
- lp->cache.gepc = GEP_INIT;
- lp->asBit = GEP_SLNK;
- lp->asPolarity = GEP_SLNK;
- lp->asBitValid = TRUE;
- lp->timeout = -1;
- lp->useSROM = useSROM;
- memcpy((char *)&lp->srom,(char *)&bus.srom,sizeof(struct de4x5_srom));
+ struct bus_type *lp = &bus;
+ int i, status = 0;
+ char *tmp;
- /*
- ** Choose correct autosensing in case someone messed up
- */
- if ((de4x5_autosense & AUTO) || lp->useSROM) {
- lp->autosense = AUTO;
+ /* Ensure we're not sleeping */
+ if (lp->bus == EISA) {
+ outb(WAKEUP, PCI_CFPM);
} else {
- if (lp->chipset != DC21140) {
- if ((lp->chipset == DC21040) && (de4x5_autosense & TP_NW)) {
- de4x5_autosense = TP;
- }
- if ((lp->chipset == DC21041) && (de4x5_autosense & BNC_AUI)) {
- de4x5_autosense = BNC;
- }
- lp->autosense = de4x5_autosense & 0x001f;
- } else {
- lp->autosense = de4x5_autosense & 0x00c0;
- }
+ pcibios_write_config_byte(lp->bus_num, lp->device << 3,
+ PCI_CFDA_PSM, WAKEUP);
}
- lp->fdx = de4x5_full_duplex;
- sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
-
- /*
- ** Set up the RX descriptor ring (Intels)
- ** Allocate contiguous receive buffers, long word aligned (Alphas)
+ de4x5_ms_delay(10);
+
+ RESET_DE4X5;
+
+ if ((inl(DE4X5_STS) & (STS_TS | STS_RS)) != 0) {
+ return -ENXIO; /* Hardware could not reset */
+ }
+ /*
+ ** Now find out what kind of DC21040/DC21041/DC21140 board we have.
*/
-#if !defined(__alpha__) && !defined(__powerpc__) && !defined(DE4X5_DO_MEMCPY)
- for (i=0; i<NUM_RX_DESC; i++) {
- lp->rx_ring[i].status = 0;
- lp->rx_ring[i].des1 = RX_BUFF_SZ;
- lp->rx_ring[i].buf = 0;
- lp->rx_ring[i].next = 0;
- lp->rx_skb[i] = (struct sk_buff *) 1; /* Dummy entry */
+ useSROM = FALSE;
+ if (lp->bus == PCI) {
+ PCI_signature(name, lp);
+ } else {
+ EISA_signature(name, EISA_ID0);
}
-#else
- if ((tmp = (void *)kmalloc(RX_BUFF_SZ * NUM_RX_DESC + ALIGN,
- GFP_KERNEL)) == NULL) {
- kfree(lp->cache.priv);
- return -ENOMEM;
+ if (*name == '\0') { /* Not found a board signature */
+ return -ENXIO;
+ }
+ dev->base_addr = iobase;
+ if (lp->bus == EISA) {
+ printk("%s: %s at 0x%04lx (EISA slot %ld)",
+ dev->name, name, iobase, ((iobase >> 12) & 0x0f));
+ } else { /* PCI port address */
+ printk("%s: %s at 0x%04lx (PCI bus %d, device %d)", dev->name, name,
+ iobase, lp->bus_num, lp->device);
}
- lp->cache.buf = tmp;
- tmp = (char *)(((u_long) tmp + ALIGN) & ~ALIGN);
- for (i=0; i<NUM_RX_DESC; i++) {
- lp->rx_ring[i].status = 0;
- lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ);
- lp->rx_ring[i].buf = cpu_to_le32(virt_to_bus(tmp+i*RX_BUFF_SZ));
- lp->rx_ring[i].next = 0;
- lp->rx_skb[i] = (struct sk_buff *) 1; /* Dummy entry */
+ printk(", h/w address ");
+ status = get_hw_addr(dev);
+ for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet addr. */
+ printk("%2.2x:", dev->dev_addr[i]);
}
+ printk("%2.2x,\n", dev->dev_addr[i]);
+
+ if (status != 0) {
+ printk(" which has an Ethernet PROM CRC error.\n");
+ return -ENXIO;
+ } else {
+ struct de4x5_private *lp;
+
+ /*
+ ** Reserve a section of kernel memory for the adapter
+ ** private area and the TX/RX descriptor rings.
+ */
+ dev->priv = (void *) kmalloc(sizeof(struct de4x5_private) + ALIGN,
+ GFP_KERNEL);
+ if (dev->priv == NULL) {
+ return -ENOMEM;
+ }
+ /*
+ ** Align to a longword boundary
+ */
+ tmp = dev->priv;
+ dev->priv = (void *) (((u_long) dev->priv + ALIGN) & ~ALIGN);
+ lp = (struct de4x5_private *) dev->priv;
+ memset(dev->priv, 0, sizeof(struct de4x5_private));
+ lp->bus = bus.bus;
+ lp->bus_num = bus.bus_num;
+ lp->device = bus.device;
+ lp->chipset = bus.chipset;
+ lp->cache.priv = tmp;
+ lp->cache.gepc = GEP_INIT;
+ lp->asBit = GEP_SLNK;
+ lp->asPolarity = GEP_SLNK;
+ lp->asBitValid = TRUE;
+ lp->timeout = -1;
+ lp->useSROM = useSROM;
+ memcpy((char *) &lp->srom, (char *) &bus.srom, sizeof(struct de4x5_srom));
+
+ /*
+ ** Choose correct autosensing in case someone messed up
+ */
+ if ((de4x5_autosense & AUTO) || lp->useSROM) {
+ lp->autosense = AUTO;
+ } else {
+ if (lp->chipset != DC21140) {
+ if ((lp->chipset == DC21040) && (de4x5_autosense & TP_NW)) {
+ de4x5_autosense = TP;
+ }
+ if ((lp->chipset == DC21041) && (de4x5_autosense & BNC_AUI)) {
+ de4x5_autosense = BNC;
+ }
+ lp->autosense = de4x5_autosense & 0x001f;
+ } else {
+ lp->autosense = de4x5_autosense & 0x00c0;
+ }
+ }
+ lp->fdx = de4x5_full_duplex;
+ sprintf(lp->adapter_name, "%s (%s)", name, dev->name);
+
+ /*
+ ** Set up the RX descriptor ring (Intels)
+ ** Allocate contiguous receive buffers, long word aligned (Alphas)
+ */
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY)
+ for (i = 0; i < NUM_RX_DESC; i++) {
+ lp->rx_ring[i].status = 0;
+ lp->rx_ring[i].des1 = RX_BUFF_SZ;
+ lp->rx_ring[i].buf = 0;
+ lp->rx_ring[i].next = 0;
+ lp->rx_skb[i] = (struct sk_buff *) 1; /* Dummy entry */
+ }
+
+#else
+ if ((tmp = (void *) kmalloc(RX_BUFF_SZ * NUM_RX_DESC + ALIGN,
+ GFP_KERNEL)) == NULL) {
+ kfree(lp->cache.priv);
+ return -ENOMEM;
+ }
+ lp->cache.buf = tmp;
+ tmp = (char *) (((u_long) tmp + ALIGN) & ~ALIGN);
+ for (i = 0; i < NUM_RX_DESC; i++) {
+ lp->rx_ring[i].status = 0;
+ lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ);
+ lp->rx_ring[i].buf = cpu_to_le32(virt_to_bus(tmp + i * RX_BUFF_SZ));
+ lp->rx_ring[i].next = 0;
+ lp->rx_skb[i] = (struct sk_buff *) 1; /* Dummy entry */
+ }
#endif
- barrier();
-
- request_region(iobase, (lp->bus == PCI ? DE4X5_PCI_TOTAL_SIZE :
- DE4X5_EISA_TOTAL_SIZE),
- lp->adapter_name);
-
- lp->rxRingSize = NUM_RX_DESC;
- lp->txRingSize = NUM_TX_DESC;
-
- /* Write the end of list marker to the descriptor lists */
- lp->rx_ring[lp->rxRingSize - 1].des1 |= cpu_to_le32(RD_RER);
- lp->tx_ring[lp->txRingSize - 1].des1 |= cpu_to_le32(TD_TER);
-
- /* Tell the adapter where the TX/RX rings are located. */
- outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA);
- outl(virt_to_bus(lp->tx_ring), DE4X5_TRBA);
-
- /* Initialise the IRQ mask and Enable/Disable */
- lp->irq_mask = IMR_RIM | IMR_TIM | IMR_TUM | IMR_UNM;
- lp->irq_en = IMR_NIM | IMR_AIM;
+ barrier();
+
+ request_region(iobase, (lp->bus == PCI ? DE4X5_PCI_TOTAL_SIZE :
+ DE4X5_EISA_TOTAL_SIZE),
+ lp->adapter_name);
+
+ lp->rxRingSize = NUM_RX_DESC;
+ lp->txRingSize = NUM_TX_DESC;
+
+ /* Write the end of list marker to the descriptor lists */
+ lp->rx_ring[lp->rxRingSize - 1].des1 |= cpu_to_le32(RD_RER);
+ lp->tx_ring[lp->txRingSize - 1].des1 |= cpu_to_le32(TD_TER);
+
+ /* Tell the adapter where the TX/RX rings are located. */
+ outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA);
+ outl(virt_to_bus(lp->tx_ring), DE4X5_TRBA);
+
+ /* Initialise the IRQ mask and Enable/Disable */
+ lp->irq_mask = IMR_RIM | IMR_TIM | IMR_TUM | IMR_UNM;
+ lp->irq_en = IMR_NIM | IMR_AIM;
- /* Create a loopback packet frame for later media probing */
- create_packet(dev, lp->frame, sizeof(lp->frame));
+ /* Create a loopback packet frame for later media probing */
+ create_packet(dev, lp->frame, sizeof(lp->frame));
- /* Check if the RX overflow bug needs testing for */
- i = cfrv & 0x000000fe;
- if ((lp->chipset == DC21140) && (i == 0x20)) {
- lp->rx_ovf = 1;
+ /* Check if the RX overflow bug needs testing for */
+ i = cfrv & 0x000000fe;
+ if ((lp->chipset == DC21140) && (i == 0x20)) {
+ lp->rx_ovf = 1;
+ }
+ /* Initialise the SROM pointers if possible */
+ if (lp->useSROM) {
+ lp->state = INITIALISED;
+ if (srom_infoleaf_info(dev)) {
+ return -ENXIO;
+ }
+ srom_init(dev);
+ }
+ lp->state = CLOSED;
+
+ /*
+ ** Check for an MII interface
+ */
+ if ((lp->chipset != DC21040) && (lp->chipset != DC21041)) {
+ mii_get_phy(dev);
+ }
+ printk(" and requires IRQ %x (provided by %s).\n", dev->irq,
+ ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG"));
}
- /* Initialise the SROM pointers if possible */
- if (lp->useSROM) {
- lp->state = INITIALISED;
- de4x5_dbg_srom((struct de4x5_srom *)&lp->srom);
- if (srom_infoleaf_info(dev)) {
- return -ENXIO;
- }
- srom_init(dev);
+ if (de4x5_debug & DEBUG_VERSION) {
+ printk(version);
}
+ /* The DE4X5-specific entries in the device structure. */
+ dev->open = &de4x5_open;
+ dev->hard_start_xmit = &de4x5_queue_pkt;
+ dev->stop = &de4x5_close;
+ dev->get_stats = &de4x5_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+ dev->do_ioctl = &de4x5_ioctl;
- lp->state = CLOSED;
+ dev->mem_start = 0;
+
+ /* Fill in the generic fields of the device structure. */
+ ether_setup(dev);
+
+ /* Let the adapter sleep to save power */
+ yawn(dev, SLEEP);
+
+ return status;
+}
+
+
+static int de4x5_open(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ int i, status = 0;
+ s32 omr;
+
+ /* Allocate the RX buffers */
+ for (i = 0; i < lp->rxRingSize; i++) {
+ if (de4x5_alloc_rx_buff(dev, i, 0) == NULL) {
+ de4x5_free_rx_buffs(dev);
+ return -EAGAIN;
+ }
+ }
/*
- ** Check for an MII interface
+ ** Wake up the adapter
+ */
+ yawn(dev, WAKEUP);
+
+ /*
+ ** Re-initialize the DE4X5...
*/
- if ((lp->chipset != DC21040) && (lp->chipset != DC21041)) {
- mii_get_phy(dev);
+ status = de4x5_init(dev);
+
+ lp->state = OPEN;
+ de4x5_dbg_open(dev);
+
+ if (request_irq(dev->irq, (void *) de4x5_interrupt, SA_SHIRQ,
+ lp->adapter_name, dev)) {
+ printk("de4x5_open(): Requested IRQ%d is busy - attemping FAST/SHARE...", dev->irq);
+ if (request_irq(dev->irq, de4x5_interrupt, SA_INTERRUPT | SA_SHIRQ,
+ lp->adapter_name, dev)) {
+ printk("\n Cannot get IRQ- reconfigure your hardware.\n");
+ disable_ast(dev);
+ de4x5_free_rx_buffs(dev);
+ de4x5_free_tx_buffs(dev);
+ yawn(dev, SLEEP);
+ lp->state = CLOSED;
+ return -EAGAIN;
+ } else {
+ printk("\n Succeeded, but you should reconfigure your hardware to avoid this.\n");
+ printk("WARNING: there may be IRQ related problems in heavily loaded systems.\n");
+ }
}
-
- printk(" and requires IRQ%d (provided by %s).\n", dev->irq,
- ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG"));
- }
-
- if (de4x5_debug & DEBUG_VERSION) {
- printk(version);
- }
-
- /* The DE4X5-specific entries in the device structure. */
- dev->open = &de4x5_open;
- dev->hard_start_xmit = &de4x5_queue_pkt;
- dev->stop = &de4x5_close;
- dev->get_stats = &de4x5_get_stats;
- dev->set_multicast_list = &set_multicast_list;
- dev->do_ioctl = &de4x5_ioctl;
-
- dev->mem_start = 0;
-
- /* Fill in the generic fields of the device structure. */
- ether_setup(dev);
-
- /* Let the adapter sleep to save power */
- yawn(dev, SLEEP);
-
- return status;
-}
-
-static int
-de4x5_open(struct device *dev)
-{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- int i, status = 0;
- s32 omr;
-
- /* Allocate the RX buffers */
- for (i=0; i<lp->rxRingSize; i++) {
- if (de4x5_alloc_rx_buff(dev, i, 0) == NULL) {
- de4x5_free_rx_buffs(dev);
- return -EAGAIN;
- }
- }
-
- /*
- ** Wake up the adapter
- */
- yawn(dev, WAKEUP);
-
- /*
- ** Re-initialize the DE4X5...
- */
- status = de4x5_init(dev);
-
- lp->state = OPEN;
- de4x5_dbg_open(dev);
-
- if (request_irq(dev->irq, (void *)de4x5_interrupt, SA_SHIRQ,
- lp->adapter_name, dev)) {
- printk("de4x5_open(): Requested IRQ%d is busy - attemping FAST/SHARE...", dev->irq);
- if (request_irq(dev->irq, de4x5_interrupt, SA_INTERRUPT | SA_SHIRQ,
- lp->adapter_name, dev)) {
- printk("\n Cannot get IRQ- reconfigure your hardware.\n");
- disable_ast(dev);
- de4x5_free_rx_buffs(dev);
- de4x5_free_tx_buffs(dev);
- yawn(dev, SLEEP);
- lp->state = CLOSED;
- return -EAGAIN;
- } else {
- printk("\n Succeeded, but you should reconfigure your hardware to avoid this.\n");
- printk("WARNING: there may be IRQ related problems in heavily loaded systems.\n");
- }
- }
- dev->tbusy = 0;
- dev->start = 1;
- dev->interrupt = UNMASK_INTERRUPTS;
- dev->trans_start = jiffies;
-
- START_DE4X5;
-
- de4x5_setup_intr(dev);
-
- if (de4x5_debug & DEBUG_OPEN) {
- printk("\tsts: 0x%08x\n", inl(DE4X5_STS));
- printk("\tbmr: 0x%08x\n", inl(DE4X5_BMR));
- printk("\timr: 0x%08x\n", inl(DE4X5_IMR));
- printk("\tomr: 0x%08x\n", inl(DE4X5_OMR));
- printk("\tsisr: 0x%08x\n", inl(DE4X5_SISR));
- printk("\tsicr: 0x%08x\n", inl(DE4X5_SICR));
- printk("\tstrr: 0x%08x\n", inl(DE4X5_STRR));
- printk("\tsigr: 0x%08x\n", inl(DE4X5_SIGR));
- }
-
- MOD_INC_USE_COUNT;
-
- return status;
+ dev->tbusy = 0;
+ dev->start = 1;
+ dev->interrupt = UNMASK_INTERRUPTS;
+ dev->trans_start = jiffies;
+
+ START_DE4X5;
+
+ de4x5_setup_intr(dev);
+
+ if (de4x5_debug & DEBUG_OPEN) {
+ printk("\tsts: 0x%08x\n", inl(DE4X5_STS));
+ printk("\tbmr: 0x%08x\n", inl(DE4X5_BMR));
+ printk("\timr: 0x%08x\n", inl(DE4X5_IMR));
+ printk("\tomr: 0x%08x\n", inl(DE4X5_OMR));
+ printk("\tsisr: 0x%08x\n", inl(DE4X5_SISR));
+ printk("\tsicr: 0x%08x\n", inl(DE4X5_SICR));
+ printk("\tstrr: 0x%08x\n", inl(DE4X5_STRR));
+ printk("\tsigr: 0x%08x\n", inl(DE4X5_SIGR));
+ }
+ MOD_INC_USE_COUNT;
+
+ return status;
}
/*
@@ -1263,166 +1301,157 @@ de4x5_open(struct device *dev)
** to be data corruption problems if it is larger (UDP errors seen from a
** ttcp source).
*/
-static int
-de4x5_init(struct device *dev)
-{
- /* Lock out other processes whilst setting up the hardware */
- set_bit(0, (void *)&dev->tbusy);
-
- de4x5_sw_reset(dev);
-
- /* Autoconfigure the connected port */
- autoconf_media(dev);
-
- return 0;
+static int de4x5_init(struct device *dev)
+{
+ /* Lock out other processes whilst setting up the hardware */
+ test_and_set_bit(0, (void *)&dev->tbusy);
+
+ de4x5_sw_reset(dev);
+
+ /* Autoconfigure the connected port */
+ autoconf_media(dev);
+
+ return 0;
}
-static int
-de4x5_sw_reset(struct device *dev)
+static int de4x5_sw_reset(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- int i, j, status = 0;
- s32 bmr, omr;
-
- /* Select the MII or SRL port now and RESET the MAC */
- if (!lp->useSROM) {
- if (lp->phy[lp->active].id != 0) {
- lp->infoblock_csr6 = OMR_PS | OMR_HBD;
- } else {
- lp->infoblock_csr6 = OMR_TTM;
- }
- de4x5_switch_mac_port(dev);
- }
-
- /*
- ** Set the programmable burst length to 8 longwords for all the DC21140
- ** Fasternet chips and 4 longwords for all others: DMA errors result
- ** without these values. Cache align 16 long.
- */
- bmr = (lp->chipset==DC21140 ? PBL_8 : PBL_4) | DESC_SKIP_LEN | CACHE_ALIGN;
- outl(bmr, DE4X5_BMR);
-
- omr = inl(DE4X5_OMR) & ~OMR_PR; /* Turn off promiscuous mode */
- if (lp->chipset == DC21140) {
- omr |= (OMR_SDP | OMR_SB);
- }
- lp->setup_f = PERFECT;
- outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA);
- outl(virt_to_bus(lp->tx_ring), DE4X5_TRBA);
-
- lp->rx_new = lp->rx_old = 0;
- lp->tx_new = lp->tx_old = 0;
-
- for (i = 0; i < lp->rxRingSize; i++) {
- lp->rx_ring[i].status = cpu_to_le32(R_OWN);
- }
-
- for (i = 0; i < lp->txRingSize; i++) {
- lp->tx_ring[i].status = cpu_to_le32(0);
- }
-
- barrier();
-
- /* Build the setup frame depending on filtering mode */
- SetMulticastFilter(dev);
-
- load_packet(dev, lp->setup_frame, PERFECT_F|TD_SET|SETUP_FRAME_LEN, NULL);
- outl(omr|OMR_ST, DE4X5_OMR);
-
- /* Poll for setup frame completion (adapter interrupts are disabled now) */
- sti(); /* Ensure timer interrupts */
- for (j=0, i=0;(i<500) && (j==0);i++) { /* Upto 500ms delay */
- udelay(1000);
- if ((s32)le32_to_cpu(lp->tx_ring[lp->tx_new].status) >= 0) j=1;
- }
- outl(omr, DE4X5_OMR); /* Stop everything! */
-
- if (j == 0) {
- printk("%s: Setup frame timed out, status %08x\n", dev->name,
- inl(DE4X5_STS));
- status = -EIO;
- }
-
- lp->tx_new = (++lp->tx_new) % lp->txRingSize;
- lp->tx_old = lp->tx_new;
-
- return status;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ int i, j, status = 0;
+ s32 bmr, omr;
+
+ /* Select the MII or SRL port now and RESET the MAC */
+ if (!lp->useSROM) {
+ if (lp->phy[lp->active].id != 0) {
+ lp->infoblock_csr6 = OMR_PS | OMR_HBD;
+ } else {
+ lp->infoblock_csr6 = OMR_TTM;
+ }
+ de4x5_switch_mac_port(dev);
+ }
+ /*
+ ** Set the programmable burst length to 8 longwords for all the DC21140
+ ** Fasternet chips and 4 longwords for all others: DMA errors result
+ ** without these values. Cache align 16 long.
+ */
+ bmr = (lp->chipset == DC21140 ? PBL_8 : PBL_4) | DESC_SKIP_LEN | CACHE_ALIGN;
+ bmr |= ((lp->chipset & ~0x00ff)==DC2114x ? BMR_RML : 0);
+ outl(bmr, DE4X5_BMR);
+
+ omr = inl(DE4X5_OMR) & ~OMR_PR; /* Turn off promiscuous mode */
+ if (lp->chipset == DC21140) {
+ omr |= (OMR_SDP | OMR_SB);
+ }
+ lp->setup_f = PERFECT;
+ outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA);
+ outl(virt_to_bus(lp->tx_ring), DE4X5_TRBA);
+
+ lp->rx_new = lp->rx_old = 0;
+ lp->tx_new = lp->tx_old = 0;
+
+ for (i = 0; i < lp->rxRingSize; i++) {
+ lp->rx_ring[i].status = cpu_to_le32(R_OWN);
+ }
+
+ for (i = 0; i < lp->txRingSize; i++) {
+ lp->tx_ring[i].status = cpu_to_le32(0);
+ }
+
+ barrier();
+
+ /* Build the setup frame depending on filtering mode */
+ SetMulticastFilter(dev);
+
+ load_packet(dev, lp->setup_frame, PERFECT_F | TD_SET | SETUP_FRAME_LEN, NULL);
+ outl(omr | OMR_ST, DE4X5_OMR);
+
+ /* Poll for setup frame completion (adapter interrupts are disabled now) */
+ sti(); /* Ensure timer interrupts */
+ for (j = 0, i = 0; (i < 500) && (j == 0); i++) { /* Upto 500ms delay */
+ udelay(1000);
+ if ((s32) le32_to_cpu(lp->tx_ring[lp->tx_new].status) >= 0)
+ j = 1;
+ }
+ outl(omr, DE4X5_OMR); /* Stop everything! */
+
+ if (j == 0) {
+ printk("%s: Setup frame timed out, status %08x\n", dev->name,
+ inl(DE4X5_STS));
+ status = -EIO;
+ }
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+ lp->tx_old = lp->tx_new;
+
+ return status;
}
/*
** Writes a socket buffer address to the next available transmit descriptor
*/
-static int
-de4x5_queue_pkt(struct sk_buff *skb, struct device *dev)
+static int de4x5_queue_pkt(struct sk_buff *skb, struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- int status = 0;
-
- if (skb == NULL) {
- dev_tint(dev);
- return 0;
- }
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ int status = 0;
- set_bit(0, (void*)&dev->tbusy); /* Stop send re-tries */
- if (lp->tx_enable == NO) { /* Cannot send for now */
- return -1;
- }
-
- /*
- ** Clean out the TX ring asynchronously to interrupts - sometimes the
- ** interrupts are lost by delayed descriptor status updates relative to
- ** the irq assertion, especially with a busy PCI bus.
- */
- cli();
- de4x5_tx(dev);
- sti();
-
- /* Test if cache is already locked - requeue skb if so */
- if (test_and_set_bit(0, (void *)&lp->cache.lock) && !dev->interrupt) return -1;
-
- /* Transmit descriptor ring full or stale skb */
- if (dev->tbusy || lp->tx_skb[lp->tx_new]) {
- if (dev->interrupt) {
- de4x5_putb_cache(dev, skb); /* Requeue the buffer */
- } else {
- de4x5_put_cache(dev, skb);
- }
- if (de4x5_debug & DEBUG_TX) {
- printk("%s: transmit busy, lost media or stale skb found:\n STS:%08x\n tbusy:%ld\n IMR:%08x\n OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), dev->tbusy, inl(DE4X5_IMR), inl(DE4X5_OMR), (lp->tx_skb[lp->tx_new] ? "YES" : "NO"));
- }
- } else if (skb->len > 0) {
- /* If we already have stuff queued locally, use that first */
- if (lp->cache.skb && !dev->interrupt) {
- de4x5_put_cache(dev, skb);
- skb = de4x5_get_cache(dev);
+ test_and_set_bit(0, (void*)&dev->tbusy); /* Stop send re-tries */
+ if (lp->tx_enable == NO) { /* Cannot send for now */
+ return -1;
}
+ /*
+ ** Clean out the TX ring asynchronously to interrupts - sometimes the
+ ** interrupts are lost by delayed descriptor status updates relative to
+ ** the irq assertion, especially with a busy PCI bus.
+ */
+ cli();
+ de4x5_tx(dev);
+ sti();
+
+ /* Test if cache is already locked - requeue skb if so */
+ if (test_and_set_bit(0, (void *) &lp->cache.lock) && !dev->interrupt)
+ return -1;
- while (skb && !dev->tbusy && !lp->tx_skb[lp->tx_new]) {
- cli();
- set_bit(0, (void*)&dev->tbusy);
- load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb);
+ /* Transmit descriptor ring full or stale skb */
+ if (dev->tbusy || lp->tx_skb[lp->tx_new]) {
+ if (dev->interrupt) {
+ de4x5_putb_cache(dev, skb); /* Requeue the buffer */
+ } else {
+ de4x5_put_cache(dev, skb);
+ }
+ if (de4x5_debug & DEBUG_TX) {
+ printk("%s: transmit busy, lost media or stale skb found:\n STS:%08x\n tbusy:%ld\n IMR:%08x\n OMR:%08x\n Stale skb: %s\n", dev->name, inl(DE4X5_STS), dev->tbusy, inl(DE4X5_IMR), inl(DE4X5_OMR), (lp->tx_skb[lp->tx_new] ? "YES" : "NO"));
+ }
+ } else if (skb->len > 0) {
+ /* If we already have stuff queued locally, use that first */
+ if (lp->cache.skb && !dev->interrupt) {
+ de4x5_put_cache(dev, skb);
+ skb = de4x5_get_cache(dev);
+ }
+ while (skb && !dev->tbusy && !lp->tx_skb[lp->tx_new]) {
+ cli();
+ test_and_set_bit(0, (void*)&dev->tbusy);
+ load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb);
#if LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
- lp->stats.tx_bytes += skb->len;
+ lp->stats.tx_bytes += skb->len;
#endif
- outl(POLL_DEMAND, DE4X5_TPD);/* Start the TX */
-
- lp->tx_new = (++lp->tx_new) % lp->txRingSize;
- dev->trans_start = jiffies;
-
- if (TX_BUFFS_AVAIL) {
- dev->tbusy = 0; /* Another pkt may be queued */
- }
- skb = de4x5_get_cache(dev);
- sti();
- }
- if (skb) de4x5_putb_cache(dev, skb);
- }
-
- lp->cache.lock = 0;
+ outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */
+
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+ dev->trans_start = jiffies;
+
+ if (TX_BUFFS_AVAIL) {
+ dev->tbusy = 0; /* Another pkt may be queued */
+ }
+ skb = de4x5_get_cache(dev);
+ sti();
+ }
+ if (skb)
+ de4x5_putb_cache(dev, skb);
+ }
+ lp->cache.lock = 0;
- return status;
+ return status;
}
/*
@@ -1436,395 +1465,389 @@ de4x5_queue_pkt(struct sk_buff *skb, struct device *dev)
** is high and descriptor status bits cannot be set before the associated
** interrupt is asserted and this routine entered.
*/
-static void
-de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static void de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- struct device *dev = (struct device *)dev_id;
- struct de4x5_private *lp;
- s32 imr, omr, sts, limit;
- u_long iobase;
-
- if (dev == NULL) {
- printk ("de4x5_interrupt(): irq %d for unknown device.\n", irq);
- return;
- }
- lp = (struct de4x5_private *)dev->priv;
- iobase = dev->base_addr;
-
- if (dev->interrupt)
- printk("%s: Re-entering the interrupt handler.\n", dev->name);
-
- DISABLE_IRQs; /* Ensure non re-entrancy */
+ struct device *dev = (struct device *) dev_id;
+ struct de4x5_private *lp;
+ s32 imr, omr, sts, limit;
+ u_long iobase;
+
+ if (dev == NULL) {
+ printk("de4x5_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+ lp = (struct de4x5_private *) dev->priv;
+ iobase = dev->base_addr;
+
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+ DISABLE_IRQs; /* Ensure non re-entrancy */
#if LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
- synchronize_irq();
+ synchronize_irq();
#endif
- dev->interrupt = MASK_INTERRUPTS;
-
- for (limit=0; limit<8; limit++) {
- sts = inl(DE4X5_STS); /* Read IRQ status */
- outl(sts, DE4X5_STS); /* Reset the board interrupts */
-
- if (!(sts & lp->irq_mask)) break;/* All done */
-
- if (sts & (STS_RI | STS_RU)) /* Rx interrupt (packet[s] arrived) */
- de4x5_rx(dev);
-
- if (sts & (STS_TI | STS_TU)) /* Tx interrupt (packet sent) */
- de4x5_tx(dev);
-
- if (sts & STS_LNF) { /* TP Link has failed */
- lp->irq_mask &= ~IMR_LFM;
- }
-
- if (sts & STS_UNF) { /* Transmit underrun */
- de4x5_txur(dev);
- }
-
- if (sts & STS_SE) { /* Bus Error */
- STOP_DE4X5;
- printk("%s: Fatal bus error occurred, sts=%#8x, device stopped.\n",
- dev->name, sts);
- return;
+ dev->interrupt = MASK_INTERRUPTS;
+
+ for (limit = 0; limit < 8; limit++) {
+ sts = inl(DE4X5_STS); /* Read IRQ status */
+ outl(sts, DE4X5_STS); /* Reset the board interrupts */
+
+ if (!(sts & lp->irq_mask))
+ break; /* All done */
+
+ if (sts & (STS_RI | STS_RU)) /* Rx interrupt (packet[s] arrived) */
+ de4x5_rx(dev);
+
+ if (sts & (STS_TI | STS_TU)) /* Tx interrupt (packet sent) */
+ de4x5_tx(dev);
+
+ if (sts & STS_LNF) { /* TP Link has failed */
+ lp->irq_mask &= ~IMR_LFM;
+ }
+ if (sts & STS_UNF) { /* Transmit underrun */
+ de4x5_txur(dev);
+ }
+ if (sts & STS_SE) { /* Bus Error */
+ STOP_DE4X5;
+ printk("%s: Fatal bus error occurred, sts=%#8x, device stopped.\n",
+ dev->name, sts);
+ return;
+ }
}
- }
- /* Load the TX ring with any locally stored packets */
- if (!test_and_set_bit(0, (void *)&lp->cache.lock)) {
- while (lp->cache.skb && !dev->tbusy && lp->tx_enable) {
- de4x5_queue_pkt(de4x5_get_cache(dev), dev);
+ /* Load the TX ring with any locally stored packets */
+ if (!test_and_set_bit(0, (void *) &lp->cache.lock)) {
+ while (lp->cache.skb && !dev->tbusy && lp->tx_enable) {
+ de4x5_queue_pkt(de4x5_get_cache(dev), dev);
+ }
+ lp->cache.lock = 0;
}
- lp->cache.lock = 0;
- }
+ dev->interrupt = UNMASK_INTERRUPTS;
+ ENABLE_IRQs;
- dev->interrupt = UNMASK_INTERRUPTS;
- ENABLE_IRQs;
-
- return;
+ return;
}
-static int
-de4x5_rx(struct device *dev)
+static int de4x5_rx(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- int entry;
- s32 status;
-
- for (entry=lp->rx_new; (s32)le32_to_cpu(lp->rx_ring[entry].status)>=0;
- entry=lp->rx_new) {
- status = (s32)le32_to_cpu(lp->rx_ring[entry].status);
-
- if (lp->rx_ovf) {
- if (inl(DE4X5_MFC) & MFC_FOCM) {
- de4x5_rx_ovfc(dev);
- break;
- }
- }
-
- if (status & RD_FS) { /* Remember the start of frame */
- lp->rx_old = entry;
- }
-
- if (status & RD_LS) { /* Valid frame status */
- if (lp->tx_enable) lp->linkOK++;
- if (status & RD_ES) { /* There was an error. */
- lp->stats.rx_errors++; /* Update the error stats. */
- if (status & (RD_RF | RD_TL)) lp->stats.rx_frame_errors++;
- if (status & RD_CE) lp->stats.rx_crc_errors++;
- if (status & RD_OF) lp->stats.rx_fifo_errors++;
- if (status & RD_TL) lp->stats.rx_length_errors++;
- if (status & RD_RF) lp->pktStats.rx_runt_frames++;
- if (status & RD_CS) lp->pktStats.rx_collision++;
- if (status & RD_DB) lp->pktStats.rx_dribble++;
- if (status & RD_OF) lp->pktStats.rx_overflow++;
- } else { /* A valid frame received */
- struct sk_buff *skb;
- short pkt_len = (short)(le32_to_cpu(lp->rx_ring[entry].status)
- >> 16) - 4;
-
- if ((skb = de4x5_alloc_rx_buff(dev, entry, pkt_len)) == NULL) {
- printk("%s: Insufficient memory; nuking packet.\n",
- dev->name);
- lp->stats.rx_dropped++;
- } else {
- de4x5_dbg_rx(skb, pkt_len);
-
- /* Push up the protocol stack */
- skb->protocol=eth_type_trans(skb,dev);
- netif_rx(skb);
-
- /* Update stats */
- lp->stats.rx_packets++;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ int entry;
+ s32 status;
+
+ for (entry = lp->rx_new; (s32) le32_to_cpu(lp->rx_ring[entry].status) >= 0;
+ entry = lp->rx_new) {
+ status = (s32) le32_to_cpu(lp->rx_ring[entry].status);
+
+ if (lp->rx_ovf) {
+ if (inl(DE4X5_MFC) & MFC_FOCM) {
+ de4x5_rx_ovfc(dev);
+ break;
+ }
+ }
+ if (status & RD_FS) { /* Remember the start of frame */
+ lp->rx_old = entry;
+ }
+ if (status & RD_LS) { /* Valid frame status */
+ if (lp->tx_enable)
+ lp->linkOK++;
+ if (status & RD_ES) { /* There was an error. */
+ lp->stats.rx_errors++; /* Update the error stats. */
+ if (status & (RD_RF | RD_TL))
+ lp->stats.rx_frame_errors++;
+ if (status & RD_CE)
+ lp->stats.rx_crc_errors++;
+ if (status & RD_OF)
+ lp->stats.rx_fifo_errors++;
+ if (status & RD_TL)
+ lp->stats.rx_length_errors++;
+ if (status & RD_RF)
+ lp->pktStats.rx_runt_frames++;
+ if (status & RD_CS)
+ lp->pktStats.rx_collision++;
+ if (status & RD_DB)
+ lp->pktStats.rx_dribble++;
+ if (status & RD_OF)
+ lp->pktStats.rx_overflow++;
+ } else { /* A valid frame received */
+ struct sk_buff *skb;
+ short pkt_len = (short) (le32_to_cpu(lp->rx_ring[entry].status)
+ >> 16) - 4;
+
+ if ((skb = de4x5_alloc_rx_buff(dev, entry, pkt_len)) == NULL) {
+ printk("%s: Insufficient memory; nuking packet.\n",
+ dev->name);
+ lp->stats.rx_dropped++;
+ } else {
+ de4x5_dbg_rx(skb, pkt_len);
+
+ /* Push up the protocol stack */
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+
+ /* Update stats */
+ lp->stats.rx_packets++;
#if LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
- lp->stats.rx_bytes += pkt_len;
+ lp->stats.rx_bytes += pkt_len;
#endif
- de4x5_local_stats(dev, skb->data, pkt_len);
+ de4x5_local_stats(dev, skb->data, pkt_len);
+ }
+ }
+
+ /* Change buffer ownership for this frame, back to the adapter */
+ for (; lp->rx_old != entry; lp->rx_old = (++lp->rx_old) % lp->rxRingSize) {
+ lp->rx_ring[lp->rx_old].status = cpu_to_le32(R_OWN);
+ barrier();
+ }
+ lp->rx_ring[entry].status = cpu_to_le32(R_OWN);
+ barrier();
}
- }
-
- /* Change buffer ownership for this frame, back to the adapter */
- for (;lp->rx_old!=entry;lp->rx_old=(++lp->rx_old)%lp->rxRingSize) {
- lp->rx_ring[lp->rx_old].status = cpu_to_le32(R_OWN);
- barrier();
- }
- lp->rx_ring[entry].status = cpu_to_le32(R_OWN);
- barrier();
+ /*
+ ** Update entry information
+ */
+ lp->rx_new = (++lp->rx_new) % lp->rxRingSize;
}
-
- /*
- ** Update entry information
- */
- lp->rx_new = (++lp->rx_new) % lp->rxRingSize;
- }
-
- return 0;
+
+ return 0;
}
/*
** Buffer sent - check for TX buffer errors.
*/
-static int
-de4x5_tx(struct device *dev)
+static int de4x5_tx(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- int entry;
- s32 status;
-
- for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) {
- status = (s32)le32_to_cpu(lp->tx_ring[entry].status);
- if (status < 0) { /* Buffer not sent yet */
- break;
- } else if (status != 0x7fffffff) { /* Not setup frame */
- if (status & TD_ES) { /* An error happened */
- lp->stats.tx_errors++;
- if (status & TD_NC) lp->stats.tx_carrier_errors++;
- if (status & TD_LC) lp->stats.tx_window_errors++;
- if (status & TD_UF) lp->stats.tx_fifo_errors++;
- if (status & TD_EC) lp->pktStats.excessive_collisions++;
- if (status & TD_DE) lp->stats.tx_aborted_errors++;
-
- if (TX_PKT_PENDING) {
- outl(POLL_DEMAND, DE4X5_TPD);/* Restart a stalled TX */
- }
- } else { /* Packet sent */
- lp->stats.tx_packets++;
- if (lp->tx_enable) lp->linkOK++;
- }
- /* Update the collision counter */
- lp->stats.collisions += ((status & TD_EC) ? 16 :
- ((status & TD_CC) >> 3));
-
- /* Free the buffer. */
- if (lp->tx_skb[entry] != NULL) {
- dev_kfree_skb(lp->tx_skb[entry], FREE_WRITE);
- lp->tx_skb[entry] = NULL;
- }
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ int entry;
+ s32 status;
+
+ for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) {
+ status = (s32) le32_to_cpu(lp->tx_ring[entry].status);
+ if (status < 0) { /* Buffer not sent yet */
+ break;
+ } else if (status != 0x7fffffff) { /* Not setup frame */
+ if (status & TD_ES) { /* An error happened */
+ lp->stats.tx_errors++;
+ if (status & TD_NC)
+ lp->stats.tx_carrier_errors++;
+ if (status & TD_LC)
+ lp->stats.tx_window_errors++;
+ if (status & TD_UF)
+ lp->stats.tx_fifo_errors++;
+ if (status & TD_EC)
+ lp->pktStats.excessive_collisions++;
+ if (status & TD_DE)
+ lp->stats.tx_aborted_errors++;
+
+ if (TX_PKT_PENDING) {
+ outl(POLL_DEMAND, DE4X5_TPD); /* Restart a stalled TX */
+ }
+ } else { /* Packet sent */
+ lp->stats.tx_packets++;
+ if (lp->tx_enable)
+ lp->linkOK++;
+ }
+ /* Update the collision counter */
+ lp->stats.collisions += ((status & TD_EC) ? 16 :
+ ((status & TD_CC) >> 3));
+
+ /* Free the buffer. */
+ if (lp->tx_skb[entry] != NULL) {
+ dev_kfree_skb(lp->tx_skb[entry], FREE_WRITE);
+ lp->tx_skb[entry] = NULL;
+ }
+ }
+ /* Update all the pointers */
+ lp->tx_old = (++lp->tx_old) % lp->txRingSize;
}
-
- /* Update all the pointers */
- lp->tx_old = (++lp->tx_old) % lp->txRingSize;
- }
-
- if (TX_BUFFS_AVAIL && dev->tbusy) { /* Any resources available? */
- dev->tbusy = 0; /* Clear TX busy flag */
- if (dev->interrupt) mark_bh(NET_BH);
- }
-
- return 0;
+
+ if (TX_BUFFS_AVAIL && dev->tbusy) { /* Any resources available? */
+ dev->tbusy = 0; /* Clear TX busy flag */
+ if (dev->interrupt)
+ mark_bh(NET_BH);
+ }
+ return 0;
}
-static int
-de4x5_ast(struct device *dev)
+static int de4x5_ast(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int next_tick = DE4X5_AUTOSENSE_MS;
-
- disable_ast(dev);
-
- if (lp->useSROM) {
- next_tick = srom_autoconf(dev);
- } else if (lp->chipset == DC21140) {
- next_tick = dc21140m_autoconf(dev);
- } else if (lp->chipset == DC21041) {
- next_tick = dc21041_autoconf(dev);
- } else if (lp->chipset == DC21040) {
- next_tick = dc21040_autoconf(dev);
- }
- lp->linkOK = 0;
- enable_ast(dev, next_tick);
-
- return 0;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ int next_tick = DE4X5_AUTOSENSE_MS;
+
+ disable_ast(dev);
+
+ if (lp->useSROM) {
+ next_tick = srom_autoconf(dev);
+ } else if (lp->chipset == DC21140) {
+ next_tick = dc21140m_autoconf(dev);
+ } else if (lp->chipset == DC21041) {
+ next_tick = dc21041_autoconf(dev);
+ } else if (lp->chipset == DC21040) {
+ next_tick = dc21040_autoconf(dev);
+ }
+ lp->linkOK = 0;
+ enable_ast(dev, next_tick);
+
+ return 0;
}
-static int
-de4x5_txur(struct device *dev)
+static int de4x5_txur(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int iobase = dev->base_addr;
- int omr;
-
- omr = inl(DE4X5_OMR);
- if (!(omr & OMR_SF) || (lp->chipset==DC21041) || (lp->chipset==DC21040)) {
- omr &= ~(OMR_ST|OMR_SR);
- outl(omr, DE4X5_OMR);
- while (inl(DE4X5_STS) & STS_TS);
- if ((omr & OMR_TR) < OMR_TR) {
- omr += 0x4000;
- } else {
- omr |= OMR_SF;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ int omr;
+
+ omr = inl(DE4X5_OMR);
+ if (!(omr & OMR_SF) || (lp->chipset == DC21041) || (lp->chipset == DC21040)) {
+ omr &= ~(OMR_ST | OMR_SR);
+ outl(omr, DE4X5_OMR);
+ while (inl(DE4X5_STS) & STS_TS);
+ if ((omr & OMR_TR) < OMR_TR) {
+ omr += 0x4000;
+ } else {
+ omr |= OMR_SF;
+ }
+ outl(omr | OMR_ST | OMR_SR, DE4X5_OMR);
}
- outl(omr | OMR_ST | OMR_SR, DE4X5_OMR);
- }
-
- return 0;
+ return 0;
}
-static int
-de4x5_rx_ovfc(struct device *dev)
+static int de4x5_rx_ovfc(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int iobase = dev->base_addr;
- int omr;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ int omr;
- omr = inl(DE4X5_OMR);
- outl(omr & ~OMR_SR, DE4X5_OMR);
- while (inl(DE4X5_STS) & STS_RS);
+ omr = inl(DE4X5_OMR);
+ outl(omr & ~OMR_SR, DE4X5_OMR);
+ while (inl(DE4X5_STS) & STS_RS);
- for (; (s32)le32_to_cpu(lp->rx_ring[lp->rx_new].status)>=0;) {
- lp->rx_ring[lp->rx_new].status = cpu_to_le32(R_OWN);
- lp->rx_new = (++lp->rx_new % lp->rxRingSize);
- }
+ for (; (s32) le32_to_cpu(lp->rx_ring[lp->rx_new].status) >= 0;) {
+ lp->rx_ring[lp->rx_new].status = cpu_to_le32(R_OWN);
+ lp->rx_new = (++lp->rx_new % lp->rxRingSize);
+ }
- outl(omr, DE4X5_OMR);
-
- return 0;
+ outl(omr, DE4X5_OMR);
+
+ return 0;
}
-static int
-de4x5_close(struct device *dev)
+static int de4x5_close(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- s32 imr, omr;
-
- disable_ast(dev);
- dev->start = 0;
- dev->tbusy = 1;
-
- if (de4x5_debug & DEBUG_CLOSE) {
- printk("%s: Shutting down ethercard, status was %8.8x.\n",
- dev->name, inl(DE4X5_STS));
- }
-
- /*
- ** We stop the DE4X5 here... mask interrupts and stop TX & RX
- */
- DISABLE_IRQs;
- STOP_DE4X5;
-
- /* Free the associated irq */
- free_irq(dev->irq, dev);
- lp->state = CLOSED;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 imr, omr;
- /* Free any socket buffers */
- de4x5_free_rx_buffs(dev);
- de4x5_free_tx_buffs(dev);
-
- MOD_DEC_USE_COUNT;
-
- /* Put the adapter to sleep to save power */
- yawn(dev, SLEEP);
-
- return 0;
+ disable_ast(dev);
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (de4x5_debug & DEBUG_CLOSE) {
+ printk("%s: Shutting down ethercard, status was %8.8x.\n",
+ dev->name, inl(DE4X5_STS));
+ }
+ /*
+ ** We stop the DE4X5 here... mask interrupts and stop TX & RX
+ */
+ DISABLE_IRQs;
+ STOP_DE4X5;
+
+ /* Free the associated irq */
+ free_irq(dev->irq, dev);
+ lp->state = CLOSED;
+
+ /* Free any socket buffers */
+ de4x5_free_rx_buffs(dev);
+ de4x5_free_tx_buffs(dev);
+
+ MOD_DEC_USE_COUNT;
+
+ /* Put the adapter to sleep to save power */
+ yawn(dev, SLEEP);
+
+ return 0;
}
static struct net_device_stats *
de4x5_get_stats(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
-
- lp->stats.rx_missed_errors = (int)(inl(DE4X5_MFC) & (MFC_OVFL | MFC_CNTR));
-
- return &lp->stats;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+
+ lp->stats.rx_missed_errors = (int) (inl(DE4X5_MFC) & (MFC_OVFL | MFC_CNTR));
+
+ return &lp->stats;
}
-static void
-de4x5_local_stats(struct device *dev, char *buf, int pkt_len)
+static void de4x5_local_stats(struct device *dev, char *buf, int pkt_len)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int i;
-
- for (i=1; i<DE4X5_PKT_STAT_SZ-1; i++) {
- if (pkt_len < (i*DE4X5_PKT_BIN_SZ)) {
- lp->pktStats.bins[i]++;
- i = DE4X5_PKT_STAT_SZ;
- }
- }
- if (buf[0] & 0x01) { /* Multicast/Broadcast */
- if ((*(s32 *)&buf[0] == -1) && (*(s16 *)&buf[4] == -1)) {
- lp->pktStats.broadcast++;
- } else {
- lp->pktStats.multicast++;
- }
- } else if ((*(s32 *)&buf[0] == *(s32 *)&dev->dev_addr[0]) &&
- (*(s16 *)&buf[4] == *(s16 *)&dev->dev_addr[4])) {
- lp->pktStats.unicast++;
- }
-
- lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */
- if (lp->pktStats.bins[0] == 0) { /* Reset counters */
- memset((char *)&lp->pktStats, 0, sizeof(lp->pktStats));
- }
-
- return;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ int i;
+
+ for (i = 1; i < DE4X5_PKT_STAT_SZ - 1; i++) {
+ if (pkt_len < (i * DE4X5_PKT_BIN_SZ)) {
+ lp->pktStats.bins[i]++;
+ i = DE4X5_PKT_STAT_SZ;
+ }
+ }
+ if (buf[0] & 0x01) { /* Multicast/Broadcast */
+ if ((*(s32 *) & buf[0] == -1) && (*(s16 *) & buf[4] == -1)) {
+ lp->pktStats.broadcast++;
+ } else {
+ lp->pktStats.multicast++;
+ }
+ } else if ((*(s32 *) & buf[0] == *(s32 *) & dev->dev_addr[0]) &&
+ (*(s16 *) & buf[4] == *(s16 *) & dev->dev_addr[4])) {
+ lp->pktStats.unicast++;
+ }
+ lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */
+ if (lp->pktStats.bins[0] == 0) { /* Reset counters */
+ memset((char *) &lp->pktStats, 0, sizeof(lp->pktStats));
+ }
+ return;
}
-static void
-load_packet(struct device *dev, char *buf, u32 flags, struct sk_buff *skb)
+static void load_packet(struct device *dev, char *buf, u32 flags, struct sk_buff *skb)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-
- lp->tx_ring[lp->tx_new].buf = cpu_to_le32(virt_to_bus(buf));
- lp->tx_ring[lp->tx_new].des1 &= cpu_to_le32(TD_TER);
- lp->tx_ring[lp->tx_new].des1 |= cpu_to_le32(flags);
- lp->tx_skb[lp->tx_new] = skb;
- barrier();
- lp->tx_ring[lp->tx_new].status = cpu_to_le32(T_OWN);
- barrier();
-
- return;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+
+ lp->tx_ring[lp->tx_new].buf = cpu_to_le32(virt_to_bus(buf));
+ lp->tx_ring[lp->tx_new].des1 &= cpu_to_le32(TD_TER);
+ lp->tx_ring[lp->tx_new].des1 |= cpu_to_le32(flags);
+ lp->tx_skb[lp->tx_new] = skb;
+ barrier();
+ lp->tx_ring[lp->tx_new].status = cpu_to_le32(T_OWN);
+ barrier();
+
+ return;
}
/*
** Set or clear the multicast filter for this adaptor.
*/
-static void
-set_multicast_list(struct device *dev)
+static void set_multicast_list(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
-
- /* First, double check that the adapter is open */
- if (lp->state == OPEN) {
- if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */
- u32 omr;
- omr = inl(DE4X5_OMR);
- omr |= OMR_PR;
- outl(omr, DE4X5_OMR);
- } else {
- SetMulticastFilter(dev);
- load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET |
- SETUP_FRAME_LEN, NULL);
-
- lp->tx_new = (++lp->tx_new) % lp->txRingSize;
- outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */
- dev->trans_start = jiffies;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+
+ /* First, double check that the adapter is open */
+ if (lp->state == OPEN) {
+ if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */
+ u32 omr;
+ omr = inl(DE4X5_OMR);
+ omr |= OMR_PR;
+ outl(omr, DE4X5_OMR);
+ } else {
+ SetMulticastFilter(dev);
+ load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET |
+ SETUP_FRAME_LEN, NULL);
+
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+ outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */
+ dev->trans_start = jiffies;
+ }
}
- }
-
- return;
+ return;
}
/*
@@ -1832,128 +1855,136 @@ set_multicast_list(struct device *dev)
** from a list of ethernet multicast addresses.
** Little endian crc one liner from Matt Thomas, DEC.
*/
-static void
-SetMulticastFilter(struct device *dev)
+static void SetMulticastFilter(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- struct dev_mc_list *dmi=dev->mc_list;
- u_long iobase = dev->base_addr;
- int i, j, bit, byte;
- u16 hashcode;
- u32 omr, crc, poly = CRC_POLYNOMIAL_LE;
- char *pa;
- unsigned char *addrs;
-
- omr = inl(DE4X5_OMR);
- omr &= ~(OMR_PR | OMR_PM);
- pa = build_setup_frame(dev, ALL); /* Build the basic frame */
-
- if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 14)) {
- omr |= OMR_PM; /* Pass all multicasts */
- } else if (lp->setup_f == HASH_PERF) { /* Hash Filtering */
- for (i=0;i<dev->mc_count;i++) { /* for each address in the list */
- addrs=dmi->dmi_addr;
- dmi=dmi->next;
- if ((*addrs & 0x01) == 1) { /* multicast address? */
- crc = 0xffffffff; /* init CRC for each address */
- for (byte=0;byte<ETH_ALEN;byte++) {/* for each address byte */
- /* process each address bit */
- for (bit = *addrs++,j=0;j<8;j++, bit>>=1) {
- crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0);
- }
- }
- hashcode = crc & HASH_BITS; /* hashcode is 9 LSb of CRC */
-
- byte = hashcode >> 3; /* bit[3-8] -> byte in filter */
- bit = 1 << (hashcode & 0x07);/* bit[0-2] -> bit in byte */
-
- byte <<= 1; /* calc offset into setup frame */
- if (byte & 0x02) {
- byte -= 1;
- }
- lp->setup_frame[byte] |= bit;
- }
- }
- } else { /* Perfect filtering */
- for (j=0; j<dev->mc_count; j++) {
- addrs=dmi->dmi_addr;
- dmi=dmi->next;
- for (i=0; i<ETH_ALEN; i++) {
- *(pa + (i&1)) = *addrs++;
- if (i & 0x01) pa += 4;
- }
- }
- }
- outl(omr, DE4X5_OMR);
-
- return;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ struct dev_mc_list *dmi = dev->mc_list;
+ u_long iobase = dev->base_addr;
+ int i, j, bit, byte;
+ u16 hashcode;
+ u32 omr, crc, poly = CRC_POLYNOMIAL_LE;
+ char *pa;
+ unsigned char *addrs;
+
+ omr = inl(DE4X5_OMR);
+ omr &= ~(OMR_PR | OMR_PM);
+ pa = build_setup_frame(dev, ALL); /* Build the basic frame */
+
+ if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 14)) {
+ omr |= OMR_PM; /* Pass all multicasts */
+ } else if (lp->setup_f == HASH_PERF) { /* Hash Filtering */
+ for (i = 0; i < dev->mc_count; i++) { /* for each address in the list */
+ addrs = dmi->dmi_addr;
+ dmi = dmi->next;
+ if ((*addrs & 0x01) == 1) { /* multicast address? */
+ crc = 0xffffffff; /* init CRC for each address */
+ for (byte = 0; byte < ETH_ALEN; byte++) { /* for each address byte */
+ /* process each address bit */
+ for (bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) {
+ crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0);
+ }
+ }
+ hashcode = crc & HASH_BITS; /* hashcode is 9 LSb of CRC */
+
+ byte = hashcode >> 3; /* bit[3-8] -> byte in filter */
+ bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */
+
+ byte <<= 1; /* calc offset into setup frame */
+ if (byte & 0x02) {
+ byte -= 1;
+ }
+ lp->setup_frame[byte] |= bit;
+ }
+ }
+ } else { /* Perfect filtering */
+ for (j = 0; j < dev->mc_count; j++) {
+ addrs = dmi->dmi_addr;
+ dmi = dmi->next;
+ for (i = 0; i < ETH_ALEN; i++) {
+ *(pa + (i & 1)) = *addrs++;
+ if (i & 0x01)
+ pa += 4;
+ }
+ }
+ }
+ outl(omr, DE4X5_OMR);
+
+ return;
}
+#ifndef __sparc_v9__
/*
** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually
** the motherboard. Upto 15 EISA devices are supported.
*/
__initfunc(static void
-eisa_probe(struct device *dev, u_long ioaddr))
+ eisa_probe(struct device *dev, u_long ioaddr))
{
- int i, maxSlots, status, device;
- u_short vendor;
- u32 cfid;
- u_long iobase;
- struct bus_type *lp = &bus;
- char name[DE4X5_STRLEN];
- struct device *tmp;
-
- if (autoprobed) return; /* Been here before ! */
-
- lp->bus = EISA;
-
- if (ioaddr == 0) { /* Autoprobing */
- iobase = EISA_SLOT_INC; /* Get the first slot address */
- i = 1;
- maxSlots = MAX_EISA_SLOTS;
- } else { /* Probe a specific location */
- iobase = ioaddr;
- i = (ioaddr >> 12);
- maxSlots = i + 1;
- }
-
- for (status= -ENODEV;(i<maxSlots)&&(dev!=NULL);i++,iobase+=EISA_SLOT_INC) {
- if (EISA_signature(name, EISA_ID)) {
- cfid = (u32) inl(PCI_CFID);
- cfrv = (u_short) inl(PCI_CFRV);
- device = (cfid >> 8) & 0x00ffff00;
- vendor = (u_short) cfid;
-
- /* Read the EISA Configuration Registers */
- dev->irq = inb(EISA_REG0);
- dev->irq = de4x5_irq[(dev->irq >> 1) & 0x03];
-
- if (is_DC2114x) device |= (cfrv & 0x00f0);
- lp->chipset = device;
- DevicePresent(DE4X5_APROM);
- /* Write the PCI Configuration Registers */
- outl(PCI_COMMAND_IO | PCI_COMMAND_MASTER, PCI_CFCS);
- outl(0x00006000, PCI_CFLT);
- outl(iobase, PCI_CBIO);
-
- if (check_region(iobase, DE4X5_EISA_TOTAL_SIZE) == 0) {
- if ((tmp = alloc_device(dev, iobase)) != NULL) {
- if ((status = de4x5_hw_init(tmp, iobase)) == 0) {
- num_de4x5s++;
- if (loading_module) link_modules(dev, tmp);
- } else if (loading_module && (tmp != dev)) {
- kfree(tmp);
- }
- }
- } else if (autoprobed) {
- printk("%s: region already allocated at 0x%04lx.\n", dev->name,iobase);
- }
- }
- }
-
- return;
+ int i, maxSlots, status, device;
+ u_char irq;
+ u_short vendor;
+ u32 cfid;
+ u_long iobase;
+ struct bus_type *lp = &bus;
+ char name[DE4X5_STRLEN];
+
+ if (lastEISA == MAX_EISA_SLOTS)
+ return; /* No more EISA devices to search */
+
+ lp->bus = EISA;
+
+ if (ioaddr == 0) { /* Autoprobing */
+ iobase = EISA_SLOT_INC; /* Get the first slot address */
+ i = 1;
+ maxSlots = MAX_EISA_SLOTS;
+ } else { /* Probe a specific location */
+ iobase = ioaddr;
+ i = (ioaddr >> 12);
+ maxSlots = i + 1;
+ }
+
+ for (status = -ENODEV; (i < maxSlots) && (dev != NULL); i++, iobase += EISA_SLOT_INC) {
+ if (EISA_signature(name, EISA_ID)) {
+ cfid = (u32) inl(PCI_CFID);
+ cfrv = (u_short) inl(PCI_CFRV);
+ device = (cfid >> 8) & 0x00ffff00;
+ vendor = (u_short) cfid;
+
+ /* Read the EISA Configuration Registers */
+ irq = inb(EISA_REG0);
+ irq = de4x5_irq[(irq >> 1) & 0x03];
+
+ if (is_DC2114x)
+ device |= (cfrv & CFRV_RN);
+ lp->chipset = device;
+
+ /* Write the PCI Configuration Registers */
+ outl(PCI_COMMAND_IO | PCI_COMMAND_MASTER, PCI_CFCS);
+ outl(0x00006000, PCI_CFLT);
+ outl(iobase, PCI_CBIO);
+
+ DevicePresent(DE4X5_APROM);
+ if (check_region(iobase, DE4X5_EISA_TOTAL_SIZE) == 0) {
+ dev->irq = irq;
+ if ((status = de4x5_hw_init(dev, iobase)) == 0) {
+ num_de4x5s++;
+ if (loading_module) link_modules(lastModule, dev);
+ lastEISA = i;
+ return;
+ }
+ } else if (ioaddr != 0) {
+ printk("%s: region already allocated at 0x%04lx.\n",
+ dev->name, iobase);
+ }
+ }
+ }
+
+ if (ioaddr == 0)
+ lastEISA = i;
+
+ return;
}
+#endif /* !(__sparc_v9__) */
/*
** PCI bus I/O device probe
@@ -1971,220 +2002,249 @@ eisa_probe(struct device *dev, u_long ioaddr))
#define PCI_LAST_DEV 32
__initfunc(static void
-pci_probe(struct device *dev, u_long ioaddr))
+ pci_probe(struct device *dev, u_long ioaddr))
{
- u_char irq;
- u_char pb, pbus, dev_num, dnum, dev_fn;
- u_short dev_id, vendor, index, status;
- u_int class = DE4X5_CLASS_CODE;
- u_int device, iobase;
- struct bus_type *lp = &bus;
- struct device *tmp;
-
- if (autoprobed) return;
-
- if (!pcibios_present()) return; /* No PCI bus in this machine! */
-
- lp->bus = PCI;
-
- if ((ioaddr < 0x1000) && loading_module) {
- pbus = (u_short)(ioaddr >> 8);
- dnum = (u_short)(ioaddr & 0xff);
- } else {
- pbus = 0;
- dnum = 0;
- }
-
- for (index=0;
- (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
- index++) {
- dev_num = PCI_SLOT(dev_fn);
-
- if ((!pbus && !dnum) || ((pbus == pb) && (dnum == dev_num))) {
- device = 0;
- pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
- pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id);
- device = dev_id;
- device <<= 8;
- if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) {
- continue;
- }
-
- /* Get the chip configuration revision register */
- pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
-
- /* Set the device number information */
- lp->device = dev_num;
- lp->bus_num = pb;
-
- /* Set the chipset information */
- if (is_DC2114x) device |= (cfrv & 0x00f0);
- lp->chipset = device;
-
- if (is_DC21142 || is_DC21143) {
- printk("de4x5: Detected a %s chip. Currently this is unsupported in this driver.\nPlease email the author to request its inclusion!\n", (is_DC21142?"DC21142":"DC21143"));
- continue;
- }
-
- /* Get the board I/O address */
- pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase);
- iobase &= CBIO_MASK;
-
- /* Fetch the IRQ to be used */
- pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &irq);
- if ((irq == 0) || (irq == (u_char) 0xff)) continue;
-
- /* Check if I/O accesses and Bus Mastering are enabled */
- pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
- if (!(status & PCI_COMMAND_IO)) continue;
- if (!(status & PCI_COMMAND_MASTER)) {
- status |= PCI_COMMAND_MASTER;
- pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status);
- pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
- }
- if (!(status & PCI_COMMAND_MASTER)) continue;
-
- DevicePresent(DE4X5_APROM);
- if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) {
- if ((tmp = alloc_device(dev, iobase)) != NULL) {
- tmp->irq = irq;
- if ((status = de4x5_hw_init(tmp, iobase)) == 0) {
- num_de4x5s++;
- if (loading_module) link_modules(dev, tmp);
- } else if (loading_module && (tmp != dev)) {
- kfree(tmp);
- }
- }
- } else if (autoprobed) {
- printk("%s: region already allocated at 0x%04x.\n", dev->name,
- (u_short)iobase);
- }
- }
- }
-
- return;
-}
+ u_char pb, pbus, dev_num, dnum, dev_fn, timer;
+ u_short dev_id, vendor, index, status;
+ u_int irq;
+ u_int class = DE4X5_CLASS_CODE;
+ u_int device;
+ u_long iobase;
+ struct bus_type *lp = &bus;
+
+ if (lastPCI == NO_MORE_PCI)
+ return;
+
+ if (!pcibios_present()) {
+ lastPCI = NO_MORE_PCI;
+ return; /* No PCI bus in this machine! */
+ }
-/*
-** Search the entire 'eth' device list for a fixed probe. If a match isn't
-** found then check for an autoprobe or unused device location. If they
-** are not available then insert a new device structure at the end of
-** the current list.
-*/
-__initfunc(static struct device *
-alloc_device(struct device *dev, u_long iobase))
-{
- struct device *adev = NULL;
- int fixed = 0, new_dev = 0;
+ lp->bus = PCI;
+
+ if ((ioaddr < 0x1000) && loading_module) {
+ pbus = (u_short) (ioaddr >> 8);
+ dnum = (u_short) (ioaddr & 0xff);
+ } else {
+ pbus = 0;
+ dnum = 0;
+ }
+
+ for (index = lastPCI+1;
+ (pcibios_find_class(class, index, &pb, &dev_fn) != PCIBIOS_DEVICE_NOT_FOUND);
+ index++) {
+ dev_num = PCI_SLOT(dev_fn);
+
+ if ((!pbus && !dnum) || ((pbus == pb) && (dnum == dev_num))) {
+#ifdef __sparc_v9__
+ struct pci_dev *pdev;
+
+ for (pdev = pci_devices; pdev; pdev = pdev->next)
+ if (pdev->bus->number == pb &&
+ pdev->devfn == dev_fn)
+ break;
+#endif
+ device = 0;
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id);
+ device = dev_id;
+ device <<= 8;
+ if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) {
+ continue;
+ }
- if (!dev) return dev;
- num_eth = de4x5_dev_index(dev->name);
+ /* Search for an SROM on this bus */
+ if (lp->bus_num != pb) {
+ lp->bus_num = pb;
+ srom_search(index);
+ }
+
+ /* Get the chip configuration revision register */
+ pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
+
+ /* Set the device number information */
+ lp->device = dev_num;
+ lp->bus_num = pb;
+
+ /* Set the chipset information */
+ if (is_DC2114x)
+ device |= (cfrv & CFRV_RN);
+ lp->chipset = device;
+
+ /* Get the board I/O address */
+#ifdef __sparc_v9__
+ iobase = pdev->base_address[0];
+#else
+ {
+ unsigned int tmp;
+ pcibios_read_config_dword(pb, PCI_DEVICE,
+ PCI_BASE_ADDRESS_0, &tmp);
+ iobase = tmp;
+ }
+#endif
+ iobase &= CBIO_MASK;
+
+ /* Fetch the IRQ to be used */
+#ifdef __sparc_v9__
+ irq = pdev->irq;
+#else
+ {
+ unsigned char tmp;
+ pcibios_read_config_byte(pb, PCI_DEVICE,
+ PCI_INTERRUPT_LINE, &tmp);
+ irq = tmp;
+ }
+ if ((irq == 0) || (irq == (u_char) 0xff))
+ continue;
+#endif
+
+ /* Check if I/O accesses and Bus Mastering are enabled */
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+#ifdef __powerpc__
+ if (!(status & PCI_COMMAND_IO)) {
+ status |= PCI_COMMAND_IO;
+ pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status);
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+ }
+#endif /* __powerpc__ */
+ if (!(status & PCI_COMMAND_IO))
+ continue;
+ if (!(status & PCI_COMMAND_MASTER)) {
+ status |= PCI_COMMAND_MASTER;
+ pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status);
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+ }
+ if (!(status & PCI_COMMAND_MASTER))
+ continue;
+
+ /* Check the latency timer for values > 0x60 */
+ pcibios_read_config_byte(pb, PCI_DEVICE,
+ PCI_LATENCY_TIMER, &timer);
+ if (timer < 0x60) {
+ pcibios_write_config_byte(pb, PCI_DEVICE,
+ PCI_LATENCY_TIMER, 0x60);
+ }
- if (loading_module) {
- if (dev->priv) {
- dev = insert_device(dev, iobase, de4x5_probe);
+ DevicePresent(DE4X5_APROM);
+ if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) {
+ dev->irq = irq;
+ if ((status = de4x5_hw_init(dev, iobase)) == 0) {
+ num_de4x5s++;
+ if (loading_module) {
+ link_modules(lastModule, dev);
+ lastPCI = index;
+ }
+ return;
+ }
+ } else if (ioaddr != 0) {
+ printk("%s: region already allocated at 0x%04x.\n", dev->name,
+ (u_short) iobase);
+ }
+ }
}
- num_eth++;
- return dev;
- }
- while (1) {
- if (((dev->base_addr == DE4X5_NDA) || (dev->base_addr==0)) && !adev) {
- adev=dev;
- } else if ((dev->priv == NULL) && (dev->base_addr==iobase)) {
- fixed = 1;
- } else {
- if (dev->next == NULL) {
- new_dev = 1;
- } else if (strncmp(dev->next->name, "eth", 3) != 0) {
- new_dev = 1;
- }
- }
- if ((dev->next == NULL) || new_dev || fixed) break;
- dev = dev->next;
- num_eth++;
- }
- if (adev && !fixed) {
- dev = adev;
- num_eth = de4x5_dev_index(dev->name);
- new_dev = 0;
- }
-
- if (((dev->next == NULL) &&
- ((dev->base_addr != DE4X5_NDA) && (dev->base_addr != 0)) && !fixed) ||
- new_dev) {
- num_eth++; /* New device */
- dev = insert_device(dev, iobase, de4x5_probe);
- }
-
- return dev;
+ if (loading_module)
+ lastPCI = NO_MORE_PCI;
+
+ return;
}
/*
-** If at end of eth device list and can't use current entry, malloc
-** one up. If memory could not be allocated, print an error message.
+** This function searches the current bus (which is >0) for a DECchip with an
+** SROM, so that in multiport cards that have one SROM shared between multiple
+** DECchips, we can find the base SROM irrespective of the BIOS scan direction.
+** For single port cards this is a time waster...
*/
-__initfunc(static struct device *
-insert_device(struct device *dev, u_long iobase, int (*init)(struct device *)))
+__initfunc(static void
+ srom_search(int index))
{
- struct device *new;
-
- new = (struct device *)kmalloc(sizeof(struct device)+8, GFP_KERNEL);
- if (new == NULL) {
- printk("eth%d: Device not initialised, insufficient memory\n",num_eth);
- return NULL;
- } else {
- memset((char *)new, 0, sizeof(struct device)+8);
- new->name = (char *)(new + 1);
- new->base_addr = iobase; /* assign the io address */
- new->init = init; /* initialisation routine */
- if (!loading_module) {
- new->next = dev->next;
- dev->next = new;
- if (num_eth > 9999) {
- sprintf(new->name,"eth????");/* New device name */
- } else {
- sprintf(new->name,"eth%d", num_eth);/* New device name */
- }
- }
- }
-
- return new;
-}
+ u_char irq, pb, dev_fn;
+ u_short dev_id, dev_num, vendor, status;
+ u_int class = DE4X5_CLASS_CODE;
+ u_int device, iobase;
+ int i, j;
+ struct bus_type *lp = &bus;
+
+ for (;
+ (pcibios_find_class(class, index,
+ &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
+ index++) {
+
+ if (lp->bus_num != pb)
+ return;
+ dev_num = PCI_SLOT(dev_fn);
+ device = 0;
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id);
+ device = dev_id;
+ device <<= 8;
+ if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) {
+ continue;
+ }
-__initfunc(static int
-de4x5_dev_index(char *s))
-{
- int i=0, j=0;
+ /* Get the chip configuration revision register */
+ pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
- for (;*s; s++) {
- if (isdigit(*s)) {
- j=1;
- i = (i * 10) + (*s - '0');
- } else if (j) break;
- }
+ /* Set the device number information */
+ lp->device = dev_num;
+ lp->bus_num = pb;
+
+ /* Set the chipset information */
+ if (is_DC2114x)
+ device |= (cfrv & CFRV_RN);
+ lp->chipset = device;
+
+ /* Get the board I/O address */
+ pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase);
+ iobase &= CBIO_MASK;
+
+ /* Fetch the IRQ to be used */
+ pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &irq);
+ if ((irq == 0) || (irq == (u_char) 0xff))
+ continue;
+
+ /* Check if I/O accesses are enabled */
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+ if (!(status & PCI_COMMAND_IO))
+ continue;
+
+ /* Search for a valid SROM attached to this DECchip */
+ DevicePresent(DE4X5_APROM);
+ for (j=0, i=0; i<ETH_ALEN; i++) {
+ j += (u_char) *((u_char *)&lp->srom + SROM_HWADD + i);
+ }
+ if ((j != 0) && (j != 0x5fa)) {
+ last.chipset = device;
+ last.bus = pb;
+ last.irq = irq;
+ for (i=0; i<ETH_ALEN; i++) {
+ last.addr[i] =
+ (u_char)*((u_char *)&lp->srom + SROM_HWADD + i);
+ }
+ return;
+ }
+ }
- return i;
+ return;
}
__initfunc(static void
-link_modules(struct device *dev, struct device *tmp))
+ link_modules(struct device *dev, struct device *tmp))
{
- struct device *p=dev;
+ struct device *p = dev;
- if (p) {
- while (((struct de4x5_private *)(p->priv))->next_module) {
- p = ((struct de4x5_private *)(p->priv))->next_module;
- }
+ if (p) {
+ while (((struct de4x5_private *) (p->priv))->next_module) {
+ p = ((struct de4x5_private *) (p->priv))->next_module;
+ }
- if (dev != tmp) {
- ((struct de4x5_private *)(p->priv))->next_module = tmp;
- } else {
- ((struct de4x5_private *)(p->priv))->next_module = NULL;
+ if (dev != tmp) {
+ ((struct de4x5_private *) (p->priv))->next_module = tmp;
+ } else {
+ ((struct de4x5_private *) (p->priv))->next_module = NULL;
+ }
}
- }
-
- return;
+ return;
}
/*
@@ -2194,31 +2254,31 @@ link_modules(struct device *dev, struct device *tmp))
** [TP] or no recent receive activity) to check whether the user has been
** sneaky and changed the port on us.
*/
-static int
-autoconf_media(struct device *dev)
+static int autoconf_media(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- int next_tick = DE4X5_AUTOSENSE_MS;;
-
- lp->linkOK = 0;
- lp->c_media = AUTO; /* Bogus last media */
- disable_ast(dev);
- inl(DE4X5_MFC); /* Zero the lost frames counter */
- lp->media = INIT;
- if (lp->useSROM) {
- next_tick = srom_autoconf(dev);
- } else if (lp->chipset == DC21040) {
- next_tick = dc21040_autoconf(dev);
- } else if (lp->chipset == DC21041) {
- next_tick = dc21041_autoconf(dev);
- } else if (lp->chipset == DC21140) {
- next_tick = dc21140m_autoconf(dev);
- }
-
- enable_ast(dev, next_tick);
-
- return (lp->media);
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ int next_tick = DE4X5_AUTOSENSE_MS;
+
+ lp->linkOK = 0;
+ lp->c_media = AUTO; /* Bogus last media */
+ disable_ast(dev);
+ inl(DE4X5_MFC); /* Zero the lost frames counter */
+ lp->media = INIT;
+ lp->tcount = 0;
+
+ if (lp->useSROM) {
+ next_tick = srom_autoconf(dev);
+ } else if (lp->chipset == DC21040) {
+ next_tick = dc21040_autoconf(dev);
+ } else if (lp->chipset == DC21041) {
+ next_tick = dc21041_autoconf(dev);
+ } else if (lp->chipset == DC21140) {
+ next_tick = dc21140m_autoconf(dev);
+ }
+ enable_ast(dev, next_tick);
+
+ return (lp->media);
}
/*
@@ -2233,149 +2293,146 @@ autoconf_media(struct device *dev)
** I may have to "age out" locally queued packets so that the higher layer
** timeouts don't effectively duplicate packets on the network.
*/
-static int
-dc21040_autoconf(struct device *dev)
+static int dc21040_autoconf(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- int next_tick = DE4X5_AUTOSENSE_MS;
- s32 imr;
-
- switch (lp->media) {
- case INIT:
- DISABLE_IRQs;
- lp->tx_enable = NO;
- lp->timeout = -1;
- de4x5_save_skbs(dev);
- if ((lp->autosense == AUTO) || (lp->autosense == TP)) {
- lp->media = TP;
- } else if ((lp->autosense == BNC) || (lp->autosense == AUI) || (lp->autosense == BNC_AUI)) {
- lp->media = BNC_AUI;
- } else if (lp->autosense == EXT_SIA) {
- lp->media = EXT_SIA;
- } else {
- lp->media = NC;
- }
- lp->local_state = 0;
- next_tick = dc21040_autoconf(dev);
- break;
-
- case TP:
- next_tick = dc21040_state(dev, 0x8f01, 0xffff, 0x0000, 3000, BNC_AUI,
- TP_SUSPECT, test_tp);
- break;
-
- case TP_SUSPECT:
- next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21040_autoconf);
- break;
-
- case BNC:
- case AUI:
- case BNC_AUI:
- next_tick = dc21040_state(dev, 0x8f09, 0x0705, 0x0006, 3000, EXT_SIA,
- BNC_AUI_SUSPECT, ping_media);
- break;
-
- case BNC_AUI_SUSPECT:
- next_tick = de4x5_suspect_state(dev, 1000, BNC_AUI, ping_media, dc21040_autoconf);
- break;
-
- case EXT_SIA:
- next_tick = dc21040_state(dev, 0x3041, 0x0000, 0x0006, 3000,
- NC, EXT_SIA_SUSPECT, ping_media);
- break;
-
- case EXT_SIA_SUSPECT:
- next_tick = de4x5_suspect_state(dev, 1000, EXT_SIA, ping_media, dc21040_autoconf);
- break;
-
- case NC:
- /* default to TP for all */
- reset_init_sia(dev, 0x8f01, 0xffff, 0x0000);
- if (lp->media != lp->c_media) {
- de4x5_dbg_media(dev);
- lp->c_media = lp->media;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ int next_tick = DE4X5_AUTOSENSE_MS;
+ s32 imr;
+
+ switch (lp->media) {
+ case INIT:
+ DISABLE_IRQs;
+ lp->tx_enable = NO;
+ lp->timeout = -1;
+ de4x5_save_skbs(dev);
+ if ((lp->autosense == AUTO) || (lp->autosense == TP)) {
+ lp->media = TP;
+ } else if ((lp->autosense == BNC) || (lp->autosense == AUI) || (lp->autosense == BNC_AUI)) {
+ lp->media = BNC_AUI;
+ } else if (lp->autosense == EXT_SIA) {
+ lp->media = EXT_SIA;
+ } else {
+ lp->media = NC;
+ }
+ lp->local_state = 0;
+ next_tick = dc21040_autoconf(dev);
+ break;
+
+ case TP:
+ next_tick = dc21040_state(dev, 0x8f01, 0xffff, 0x0000, 3000, BNC_AUI,
+ TP_SUSPECT, test_tp);
+ break;
+
+ case TP_SUSPECT:
+ next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21040_autoconf);
+ break;
+
+ case BNC:
+ case AUI:
+ case BNC_AUI:
+ next_tick = dc21040_state(dev, 0x8f09, 0x0705, 0x0006, 3000, EXT_SIA,
+ BNC_AUI_SUSPECT, ping_media);
+ break;
+
+ case BNC_AUI_SUSPECT:
+ next_tick = de4x5_suspect_state(dev, 1000, BNC_AUI, ping_media, dc21040_autoconf);
+ break;
+
+ case EXT_SIA:
+ next_tick = dc21040_state(dev, 0x3041, 0x0000, 0x0006, 3000,
+ NC, EXT_SIA_SUSPECT, ping_media);
+ break;
+
+ case EXT_SIA_SUSPECT:
+ next_tick = de4x5_suspect_state(dev, 1000, EXT_SIA, ping_media, dc21040_autoconf);
+ break;
+
+ case NC:
+ /* default to TP for all */
+ reset_init_sia(dev, 0x8f01, 0xffff, 0x0000);
+ if (lp->media != lp->c_media) {
+ de4x5_dbg_media(dev);
+ lp->c_media = lp->media;
+ }
+ lp->media = INIT;
+ lp->tx_enable = NO;
+ break;
}
- lp->media = INIT;
- lp->tx_enable = NO;
- break;
- }
-
- return next_tick;
+
+ return next_tick;
}
-static int
-dc21040_state(struct device *dev, int csr13, int csr14, int csr15, int timeout,
- int next_state, int suspect_state,
- int (*fn)(struct device *, int))
+static int dc21040_state(struct device *dev, int csr13, int csr14, int csr15, int timeout,
+ int next_state, int suspect_state,
+ int (*fn) (struct device *, int))
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int next_tick = DE4X5_AUTOSENSE_MS;
- int linkBad;
-
- switch (lp->local_state) {
- case 0:
- reset_init_sia(dev, csr13, csr14, csr15);
- lp->local_state++;
- next_tick = 500;
- break;
-
- case 1:
- if (!lp->tx_enable) {
- linkBad = fn(dev, timeout);
- if (linkBad < 0) {
- next_tick = linkBad & ~TIMER_CB;
- } else {
- if (linkBad && (lp->autosense == AUTO)) {
- lp->local_state = 0;
- lp->media = next_state;
- } else {
- de4x5_init_connection(dev);
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ int next_tick = DE4X5_AUTOSENSE_MS;
+ int linkBad;
+
+ switch (lp->local_state) {
+ case 0:
+ reset_init_sia(dev, csr13, csr14, csr15);
+ lp->local_state++;
+ next_tick = 500;
+ break;
+
+ case 1:
+ if (!lp->tx_enable) {
+ linkBad = fn(dev, timeout);
+ if (linkBad < 0) {
+ next_tick = linkBad & ~TIMER_CB;
+ } else {
+ if (linkBad && (lp->autosense == AUTO)) {
+ lp->local_state = 0;
+ lp->media = next_state;
+ } else {
+ de4x5_init_connection(dev);
+ }
+ }
+ } else if (!lp->linkOK && (lp->autosense == AUTO)) {
+ lp->media = suspect_state;
+ next_tick = 3000;
}
- }
- } else if (!lp->linkOK && (lp->autosense == AUTO)) {
- lp->media = suspect_state;
- next_tick = 3000;
+ break;
}
- break;
- }
-
- return next_tick;
+
+ return next_tick;
}
-static int
-de4x5_suspect_state(struct device *dev, int timeout, int prev_state,
- int (*fn)(struct device *, int),
- int (*asfn)(struct device *))
+static int de4x5_suspect_state(struct device *dev, int timeout, int prev_state,
+ int (*fn) (struct device *, int),
+ int (*asfn) (struct device *))
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int next_tick = DE4X5_AUTOSENSE_MS;
- int linkBad;
-
- switch (lp->local_state) {
- case 1:
- if (lp->linkOK) {
- lp->media = prev_state;
- } else {
- lp->local_state++;
- next_tick = asfn(dev);
- }
- break;
-
- case 2:
- linkBad = fn(dev, timeout);
- if (linkBad < 0) {
- next_tick = linkBad & ~TIMER_CB;
- } else if (!linkBad) {
- lp->local_state--;
- lp->media = prev_state;
- } else {
- lp->media = INIT;
- lp->tcount++;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ int next_tick = DE4X5_AUTOSENSE_MS;
+ int linkBad;
+
+ switch (lp->local_state) {
+ case 1:
+ if (lp->linkOK) {
+ lp->media = prev_state;
+ } else {
+ lp->local_state++;
+ next_tick = asfn(dev);
+ }
+ break;
+
+ case 2:
+ linkBad = fn(dev, timeout);
+ if (linkBad < 0) {
+ next_tick = linkBad & ~TIMER_CB;
+ } else if (!linkBad) {
+ lp->local_state--;
+ lp->media = prev_state;
+ } else {
+ lp->media = INIT;
+ lp->tcount++;
+ }
}
- }
- return next_tick;
+ return next_tick;
}
/*
@@ -2387,460 +2444,725 @@ de4x5_suspect_state(struct device *dev, int timeout, int prev_state,
** any more packets to be queued to the hardware. Re-enable everything only
** when the media is found.
*/
-static int
-dc21041_autoconf(struct device *dev)
+static int dc21041_autoconf(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- s32 sts, irqs, irq_mask, imr, omr;
- int next_tick = DE4X5_AUTOSENSE_MS;
-
- switch (lp->media) {
- case INIT:
- DISABLE_IRQs;
- lp->tx_enable = NO;
- lp->timeout = -1;
- de4x5_save_skbs(dev); /* Save non transmitted skb's */
- if ((lp->autosense == AUTO) || (lp->autosense == TP_NW)) {
- lp->media = TP; /* On chip auto negotiation is broken */
- } else if (lp->autosense == TP) {
- lp->media = TP;
- } else if (lp->autosense == BNC) {
- lp->media = BNC;
- } else if (lp->autosense == AUI) {
- lp->media = AUI;
- } else {
- lp->media = NC;
- }
- lp->local_state = 0;
- next_tick = dc21041_autoconf(dev);
- break;
-
- case TP_NW:
- if (lp->timeout < 0) {
- omr = inl(DE4X5_OMR);/* Set up full duplex for the autonegotiate */
- outl(omr | OMR_FDX, DE4X5_OMR);
- }
- irqs = STS_LNF | STS_LNP;
- irq_mask = IMR_LFM | IMR_LPM;
- sts = test_media(dev, irqs, irq_mask, 0xef01, 0xffff, 0x0008, 2400);
- if (sts < 0) {
- next_tick = sts & ~TIMER_CB;
- } else {
- if (sts & STS_LNP) {
- lp->media = ANS;
- } else {
- lp->media = AUI;
- }
- next_tick = dc21041_autoconf(dev);
- }
- break;
-
- case ANS:
- if (!lp->tx_enable) {
- irqs = STS_LNP;
- irq_mask = IMR_LPM;
- sts = test_ans(dev, irqs, irq_mask, 3000);
- if (sts < 0) {
- next_tick = sts & ~TIMER_CB;
- } else {
- if (!(sts & STS_LNP) && (lp->autosense == AUTO)) {
- lp->media = TP;
- next_tick = dc21041_autoconf(dev);
- } else {
- lp->local_state = 1;
- de4x5_init_connection(dev);
- }
- }
- } else if (!lp->linkOK && (lp->autosense == AUTO)) {
- lp->media = ANS_SUSPECT;
- next_tick = 3000;
- }
- break;
-
- case ANS_SUSPECT:
- next_tick = de4x5_suspect_state(dev, 1000, ANS, test_tp, dc21041_autoconf);
- break;
-
- case TP:
- if (!lp->tx_enable) {
- if (lp->timeout < 0) {
- omr = inl(DE4X5_OMR); /* Set up half duplex for TP */
- outl(omr & ~OMR_FDX, DE4X5_OMR);
- }
- irqs = STS_LNF | STS_LNP;
- irq_mask = IMR_LFM | IMR_LPM;
- sts = test_media(dev,irqs, irq_mask, 0xef01, 0xff3f, 0x0008, 2400);
- if (sts < 0) {
- next_tick = sts & ~TIMER_CB;
- } else {
- if (!(sts & STS_LNP) && (lp->autosense == AUTO)) {
- if (inl(DE4X5_SISR) & SISR_NRA) {
- lp->media = AUI; /* Non selected port activity */
- } else {
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 sts, irqs, irq_mask, imr, omr;
+ int next_tick = DE4X5_AUTOSENSE_MS;
+
+ switch (lp->media) {
+ case INIT:
+ DISABLE_IRQs;
+ lp->tx_enable = NO;
+ lp->timeout = -1;
+ de4x5_save_skbs(dev); /* Save non transmitted skb's */
+ if ((lp->autosense == AUTO) || (lp->autosense == TP_NW)) {
+ lp->media = TP; /* On chip auto negotiation is broken */
+ } else if (lp->autosense == TP) {
+ lp->media = TP;
+ } else if (lp->autosense == BNC) {
lp->media = BNC;
- }
- next_tick = dc21041_autoconf(dev);
+ } else if (lp->autosense == AUI) {
+ lp->media = AUI;
} else {
- lp->local_state = 1;
- de4x5_init_connection(dev);
+ lp->media = NC;
}
- }
- } else if (!lp->linkOK && (lp->autosense == AUTO)) {
- lp->media = TP_SUSPECT;
- next_tick = 3000;
- }
- break;
-
- case TP_SUSPECT:
- next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21041_autoconf);
- break;
-
- case AUI:
- if (!lp->tx_enable) {
- if (lp->timeout < 0) {
- omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */
- outl(omr & ~OMR_FDX, DE4X5_OMR);
- }
- irqs = 0;
- irq_mask = 0;
- sts = test_media(dev,irqs, irq_mask, 0xef09, 0xf73d, 0x000e, 1000);
- if (sts < 0) {
- next_tick = sts & ~TIMER_CB;
- } else {
- if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) {
- lp->media = BNC;
- next_tick = dc21041_autoconf(dev);
+ lp->local_state = 0;
+ next_tick = dc21041_autoconf(dev);
+ break;
+
+ case TP_NW:
+ if (lp->timeout < 0) {
+ omr = inl(DE4X5_OMR); /* Set up full duplex for the autonegotiate */
+ outl(omr | OMR_FDX, DE4X5_OMR);
+ }
+ irqs = STS_LNF | STS_LNP;
+ irq_mask = IMR_LFM | IMR_LPM;
+ sts = test_media(dev, irqs, irq_mask, 0xef01, 0xffff, 0x0008, 2400);
+ if (sts < 0) {
+ next_tick = sts & ~TIMER_CB;
} else {
- lp->local_state = 1;
- de4x5_init_connection(dev);
+ if (sts & STS_LNP) {
+ lp->media = ANS;
+ } else {
+ lp->media = AUI;
+ }
+ next_tick = dc21041_autoconf(dev);
+ }
+ break;
+
+ case ANS:
+ if (!lp->tx_enable) {
+ irqs = STS_LNP;
+ irq_mask = IMR_LPM;
+ sts = test_ans(dev, irqs, irq_mask, 3000);
+ if (sts < 0) {
+ next_tick = sts & ~TIMER_CB;
+ } else {
+ if (!(sts & STS_LNP) && (lp->autosense == AUTO)) {
+ lp->media = TP;
+ next_tick = dc21041_autoconf(dev);
+ } else {
+ lp->local_state = 1;
+ de4x5_init_connection(dev);
+ }
+ }
+ } else if (!lp->linkOK && (lp->autosense == AUTO)) {
+ lp->media = ANS_SUSPECT;
+ next_tick = 3000;
+ }
+ break;
+
+ case ANS_SUSPECT:
+ next_tick = de4x5_suspect_state(dev, 1000, ANS, test_tp, dc21041_autoconf);
+ break;
+
+ case TP:
+ if (!lp->tx_enable) {
+ if (lp->timeout < 0) {
+ omr = inl(DE4X5_OMR); /* Set up half duplex for TP */
+ outl(omr & ~OMR_FDX, DE4X5_OMR);
+ }
+ irqs = STS_LNF | STS_LNP;
+ irq_mask = IMR_LFM | IMR_LPM;
+ sts = test_media(dev, irqs, irq_mask, 0xef01, 0xff3f, 0x0008, 2400);
+ if (sts < 0) {
+ next_tick = sts & ~TIMER_CB;
+ } else {
+ if (!(sts & STS_LNP) && (lp->autosense == AUTO)) {
+ if (inl(DE4X5_SISR) & SISR_NRA) {
+ lp->media = AUI; /* Non selected port activity */
+ } else {
+ lp->media = BNC;
+ }
+ next_tick = dc21041_autoconf(dev);
+ } else {
+ lp->local_state = 1;
+ de4x5_init_connection(dev);
+ }
+ }
+ } else if (!lp->linkOK && (lp->autosense == AUTO)) {
+ lp->media = TP_SUSPECT;
+ next_tick = 3000;
+ }
+ break;
+
+ case TP_SUSPECT:
+ next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21041_autoconf);
+ break;
+
+ case AUI:
+ if (!lp->tx_enable) {
+ if (lp->timeout < 0) {
+ omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */
+ outl(omr & ~OMR_FDX, DE4X5_OMR);
+ }
+ irqs = 0;
+ irq_mask = 0;
+ sts = test_media(dev, irqs, irq_mask, 0xef09, 0xf73d, 0x000e, 1000);
+ if (sts < 0) {
+ next_tick = sts & ~TIMER_CB;
+ } else {
+ if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) {
+ lp->media = BNC;
+ next_tick = dc21041_autoconf(dev);
+ } else {
+ lp->local_state = 1;
+ de4x5_init_connection(dev);
+ }
+ }
+ } else if (!lp->linkOK && (lp->autosense == AUTO)) {
+ lp->media = AUI_SUSPECT;
+ next_tick = 3000;
}
- }
- } else if (!lp->linkOK && (lp->autosense == AUTO)) {
- lp->media = AUI_SUSPECT;
- next_tick = 3000;
+ break;
+
+ case AUI_SUSPECT:
+ next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc21041_autoconf);
+ break;
+
+ case BNC:
+ switch (lp->local_state) {
+ case 0:
+ if (lp->timeout < 0) {
+ omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */
+ outl(omr & ~OMR_FDX, DE4X5_OMR);
+ }
+ irqs = 0;
+ irq_mask = 0;
+ sts = test_media(dev, irqs, irq_mask, 0xef09, 0xf73d, 0x0006, 1000);
+ if (sts < 0) {
+ next_tick = sts & ~TIMER_CB;
+ } else {
+ lp->local_state++; /* Ensure media connected */
+ next_tick = dc21041_autoconf(dev);
+ }
+ break;
+
+ case 1:
+ if (!lp->tx_enable) {
+ if ((sts = ping_media(dev, 3000)) < 0) {
+ next_tick = sts & ~TIMER_CB;
+ } else {
+ if (sts) {
+ lp->local_state = 0;
+ lp->media = NC;
+ } else {
+ de4x5_init_connection(dev);
+ }
+ }
+ } else if (!lp->linkOK && (lp->autosense == AUTO)) {
+ lp->media = BNC_SUSPECT;
+ next_tick = 3000;
+ }
+ break;
+ }
+ break;
+
+ case BNC_SUSPECT:
+ next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc21041_autoconf);
+ break;
+
+ case NC:
+ omr = inl(DE4X5_OMR); /* Set up full duplex for the autonegotiate */
+ outl(omr | OMR_FDX, DE4X5_OMR);
+ reset_init_sia(dev, 0xef01, 0xffff, 0x0008); /* Initialise the SIA */
+ if (lp->media != lp->c_media) {
+ de4x5_dbg_media(dev);
+ lp->c_media = lp->media;
+ }
+ lp->media = INIT;
+ lp->tx_enable = NO;
+ break;
}
- break;
-
- case AUI_SUSPECT:
- next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc21041_autoconf);
- break;
-
- case BNC:
- switch (lp->local_state) {
- case 0:
- if (lp->timeout < 0) {
- omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */
- outl(omr & ~OMR_FDX, DE4X5_OMR);
- }
- irqs = 0;
- irq_mask = 0;
- sts = test_media(dev,irqs, irq_mask, 0xef09, 0xf73d, 0x0006, 1000);
- if (sts < 0) {
- next_tick = sts & ~TIMER_CB;
- } else {
- lp->local_state++; /* Ensure media connected */
- next_tick = dc21041_autoconf(dev);
- }
- break;
-
- case 1:
- if (!lp->tx_enable) {
- if ((sts = ping_media(dev, 3000)) < 0) {
- next_tick = sts & ~TIMER_CB;
+
+ return next_tick;
+}
+
+/*
+** Some autonegotiation chips are broken in that they do not return the
+** acknowledge bit (anlpa & MII_ANLPA_ACK) in the link partner advertisement
+** register, except at the first power up negotiation.
+*/
+static int dc21140m_autoconf(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ int ana, anlpa, cap, cr, slnk, sr;
+ u_long iobase = dev->base_addr;
+ int next_tick = DE4X5_AUTOSENSE_MS;
+ u_long imr, omr;
+
+ switch (lp->media) {
+ case INIT:
+ if (lp->timeout < 0) {
+ DISABLE_IRQs;
+ lp->tx_enable = FALSE;
+ lp->linkOK = 0;
+ de4x5_save_skbs(dev); /* Save non transmitted skb's */
+ }
+ if ((next_tick = de4x5_reset_phy(dev)) < 0) {
+ next_tick &= ~TIMER_CB;
} else {
- if (sts) {
+ if (lp->useSROM) {
+ if (srom_map_media(dev) < 0) {
+ lp->tcount++;
+ return next_tick;
+ }
+ srom_exec(dev, lp->phy[lp->active].gep);
+ if (lp->infoblock_media == ANS) {
+ ana = lp->phy[lp->active].ana | MII_ANA_CSMA;
+ mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
+ }
+ } else {
+ lp->tmp = MII_SR_ASSC; /* Fake out the MII speed set */
+ SET_10Mb;
+ if (lp->autosense == _100Mb) {
+ lp->media = _100Mb;
+ } else if (lp->autosense == _10Mb) {
+ lp->media = _10Mb;
+ } else if ((lp->autosense == AUTO) &&
+ ((sr = is_anc_capable(dev)) & MII_SR_ANC)) {
+ ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA);
+ ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM);
+ mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
+ lp->media = ANS;
+ } else if (lp->autosense == AUTO) {
+ lp->media = SPD_DET;
+ } else if (is_spd_100(dev) && is_100_up(dev)) {
+ lp->media = _100Mb;
+ } else {
+ lp->media = NC;
+ }
+ }
lp->local_state = 0;
- lp->media = NC;
- } else {
+ next_tick = dc21140m_autoconf(dev);
+ }
+ break;
+
+ case ANS:
+ switch (lp->local_state) {
+ case 0:
+ if (lp->timeout < 0) {
+ mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
+ }
+ cr = test_mii_reg(dev, MII_CR, MII_CR_RAN, FALSE, 500);
+ if (cr < 0) {
+ next_tick = cr & ~TIMER_CB;
+ } else {
+ if (cr) {
+ lp->local_state = 0;
+ lp->media = SPD_DET;
+ } else {
+ lp->local_state++;
+ }
+ next_tick = dc21140m_autoconf(dev);
+ }
+ break;
+
+ case 1:
+ if ((sr = test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) {
+ next_tick = sr & ~TIMER_CB;
+ } else {
+ lp->media = SPD_DET;
+ lp->local_state = 0;
+ if (sr) { /* Success! */
+ lp->tmp = MII_SR_ASSC;
+ anlpa = mii_rd(MII_ANLPA, lp->phy[lp->active].addr, DE4X5_MII);
+ ana = mii_rd(MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
+ if (!(anlpa & MII_ANLPA_RF) &&
+ (cap = anlpa & MII_ANLPA_TAF & ana)) {
+ if (cap & MII_ANA_100M) {
+ lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE);
+ lp->media = _100Mb;
+ } else if (cap & MII_ANA_10M) {
+ lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE);
+
+ lp->media = _10Mb;
+ }
+ }
+ } /* Auto Negotiation failed to finish */
+ next_tick = dc21140m_autoconf(dev);
+ } /* Auto Negotiation failed to start */
+ break;
+ }
+ break;
+
+ case SPD_DET: /* Choose 10Mb/s or 100Mb/s */
+ if (lp->timeout < 0) {
+ lp->tmp = (lp->phy[lp->active].id ? MII_SR_LKS :
+ (~gep_rd(dev) & GEP_LNP));
+ SET_100Mb_PDET;
+ }
+ if ((slnk = test_for_100Mb(dev, 6500)) < 0) {
+ next_tick = slnk & ~TIMER_CB;
+ } else {
+ if (is_spd_100(dev) && is_100_up(dev)) {
+ lp->media = _100Mb;
+ } else if ((!is_spd_100(dev) && (is_10_up(dev) & lp->tmp))) {
+ lp->media = _10Mb;
+ } else {
+ lp->media = NC;
+ }
+ next_tick = dc21140m_autoconf(dev);
+ }
+ break;
+
+ case _100Mb: /* Set 100Mb/s */
+ next_tick = 3000;
+ if (!lp->tx_enable) {
+ SET_100Mb;
de4x5_init_connection(dev);
- }
+ } else {
+ if (!lp->linkOK && (lp->autosense == AUTO)) {
+ if (!is_100_up(dev) ||
+ (!lp->useSROM && !is_spd_100(dev))) {
+ lp->media = INIT;
+ lp->tcount++;
+ next_tick = DE4X5_AUTOSENSE_MS;
+ }
+ }
}
- } else if (!lp->linkOK && (lp->autosense == AUTO)) {
- lp->media = BNC_SUSPECT;
+ break;
+
+ case _10Mb: /* Set 10Mb/s */
next_tick = 3000;
- }
- break;
- }
- break;
-
- case BNC_SUSPECT:
- next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc21041_autoconf);
- break;
-
- case NC:
- omr = inl(DE4X5_OMR); /* Set up full duplex for the autonegotiate */
- outl(omr | OMR_FDX, DE4X5_OMR);
- reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */
- if (lp->media != lp->c_media) {
- de4x5_dbg_media(dev);
- lp->c_media = lp->media;
+ if (!lp->tx_enable) {
+ SET_10Mb;
+ de4x5_init_connection(dev);
+ } else {
+ if (!lp->linkOK && (lp->autosense == AUTO)) {
+ if (!is_10_up(dev) ||
+ (!lp->useSROM && is_spd_100(dev))) {
+ lp->media = INIT;
+ lp->tcount++;
+ next_tick = DE4X5_AUTOSENSE_MS;
+ }
+ }
+ }
+ break;
+
+ case NC:
+ if (lp->media != lp->c_media) {
+ de4x5_dbg_media(dev);
+ lp->c_media = lp->media;
+ }
+ lp->media = INIT;
+ lp->tx_enable = FALSE;
+ break;
}
- lp->media = INIT;
- lp->tx_enable = NO;
- break;
- }
-
- return next_tick;
+
+ return next_tick;
}
/*
-** Some autonegotiation chips are broken in that they do not return the
-** acknowledge bit (anlpa & MII_ANLPA_ACK) in the link partner advertisement
-** register, except at the first power up negotiation.
+** This routine may be merged into dc21140m_autoconf() sometime as I'm
+** changing how I figure out the media - but trying to keep it backwards
+** compatible with the de500-xa and de500-aa.
+** Whether it's BNC, AUI, SYM or MII is sorted out in the infoblock
+** functions and set during de4x5_mac_port() and/or de4x5_reset_phy().
+** This routine just has to figure out whether 10Mb/s or 100Mb/s is
+** active.
+** When autonegotiation is working, the ANS part searches the SROM for
+** the highest common speed (TP) link that both can run and if that can
+** be full duplex. That infoblock is executed and then the link speed set.
+**
+** Only _10Mb and _100Mb are tested here.
*/
static int
-dc21140m_autoconf(struct device *dev)
+dc2114x_autoconf(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int ana, anlpa, cap, cr, slnk, sr, iobase = dev->base_addr;
- int next_tick = DE4X5_AUTOSENSE_MS;
- u_long imr, omr;
-
- switch(lp->media) {
- case INIT:
- if (lp->timeout < 0) {
- DISABLE_IRQs;
- lp->tx_enable = FALSE;
- lp->linkOK = 0;
- de4x5_save_skbs(dev); /* Save non transmitted skb's */
- }
- if ((next_tick = de4x5_reset_phy(dev)) < 0) {
- next_tick &= ~TIMER_CB;
- } else {
- if (lp->useSROM) {
- srom_map_media(dev);
- srom_exec(dev, lp->phy[lp->active].gep);
- if (lp->infoblock_media == ANS) {
- ana = lp->phy[lp->active].ana | MII_ANA_CSMA;
- mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
- }
- } else {
- lp->tmp = MII_SR_ASSC; /* Fake out the MII speed set */
- SET_10Mb;
- if (lp->autosense == _100Mb) {
- lp->media = _100Mb;
- } else if (lp->autosense == _10Mb) {
- lp->media = _10Mb;
- } else if ((lp->autosense == AUTO) &&
- ((sr=is_anc_capable(dev)) & MII_SR_ANC)) {
- ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA);
- ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM);
- mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
- lp->media = ANS;
- } else if (lp->autosense == AUTO) {
- lp->media = SPD_DET;
- } else if (is_spd_100(dev) && is_100_up(dev)) {
- lp->media = _100Mb;
- } else {
- lp->media = NC;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 cr, anlpa, ana, cap, irqs, irq_mask, imr, omr, slnk, sr, sts;
+ int next_tick = DE4X5_AUTOSENSE_MS;
+
+ switch (lp->media) {
+ case INIT:
+ if (lp->timeout < 0) {
+ DISABLE_IRQs;
+ lp->tx_enable = FALSE;
+ lp->linkOK = 0;
+ lp->timeout = -1;
+ de4x5_save_skbs(dev); /* Save non transmitted skb's */
}
- }
- lp->local_state = 0;
- next_tick = dc21140m_autoconf(dev);
- }
- break;
-
- case ANS:
- switch (lp->local_state) {
- case 0:
- if (lp->timeout < 0) {
- mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
- }
- cr = test_mii_reg(dev, MII_CR, MII_CR_RAN, FALSE, 500);
- if (cr < 0) {
- next_tick = cr & ~TIMER_CB;
- } else {
- if (cr) {
- lp->local_state = 0;
- lp->media = SPD_DET;
+ if ((next_tick = de4x5_reset_phy(dev)) < 0) {
+ next_tick &= ~TIMER_CB;
} else {
- lp->local_state++;
+ lp->media = SPD_DET;
+ if ((lp->infoblock_media == ANS) &&
+ ((sr=is_anc_capable(dev)) & MII_SR_ANC)) {
+ ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA);
+ ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM);
+ mii_wr(ana, MII_ANA,
+ lp->phy[lp->active].addr,
+ DE4X5_MII);
+ lp->media = ANS;
+ }
+ lp->local_state = 0;
+ next_tick = dc2114x_autoconf(dev);
}
- next_tick = dc21140m_autoconf(dev);
- }
- break;
+ break;
+
+ case ANS:
+ switch (lp->local_state) {
+ case 0:
+ if (lp->timeout < 0) {
+ mii_wr(MII_CR_ASSE | MII_CR_RAN,
+ MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
+ }
+ cr = test_mii_reg(dev, MII_CR, MII_CR_RAN, FALSE, 500);
+ if (cr < 0) {
+ next_tick = cr & ~TIMER_CB;
+ } else {
+ if (cr) {
+ lp->local_state = 0;
+ lp->media = SPD_DET;
+ } else {
+ lp->local_state++;
+ }
+ next_tick = dc2114x_autoconf(dev);
+ }
+ break;
- case 1:
- if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) {
- next_tick = sr & ~TIMER_CB;
- } else {
- lp->media = SPD_DET;
- lp->local_state = 0;
- if (sr) { /* Success! */
- lp->tmp = MII_SR_ASSC;
- anlpa = mii_rd(MII_ANLPA, lp->phy[lp->active].addr, DE4X5_MII);
- ana = mii_rd(MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
- if (!(anlpa & MII_ANLPA_RF) &&
- (cap = anlpa & MII_ANLPA_TAF & ana)) {
- if (cap & MII_ANA_100M) {
- lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE);
- lp->media = _100Mb;
- } else if (cap & MII_ANA_10M) {
- lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE);
-
- lp->media = _10Mb;
+ case 1:
+ if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) {
+ next_tick = sr & ~TIMER_CB;
+ } else {
+ lp->media = SPD_DET;
+ lp->local_state = 0;
+ if (sr) { /* Success! */
+ lp->tmp = MII_SR_ASSC;
+ anlpa = mii_rd(MII_ANLPA,
+ lp->phy[lp->active].addr,
+ DE4X5_MII);
+ ana = mii_rd(MII_ANA,
+ lp->phy[lp->active].addr,
+ DE4X5_MII);
+ if (!(anlpa & MII_ANLPA_RF) &&
+ (cap = anlpa & MII_ANLPA_TAF & ana)) {
+ if (cap & MII_ANA_100M) {
+ lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE);
+ lp->media = _100Mb;
+ } else if (cap & MII_ANA_10M) {
+ lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE);
+ lp->media = _10Mb;
+ }
+ }
+ } /* Auto Negotiation failed to finish */
+ next_tick = dc2114x_autoconf(dev);
+ } /* Auto Negotiation failed to start */
+ break;
+ }
+ break;
+
+ case AUI:
+ if (!lp->tx_enable) {
+ if (lp->timeout < 0) {
+ omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */
+ outl(omr & ~OMR_FDX, DE4X5_OMR);
}
- }
- } /* Auto Negotiation failed to finish */
- next_tick = dc21140m_autoconf(dev);
- } /* Auto Negotiation failed to start */
- break;
- }
- break;
+ irqs = 0;
+ irq_mask = 0;
+ sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000);
+ if (sts < 0) {
+ next_tick = sts & ~TIMER_CB;
+ } else {
+ if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) {
+ lp->media = BNC;
+ next_tick = dc2114x_autoconf(dev);
+ } else {
+ lp->local_state = 1;
+ de4x5_init_connection(dev);
+ }
+ }
+ } else if (!lp->linkOK && (lp->autosense == AUTO)) {
+ lp->media = AUI_SUSPECT;
+ next_tick = 3000;
+ }
+ break;
- case SPD_DET: /* Choose 10Mb/s or 100Mb/s */
- if (lp->timeout < 0) {
- lp->tmp = (lp->phy[lp->active].id ? MII_SR_LKS :
- (~inl(DE4X5_GEP) & GEP_LNP));
- SET_100Mb_PDET;
- }
- if ((slnk = test_sym_link(dev, 6200)) < 0) {
- next_tick = slnk & ~TIMER_CB;
- } else {
- if (is_spd_100(dev) && is_100_up(dev)) {
- lp->media = _100Mb;
- } else if ((!is_spd_100(dev) && (is_10_up(dev) & lp->tmp))) {
- lp->media = _10Mb;
- } else {
- lp->media = NC;
- }
- next_tick = dc21140m_autoconf(dev);
- }
- break;
+ case AUI_SUSPECT:
+ next_tick = de4x5_suspect_state(dev, 1000, AUI,
+ ping_media, dc2114x_autoconf);
+ break;
- case _100Mb: /* Set 100Mb/s */
- next_tick = 3000;
- if (!lp->tx_enable) {
- SET_100Mb;
- de4x5_init_connection(dev);
- } else {
- if (!lp->linkOK && (lp->autosense == AUTO)) {
- if (!(is_spd_100(dev) && is_100_up(dev))) {
- lp->media = INIT;
- lp->tcount++;
- next_tick = DE4X5_AUTOSENSE_MS;
+ case BNC:
+ switch (lp->local_state) {
+ case 0:
+ if (lp->timeout < 0) {
+ omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */
+ outl(omr & ~OMR_FDX, DE4X5_OMR);
+ }
+ irqs = 0;
+ irq_mask = 0;
+ sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000);
+ if (sts < 0) {
+ next_tick = sts & ~TIMER_CB;
+ } else {
+ lp->local_state++; /* Ensure media connected */
+ next_tick = dc2114x_autoconf(dev);
+ }
+ break;
+
+ case 1:
+ if (!lp->tx_enable) {
+ if ((sts = ping_media(dev, 3000)) < 0) {
+ next_tick = sts & ~TIMER_CB;
+ } else {
+ if (sts) {
+ lp->local_state = 0;
+ lp->tcount++;
+ lp->media = INIT;
+ } else {
+ de4x5_init_connection(dev);
+ }
+ }
+ } else if (!lp->linkOK && (lp->autosense == AUTO)) {
+ lp->media = BNC_SUSPECT;
+ next_tick = 3000;
+ }
+ break;
}
- }
- }
- break;
+ break;
- case _10Mb: /* Set 10Mb/s */
- next_tick = 3000;
- if (!lp->tx_enable) {
- SET_10Mb;
- de4x5_init_connection(dev);
- } else {
- if (!lp->linkOK && (lp->autosense == AUTO)) {
- if (!(!is_spd_100(dev) && is_10_up(dev))) {
- lp->media = INIT;
- lp->tcount++;
- next_tick = DE4X5_AUTOSENSE_MS;
+ case BNC_SUSPECT:
+ next_tick = de4x5_suspect_state(dev, 1000, BNC,
+ ping_media, dc2114x_autoconf);
+ break;
+
+ case SPD_DET: /* Choose 10Mb/s or 100Mb/s */
+ if (srom_map_media(dev) < 0) {
+ lp->tcount++;
+ lp->media = INIT;
+ return next_tick;
}
- }
- }
- break;
+ if (lp->media == _100Mb) {
+ if ((slnk = test_for_100Mb(dev, 6500)) < 0) {
+ lp->media = SPD_DET;
+ return (slnk & ~TIMER_CB);
+ }
+ } else {
+ if (wait_for_link(dev) < 0) {
+ lp->media = SPD_DET;
+ return PDET_LINK_WAIT;
+ }
+ }
+ if (((lp->media == _100Mb) && is_100_up(dev)) ||
+ ((lp->media == _10Mb) && is_10_up(dev)) ||
+ (lp->media == BNC) || (lp->media == AUI)) {
+ next_tick = dc2114x_autoconf(dev);
+ } else {
+ lp->tcount++;
+ lp->media = INIT;
+ }
+ break;
- case NC:
- if (lp->media != lp->c_media) {
- de4x5_dbg_media(dev);
- lp->c_media = lp->media;
+ case _10Mb:
+ next_tick = 3000;
+ if (!lp->tx_enable) {
+ SET_10Mb;
+ de4x5_init_connection(dev);
+ } else {
+ if (!lp->linkOK && (lp->autosense == AUTO)) {
+ if (!is_10_up(dev) ||
+ (!lp->useSROM && is_spd_100(dev))) {
+ lp->media = INIT;
+ lp->tcount++;
+ next_tick = DE4X5_AUTOSENSE_MS;
+ }
+ }
+ }
+ break;
+
+ case _100Mb:
+ next_tick = 3000;
+ if (!lp->tx_enable) {
+ SET_100Mb;
+ de4x5_init_connection(dev);
+ } else {
+ if (!lp->linkOK && (lp->autosense == AUTO)) {
+ if (!is_100_up(dev) ||
+ (!lp->useSROM && !is_spd_100(dev))) {
+ lp->media = INIT;
+ lp->tcount++;
+ next_tick = DE4X5_AUTOSENSE_MS;
+ }
+ }
+ }
+ break;
+
+ default:
+ lp->tcount++;
+ printk("Huh?: media:%02x\n", lp->media);
+ lp->media = INIT;
+ break;
}
- lp->media = INIT;
- lp->tx_enable = FALSE;
- break;
- }
- return next_tick;
+ return next_tick;
}
-static int
-srom_autoconf(struct device *dev)
+static int srom_autoconf(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- return lp->infoleaf_fn(dev);
+ return lp->infoleaf_fn(dev);
}
/*
** This mapping keeps the original media codes and FDX flag unchanged.
** While it isn't strictly necessary, it helps me for the moment...
+** The early return avoids a media state / SROM media space clash.
*/
-static void
-srom_map_media(struct device *dev)
+static int srom_map_media(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-
- lp->fdx = 0;
- switch(lp->infoblock_media) {
- case SROM_10BASETF:
- lp->fdx = TRUE;
- case SROM_10BASET:
- if (lp->chipset == DC21140) {
- lp->media = _10Mb;
- } else {
- lp->media = TP;
- }
- break;
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+
+ lp->fdx = 0;
+ if (lp->infoblock_media == lp->media)
+ return 0;
+
+ switch(lp->infoblock_media) {
+ case SROM_10BASETF:
+ if (!de4x5_full_duplex)
+ return -1;
+ lp->fdx = TRUE;
+ case SROM_10BASET:
+ if (de4x5_full_duplex && !lp->fdx)
+ return -1;
+ if ((lp->chipset == DC21140) || ((lp->chipset & ~0x00ff) == DC2114x)) {
+ lp->media = _10Mb;
+ } else {
+ lp->media = TP;
+ }
+ break;
- case SROM_10BASE2:
- lp->media = BNC;
- break;
+ case SROM_10BASE2:
+ lp->media = BNC;
+ break;
- case SROM_10BASE5:
- lp->media = AUI;
- break;
+ case SROM_10BASE5:
+ lp->media = AUI;
+ break;
- case SROM_100BASETF:
- lp->fdx = TRUE;
- case SROM_100BASET:
- lp->media = _100Mb;
- break;
+ case SROM_100BASETF:
+ if (!de4x5_full_duplex)
+ return -1;
+ lp->fdx = TRUE;
+ case SROM_100BASET:
+ if (de4x5_full_duplex && !lp->fdx)
+ return -1;
+ lp->media = _100Mb;
+ break;
- case SROM_100BASET4:
- lp->media = _100Mb;
- break;
+ case SROM_100BASET4:
+ lp->media = _100Mb;
+ break;
- case SROM_100BASEFF:
- lp->fdx = TRUE;
- case SROM_100BASEF:
- lp->media = _100Mb;
- break;
+ case SROM_100BASEFF:
+ if (!de4x5_full_duplex)
+ return -1;
+ lp->fdx = TRUE;
+ case SROM_100BASEF:
+ if (de4x5_full_duplex && !lp->fdx)
+ return -1;
+ lp->media = _100Mb;
+ break;
- case ANS:
- lp->media = ANS;
- break;
+ case ANS:
+ lp->media = ANS;
+ break;
- default:
- printk("%s: Bad media code [%d] detected in SROM!\n", dev->name,
- lp->infoblock_media);
- break;
- }
+ default:
+ printk("%s: Bad media code [%d] detected in SROM!\n", dev->name,
+ lp->infoblock_media);
+ return -1;
+ break;
+ }
- return;
+ return 0;
}
-static void
-de4x5_init_connection(struct device *dev)
+static void de4x5_init_connection(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
-
- if (lp->media != lp->c_media) {
- de4x5_dbg_media(dev);
- lp->c_media = lp->media; /* Stop scrolling media messages */
- }
-
- cli();
- de4x5_restore_skbs(dev);
- de4x5_setup_intr(dev);
- lp->tx_enable = YES;
- dev->tbusy = 0;
- sti();
- outl(POLL_DEMAND, DE4X5_TPD);
- mark_bh(NET_BH);
-
- return;
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+
+ if (lp->media != lp->c_media) {
+ de4x5_dbg_media(dev);
+ lp->c_media = lp->media; /* Stop scrolling media messages */
+ }
+
+ cli();
+ de4x5_restore_skbs(dev);
+ de4x5_setup_intr(dev);
+ lp->tx_enable = YES;
+ dev->tbusy = 0;
+ sti();
+ outl(POLL_DEMAND, DE4X5_TPD);
+ mark_bh(NET_BH);
+
+ return;
}
/*
@@ -2848,261 +3170,292 @@ de4x5_init_connection(struct device *dev)
** since their MII address pins can float at voltages that are dependent
** on the signal pin use. Do a double reset to ensure a reset.
*/
-static int
-de4x5_reset_phy(struct device *dev)
+static int de4x5_reset_phy(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int next_tick = 0;
+
+ if ((lp->useSROM) || (lp->phy[lp->active].id)) {
+ if (lp->timeout < 0) {
+ if (lp->useSROM) {
+ if (lp->phy[lp->active].rst) {
+ srom_exec(dev, lp->phy[lp->active].rst);
+ srom_exec(dev, lp->phy[lp->active].rst);
+ } else if (lp->rst) { /* Type 5 infoblock reset */
+ srom_exec(dev, lp->rst);
+ srom_exec(dev, lp->rst);
+ }
+ } else {
+ PHY_HARD_RESET;
+ }
+ if (lp->useMII) {
+ mii_wr(MII_CR_RST, MII_CR,
+ lp->phy[lp->active].addr, DE4X5_MII);
+ }
+ }
+ if (lp->useMII) {
+ next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500);
+ }
+ } else if (lp->chipset == DC21140) {
+ PHY_HARD_RESET;
+ }
+
+ return next_tick;
+}
+
+static int test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- int next_tick = 0;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 sts, csr12;
- if ((lp->useSROM) || (lp->phy[lp->active].id)) {
if (lp->timeout < 0) {
- if (lp->useSROM) {
- if (lp->phy[lp->active].rst) { /* MII device specific reset */
- srom_exec(dev, lp->phy[lp->active].rst);
- srom_exec(dev, lp->phy[lp->active].rst);
- } else if (lp->rst) { /* Type 5 infoblock reset */
- srom_exec(dev, lp->rst);
- srom_exec(dev, lp->rst);
- }
- } else {
- PHY_HARD_RESET;
- }
- if (lp->useMII) {
- mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
- }
- }
- if (lp->useMII) {
- next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500);
+ lp->timeout = msec / 100;
+ if (!lp->useSROM) { /* Already done if by SROM, else dc2104[01] */
+ reset_init_sia(dev, csr13, csr14, csr15);
+ }
+
+ /* set up the interrupt mask */
+ outl(irq_mask, DE4X5_IMR);
+
+ /* clear all pending interrupts */
+ sts = inl(DE4X5_STS);
+ outl(sts, DE4X5_STS);
+
+ /* clear csr12 NRA and SRA bits */
+ if ((lp->chipset == DC21041) || lp->useSROM) {
+ csr12 = inl(DE4X5_SISR);
+ outl(csr12, DE4X5_SISR);
+ }
+ }
+ sts = inl(DE4X5_STS) & ~TIMER_CB;
+
+ if (!(sts & irqs) && --lp->timeout) {
+ sts = 100 | TIMER_CB;
+ } else {
+ lp->timeout = -1;
}
- } else if (lp->chipset == DC21140) {
- PHY_HARD_RESET;
- }
- return next_tick;
+ return sts;
}
-static int
-test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec)
+static int test_tp(struct device *dev, s32 msec)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- s32 sts, csr12;
-
- if (lp->timeout < 0) {
- lp->timeout = msec/100;
- reset_init_sia(dev, csr13, csr14, csr15);
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ int sisr;
- /* set up the interrupt mask */
- outl(irq_mask, DE4X5_IMR);
+ if (lp->timeout < 0) {
+ lp->timeout = msec / 100;
+ }
+ sisr = (inl(DE4X5_SISR) & ~TIMER_CB) & (SISR_LKF | SISR_NCR);
- /* clear all pending interrupts */
- sts = inl(DE4X5_STS);
- outl(sts, DE4X5_STS);
-
- /* clear csr12 NRA and SRA bits */
- if (lp->chipset == DC21041) {
- csr12 = inl(DE4X5_SISR);
- outl(csr12, DE4X5_SISR);
+ if (sisr && --lp->timeout) {
+ sisr = 100 | TIMER_CB;
+ } else {
+ lp->timeout = -1;
}
- }
-
- sts = inl(DE4X5_STS) & ~TIMER_CB;
-
- if (!(sts & irqs) && --lp->timeout) {
- sts = 100 | TIMER_CB;
- } else {
- lp->timeout = -1;
- }
-
- return sts;
+
+ return sisr;
}
-static int
-test_tp(struct device *dev, s32 msec)
+/*
+** Samples the 100Mb Link State Signal. The sample interval is important
+** because too fast a rate can give erroneous results and confuse the
+** speed sense algorithm.
+*/
+#define SAMPLE_INTERVAL 500 /* ms */
+#define SAMPLE_DELAY 2000 /* ms */
+static int test_for_100Mb(struct device *dev, int msec)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- int sisr;
-
- if (lp->timeout < 0) {
- lp->timeout = msec/100;
- }
-
- sisr = (inl(DE4X5_SISR) & ~TIMER_CB) & (SISR_LKF | SISR_NCR);
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ int gep = 0, ret = ((lp->chipset & ~0x00ff)==DC2114x? -1 :GEP_SLNK);
- if (sisr && --lp->timeout) {
- sisr = 100 | TIMER_CB;
- } else {
- lp->timeout = -1;
- }
-
- return sisr;
+ if (lp->timeout < 0) {
+ if ((msec/SAMPLE_INTERVAL) <= 0)
+ return 0;
+ if (msec > SAMPLE_DELAY) {
+ lp->timeout = (msec - SAMPLE_DELAY)/SAMPLE_INTERVAL;
+ gep = SAMPLE_DELAY | TIMER_CB;
+ return gep;
+ } else {
+ lp->timeout = msec/SAMPLE_INTERVAL;
+ }
+ }
+ if (lp->phy[lp->active].id || lp->useSROM) {
+ gep = is_100_up(dev) | is_spd_100(dev);
+ } else {
+ gep = (~gep_rd(dev) & (GEP_SLNK | GEP_LNP));
+ }
+ if (!(gep & ret) && --lp->timeout) {
+ gep = SAMPLE_INTERVAL | TIMER_CB;
+ } else {
+ lp->timeout = -1;
+ }
+
+ return gep;
}
-static int
-test_sym_link(struct device *dev, int msec)
+static int wait_for_link(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int iobase = dev->base_addr;
- int gep = 0;
-
- if (lp->timeout < 0) {
- lp->timeout = msec/100;
- }
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+
+ if (lp->timeout < 0) {
+ lp->timeout = 1;
+ }
- if (lp->phy[lp->active].id || lp->useSROM) {
- gep = ((is_100_up(dev) && is_spd_100(dev)) ? GEP_SLNK : 0);
- } else {
- gep = (~inl(DE4X5_GEP) & (GEP_SLNK | GEP_LNP));
- }
- if (!(gep & GEP_SLNK) && --lp->timeout) {
- gep = 100 | TIMER_CB;
- } else {
- lp->timeout = -1;
- }
+ if (lp->timeout--) {
+ return TIMER_CB;
+ } else {
+ lp->timeout = -1;
+ }
- return gep;
+ return 0;
}
/*
**
**
*/
-static int
-test_mii_reg(struct device *dev, int reg, int mask, int pol, long msec)
+static int test_mii_reg(struct device *dev, int reg, int mask, int pol, long msec)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int test, iobase = dev->base_addr;
-
- if (lp->timeout < 0) {
- lp->timeout = msec/100;
- }
-
- if (pol) pol = ~0;
- reg = mii_rd((u_char)reg, lp->phy[lp->active].addr, DE4X5_MII) & mask;
- test = (reg ^ pol) & mask;
-
- if (test && --lp->timeout) {
- reg = 100 | TIMER_CB;
- } else {
- lp->timeout = -1;
- }
-
- return reg;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ int test;
+ u_long iobase = dev->base_addr;
+
+ if (lp->timeout < 0) {
+ lp->timeout = msec / 100;
+ }
+ if (pol)
+ pol = ~0;
+ reg = mii_rd((u_char) reg, lp->phy[lp->active].addr, DE4X5_MII) & mask;
+ test = (reg ^ pol) & mask;
+
+ if (test && --lp->timeout) {
+ reg = 100 | TIMER_CB;
+ } else {
+ lp->timeout = -1;
+ }
+
+ return reg;
}
-static int
-is_spd_100(struct device *dev)
+static int is_spd_100(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- int spd;
-
- if (lp->useSROM && !lp->useMII) {
- spd = (lp->asBitValid &
- (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) |
- (lp->linkOK & ~lp->asBitValid);
- } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
- spd = mii_rd(lp->phy[lp->active].spd.reg, lp->phy[lp->active].addr, DE4X5_MII);
- spd = ~(spd ^ lp->phy[lp->active].spd.value);
- spd &= lp->phy[lp->active].spd.mask;
- } else {
- spd = ((~inl(DE4X5_GEP)) & GEP_SLNK);
- }
-
- return spd;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ int spd;
+
+ if (lp->useMII) {
+ spd = mii_rd(lp->phy[lp->active].spd.reg, lp->phy[lp->active].addr, DE4X5_MII);
+ spd = ~(spd ^ lp->phy[lp->active].spd.value);
+ spd &= lp->phy[lp->active].spd.mask;
+ } else if (!lp->useSROM) { /* de500-xa */
+ spd = ((~gep_rd(dev)) & GEP_SLNK);
+ } else {
+ if ((lp->ibn == 2) || !lp->asBitValid)
+ return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0);
+
+ spd = (lp->asBitValid & (lp->asPolarity ^ (gep_rd(dev) & lp->asBit))) |
+ (lp->linkOK & ~lp->asBitValid);
+ }
+
+ return spd;
}
-static int
-is_100_up(struct device *dev)
+static int is_100_up(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
-
- if (lp->useSROM && !lp->useMII) {
- return ((lp->asBitValid &
- (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) |
- (lp->linkOK & ~lp->asBitValid));
- } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
- /* Double read for sticky bits & temporary drops */
- mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);
- return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS);
- } else {
- return ((~inl(DE4X5_GEP)) & GEP_SLNK);
- }
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+
+ if (lp->useMII) {
+ /* Double read for sticky bits & temporary drops */
+ mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);
+ return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS);
+ } else if (!lp->useSROM) { /* de500-xa */
+ return ((~gep_rd(dev)) & GEP_SLNK);
+ } else {
+ if ((lp->ibn == 2) || !lp->asBitValid)
+ return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0);
+
+ return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) |
+ (lp->linkOK & ~lp->asBitValid));
+ }
}
-static int
-is_10_up(struct device *dev)
+static int is_10_up(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
-
- if (lp->useSROM && !lp->useMII) {
- return ((lp->asBitValid &
- (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) |
- (lp->linkOK & ~lp->asBitValid));
- } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
- /* Double read for sticky bits & temporary drops */
- mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);
- return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS);
- } else {
- return ((~inl(DE4X5_GEP)) & GEP_LNP);
- }
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+
+ if (lp->useMII) {
+ /* Double read for sticky bits & temporary drops */
+ mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);
+ return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS);
+ } else if (!lp->useSROM) { /* de500-xa */
+ return ((~gep_rd(dev)) & GEP_LNP);
+ } else {
+ if ((lp->ibn == 2) || !lp->asBitValid)
+ return (((lp->chipset & ~0x00ff) == DC2114x) ?
+ (~inl(DE4X5_SISR)&SISR_LS10):
+ 0);
+
+ return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) |
+ (lp->linkOK & ~lp->asBitValid));
+ }
}
-static int
-is_anc_capable(struct device *dev)
+static int is_anc_capable(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
-
- if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
- return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII));
- } else {
- return 0;
- }
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+
+ if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
+ return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII));
+ } else if ((lp->chipset & ~0x00ff) == DC2114x) {
+ return (inl(DE4X5_SISR) & SISR_LPN) >> 11;
+ } else {
+ return 0;
+ }
}
/*
** Send a packet onto the media and watch for send errors that indicate the
** media is bad or unconnected.
*/
-static int
-ping_media(struct device *dev, int msec)
+static int ping_media(struct device *dev, int msec)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- int sisr;
-
- if (lp->timeout < 0) {
- lp->timeout = msec/100;
-
- lp->tmp = lp->tx_new; /* Remember the ring position */
- load_packet(dev, lp->frame, TD_LS | TD_FS | sizeof(lp->frame), NULL);
- lp->tx_new = (++lp->tx_new) % lp->txRingSize;
- outl(POLL_DEMAND, DE4X5_TPD);
- }
-
- sisr = inl(DE4X5_SISR);
-
- if ((!(sisr & SISR_NCR)) &&
- ((s32)le32_to_cpu(lp->tx_ring[lp->tmp].status) < 0) &&
- (--lp->timeout)) {
- sisr = 100 | TIMER_CB;
- } else {
- if ((!(sisr & SISR_NCR)) &&
- !(le32_to_cpu(lp->tx_ring[lp->tmp].status) & (T_OWN | TD_ES)) &&
- lp->timeout) {
- sisr = 0;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ int sisr;
+
+ if (lp->timeout < 0) {
+ lp->timeout = msec / 100;
+
+ lp->tmp = lp->tx_new; /* Remember the ring position */
+ load_packet(dev, lp->frame, TD_LS | TD_FS | sizeof(lp->frame), NULL);
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+ outl(POLL_DEMAND, DE4X5_TPD);
+ }
+ sisr = inl(DE4X5_SISR);
+
+ if ((!(sisr & SISR_NCR)) &&
+ ((s32) le32_to_cpu(lp->tx_ring[lp->tmp].status) < 0) &&
+ (--lp->timeout)) {
+ sisr = 100 | TIMER_CB;
} else {
- sisr = 1;
+ if ((!(sisr & SISR_NCR)) &&
+ !(le32_to_cpu(lp->tx_ring[lp->tmp].status) & (T_OWN | TD_ES)) &&
+ lp->timeout) {
+ sisr = 0;
+ } else {
+ sisr = 1;
+ }
+ lp->timeout = -1;
}
- lp->timeout = -1;
- }
-
- return sisr;
+
+ return sisr;
}
/*
@@ -3110,94 +3463,93 @@ ping_media(struct device *dev, int msec)
** replace the one about to be passed up. On Alpha's it kmallocs a buffer
** into which the packet is copied.
*/
-static struct sk_buff *
-de4x5_alloc_rx_buff(struct device *dev, int index, int len)
+static struct sk_buff *de4x5_alloc_rx_buff(struct device *dev, int index, int len)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- struct sk_buff *p;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ struct sk_buff *p;
-#if !defined(__alpha__) && !defined(__powerpc__) && !defined(DE4X5_DO_MEMCPY)
- struct sk_buff *ret;
- u_long i=0, tmp;
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY)
+ struct sk_buff *ret;
+ u_long i = 0, tmp;
- p = dev_alloc_skb(IEEE802_3_SZ + ALIGN + 2);
- if (!p) return NULL;
+ p = dev_alloc_skb(IEEE802_3_SZ + ALIGN + 2);
+ if (!p)
+ return NULL;
- p->dev = dev;
- tmp = virt_to_bus(p->data);
- i = ((tmp + ALIGN) & ~ALIGN) - tmp;
- skb_reserve(p, i);
- lp->rx_ring[index].buf = tmp + i;
+ p->dev = dev;
+ tmp = virt_to_bus(p->data);
+ i = ((tmp + ALIGN) & ~ALIGN) - tmp;
+ skb_reserve(p, i);
+ lp->rx_ring[index].buf = tmp + i;
- ret = lp->rx_skb[index];
- lp->rx_skb[index] = p;
+ ret = lp->rx_skb[index];
+ lp->rx_skb[index] = p;
- if ((u_long) ret > 1) {
- skb_put(ret, len);
- }
-
- return ret;
+ if ((u_long) ret > 1) {
+ skb_put(ret, len);
+ }
+ return ret;
#else
- if (lp->state != OPEN) return (struct sk_buff *)1; /* Fake out the open */
-
- p = dev_alloc_skb(len + 2);
- if (!p) return NULL;
-
- p->dev = dev;
- skb_reserve(p, 2); /* Align */
- if (index < lp->rx_old) { /* Wrapped buffer */
- short tlen = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ;
- memcpy(skb_put(p,tlen),
- bus_to_virt(le32_to_cpu(lp->rx_ring[lp->rx_old].buf)),tlen);
- memcpy(skb_put(p,len-tlen),
- bus_to_virt(le32_to_cpu(lp->rx_ring[0].buf)), len-tlen);
- } else { /* Linear buffer */
- memcpy(skb_put(p,len),
- bus_to_virt(le32_to_cpu(lp->rx_ring[lp->rx_old].buf)),len);
- }
-
- return p;
+ if (lp->state != OPEN)
+ return (struct sk_buff *) 1; /* Fake out the open */
+
+ p = dev_alloc_skb(len + 2);
+ if (!p)
+ return NULL;
+
+ p->dev = dev;
+ skb_reserve(p, 2); /* Align */
+ if (index < lp->rx_old) { /* Wrapped buffer */
+ short tlen = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ;
+ memcpy(skb_put(p, tlen),
+ bus_to_virt(le32_to_cpu(lp->rx_ring[lp->rx_old].buf)), tlen);
+ memcpy(skb_put(p, len - tlen),
+ bus_to_virt(le32_to_cpu(lp->rx_ring[0].buf)), len - tlen);
+ } else { /* Linear buffer */
+ memcpy(skb_put(p, len),
+ bus_to_virt(le32_to_cpu(lp->rx_ring[lp->rx_old].buf)), len);
+ }
+
+ return p;
#endif
}
-static void
-de4x5_free_rx_buffs(struct device *dev)
+static void de4x5_free_rx_buffs(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int i;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ int i;
- for (i=0; i<lp->rxRingSize; i++) {
- if ((u_long) lp->rx_skb[i] > 1) {
- dev_kfree_skb(lp->rx_skb[i], FREE_WRITE);
+ for (i = 0; i < lp->rxRingSize; i++) {
+ if ((u_long) lp->rx_skb[i] > 1) {
+ dev_kfree_skb(lp->rx_skb[i], FREE_WRITE);
+ }
+ lp->rx_ring[i].status = 0;
+ lp->rx_skb[i] = (struct sk_buff *) 1; /* Dummy entry */
}
- lp->rx_ring[i].status = 0;
- lp->rx_skb[i] = (struct sk_buff *)1; /* Dummy entry */
- }
- return;
+ return;
}
-static void
-de4x5_free_tx_buffs(struct device *dev)
+static void de4x5_free_tx_buffs(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int i;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ int i;
- for (i=0; i<lp->txRingSize; i++) {
- if (lp->tx_skb[i]) {
- dev_kfree_skb(lp->tx_skb[i], FREE_WRITE);
- lp->tx_skb[i] = NULL;
+ for (i = 0; i < lp->txRingSize; i++) {
+ if (lp->tx_skb[i]) {
+ dev_kfree_skb(lp->tx_skb[i], FREE_WRITE);
+ lp->tx_skb[i] = NULL;
+ }
+ lp->tx_ring[i].status = 0;
}
- lp->tx_ring[i].status = 0;
- }
- /* Unload the locally queued packets */
- while (lp->cache.skb) {
- dev_kfree_skb(de4x5_get_cache(dev), FREE_WRITE);
- }
+ /* Unload the locally queued packets */
+ while (lp->cache.skb) {
+ dev_kfree_skb(de4x5_get_cache(dev), FREE_WRITE);
+ }
- return;
+ return;
}
/*
@@ -3207,349 +3559,358 @@ de4x5_free_tx_buffs(struct device *dev)
** the hardware and software and make any media probes using a loopback
** packet meaningful.
*/
-static void
-de4x5_save_skbs(struct device *dev)
+static void de4x5_save_skbs(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- s32 omr;
-
- if (!lp->cache.save_cnt) {
- STOP_DE4X5;
- de4x5_tx(dev); /* Flush any sent skb's */
- de4x5_free_tx_buffs(dev);
- de4x5_cache_state(dev, DE4X5_SAVE_STATE);
- de4x5_sw_reset(dev);
- de4x5_cache_state(dev, DE4X5_RESTORE_STATE);
- lp->cache.save_cnt++;
- START_DE4X5;
- }
-
- return;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 omr;
+
+ if (!lp->cache.save_cnt) {
+ STOP_DE4X5;
+ de4x5_tx(dev); /* Flush any sent skb's */
+ de4x5_free_tx_buffs(dev);
+ de4x5_cache_state(dev, DE4X5_SAVE_STATE);
+ de4x5_sw_reset(dev);
+ de4x5_cache_state(dev, DE4X5_RESTORE_STATE);
+ lp->cache.save_cnt++;
+ START_DE4X5;
+ }
+ return;
}
-static void
-de4x5_restore_skbs(struct device *dev)
+static void de4x5_restore_skbs(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- int i;
- s32 omr;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ int i;
+ s32 omr;
- if (lp->cache.save_cnt) {
- STOP_DE4X5;
- outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA);
- outl(virt_to_bus(lp->tx_ring), DE4X5_TRBA);
-
- lp->rx_new = lp->rx_old = 0;
- lp->tx_new = lp->tx_old = 0;
-
- for (i = 0; i < lp->rxRingSize; i++) {
- lp->rx_ring[i].status = cpu_to_le32(R_OWN);
- }
-
- for (i = 0; i < lp->txRingSize; i++) {
- lp->tx_ring[i].status = cpu_to_le32(0);
+ if (lp->cache.save_cnt) {
+ STOP_DE4X5;
+ outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA);
+ outl(virt_to_bus(lp->tx_ring), DE4X5_TRBA);
+
+ lp->rx_new = lp->rx_old = 0;
+ lp->tx_new = lp->tx_old = 0;
+
+ for (i = 0; i < lp->rxRingSize; i++) {
+ lp->rx_ring[i].status = cpu_to_le32(R_OWN);
+ }
+
+ for (i = 0; i < lp->txRingSize; i++) {
+ lp->tx_ring[i].status = cpu_to_le32(0);
+ }
+
+ barrier();
+ lp->cache.save_cnt--;
+ START_DE4X5;
}
-
- barrier();
- lp->cache.save_cnt--;
- START_DE4X5;
- }
-
- return;
+ return;
}
-static void
-de4x5_cache_state(struct device *dev, int flag)
+static void de4x5_cache_state(struct device *dev, int flag)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
-
- switch(flag) {
- case DE4X5_SAVE_STATE:
- lp->cache.csr0 = inl(DE4X5_BMR);
- lp->cache.csr6 = (inl(DE4X5_OMR) & ~(OMR_ST | OMR_SR));
- lp->cache.csr7 = inl(DE4X5_IMR);
- if (lp->chipset != DC21140) {
- lp->cache.csr13 = inl(DE4X5_SICR);
- lp->cache.csr14 = inl(DE4X5_STRR);
- lp->cache.csr15 = inl(DE4X5_SIGR);
- }
- break;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+
+ switch (flag) {
+ case DE4X5_SAVE_STATE:
+ lp->cache.csr0 = inl(DE4X5_BMR);
+ lp->cache.csr6 = (inl(DE4X5_OMR) & ~(OMR_ST | OMR_SR));
+ lp->cache.csr7 = inl(DE4X5_IMR);
+ break;
- case DE4X5_RESTORE_STATE:
- outl(lp->cache.csr0, DE4X5_BMR);
- outl(lp->cache.csr6, DE4X5_OMR);
- outl(lp->cache.csr7, DE4X5_IMR);
- if (lp->chipset == DC21140) {
- outl(lp->cache.gepc, DE4X5_GEP);
- outl(lp->cache.gep, DE4X5_GEP);
- } else {
- reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14,
- lp->cache.csr15);
+ case DE4X5_RESTORE_STATE:
+ outl(lp->cache.csr0, DE4X5_BMR);
+ outl(lp->cache.csr6, DE4X5_OMR);
+ outl(lp->cache.csr7, DE4X5_IMR);
+ if (lp->chipset == DC21140) {
+ gep_wr(lp->cache.gepc, dev);
+ gep_wr(lp->cache.gep, dev);
+ } else {
+ reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14,
+ lp->cache.csr15);
+ }
+ break;
}
- break;
- }
- return;
+ return;
}
-static void
-de4x5_put_cache(struct device *dev, struct sk_buff *skb)
+static void de4x5_put_cache(struct device *dev, struct sk_buff *skb)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- struct sk_buff *p;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ struct sk_buff *p;
- if (lp->cache.skb) {
- for (p=lp->cache.skb; p->next; p=p->next);
- p->next = skb;
- } else {
- lp->cache.skb = skb;
- }
- skb->next = NULL;
+ if (lp->cache.skb) {
+ for (p = lp->cache.skb; p->next; p = p->next);
+ p->next = skb;
+ } else {
+ lp->cache.skb = skb;
+ }
+ skb->next = NULL;
- return;
+ return;
}
-static void
-de4x5_putb_cache(struct device *dev, struct sk_buff *skb)
+static void de4x5_putb_cache(struct device *dev, struct sk_buff *skb)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- struct sk_buff *p = lp->cache.skb;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ struct sk_buff *p = lp->cache.skb;
- lp->cache.skb = skb;
- skb->next = p;
+ lp->cache.skb = skb;
+ skb->next = p;
- return;
+ return;
}
-static struct sk_buff *
-de4x5_get_cache(struct device *dev)
+static struct sk_buff *de4x5_get_cache(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- struct sk_buff *p = lp->cache.skb;
-
- if (p) {
- lp->cache.skb = p->next;
- p->next = NULL;
- }
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ struct sk_buff *p = lp->cache.skb;
- return p;
+ if (p) {
+ lp->cache.skb = p->next;
+ p->next = NULL;
+ }
+ return p;
}
/*
** Check the Auto Negotiation State. Return OK when a link pass interrupt
** is received and the auto-negotiation status is NWAY OK.
*/
-static int
-test_ans(struct device *dev, s32 irqs, s32 irq_mask, s32 msec)
+static int test_ans(struct device *dev, s32 irqs, s32 irq_mask, s32 msec)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- s32 sts, ans;
-
- if (lp->timeout < 0) {
- lp->timeout = msec/100;
- outl(irq_mask, DE4X5_IMR);
-
- /* clear all pending interrupts */
- sts = inl(DE4X5_STS);
- outl(sts, DE4X5_STS);
- }
-
- ans = inl(DE4X5_SISR) & SISR_ANS;
- sts = inl(DE4X5_STS) & ~TIMER_CB;
-
- if (!(sts & irqs) && (ans ^ ANS_NWOK) && --lp->timeout) {
- sts = 100 | TIMER_CB;
- } else {
- lp->timeout = -1;
- }
-
- return sts;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 sts, ans;
+
+ if (lp->timeout < 0) {
+ lp->timeout = msec / 100;
+ outl(irq_mask, DE4X5_IMR);
+
+ /* clear all pending interrupts */
+ sts = inl(DE4X5_STS);
+ outl(sts, DE4X5_STS);
+ }
+ ans = inl(DE4X5_SISR) & SISR_ANS;
+ sts = inl(DE4X5_STS) & ~TIMER_CB;
+
+ if (!(sts & irqs) && (ans ^ ANS_NWOK) && --lp->timeout) {
+ sts = 100 | TIMER_CB;
+ } else {
+ lp->timeout = -1;
+ }
+
+ return sts;
}
-static void
-de4x5_setup_intr(struct device *dev)
+static void de4x5_setup_intr(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- s32 imr, sts;
-
- if (inl(DE4X5_OMR) & OMR_SR) { /* Only unmask if TX/RX is enabled */
- imr = 0;
- UNMASK_IRQs;
- sts = inl(DE4X5_STS); /* Reset any pending (stale) interrupts */
- outl(sts, DE4X5_STS);
- ENABLE_IRQs;
- }
-
- return;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 imr, sts;
+
+ if (inl(DE4X5_OMR) & OMR_SR) { /* Only unmask if TX/RX is enabled */
+ imr = 0;
+ UNMASK_IRQs;
+ sts = inl(DE4X5_STS); /* Reset any pending (stale) interrupts */
+ outl(sts, DE4X5_STS);
+ ENABLE_IRQs;
+ }
+ return;
}
/*
**
*/
-static void
-reset_init_sia(struct device *dev, s32 sicr, s32 strr, s32 sigr)
+void reset_init_sia(struct device *dev, s32 csr13, s32 csr14, s32 csr15)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
-
- RESET_SIA;
- outl(sigr, DE4X5_SIGR);
- outl(strr, DE4X5_STRR);
- outl(sicr, DE4X5_SICR);
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+
+ RESET_SIA;
+ if (lp->useSROM) {
+ if (lp->ibn == 3) {
+ srom_exec(dev, lp->phy[lp->active].rst);
+ srom_exec(dev, lp->phy[lp->active].gep);
+ outl(1, DE4X5_SICR);
+ return;
+ } else {
+ csr15 = lp->cache.csr15;
+ csr14 = lp->cache.csr14;
+ csr13 = lp->cache.csr13;
+ outl(csr15 | lp->cache.gepc, DE4X5_SIGR);
+ outl(csr15 | lp->cache.gep, DE4X5_SIGR);
+ }
+ } else {
+ outl(csr15, DE4X5_SIGR);
+ }
+ outl(csr14, DE4X5_STRR);
+ outl(csr13, DE4X5_SICR);
+
+ de4x5_ms_delay(10);
- return;
+ return;
}
/*
** Create a loopback ethernet packet
*/
-static void
-create_packet(struct device *dev, char *frame, int len)
+static void create_packet(struct device *dev, char *frame, int len)
{
- int i;
- char *buf = frame;
-
- for (i=0; i<ETH_ALEN; i++) { /* Use this source address */
- *buf++ = dev->dev_addr[i];
- }
- for (i=0; i<ETH_ALEN; i++) { /* Use this destination address */
- *buf++ = dev->dev_addr[i];
- }
-
- *buf++ = 0; /* Packet length (2 bytes) */
- *buf++ = 1;
-
- return;
+ int i;
+ char *buf = frame;
+
+ for (i = 0; i < ETH_ALEN; i++) { /* Use this source address */
+ *buf++ = dev->dev_addr[i];
+ }
+ for (i = 0; i < ETH_ALEN; i++) { /* Use this destination address */
+ *buf++ = dev->dev_addr[i];
+ }
+
+ *buf++ = 0; /* Packet length (2 bytes) */
+ *buf++ = 1;
+
+ return;
}
/*
** Known delay in microseconds
*/
-static void
-de4x5_us_delay(u32 usec)
+static void de4x5_us_delay(u32 usec)
{
- udelay(usec);
-
- return;
+ udelay(usec);
+
+ return;
}
/*
** Known delay in milliseconds, in millisecond steps.
*/
-static void
-de4x5_ms_delay(u32 msec)
+static void de4x5_ms_delay(u32 msec)
{
- u_int i;
-
- for (i=0; i<msec; i++) {
- de4x5_us_delay(1000);
- }
-
- return;
+ u_int i;
+
+ for (i = 0; i < msec; i++) {
+ de4x5_us_delay(1000);
+ }
+
+ return;
}
/*
** Look for a particular board name in the EISA configuration space
*/
-static int
-EISA_signature(char *name, s32 eisa_id)
+static int EISA_signature(char *name, s32 eisa_id)
{
- c_char *signatures[] = DE4X5_SIGNATURE;
- char ManCode[DE4X5_STRLEN];
- union {
- s32 ID;
- char Id[4];
- } Eisa;
- int i, status = 0, siglen = sizeof(signatures)/sizeof(c_char *);
-
- *name = '\0';
- Eisa.ID = inl(eisa_id);
-
- ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40);
- ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40);
- ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30);
- ManCode[3]=((Eisa.Id[2]&0x0f)+0x30);
- ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30);
- ManCode[5]='\0';
-
- for (i=0;i<siglen;i++) {
- if (strstr(ManCode, signatures[i]) != NULL) {
- strcpy(name,ManCode);
- status = 1;
- break;
+ c_char *signatures[] = DE4X5_SIGNATURE;
+ char ManCode[DE4X5_STRLEN];
+ union {
+ s32 ID;
+ char Id[4];
+ } Eisa;
+ int i, status = 0, siglen = sizeof(signatures) / sizeof(c_char *);
+
+ *name = '\0';
+ Eisa.ID = inl(eisa_id);
+
+ ManCode[0] = (((Eisa.Id[0] >> 2) & 0x1f) + 0x40);
+ ManCode[1] = (((Eisa.Id[1] & 0xe0) >> 5) + ((Eisa.Id[0] & 0x03) << 3) + 0x40);
+ ManCode[2] = (((Eisa.Id[2] >> 4) & 0x0f) + 0x30);
+ ManCode[3] = ((Eisa.Id[2] & 0x0f) + 0x30);
+ ManCode[4] = (((Eisa.Id[3] >> 4) & 0x0f) + 0x30);
+ ManCode[5] = '\0';
+
+ for (i = 0; i < siglen; i++) {
+ if (strstr(ManCode, signatures[i]) != NULL) {
+ strcpy(name, ManCode);
+ status = 1;
+ break;
+ }
}
- }
-
- return status; /* return the device name string */
+
+ return status; /* return the device name string */
}
/*
** Look for a particular board name in the PCI configuration space
*/
-static int
-PCI_signature(char *name, struct bus_type *lp)
+static int PCI_signature(char *name, struct bus_type *lp)
{
- c_char *de4x5_signatures[] = DE4X5_SIGNATURE;
- int i, status = 0, siglen = sizeof(de4x5_signatures)/sizeof(c_char *);
-
- if (lp->chipset == DC21040) {
- strcpy(name, "DE434/5");
+ c_char *de4x5_signatures[] = DE4X5_SIGNATURE;
+ int i, status = 0, siglen = sizeof(de4x5_signatures) / sizeof(c_char *);
+
+ if (lp->chipset == DC21040) {
+ strcpy(name, "DE434/5");
+ return status;
+ } else { /* Search for a DEC name in the SROM */
+ int i = *((char *) &lp->srom + 19) * 3;
+ strncpy(name, (char *) &lp->srom + 26 + i, 8);
+ }
+ name[8] = '\0';
+ for (i = 0; i < siglen; i++) {
+ if (strstr(name, de4x5_signatures[i]) != NULL)
+ break;
+ }
+ if (i == siglen) {
+ if (dec_only) {
+ *name = '\0';
+ } else { /* Use chip name to avoid confusion */
+ strcpy(name, (((lp->chipset == DC21040) ? "DC21040" :
+ ((lp->chipset == DC21041) ? "DC21041" :
+ ((lp->chipset == DC21140) ? "DC21140" :
+ ((lp->chipset == DC21142) ? "DC21142" :
+ ((lp->chipset == DC21143) ? "DC21143" : "UNKNOWN"
+ )))))));
+ }
+ if (lp->chipset != DC21041) {
+ useSROM = TRUE; /* card is not recognisably DEC */
+ }
+ } else if ((lp->chipset & ~0x00ff) == DC2114x) {
+ useSROM = TRUE;
+ }
+
return status;
- } else { /* Search for a DEC name in the SROM */
- int i = *((char *)&lp->srom + 19) * 3;
- strncpy(name, (char *)&lp->srom + 26 + i, 8);
- }
- name[8] = '\0';
- for (i=0; i<siglen; i++) {
- if (strstr(name,de4x5_signatures[i])!=NULL) break;
- }
- if (i == siglen) {
- if (dec_only) {
- *name = '\0';
- } else { /* Use chip name to avoid confusion */
- strcpy(name, (((lp->chipset == DC21040) ? "DC21040" :
- ((lp->chipset == DC21041) ? "DC21041" :
- ((lp->chipset == DC21140) ? "DC21140" :
- ((lp->chipset == DC21142) ? "DC21142" :
- ((lp->chipset == DC21143) ? "DC21143" : "UNKNOWN"
- )))))));
- }
- if (lp->chipset != DC21041) {
- useSROM = TRUE; /* card is not recognisably DEC */
- }
- }
-
- return status;
}
/*
** Set up the Ethernet PROM counter to the start of the Ethernet address on
** the DC21040, else read the SROM for the other chips.
+** The SROM may not be present in a multi-MAC card, so first read the
+** MAC address and check for a bad address. If there is a bad one then exit
+** immediately with the prior srom contents intact (the h/w address will
+** be fixed up later).
*/
-static void
-DevicePresent(u_long aprom_addr)
+static void DevicePresent(u_long aprom_addr)
{
- int i;
- struct bus_type *lp = &bus;
-
- if (lp->chipset == DC21040) {
- outl(0, aprom_addr); /* Reset Ethernet Address ROM Pointer */
- } else { /* Read new srom */
- u_short tmp, *p = (short *)&lp->srom;
- for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) {
- tmp = srom_rd(aprom_addr, i);
- *p++ = le16_to_cpu(tmp);
- }
- de4x5_dbg_srom((struct de4x5_srom *)&lp->srom);
- }
-
- return;
+ int i, j=0;
+ struct bus_type *lp = &bus;
+
+ if (lp->chipset == DC21040) {
+ outl(0, aprom_addr); /* Reset Ethernet Address ROM Pointer */
+ } else { /* Read new srom */
+ u_short tmp, *p = (short *)((char *)&lp->srom + SROM_HWADD);
+ for (i=0; i<(ETH_ALEN>>1); i++) {
+ tmp = srom_rd(aprom_addr, (SROM_HWADD>>1) + i);
+ *p = le16_to_cpu(tmp);
+ j += *p++;
+ }
+ if ((j == 0) || (j == 0x2fffd)) {
+ return;
+ }
+
+ p=(short *)&lp->srom;
+ for (i = 0; i < (sizeof(struct de4x5_srom) >> 1); i++) {
+ tmp = srom_rd(aprom_addr, i);
+ *p++ = le16_to_cpu(tmp);
+ }
+ de4x5_dbg_srom((struct de4x5_srom *) &lp->srom);
+ }
+
+ return;
}
/*
@@ -3558,303 +3919,302 @@ DevicePresent(u_long aprom_addr)
** as one or more of the bytes. Only the last 3 bytes should be checked
** as the first three are invariant - assigned to an organisation.
*/
-static int
-get_hw_addr(struct device *dev)
+static int get_hw_addr(struct device *dev)
{
- u_long iobase = dev->base_addr;
- int broken, i, k, tmp, status = 0;
- u_short j,chksum;
- struct bus_type *lp = &bus;
-
- broken = de4x5_bad_srom(lp);
- for (i=0,k=0,j=0;j<3;j++) {
- k <<= 1;
- if (k > 0xffff) k-=0xffff;
-
+ u_long iobase = dev->base_addr;
+ int broken, i, k, tmp, status = 0;
+ u_short j, chksum;
+ struct bus_type *lp = &bus;
+
+ broken = de4x5_bad_srom(lp);
+
+ for (i = 0, k = 0, j = 0; j < 3; j++) {
+ k <<= 1;
+ if (k > 0xffff)
+ k -= 0xffff;
+
+ if (lp->bus == PCI) {
+ if (lp->chipset == DC21040) {
+ while ((tmp = inl(DE4X5_APROM)) < 0);
+ k += (u_char) tmp;
+ dev->dev_addr[i++] = (u_char) tmp;
+ while ((tmp = inl(DE4X5_APROM)) < 0);
+ k += (u_short) (tmp << 8);
+ dev->dev_addr[i++] = (u_char) tmp;
+ } else if (!broken) {
+ dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i];
+ i++;
+ dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i];
+ i++;
+ } else if ((broken == SMC) || (broken == ACCTON)) {
+ dev->dev_addr[i] = *((u_char *) & lp->srom + i);
+ i++;
+ dev->dev_addr[i] = *((u_char *) & lp->srom + i);
+ i++;
+ }
+ } else {
+ k += (u_char) (tmp = inb(EISA_APROM));
+ dev->dev_addr[i++] = (u_char) tmp;
+ k += (u_short) ((tmp = inb(EISA_APROM)) << 8);
+ dev->dev_addr[i++] = (u_char) tmp;
+ }
+
+ if (k > 0xffff)
+ k -= 0xffff;
+ }
+ if (k == 0xffff)
+ k = 0;
+
if (lp->bus == PCI) {
- if (lp->chipset == DC21040) {
- while ((tmp = inl(DE4X5_APROM)) < 0);
- k += (u_char) tmp;
- dev->dev_addr[i++] = (u_char) tmp;
- while ((tmp = inl(DE4X5_APROM)) < 0);
- k += (u_short) (tmp << 8);
- dev->dev_addr[i++] = (u_char) tmp;
- } else if (!broken) {
- dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++;
- dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++;
- } else if ((broken == SMC) || (broken == ACCTON)) {
- dev->dev_addr[i] = *((u_char *)&lp->srom + i); i++;
- dev->dev_addr[i] = *((u_char *)&lp->srom + i); i++;
- }
+ if (lp->chipset == DC21040) {
+ while ((tmp = inl(DE4X5_APROM)) < 0);
+ chksum = (u_char) tmp;
+ while ((tmp = inl(DE4X5_APROM)) < 0);
+ chksum |= (u_short) (tmp << 8);
+ if ((k != chksum) && (dec_only))
+ status = -1;
+ }
} else {
- k += (u_char) (tmp = inb(EISA_APROM));
- dev->dev_addr[i++] = (u_char) tmp;
- k += (u_short) ((tmp = inb(EISA_APROM)) << 8);
- dev->dev_addr[i++] = (u_char) tmp;
+ chksum = (u_char) inb(EISA_APROM);
+ chksum |= (u_short) (inb(EISA_APROM) << 8);
+ if ((k != chksum) && (dec_only))
+ status = -1;
}
-
- if (k > 0xffff) k-=0xffff;
- }
- if (k == 0xffff) k=0;
-
- if (lp->bus == PCI) {
- if (lp->chipset == DC21040) {
- while ((tmp = inl(DE4X5_APROM)) < 0);
- chksum = (u_char) tmp;
- while ((tmp = inl(DE4X5_APROM)) < 0);
- chksum |= (u_short) (tmp << 8);
- if ((k != chksum) && (dec_only)) status = -1;
- }
- } else {
- chksum = (u_char) inb(EISA_APROM);
- chksum |= (u_short) (inb(EISA_APROM) << 8);
- if ((k != chksum) && (dec_only)) status = -1;
- }
- /* If possible, try to fix a broken card - SMC only so far */
- srom_repair(dev, broken);
+ /* If possible, try to fix a broken card - SMC only so far */
+ srom_repair(dev, broken);
- /* Test for a bad enet address */
- status = test_bad_enet(dev, status);
+ /* Test for a bad enet address */
+ status = test_bad_enet(dev, status);
- return status;
+ return status;
}
/*
** Test for enet addresses in the first 32 bytes. The built-in strncmp
** didn't seem to work here...?
*/
-static int
-de4x5_bad_srom(struct bus_type *lp)
+static int de4x5_bad_srom(struct bus_type *lp)
{
- int i, status = 0;
-
- for (i=0; i<sizeof(enet_det)/ETH_ALEN; i++) {
- if (!de4x5_strncmp((char *)&lp->srom, (char *)&enet_det[i], 3) &&
- !de4x5_strncmp((char *)&lp->srom+0x10, (char *)&enet_det[i], 3)) {
- if (i == 0) {
- status = SMC;
- } else if (i == 1) {
- status = ACCTON;
- }
- break;
+ int i, status = 0;
+
+ for (i = 0; i < sizeof(enet_det) / ETH_ALEN; i++) {
+ if (!de4x5_strncmp((char *) &lp->srom, (char *) &enet_det[i], 3) &&
+ !de4x5_strncmp((char *) &lp->srom + 0x10, (char *) &enet_det[i], 3)) {
+ if (i == 0) {
+ status = SMC;
+ } else if (i == 1) {
+ status = ACCTON;
+ }
+ break;
+ }
}
- }
- return status;
+ return status;
}
-static int
-de4x5_strncmp(char *a, char *b, int n)
+static int de4x5_strncmp(char *a, char *b, int n)
{
- int ret=0;
+ int ret = 0;
- for (;n && !ret;n--) {
- ret = *a++ - *b++;
- }
+ for (; n && !ret; n--) {
+ ret = *a++ - *b++;
+ }
- return ret;
+ return ret;
}
-static void
-srom_repair(struct device *dev, int card)
+static void srom_repair(struct device *dev, int card)
{
- struct bus_type *lp = &bus;
-
- switch(card) {
- case SMC:
- memset((char *)&bus.srom, 0, sizeof(struct de4x5_srom));
- memcpy(lp->srom.ieee_addr, (char *)dev->dev_addr, ETH_ALEN);
- memcpy(lp->srom.info, (char *)&srom_repair_info[SMC-1], 100);
- useSROM = TRUE;
- break;
- }
-
- return;
+ struct bus_type *lp = &bus;
+
+ switch (card) {
+ case SMC:
+ memset((char *) &bus.srom, 0, sizeof(struct de4x5_srom));
+ memcpy(lp->srom.ieee_addr, (char *) dev->dev_addr, ETH_ALEN);
+ memcpy(lp->srom.info, (char *) &srom_repair_info[SMC - 1], 100);
+ useSROM = TRUE;
+ break;
+ }
+
+ return;
}
-static int
-test_bad_enet(struct device *dev, int status)
+/*
+** Assume that the irq's do not follow the PCI spec - this is seems
+** to be true so far (2 for 2).
+*/
+static int test_bad_enet(struct device *dev, int status)
{
- struct bus_type *lp = &bus;
- int i, tmp;
-
- for (tmp=0,i=0; i<ETH_ALEN; i++) tmp += (u_char)dev->dev_addr[i];
- if ((tmp == 0) || (tmp == 0x5fa)) {
- if ((lp->chipset == last.chipset) &&
- (lp->bus_num == last.bus) && (lp->bus_num > 0)) {
- for (i=0; i<ETH_ALEN; i++) dev->dev_addr[i] = last.addr[i];
- for (i=ETH_ALEN-1; i>2; --i) {
- dev->dev_addr[i] += 1;
- if (dev->dev_addr[i] != 0) break;
- }
- for (i=0; i<ETH_ALEN; i++) last.addr[i] = dev->dev_addr[i];
- if (((*((int *)dev->dev_addr) & 0x00ffffff) == 0x95c000) &&
- (lp->chipset == DC21040)) {
- dev->irq = last.irq;
- }
- status = 0;
- }
- } else if (!status) {
- last.chipset = lp->chipset;
- last.bus = lp->bus_num;
- last.irq = dev->irq;
- for (i=0; i<ETH_ALEN; i++) last.addr[i] = dev->dev_addr[i];
- }
-
- return status;
+ struct bus_type *lp = &bus;
+ int i, tmp;
+
+ for (tmp = 0, i = 0; i < ETH_ALEN; i++)
+ tmp += (u_char) dev->dev_addr[i];
+ if ((tmp == 0) || (tmp == 0x5fa)) {
+ if ((lp->chipset == last.chipset) &&
+ (lp->bus_num == last.bus) && (lp->bus_num > 0)) {
+ for (i = 0; i < ETH_ALEN; i++)
+ dev->dev_addr[i] = last.addr[i];
+ for (i = ETH_ALEN - 1; i > 2; --i) {
+ dev->dev_addr[i] += 1;
+ if (dev->dev_addr[i] != 0)
+ break;
+ }
+ for (i = 0; i < ETH_ALEN; i++)
+ last.addr[i] = dev->dev_addr[i];
+ dev->irq = last.irq;
+
+ status = 0;
+ }
+ } else if (!status) {
+ last.chipset = lp->chipset;
+ last.bus = lp->bus_num;
+ last.irq = dev->irq;
+ for (i = 0; i < ETH_ALEN; i++)
+ last.addr[i] = dev->dev_addr[i];
+ }
+ return status;
}
/*
** SROM Read
*/
-static short
-srom_rd(u_long addr, u_char offset)
+static short srom_rd(u_long addr, u_char offset)
{
- sendto_srom(SROM_RD | SROM_SR, addr);
-
- srom_latch(SROM_RD | SROM_SR | DT_CS, addr);
- srom_command(SROM_RD | SROM_SR | DT_IN | DT_CS, addr);
- srom_address(SROM_RD | SROM_SR | DT_CS, addr, offset);
-
- return srom_data(SROM_RD | SROM_SR | DT_CS, addr);
+ sendto_srom(SROM_RD | SROM_SR, addr);
+
+ srom_latch(SROM_RD | SROM_SR | DT_CS, addr);
+ srom_command(SROM_RD | SROM_SR | DT_IN | DT_CS, addr);
+ srom_address(SROM_RD | SROM_SR | DT_CS, addr, offset);
+
+ return srom_data(SROM_RD | SROM_SR | DT_CS, addr);
}
-static void
-srom_latch(u_int command, u_long addr)
+static void srom_latch(u_int command, u_long addr)
{
- sendto_srom(command, addr);
- sendto_srom(command | DT_CLK, addr);
- sendto_srom(command, addr);
-
- return;
+ sendto_srom(command, addr);
+ sendto_srom(command | DT_CLK, addr);
+ sendto_srom(command, addr);
+
+ return;
}
-static void
-srom_command(u_int command, u_long addr)
+static void srom_command(u_int command, u_long addr)
{
- srom_latch(command, addr);
- srom_latch(command, addr);
- srom_latch((command & 0x0000ff00) | DT_CS, addr);
-
- return;
+ srom_latch(command, addr);
+ srom_latch(command, addr);
+ srom_latch((command & 0x0000ff00) | DT_CS, addr);
+
+ return;
}
-static void
-srom_address(u_int command, u_long addr, u_char offset)
+static void srom_address(u_int command, u_long addr, u_char offset)
{
- int i;
- char a;
-
- a = (char)(offset << 2);
- for (i=0; i<6; i++, a <<= 1) {
- srom_latch(command | ((a < 0) ? DT_IN : 0), addr);
- }
- de4x5_us_delay(1);
-
- i = (getfrom_srom(addr) >> 3) & 0x01;
- if (i != 0) {
- printk("Bad SROM address phase.....\n");
- }
-
- return;
+ int i;
+ char a;
+
+ a = (char) (offset << 2);
+ for (i = 0; i < 6; i++, a <<= 1) {
+ srom_latch(command | ((a < 0) ? DT_IN : 0), addr);
+ }
+ de4x5_us_delay(1);
+
+ i = (getfrom_srom(addr) >> 3) & 0x01;
+ return;
}
-static short
-srom_data(u_int command, u_long addr)
+static short srom_data(u_int command, u_long addr)
{
- int i;
- short word = 0;
- s32 tmp;
-
- for (i=0; i<16; i++) {
- sendto_srom(command | DT_CLK, addr);
- tmp = getfrom_srom(addr);
- sendto_srom(command, addr);
-
- word = (word << 1) | ((tmp >> 3) & 0x01);
- }
-
- sendto_srom(command & 0x0000ff00, addr);
-
- return word;
+ int i;
+ short word = 0;
+ s32 tmp;
+
+ for (i = 0; i < 16; i++) {
+ sendto_srom(command | DT_CLK, addr);
+ tmp = getfrom_srom(addr);
+ sendto_srom(command, addr);
+
+ word = (word << 1) | ((tmp >> 3) & 0x01);
+ }
+
+ sendto_srom(command & 0x0000ff00, addr);
+
+ return word;
}
/*
-static void
-srom_busy(u_int command, u_long addr)
-{
+ static void
+ srom_busy(u_int command, u_long addr)
+ {
sendto_srom((command & 0x0000ff00) | DT_CS, addr);
-
+
while (!((getfrom_srom(addr) >> 3) & 0x01)) {
- de4x5_ms_delay(1);
+ de4x5_ms_delay(1);
}
-
+
sendto_srom(command & 0x0000ff00, addr);
-
+
return;
-}
-*/
+ }
+ */
-static void
-sendto_srom(u_int command, u_long addr)
+static void sendto_srom(u_int command, u_long addr)
{
- outl(command, addr);
- udelay(1);
-
- return;
+ outl(command, addr);
+ udelay(1);
+
+ return;
}
-static int
-getfrom_srom(u_long addr)
+static int getfrom_srom(u_long addr)
{
- s32 tmp;
-
- tmp = inl(addr);
- udelay(1);
-
- return tmp;
+ s32 tmp;
+
+ tmp = inl(addr);
+ udelay(1);
+
+ return tmp;
}
-static int
-srom_infoleaf_info(struct device *dev)
+static int srom_infoleaf_info(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int i, count;
- u_char *p;
-
- /* Find the infoleaf decoder function that matches this chipset */
- for (i=0; i<INFOLEAF_SIZE; i++) {
- if (lp->chipset == infoleaf_array[i].chipset) break;
- }
- if (i == INFOLEAF_SIZE) {
- lp->useSROM = FALSE;
- printk("%s: Cannot find correct chipset for SROM decoding!\n",
- dev->name);
- return -ENXIO;
- }
-
- lp->infoleaf_fn = infoleaf_array[i].fn;
-
- /* Find the information offset that this function should use */
- count = *((u_char *)&lp->srom + 19);
- p = (u_char *)&lp->srom + 26;
-
- if (count > 1) {
- for (i=count; i; --i, p+=3) {
- if (lp->device == *p) break;
- }
- if (i == 0) {
- lp->useSROM = FALSE;
- printk("%s: Cannot find correct PCI device [%d] for SROM decoding!\n",
- dev->name, lp->device);
- return -ENXIO;
- }
- }
-
- lp->infoleaf_offset = TWIDDLE(p+1);
-
- return 0;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ int i, count;
+ u_char *p;
+
+ /* Find the infoleaf decoder function that matches this chipset */
+ for (i = 0; i < INFOLEAF_SIZE; i++) {
+ if (lp->chipset == infoleaf_array[i].chipset)
+ break;
+ }
+ if (i == INFOLEAF_SIZE) {
+ lp->useSROM = FALSE;
+ printk("%s: Cannot find correct chipset for SROM decoding!\n",
+ dev->name);
+ return -ENXIO;
+ }
+ lp->infoleaf_fn = infoleaf_array[i].fn;
+
+ /* Find the information offset that this function should use */
+ count = *((u_char *) & lp->srom + 19);
+ p = (u_char *) & lp->srom + 26;
+
+ if (count > 1) {
+ for (i = count; i; --i, p += 3) {
+ if (lp->device == *p)
+ break;
+ }
+ if (i == 0) {
+ lp->useSROM = FALSE;
+ printk("%s: Cannot find correct PCI device [%d] for SROM decoding!\n",
+ dev->name, lp->device);
+ return -ENXIO;
+ }
+ }
+ lp->infoleaf_offset = TWIDDLE(p + 1);
+
+ return 0;
}
/*
@@ -3864,59 +4224,74 @@ srom_infoleaf_info(struct device *dev)
** The info for the MII devices will be valid since the index used
** will follow the discovery process from MII address 1-31 then 0.
*/
-static void
-srom_init(struct device *dev)
+static void srom_init(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset;
- u_char count;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_char *p = (u_char *) & lp->srom + lp->infoleaf_offset;
+ u_char count;
- p+=2;
- if (lp->chipset == DC21140) {
- lp->cache.gepc = (*p++ | GEP_CTRL);
- outl(lp->cache.gepc, DE4X5_GEP);
- }
-
- /* Block count */
- count = *p++;
-
- /* Jump the infoblocks to find types */
- for (;count; --count) {
- if (*p < 128) {
- p += COMPACT_LEN;
- } else if (*(p+1) == 5) {
- type5_infoblock(dev, 1, p);
- p += ((*p & BLOCK_LEN) + 1);
- } else if (*(p+1) == 3) {
- type3_infoblock(dev, 1, p);
- p += ((*p & BLOCK_LEN) + 1);
- } else if (*(p+1) == 1) {
- type1_infoblock(dev, 1, p);
- p += ((*p & BLOCK_LEN) + 1);
- } else {
- p += ((*p & BLOCK_LEN) + 1);
+ p += 2;
+ if (lp->chipset == DC21140) {
+ lp->cache.gepc = (*p++ | GEP_CTRL);
+ gep_wr(lp->cache.gepc, dev);
+ }
+ /* Block count */
+ count = *p++;
+
+ /* Jump the infoblocks to find types */
+ for (; count; --count) {
+ if (*p < 128) {
+ p += COMPACT_LEN;
+ } else if (*(p + 1) == 5) {
+ type5_infoblock(dev, 1, p);
+ p += ((*p & BLOCK_LEN) + 1);
+ } else if (*(p+1) == 4) {
+ p += ((*p & BLOCK_LEN) + 1);
+ } else if (*(p + 1) == 3) {
+ type3_infoblock(dev, 1, p);
+ p += ((*p & BLOCK_LEN) + 1);
+ } else if (*(p+1) == 2) {
+ p += ((*p & BLOCK_LEN) + 1);
+ } else if (*(p + 1) == 1) {
+ type1_infoblock(dev, 1, p);
+ p += ((*p & BLOCK_LEN) + 1);
+ } else {
+ p += ((*p & BLOCK_LEN) + 1);
+ }
}
- }
- return;
+ return;
}
-static void
-srom_exec(struct device *dev, u_char *p)
+/*
+** A generic routine that writes GEP control, data and reset information
+** to the GEP register (21140) or csr15 GEP portion (2114[23]).
+*/
+static void srom_exec(struct device *dev, u_char * p)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- u_char count = (p ? *p++ : 0);
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ u_char count = (p ? *p++ : 0);
+ u_short *w = (u_short *)p;
+
+ if (((lp->ibn != 1) && (lp->ibn != 3) && (lp->ibn != 5)) || !count)
+ return;
+
+ if (lp->chipset != DC21140)
+ RESET_SIA;
+
+ while (count--) {
+ gep_wr(((lp->chipset==DC21140) && (lp->ibn!=5) ?
+ *p++ : TWIDDLE(w++)), dev);
+ udelay(2000); /* 2ms per action */
+ }
- while (count--) {
- if (lp->chipset == DC21140) {
- outl(*p++, DE4X5_GEP);
+ if (lp->chipset != DC21140) {
+ outl(lp->cache.csr14, DE4X5_STRR);
+ outl(lp->cache.csr13, DE4X5_SICR);
}
- udelay(2000); /* 2ms per action */
- }
- return;
+ return;
}
/*
@@ -3924,1075 +4299,1193 @@ srom_exec(struct device *dev, u_char *p)
** unless I implement the DC21041 SROM functions. There's no need
** since the existing code will be satisfactory for all boards.
*/
-static int
-dc21041_infoleaf(struct device *dev)
+static int dc21041_infoleaf(struct device *dev)
{
- return DE4X5_AUTOSENSE_MS;
+ return DE4X5_AUTOSENSE_MS;
}
-static int
-dc21140_infoleaf(struct device *dev)
+static int dc21140_infoleaf(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_char count = 0;
- u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset;
- int next_tick = DE4X5_AUTOSENSE_MS;
-
- /* Read the connection type */
- p+=2;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_char count = 0;
+ u_char *p = (u_char *) & lp->srom + lp->infoleaf_offset;
+ int next_tick = DE4X5_AUTOSENSE_MS;
- /* GEP control */
- lp->cache.gepc = (*p++ | GEP_CTRL);
+ /* Read the connection type */
+ p += 2;
- /* Block count */
- count = *p++;
+ /* GEP control */
+ lp->cache.gepc = (*p++ | GEP_CTRL);
- /* Recursively figure out the info blocks */
- if (*p < 128) {
- next_tick = dc_infoblock[COMPACT](dev, count, p);
- } else {
- next_tick = dc_infoblock[*(p+1)](dev, count, p);
- }
+ /* Block count */
+ count = *p++;
- if (lp->tcount == count) {
- lp->media = NC;
- if (lp->media != lp->c_media) {
- de4x5_dbg_media(dev);
- lp->c_media = lp->media;
+ /* Recursively figure out the info blocks */
+ if (*p < 128) {
+ next_tick = dc_infoblock[COMPACT] (dev, count, p);
+ } else {
+ next_tick = dc_infoblock[*(p + 1)] (dev, count, p);
}
- lp->media = INIT;
- lp->tcount = 0;
- lp->tx_enable = FALSE;
- }
- return next_tick & ~TIMER_CB;
+ if (lp->tcount == count) {
+ lp->media = NC;
+ if (lp->media != lp->c_media) {
+ de4x5_dbg_media(dev);
+ lp->c_media = lp->media;
+ }
+ lp->media = INIT;
+ lp->tcount = 0;
+ lp->tx_enable = FALSE;
+ }
+ return next_tick & ~TIMER_CB;
}
-static int
-dc21142_infoleaf(struct device *dev)
+static int dc21142_infoleaf(struct device *dev)
{
-printk("dc21142_infoleaf()\n");
- return DE4X5_AUTOSENSE_MS;
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_char count = 0;
+ u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset;
+ int next_tick = DE4X5_AUTOSENSE_MS;
+
+ /* Read the connection type */
+ p+=2;
+
+ /* Block count */
+ count = *p++;
+
+ /* Recursively figure out the info blocks */
+ if (*p < 128) {
+ next_tick = dc_infoblock[COMPACT](dev, count, p);
+ } else {
+ next_tick = dc_infoblock[*(p+1)](dev, count, p);
+ }
+
+ if (lp->tcount == count) {
+ lp->media = NC;
+ if (lp->media != lp->c_media) {
+ de4x5_dbg_media(dev);
+ lp->c_media = lp->media;
+ }
+ lp->media = INIT;
+ lp->tcount = 0;
+ lp->tx_enable = FALSE;
+ }
+
+ return next_tick & ~TIMER_CB;
}
-static int
-dc21143_infoleaf(struct device *dev)
+static int dc21143_infoleaf(struct device *dev)
{
-printk("dc21143_infoleaf()\n");
- return DE4X5_AUTOSENSE_MS;
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_char count = 0;
+ u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset;
+ int next_tick = DE4X5_AUTOSENSE_MS;
+
+ /* Read the connection type */
+ p+=2;
+
+ /* Block count */
+ count = *p++;
+
+ /* Recursively figure out the info blocks */
+ if (*p < 128) {
+ next_tick = dc_infoblock[COMPACT](dev, count, p);
+ } else {
+ next_tick = dc_infoblock[*(p+1)](dev, count, p);
+ }
+ if (lp->tcount == count) {
+ lp->media = NC;
+ if (lp->media != lp->c_media) {
+ de4x5_dbg_media(dev);
+ lp->c_media = lp->media;
+ }
+ lp->media = INIT;
+ lp->tcount = 0;
+ lp->tx_enable = FALSE;
+ }
+
+ return next_tick & ~TIMER_CB;
}
/*
** The compact infoblock is only designed for DC21140[A] chips, so
** we'll reuse the dc21140m_autoconf function. Non MII media only.
*/
-static int
-compact_infoblock(struct device *dev, u_char count, u_char *p)
+static int compact_infoblock(struct device *dev, u_char count, u_char * p)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- u_char flags, csr6;
-
- /* Recursively figure out the info blocks */
- if (--count > lp->tcount) {
- if (*(p+COMPACT_LEN) < 128) {
- return dc_infoblock[COMPACT](dev, count, p+COMPACT_LEN);
- } else {
- return dc_infoblock[*(p+COMPACT_LEN+1)](dev, count, p+COMPACT_LEN);
- }
- }
-
- if ((lp->media == INIT) && (lp->timeout < 0)) {
- outl(lp->cache.gepc, DE4X5_GEP);
- lp->infoblock_media = (*p++) & COMPACT_MC;
- lp->cache.gep = *p++;
- csr6 = *p++;
- flags = *p++;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_char flags, csr6;
- lp->asBitValid = (flags & 0x80) ? 0 : -1;
- lp->defMedium = (flags & 0x40) ? -1 : 0;
- lp->asBit = 1 << ((csr6 >> 1) & 0x07);
- lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
- lp->infoblock_csr6 = (csr6 & 0x71) << 18;
- lp->useMII = FALSE;
-
- de4x5_switch_mac_port(dev);
- }
-
- return dc21140m_autoconf(dev);
+ /* Recursively figure out the info blocks */
+ if (--count > lp->tcount) {
+ if (*(p + COMPACT_LEN) < 128) {
+ return dc_infoblock[COMPACT] (dev, count, p + COMPACT_LEN);
+ } else {
+ return dc_infoblock[*(p + COMPACT_LEN + 1)] (dev, count, p + COMPACT_LEN);
+ }
+ }
+ if ((lp->media == INIT) && (lp->timeout < 0)) {
+ lp->ibn = COMPACT;
+ lp->active = 0;
+ gep_wr(lp->cache.gepc, dev);
+ lp->infoblock_media = (*p++) & COMPACT_MC;
+ lp->cache.gep = *p++;
+ csr6 = *p++;
+ flags = *p++;
+
+ lp->asBitValid = (flags & 0x80) ? 0 : -1;
+ lp->defMedium = (flags & 0x40) ? -1 : 0;
+ lp->asBit = 1 << ((csr6 >> 1) & 0x07);
+ lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
+ lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18);
+ lp->useMII = FALSE;
+
+ de4x5_switch_mac_port(dev);
+ }
+ return dc21140m_autoconf(dev);
}
/*
** This block describes non MII media for the DC21140[A] only.
- */
-static int
-type0_infoblock(struct device *dev, u_char count, u_char *p)
+*/
+static int type0_infoblock(struct device *dev, u_char count, u_char * p)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- u_char flags, csr6, len = (*p & BLOCK_LEN)+1;
-
- /* Recursively figure out the info blocks */
- if (--count > lp->tcount) {
- if (*(p+len) < 128) {
- return dc_infoblock[COMPACT](dev, count, p+len);
- } else {
- return dc_infoblock[*(p+len+1)](dev, count, p+len);
- }
- }
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_char flags, csr6, len = (*p & BLOCK_LEN) + 1;
- if ((lp->media == INIT) && (lp->timeout < 0)) {
- outl(lp->cache.gepc, DE4X5_GEP);
- p+=2;
- lp->infoblock_media = (*p++) & BLOCK0_MC;
- lp->cache.gep = *p++;
- csr6 = *p++;
- flags = *p++;
-
- lp->asBitValid = (flags & 0x80) ? 0 : -1;
- lp->defMedium = (flags & 0x40) ? -1 : 0;
- lp->asBit = 1 << ((csr6 >> 1) & 0x07);
- lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
- lp->infoblock_csr6 = (csr6 & 0x71) << 18;
- lp->useMII = FALSE;
-
- de4x5_switch_mac_port(dev);
- }
-
- return dc21140m_autoconf(dev);
+ /* Recursively figure out the info blocks */
+ if (--count > lp->tcount) {
+ if (*(p + len) < 128) {
+ return dc_infoblock[COMPACT] (dev, count, p + len);
+ } else {
+ return dc_infoblock[*(p + len + 1)] (dev, count, p + len);
+ }
+ }
+ if ((lp->media == INIT) && (lp->timeout < 0)) {
+ lp->ibn = 0;
+ lp->active = 0;
+ gep_wr(lp->cache.gepc, dev);
+ p += 2;
+ lp->infoblock_media = (*p++) & BLOCK0_MC;
+ lp->cache.gep = *p++;
+ csr6 = *p++;
+ flags = *p++;
+
+ lp->asBitValid = (flags & 0x80) ? 0 : -1;
+ lp->defMedium = (flags & 0x40) ? -1 : 0;
+ lp->asBit = 1 << ((csr6 >> 1) & 0x07);
+ lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
+ lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18);
+ lp->useMII = FALSE;
+
+ de4x5_switch_mac_port(dev);
+ }
+ return dc21140m_autoconf(dev);
}
/* These functions are under construction! */
-static int
-type1_infoblock(struct device *dev, u_char count, u_char *p)
+static int type1_infoblock(struct device *dev, u_char count, u_char * p)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_char len = (*p & BLOCK_LEN)+1;
-
- /* Recursively figure out the info blocks */
- if (--count > lp->tcount) {
- if (*(p+len) < 128) {
- return dc_infoblock[COMPACT](dev, count, p+len);
- } else {
- return dc_infoblock[*(p+len+1)](dev, count, p+len);
- }
- }
-
- p += 2;
- if (lp->state == INITIALISED) {
- lp->ibn = 1;
- lp->active = *p++;
- lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1);
- lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1);
- lp->phy[lp->active].mc = TWIDDLE(p); p += 2;
- lp->phy[lp->active].ana = TWIDDLE(p); p += 2;
- lp->phy[lp->active].fdx = TWIDDLE(p); p += 2;
- lp->phy[lp->active].ttm = TWIDDLE(p);
- return 0;
- } else if ((lp->media == INIT) && (lp->timeout < 0)) {
- lp->ibn = 1;
- lp->active = *p;
- lp->infoblock_csr6 = OMR_PS | OMR_HBD;
- lp->useMII = TRUE;
- lp->infoblock_media = ANS;
- de4x5_switch_mac_port(dev);
- }
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_char len = (*p & BLOCK_LEN) + 1;
- return dc21140m_autoconf(dev);
+ /* Recursively figure out the info blocks */
+ if (--count > lp->tcount) {
+ if (*(p + len) < 128) {
+ return dc_infoblock[COMPACT] (dev, count, p + len);
+ } else {
+ return dc_infoblock[*(p + len + 1)] (dev, count, p + len);
+ }
+ }
+ p += 2;
+ if (lp->state == INITIALISED) {
+ lp->active = *p++;
+ lp->phy[lp->active].gep = (*p ? p : 0);
+ p += (*p + 1);
+ lp->phy[lp->active].rst = (*p ? p : 0);
+ p += (*p + 1);
+ lp->phy[lp->active].mc = TWIDDLE(p);
+ p += 2;
+ lp->phy[lp->active].ana = TWIDDLE(p);
+ p += 2;
+ lp->phy[lp->active].fdx = TWIDDLE(p);
+ p += 2;
+ lp->phy[lp->active].ttm = TWIDDLE(p);
+ return 0;
+ } else if ((lp->media == INIT) && (lp->timeout < 0)) {
+ lp->ibn = 1;
+ lp->active = *p;
+ lp->infoblock_csr6 = OMR_MII_100;
+ lp->useMII = TRUE;
+ lp->infoblock_media = ANS;
+ lp->media = ANS;
+ de4x5_switch_mac_port(dev);
+ }
+ return dc21140m_autoconf(dev);
}
-static int
-type2_infoblock(struct device *dev, u_char count, u_char *p)
+static int type2_infoblock(struct device *dev, u_char count, u_char * p)
{
- return DE4X5_AUTOSENSE_MS;
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_char len = (*p & BLOCK_LEN)+1;
+
+ /* Recursively figure out the info blocks */
+ if (--count > lp->tcount) {
+ if (*(p+len) < 128) {
+ return dc_infoblock[COMPACT](dev, count, p+len);
+ } else {
+ return dc_infoblock[*(p+len+1)](dev, count, p+len);
+ }
+ }
+
+ if ((lp->media == INIT) && (lp->timeout < 0)) {
+ lp->ibn = 2;
+ lp->active = 0;
+ p += 2;
+ lp->infoblock_media = (*p) & MEDIA_CODE;
+
+ if ((*p++) & EXT_FIELD) {
+ lp->cache.csr13 = TWIDDLE(p); p += 2;
+ lp->cache.csr14 = TWIDDLE(p); p += 2;
+ lp->cache.csr15 = TWIDDLE(p); p += 2;
+ } else {
+ lp->cache.csr13 = CSR13;
+ lp->cache.csr14 = CSR14;
+ lp->cache.csr15 = CSR15;
+ }
+ lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2;
+ lp->cache.gep = ((s32)(TWIDDLE(p)) << 16);
+ lp->infoblock_csr6 = OMR_SIA;
+ lp->useMII = FALSE;
+
+ de4x5_switch_mac_port(dev);
+ }
+
+ return dc2114x_autoconf(dev);
}
-static int
-type3_infoblock(struct device *dev, u_char count, u_char *p)
+static int type3_infoblock(struct device *dev, u_char count, u_char * p)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_char flags, csr6, len = (*p & BLOCK_LEN)+1;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_char len = (*p & BLOCK_LEN)+1;
- /* Recursively figure out the info blocks */
- if (--count > lp->tcount) {
- if (*(p+len) < 128) {
- return dc_infoblock[COMPACT](dev, count, p+len);
- } else {
- return dc_infoblock[*(p+len+1)](dev, count, p+len);
- }
- }
-
- if (lp->state == INITIALISED) {
- lp->ibn = 3; p += 2;
- lp->active = *p++;
- lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1);
- lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1);
- lp->phy[lp->active].mc = TWIDDLE(p); p += 2;
- lp->phy[lp->active].ana = TWIDDLE(p); p += 2;
- lp->phy[lp->active].fdx = TWIDDLE(p); p += 2;
- lp->phy[lp->active].ttm = TWIDDLE(p);
- return 0;
- } else if (lp->media == INIT) {
- p+=2;
- lp->infoblock_media = (*p++) & BLOCK0_MC;
- lp->cache.gep = *p++;
- csr6 = *p++;
- flags = *p++;
-
- lp->asBitValid = (flags & 0x80) ? 0 : -1;
- lp->defMedium = (flags & 0x40) ? -1 : 0;
- lp->asBit = 1 << ((csr6 >> 1) & 0x07);
- lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
- lp->infoblock_csr6 = (csr6 & 0x71) << 18;
- lp->useMII = TRUE;
+ /* Recursively figure out the info blocks */
+ if (--count > lp->tcount) {
+ if (*(p + len) < 128) {
+ return dc_infoblock[COMPACT] (dev, count, p + len);
+ } else {
+ return dc_infoblock[*(p + len + 1)] (dev, count, p + len);
+ }
+ }
- de4x5_switch_mac_port(dev);
- }
+ p += 2;
+ if (lp->state == INITIALISED) {
+ lp->active = *p++;
+ lp->phy[lp->active].gep = (*p ? p : 0);
+ p += (2 * (*p) + 1);
+ lp->phy[lp->active].rst = (*p ? p : 0);
+ p += (2 * (*p) + 1);
+ lp->phy[lp->active].mc = TWIDDLE(p);
+ p += 2;
+ lp->phy[lp->active].ana = TWIDDLE(p);
+ p += 2;
+ lp->phy[lp->active].fdx = TWIDDLE(p);
+ p += 2;
+ lp->phy[lp->active].ttm = TWIDDLE(p);
+ p += 2;
+ lp->phy[lp->active].mci = *p;
+ return 0;
+ } else if ((lp->media == INIT) && (lp->timeout < 0)) {
+ lp->ibn = 3;
+ lp->active = *p;
+ lp->infoblock_csr6 = OMR_MII_100;
+ lp->useMII = TRUE;
+ lp->infoblock_media = ANS;
+
+ de4x5_switch_mac_port(dev);
+ }
- return dc21140m_autoconf(dev);
+ return dc2114x_autoconf(dev);
}
-static int
-type4_infoblock(struct device *dev, u_char count, u_char *p)
+static int type4_infoblock(struct device *dev, u_char count, u_char *p)
{
- return DE4X5_AUTOSENSE_MS;
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_char flags, csr6, len = (*p & BLOCK_LEN)+1;
+
+ /* Recursively figure out the info blocks */
+ if (--count > lp->tcount) {
+ if (*(p+len) < 128) {
+ return dc_infoblock[COMPACT](dev, count, p+len);
+ } else {
+ return dc_infoblock[*(p+len+1)](dev, count, p+len);
+ }
+ }
+
+ if ((lp->media == INIT) && (lp->timeout < 0)) {
+ lp->ibn = 4;
+ lp->active = 0;
+ p += 2;
+ lp->infoblock_media = (*p++) & MEDIA_CODE;
+ lp->cache.csr13 = CSR13; /* Hard coded defaults */
+ lp->cache.csr14 = CSR14;
+ lp->cache.csr15 = CSR15;
+ lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2;
+ lp->cache.gep = ((s32)(TWIDDLE(p)) << 16); p += 2;
+ csr6 = *p++;
+ flags = *p++;
+
+ lp->asBitValid = (flags & 0x80) ? 0 : -1;
+ lp->defMedium = (flags & 0x40) ? -1 : 0;
+ lp->asBit = 1 << ((csr6 >> 1) & 0x07);
+ lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
+ lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18);
+ lp->useMII = FALSE;
+
+ de4x5_switch_mac_port(dev);
+ }
+
+ return dc2114x_autoconf(dev);
}
/*
** This block type provides information for resetting external devices
** (chips) through the General Purpose Register.
*/
-static int
-type5_infoblock(struct device *dev, u_char count, u_char *p)
+static int type5_infoblock(struct device *dev, u_char count, u_char * p)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- u_long iobase = dev->base_addr;
- u_char i, j, len = (*p & BLOCK_LEN)+1;
-
- /* Recursively figure out the info blocks */
- if (--count > lp->tcount) {
- if (*(p+len) < 128) {
- return dc_infoblock[COMPACT](dev, count, p+len);
- } else {
- return dc_infoblock[*(p+len+1)](dev, count, p+len);
- }
- }
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_char len = (*p & BLOCK_LEN)+1;
- /* Must be initializing to run this code */
- if ((lp->state == INITIALISED) || (lp->media == INIT)) {
- p+=2;
- lp->rst = p;
- i = *p++;
- for (j=0;i;--i) {
- if (lp->chipset == DC21140) {
- if (!j) {
- outl(*p++ | GEP_CTRL, DE4X5_GEP);
- j++;
- }
- outl(*p++, DE4X5_GEP);
- } else if (lp->chipset == DC21142) {
- } else if (lp->chipset == DC21143) {
- }
+ /* Recursively figure out the info blocks */
+ if (--count > lp->tcount) {
+ if (*(p + len) < 128) {
+ return dc_infoblock[COMPACT] (dev, count, p + len);
+ } else {
+ return dc_infoblock[*(p + len + 1)] (dev, count, p + len);
+ }
}
-
- }
-
- return DE4X5_AUTOSENSE_MS;
+ /* Must be initializing to run this code */
+ if ((lp->state == INITIALISED) || (lp->media == INIT)) {
+ p += 2;
+ lp->rst = p;
+ srom_exec(dev, lp->rst);
+ }
+ return DE4X5_AUTOSENSE_MS;
}
/*
** MII Read/Write
*/
-static int
-mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr)
+static int mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr)
{
- mii_wdata(MII_PREAMBLE, 2, ioaddr); /* Start of 34 bit preamble... */
- mii_wdata(MII_PREAMBLE, 32, ioaddr); /* ...continued */
- mii_wdata(MII_STRD, 4, ioaddr); /* SFD and Read operation */
- mii_address(phyaddr, ioaddr); /* PHY address to be accessed */
- mii_address(phyreg, ioaddr); /* PHY Register to read */
- mii_ta(MII_STRD, ioaddr); /* Turn around time - 2 MDC */
-
- return mii_rdata(ioaddr); /* Read data */
+ mii_wdata(MII_PREAMBLE, 2, ioaddr); /* Start of 34 bit preamble... */
+ mii_wdata(MII_PREAMBLE, 32, ioaddr); /* ...continued */
+ mii_wdata(MII_STRD, 4, ioaddr); /* SFD and Read operation */
+ mii_address(phyaddr, ioaddr); /* PHY address to be accessed */
+ mii_address(phyreg, ioaddr); /* PHY Register to read */
+ mii_ta(MII_STRD, ioaddr); /* Turn around time - 2 MDC */
+
+ return mii_rdata(ioaddr); /* Read data */
}
-static void
-mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr)
+static void mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr)
{
- mii_wdata(MII_PREAMBLE, 2, ioaddr); /* Start of 34 bit preamble... */
- mii_wdata(MII_PREAMBLE, 32, ioaddr); /* ...continued */
- mii_wdata(MII_STWR, 4, ioaddr); /* SFD and Write operation */
- mii_address(phyaddr, ioaddr); /* PHY address to be accessed */
- mii_address(phyreg, ioaddr); /* PHY Register to write */
- mii_ta(MII_STWR, ioaddr); /* Turn around time - 2 MDC */
- data = mii_swap(data, 16); /* Swap data bit ordering */
- mii_wdata(data, 16, ioaddr); /* Write data */
-
- return;
+ mii_wdata(MII_PREAMBLE, 2, ioaddr); /* Start of 34 bit preamble... */
+ mii_wdata(MII_PREAMBLE, 32, ioaddr); /* ...continued */
+ mii_wdata(MII_STWR, 4, ioaddr); /* SFD and Write operation */
+ mii_address(phyaddr, ioaddr); /* PHY address to be accessed */
+ mii_address(phyreg, ioaddr); /* PHY Register to write */
+ mii_ta(MII_STWR, ioaddr); /* Turn around time - 2 MDC */
+ data = mii_swap(data, 16); /* Swap data bit ordering */
+ mii_wdata(data, 16, ioaddr); /* Write data */
+
+ return;
}
-static int
-mii_rdata(u_long ioaddr)
+static int mii_rdata(u_long ioaddr)
{
- int i;
- s32 tmp = 0;
-
- for (i=0; i<16; i++) {
- tmp <<= 1;
- tmp |= getfrom_mii(MII_MRD | MII_RD, ioaddr);
- }
-
- return tmp;
+ int i;
+ s32 tmp = 0;
+
+ for (i = 0; i < 16; i++) {
+ tmp <<= 1;
+ tmp |= getfrom_mii(MII_MRD | MII_RD, ioaddr);
+ }
+
+ return tmp;
}
-static void
-mii_wdata(int data, int len, u_long ioaddr)
+static void mii_wdata(int data, int len, u_long ioaddr)
{
- int i;
-
- for (i=0; i<len; i++) {
- sendto_mii(MII_MWR | MII_WR, data, ioaddr);
- data >>= 1;
- }
-
- return;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ sendto_mii(MII_MWR | MII_WR, data, ioaddr);
+ data >>= 1;
+ }
+
+ return;
}
-static void
-mii_address(u_char addr, u_long ioaddr)
+static void mii_address(u_char addr, u_long ioaddr)
{
- int i;
-
- addr = mii_swap(addr, 5);
- for (i=0; i<5; i++) {
- sendto_mii(MII_MWR | MII_WR, addr, ioaddr);
- addr >>= 1;
- }
-
- return;
+ int i;
+
+ addr = mii_swap(addr, 5);
+ for (i = 0; i < 5; i++) {
+ sendto_mii(MII_MWR | MII_WR, addr, ioaddr);
+ addr >>= 1;
+ }
+
+ return;
}
-static void
-mii_ta(u_long rw, u_long ioaddr)
+static void mii_ta(u_long rw, u_long ioaddr)
{
- if (rw == MII_STWR) {
- sendto_mii(MII_MWR | MII_WR, 1, ioaddr);
- sendto_mii(MII_MWR | MII_WR, 0, ioaddr);
- } else {
- getfrom_mii(MII_MRD | MII_RD, ioaddr); /* Tri-state MDIO */
- }
-
- return;
+ if (rw == MII_STWR) {
+ sendto_mii(MII_MWR | MII_WR, 1, ioaddr);
+ sendto_mii(MII_MWR | MII_WR, 0, ioaddr);
+ } else {
+ getfrom_mii(MII_MRD | MII_RD, ioaddr); /* Tri-state MDIO */
+ }
+
+ return;
}
-static int
-mii_swap(int data, int len)
+static int mii_swap(int data, int len)
{
- int i, tmp = 0;
-
- for (i=0; i<len; i++) {
- tmp <<= 1;
- tmp |= (data & 1);
- data >>= 1;
- }
-
- return tmp;
+ int i, tmp = 0;
+
+ for (i = 0; i < len; i++) {
+ tmp <<= 1;
+ tmp |= (data & 1);
+ data >>= 1;
+ }
+
+ return tmp;
}
-static void
-sendto_mii(u32 command, int data, u_long ioaddr)
+static void sendto_mii(u32 command, int data, u_long ioaddr)
{
- u32 j;
-
- j = (data & 1) << 17;
- outl(command | j, ioaddr);
- udelay(1);
- outl(command | MII_MDC | j, ioaddr);
- udelay(1);
-
- return;
+ u32 j;
+
+ j = (data & 1) << 17;
+ outl(command | j, ioaddr);
+ udelay(1);
+ outl(command | MII_MDC | j, ioaddr);
+ udelay(1);
+
+ return;
}
-static int
-getfrom_mii(u32 command, u_long ioaddr)
+static int getfrom_mii(u32 command, u_long ioaddr)
{
- outl(command, ioaddr);
- udelay(1);
- outl(command | MII_MDC, ioaddr);
- udelay(1);
-
- return ((inl(ioaddr) >> 19) & 1);
+ outl(command, ioaddr);
+ udelay(1);
+ outl(command | MII_MDC, ioaddr);
+ udelay(1);
+
+ return ((inl(ioaddr) >> 19) & 1);
}
/*
-** Here's 3 ways to calculate the OUI from the ID registers. One's a brain
-** dead approach, 2 aren't (clue: mine isn't!).
+** Here's 3 ways to calculate the OUI from the ID registers.
*/
-static int
-mii_get_oui(u_char phyaddr, u_long ioaddr)
+static int mii_get_oui(u_char phyaddr, u_long ioaddr)
{
/*
- union {
- u_short reg;
- u_char breg[2];
- } a;
- int i, r2, r3, ret=0;*/
- int r2, r3;
-
- /* Read r2 and r3 */
- r2 = mii_rd(MII_ID0, phyaddr, ioaddr);
- r3 = mii_rd(MII_ID1, phyaddr, ioaddr);
- /* SEEQ and Cypress way * /
- / * Shuffle r2 and r3 * /
- a.reg=0;
- r3 = ((r3>>10)|(r2<<6))&0x0ff;
- r2 = ((r2>>2)&0x3fff);
-
- / * Bit reverse r3 * /
- for (i=0;i<8;i++) {
- ret<<=1;
- ret |= (r3&1);
- r3>>=1;
- }
-
- / * Bit reverse r2 * /
- for (i=0;i<16;i++) {
- a.reg<<=1;
- a.reg |= (r2&1);
- r2>>=1;
- }
-
- / * Swap r2 bytes * /
- i=a.breg[0];
- a.breg[0]=a.breg[1];
- a.breg[1]=i;
-
- return ((a.reg<<8)|ret); */ /* SEEQ and Cypress way */
-/* return ((r2<<6)|(u_int)(r3>>10)); */ /* NATIONAL and BROADCOM way */
- return r2; /* (I did it) My way */
+ union {
+ u_short reg;
+ u_char breg[2];
+ } a;
+ int i, r2, r3, ret=0; */
+ int r2, r3;
+
+ /* Read r2 and r3 */
+ r2 = mii_rd(MII_ID0, phyaddr, ioaddr);
+ r3 = mii_rd(MII_ID1, phyaddr, ioaddr);
+ /* SEEQ and Cypress way * /
+ / * Shuffle r2 and r3 * /
+ a.reg=0;
+ r3 = ((r3>>10)|(r2<<6))&0x0ff;
+ r2 = ((r2>>2)&0x3fff);
+
+ / * Bit reverse r3 * /
+ for (i=0;i<8;i++) {
+ ret<<=1;
+ ret |= (r3&1);
+ r3>>=1;
+ }
+
+ / * Bit reverse r2 * /
+ for (i=0;i<16;i++) {
+ a.reg<<=1;
+ a.reg |= (r2&1);
+ r2>>=1;
+ }
+
+ / * Swap r2 bytes * /
+ i=a.breg[0];
+ a.breg[0]=a.breg[1];
+ a.breg[1]=i;
+
+ return ((a.reg<<8)|ret); *//* SEEQ and Cypress way */
+ /* return ((r2<<6)|(u_int)(r3>>10)); *//* NATIONAL and BROADCOM way */
+ return r2; /* (I did it) My way */
}
/*
** The SROM spec forces us to search addresses [1-31 0]. Bummer.
*/
-static int
-mii_get_phy(struct device *dev)
+static int mii_get_phy(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int iobase = dev->base_addr;
- int i, j, k, n, limit=sizeof(phy_info)/sizeof(struct phy_table);
- int id;
-
- lp->active = 0;
- lp->useMII = TRUE;
-
- /* Search the MII address space for possible PHY devices */
- for (n=0, lp->mii_cnt=0, i=1; !((i==1) && (n==1)); i=(++i)%DE4X5_MAX_MII) {
- lp->phy[lp->active].addr = i;
- if (i==0) n++; /* Count cycles */
- while (de4x5_reset_phy(dev)<0) udelay(100);/* Wait for reset */
- id = mii_get_oui(i, DE4X5_MII);
- if ((id == 0) || (id == -1)) continue; /* Valid ID? */
- for (j=0; j<limit; j++) { /* Search PHY table */
- if (id != phy_info[j].id) continue; /* ID match? */
- for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++);
- if (k < DE4X5_MAX_PHY) {
- memcpy((char *)&lp->phy[k],
- (char *)&phy_info[j], sizeof(struct phy_table));
- lp->phy[k].addr = i;
- lp->mii_cnt++;
- lp->active++;
- } else {
- i = DE4X5_MAX_MII; /* Stop the search */
- j = limit;
- }
- }
- }
- lp->active = 0;
- if (lp->phy[0].id) { /* Reset the PHY devices */
- for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++) { /*For each PHY*/
- mii_wr(MII_CR_RST, MII_CR, lp->phy[k].addr, DE4X5_MII);
- while (mii_rd(MII_CR, lp->phy[k].addr, DE4X5_MII) & MII_CR_RST);
-
- de4x5_dbg_mii(dev, k);
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ int i, j, k, n, limit = sizeof(phy_info) / sizeof(struct phy_table);
+ int id;
+
+ lp->active = 0;
+ lp->useMII = TRUE;
+
+ /* Search the MII address space for possible PHY devices */
+ for (n = 0, lp->mii_cnt = 0, i = 1; !((i == 1) && (n == 1)); i = (++i) % DE4X5_MAX_MII) {
+ lp->phy[lp->active].addr = i;
+ if (i == 0)
+ n++; /* Count cycles */
+ while (de4x5_reset_phy(dev) < 0)
+ udelay(100); /* Wait for reset */
+ id = mii_get_oui(i, DE4X5_MII);
+ if ((id == 0) || (id == 65535))
+ continue; /* Valid ID? */
+ for (j = 0; j < limit; j++) { /* Search PHY table */
+ if (id != phy_info[j].id)
+ continue; /* ID match? */
+ for (k = 0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++);
+ if (k < DE4X5_MAX_PHY) {
+ memcpy((char *) &lp->phy[k],
+ (char *) &phy_info[j], sizeof(struct phy_table));
+ lp->phy[k].addr = i;
+ lp->mii_cnt++;
+ lp->active++;
+ } else {
+ goto purgatory; /* Stop the search */
+ }
+ break;
+ }
+ if ((j == limit) && (i < DE4X5_MAX_MII)) {
+ printk("%s: Found MII device not currently supported. Please mail the following dump to\nthe author:\n", dev->name);
+ de4x5_debug |= DEBUG_MII;
+ de4x5_dbg_mii(dev, i);
+ printk("\n");
+ }
}
- }
-
- return lp->mii_cnt;
+ purgatory:
+ lp->active = 0;
+ if (lp->phy[0].id) { /* Reset the PHY devices */
+ for (k = 0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++) { /*For each PHY */
+ mii_wr(MII_CR_RST, MII_CR, lp->phy[k].addr, DE4X5_MII);
+ while (mii_rd(MII_CR, lp->phy[k].addr, DE4X5_MII) & MII_CR_RST);
+
+ de4x5_dbg_mii(dev, k);
+ }
+ }
+ if (!lp->mii_cnt)
+ lp->useMII = FALSE;
+
+ return lp->mii_cnt;
}
-static char *
-build_setup_frame(struct device *dev, int mode)
+static char *build_setup_frame(struct device *dev, int mode)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int i;
- char *pa = lp->setup_frame;
-
- /* Initialise the setup frame */
- if (mode == ALL) {
- memset(lp->setup_frame, 0, SETUP_FRAME_LEN);
- }
-
- if (lp->setup_f == HASH_PERF) {
- for (pa=lp->setup_frame+IMPERF_PA_OFFSET, i=0; i<ETH_ALEN; i++) {
- *(pa + i) = dev->dev_addr[i]; /* Host address */
- if (i & 0x01) pa += 2;
- }
- *(lp->setup_frame + (HASH_TABLE_LEN >> 3) - 3) = 0x80;
- } else {
- for (i=0; i<ETH_ALEN; i++) { /* Host address */
- *(pa + (i&1)) = dev->dev_addr[i];
- if (i & 0x01) pa += 4;
- }
- for (i=0; i<ETH_ALEN; i++) { /* Broadcast address */
- *(pa + (i&1)) = (char) 0xff;
- if (i & 0x01) pa += 4;
- }
- }
-
- return pa; /* Points to the next entry */
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ int i;
+ char *pa = lp->setup_frame;
+
+ /* Initialise the setup frame */
+ if (mode == ALL) {
+ memset(lp->setup_frame, 0, SETUP_FRAME_LEN);
+ }
+ if (lp->setup_f == HASH_PERF) {
+ for (pa = lp->setup_frame + IMPERF_PA_OFFSET, i = 0; i < ETH_ALEN; i++) {
+ *(pa + i) = dev->dev_addr[i]; /* Host address */
+ if (i & 0x01)
+ pa += 2;
+ }
+ *(lp->setup_frame + (HASH_TABLE_LEN >> 3) - 3) = 0x80;
+ } else {
+ for (i = 0; i < ETH_ALEN; i++) { /* Host address */
+ *(pa + (i & 1)) = dev->dev_addr[i];
+ if (i & 0x01)
+ pa += 4;
+ }
+ for (i = 0; i < ETH_ALEN; i++) { /* Broadcast address */
+ *(pa + (i & 1)) = (char) 0xff;
+ if (i & 0x01)
+ pa += 4;
+ }
+ }
+
+ return pa; /* Points to the next entry */
}
-static void
-enable_ast(struct device *dev, u32 time_out)
+static void enable_ast(struct device *dev, u32 time_out)
{
- timeout(dev, (void *)&de4x5_ast, (u_long)dev, time_out);
-
- return;
+ timeout(dev, (void *) &de4x5_ast, (u_long) dev, time_out);
+
+ return;
}
-static void
-disable_ast(struct device *dev)
+static void disable_ast(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-
- del_timer(&lp->timer);
-
- return;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+
+ del_timer(&lp->timer);
+
+ return;
}
-static long
-de4x5_switch_mac_port(struct device *dev)
+static long de4x5_switch_mac_port(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int iobase = dev->base_addr;
- s32 omr;
-
- /* Assert the OMR_PS bit in CSR6 */
- omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR |
- OMR_FDX));
- omr |= lp->infoblock_csr6;
- if (omr & OMR_PS) omr |= OMR_HBD;
- outl(omr, DE4X5_OMR);
-
- /* Soft Reset */
- RESET_DE4X5;
-
- /* Restore the GEP */
- if (lp->chipset == DC21140) {
- outl(lp->cache.gepc, DE4X5_GEP);
- outl(lp->cache.gep, DE4X5_GEP);
- }
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 omr;
+
+ STOP_DE4X5;
- /* Restore CSR6 */
- outl(omr, DE4X5_OMR);
+ /* Assert the OMR_PS bit in CSR6 */
+ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR |
+ OMR_FDX));
+ omr |= lp->infoblock_csr6;
+ if (omr & OMR_PS)
+ omr |= OMR_HBD;
+ outl(omr, DE4X5_OMR);
+
+ /* Soft Reset */
+ RESET_DE4X5;
+
+ /* Restore the GEP - especially for COMPACT and Type 0 Infoblocks */
+ if (lp->chipset == DC21140) {
+ gep_wr(lp->cache.gepc, dev);
+ gep_wr(lp->cache.gep, dev);
+ } else if ((lp->chipset & ~0x0ff) == DC2114x) {
+ reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, lp->cache.csr15);
+ }
+ /* Restore CSR6 */
+ outl(omr, DE4X5_OMR);
- /* Reset CSR8 */
- inl(DE4X5_MFC);
+ /* Reset CSR8 */
+ inl(DE4X5_MFC);
- return omr;
+ return omr;
}
-static void
-timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec)
+static void gep_wr(s32 data, struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int dt;
-
- /* First, cancel any pending timer events */
- del_timer(&lp->timer);
-
- /* Convert msec to ticks */
- dt = (msec * HZ) / 1000;
- if (dt==0) dt=1;
-
- /* Set up timer */
- lp->timer.expires = jiffies + dt;
- lp->timer.function = fn;
- lp->timer.data = data;
- add_timer(&lp->timer);
-
- return;
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+
+ if (lp->chipset == DC21140) {
+ outl(data, DE4X5_GEP);
+ } else if ((lp->chipset & ~0x00ff) == DC2114x) {
+ outl((data<<16) | lp->cache.csr15, DE4X5_SIGR);
+ }
+
+ return;
}
-static void
-yawn(struct device *dev, int state)
+static int gep_rd(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int iobase = dev->base_addr;
-
- if ((lp->chipset == DC21040) || (lp->chipset == DC21140)) return;
-
- if(lp->bus == EISA) {
- switch(state) {
- case WAKEUP:
- outb(WAKEUP, PCI_CFPM);
- de4x5_ms_delay(10);
- break;
-
- case SNOOZE:
- outb(SNOOZE, PCI_CFPM);
- break;
-
- case SLEEP:
- outl(0, DE4X5_SICR);
- outb(SLEEP, PCI_CFPM);
- break;
- }
- } else {
- switch(state) {
- case WAKEUP:
- pcibios_write_config_byte(lp->bus_num, lp->device << 3,
- PCI_CFDA_PSM, WAKEUP);
- de4x5_ms_delay(10);
- break;
-
- case SNOOZE:
- pcibios_write_config_byte(lp->bus_num, lp->device << 3,
- PCI_CFDA_PSM, SNOOZE);
- break;
-
- case SLEEP:
- outl(0, DE4X5_SICR);
- pcibios_write_config_byte(lp->bus_num, lp->device << 3,
- PCI_CFDA_PSM, SLEEP);
- break;
- }
- }
-
- return;
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+
+ if (lp->chipset == DC21140) {
+ return inl(DE4X5_GEP);
+ } else if ((lp->chipset & ~0x00ff) == DC2114x) {
+ return (inl(DE4X5_SIGR) & 0x000fffff);
+ }
+
+ return 0;
}
-static void
-de4x5_dbg_open(struct device *dev)
+static void timeout(struct device *dev, void (*fn) (u_long data), u_long data, u_long msec)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int i;
-
- if (de4x5_debug & DEBUG_OPEN) {
- printk("%s: de4x5 opening with irq %d\n",dev->name,dev->irq);
- printk("\tphysical address: ");
- for (i=0;i<6;i++) {
- printk("%2.2x:",(short)dev->dev_addr[i]);
- }
- printk("\n");
- printk("Descriptor head addresses:\n");
- printk("\t0x%8.8lx 0x%8.8lx\n",(u_long)lp->rx_ring,(u_long)lp->tx_ring);
- printk("Descriptor addresses:\nRX: ");
- for (i=0;i<lp->rxRingSize-1;i++){
- if (i < 3) {
- printk("0x%8.8lx ",(u_long)&lp->rx_ring[i].status);
- }
- }
- printk("...0x%8.8lx\n",(u_long)&lp->rx_ring[i].status);
- printk("TX: ");
- for (i=0;i<lp->txRingSize-1;i++){
- if (i < 3) {
- printk("0x%8.8lx ", (u_long)&lp->tx_ring[i].status);
- }
- }
- printk("...0x%8.8lx\n", (u_long)&lp->tx_ring[i].status);
- printk("Descriptor buffers:\nRX: ");
- for (i=0;i<lp->rxRingSize-1;i++){
- if (i < 3) {
- printk("0x%8.8x ",le32_to_cpu(lp->rx_ring[i].buf));
- }
- }
- printk("...0x%8.8x\n",le32_to_cpu(lp->rx_ring[i].buf));
- printk("TX: ");
- for (i=0;i<lp->txRingSize-1;i++){
- if (i < 3) {
- printk("0x%8.8x ", le32_to_cpu(lp->tx_ring[i].buf));
- }
- }
- printk("...0x%8.8x\n", le32_to_cpu(lp->tx_ring[i].buf));
- printk("Ring size: \nRX: %d\nTX: %d\n",
- (short)lp->rxRingSize,
- (short)lp->txRingSize);
- }
-
- return;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ int dt;
+
+ /* First, cancel any pending timer events */
+ del_timer(&lp->timer);
+
+ /* Convert msec to ticks */
+ dt = (msec * HZ) / 1000;
+ if (dt == 0)
+ dt = 1;
+
+ /* Set up timer */
+ lp->timer.expires = jiffies + dt;
+ lp->timer.function = fn;
+ lp->timer.data = data;
+ add_timer(&lp->timer);
+
+ return;
}
-static void
-de4x5_dbg_mii(struct device *dev, int k)
+static void yawn(struct device *dev, int state)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- int iobase = dev->base_addr;
-
- if (de4x5_debug & DEBUG_MII) {
- printk("\nMII CR: %x\n",mii_rd(MII_CR,lp->phy[k].addr,DE4X5_MII));
- printk("MII SR: %x\n",mii_rd(MII_SR,lp->phy[k].addr,DE4X5_MII));
- printk("MII ID0: %x\n",mii_rd(MII_ID0,lp->phy[k].addr,DE4X5_MII));
- printk("MII ID1: %x\n",mii_rd(MII_ID1,lp->phy[k].addr,DE4X5_MII));
- if (lp->phy[k].id != BROADCOM_T4) {
- printk("MII ANA: %x\n",mii_rd(0x04,lp->phy[k].addr,DE4X5_MII));
- printk("MII ANC: %x\n",mii_rd(0x05,lp->phy[k].addr,DE4X5_MII));
- }
- printk("MII 16: %x\n",mii_rd(0x10,lp->phy[k].addr,DE4X5_MII));
- if (lp->phy[k].id != BROADCOM_T4) {
- printk("MII 17: %x\n",mii_rd(0x11,lp->phy[k].addr,DE4X5_MII));
- printk("MII 18: %x\n",mii_rd(0x12,lp->phy[k].addr,DE4X5_MII));
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+
+ if ((lp->chipset == DC21040) || (lp->chipset == DC21140))
+ return;
+
+ if (lp->bus == EISA) {
+ switch (state) {
+ case WAKEUP:
+ outb(WAKEUP, PCI_CFPM);
+ de4x5_ms_delay(10);
+ break;
+
+ case SNOOZE:
+ outb(SNOOZE, PCI_CFPM);
+ break;
+
+ case SLEEP:
+ outl(0, DE4X5_SICR);
+ outb(SLEEP, PCI_CFPM);
+ break;
+ }
} else {
- printk("MII 20: %x\n",mii_rd(0x14,lp->phy[k].addr,DE4X5_MII));
+ switch (state) {
+ case WAKEUP:
+ pcibios_write_config_byte(lp->bus_num, lp->device << 3,
+ PCI_CFDA_PSM, WAKEUP);
+ de4x5_ms_delay(10);
+ break;
+
+ case SNOOZE:
+ pcibios_write_config_byte(lp->bus_num, lp->device << 3,
+ PCI_CFDA_PSM, SNOOZE);
+ break;
+
+ case SLEEP:
+ outl(0, DE4X5_SICR);
+ pcibios_write_config_byte(lp->bus_num, lp->device << 3,
+ PCI_CFDA_PSM, SLEEP);
+ break;
+ }
}
- }
-
- return;
+
+ return;
}
-static void
-de4x5_dbg_media(struct device *dev)
+static void de4x5_dbg_open(struct device *dev)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-
- if (lp->media != lp->c_media) {
- if (de4x5_debug & DEBUG_MEDIA) {
- if (lp->chipset != DC21140) {
- printk("%s: media is %s\n", dev->name,
- (lp->media == NC ? "unconnected!" :
- (lp->media == TP ? "TP." :
- (lp->media == ANS ? "TP/Nway." :
- (lp->media == BNC ? "BNC." :
- (lp->media == AUI ? "AUI." :
- (lp->media == BNC_AUI ? "BNC/AUI." :
- (lp->media == EXT_SIA ? "EXT SIA." :
- "???."
- ))))))));
- } else {
- printk("%s: mode is %s\n", dev->name,
- (lp->media == NC ? "link down or incompatible connection.":
- (lp->media == _100Mb ? "100Mb/s." :
- (lp->media == _10Mb ? "10Mb/s." :
- "\?\?\?"
- ))));
- }
- }
- lp->c_media = lp->media;
- }
-
- return;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ int i;
+
+ if (de4x5_debug & DEBUG_OPEN) {
+ printk("%s: de4x5 opening with irq %d\n", dev->name, dev->irq);
+ printk("\tphysical address: ");
+ for (i = 0; i < 6; i++) {
+ printk("%2.2x:", (short) dev->dev_addr[i]);
+ }
+ printk("\n");
+ printk("Descriptor head addresses:\n");
+ printk("\t0x%8.8lx 0x%8.8lx\n", (u_long) lp->rx_ring, (u_long) lp->tx_ring);
+ printk("Descriptor addresses:\nRX: ");
+ for (i = 0; i < lp->rxRingSize - 1; i++) {
+ if (i < 3) {
+ printk("0x%8.8lx ", (u_long) & lp->rx_ring[i].status);
+ }
+ }
+ printk("...0x%8.8lx\n", (u_long) & lp->rx_ring[i].status);
+ printk("TX: ");
+ for (i = 0; i < lp->txRingSize - 1; i++) {
+ if (i < 3) {
+ printk("0x%8.8lx ", (u_long) & lp->tx_ring[i].status);
+ }
+ }
+ printk("...0x%8.8lx\n", (u_long) & lp->tx_ring[i].status);
+ printk("Descriptor buffers:\nRX: ");
+ for (i = 0; i < lp->rxRingSize - 1; i++) {
+ if (i < 3) {
+ printk("0x%8.8x ", le32_to_cpu(lp->rx_ring[i].buf));
+ }
+ }
+ printk("...0x%8.8x\n", le32_to_cpu(lp->rx_ring[i].buf));
+ printk("TX: ");
+ for (i = 0; i < lp->txRingSize - 1; i++) {
+ if (i < 3) {
+ printk("0x%8.8x ", le32_to_cpu(lp->tx_ring[i].buf));
+ }
+ }
+ printk("...0x%8.8x\n", le32_to_cpu(lp->tx_ring[i].buf));
+ printk("Ring size: \nRX: %d\nTX: %d\n",
+ (short) lp->rxRingSize,
+ (short) lp->txRingSize);
+ }
+ return;
}
-static void
-de4x5_dbg_srom(struct de4x5_srom *p)
+static void de4x5_dbg_mii(struct device *dev, int k)
{
- int i;
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+
+ if (de4x5_debug & DEBUG_MII) {
+ printk("\nMII CR: %x\n", mii_rd(MII_CR, lp->phy[k].addr, DE4X5_MII));
+ printk("MII SR: %x\n", mii_rd(MII_SR, lp->phy[k].addr, DE4X5_MII));
+ printk("MII ID0: %x\n", mii_rd(MII_ID0, lp->phy[k].addr, DE4X5_MII));
+ printk("MII ID1: %x\n", mii_rd(MII_ID1, lp->phy[k].addr, DE4X5_MII));
+ if (lp->phy[k].id != BROADCOM_T4) {
+ printk("MII ANA: %x\n", mii_rd(0x04, lp->phy[k].addr, DE4X5_MII));
+ printk("MII ANC: %x\n", mii_rd(0x05, lp->phy[k].addr, DE4X5_MII));
+ }
+ printk("MII 16: %x\n", mii_rd(0x10, lp->phy[k].addr, DE4X5_MII));
+ if (lp->phy[k].id != BROADCOM_T4) {
+ printk("MII 17: %x\n", mii_rd(0x11, lp->phy[k].addr, DE4X5_MII));
+ printk("MII 18: %x\n", mii_rd(0x12, lp->phy[k].addr, DE4X5_MII));
+ } else {
+ printk("MII 20: %x\n", mii_rd(0x14, lp->phy[k].addr, DE4X5_MII));
+ }
+ }
+ return;
+}
- if (de4x5_debug & DEBUG_SROM) {
- printk("Sub-system Vendor ID: %04x\n", *((u_short *)p->sub_vendor_id));
- printk("Sub-system ID: %04x\n", *((u_short *)p->sub_system_id));
- printk("ID Block CRC: %02x\n", (u_char)(p->id_block_crc));
- printk("SROM version: %02x\n", (u_char)(p->version));
- printk("# controllers: %02x\n", (u_char)(p->num_controllers));
+static void de4x5_dbg_media(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
- printk("Hardware Address: ");
- for (i=0;i<ETH_ALEN-1;i++) {
- printk("%02x:", (u_char)*(p->ieee_addr+i));
- }
- printk("%02x\n", (u_char)*(p->ieee_addr+i));
- printk("CRC checksum: %04x\n", (u_short)(p->chksum));
- for (i=0; i<64; i++) {
- printk("%3d %04x\n", i<<1, (u_short)*((u_short *)p+i));
+ if (lp->media != lp->c_media) {
+ if (de4x5_debug & DEBUG_MEDIA) {
+ if (lp->chipset != DC21140) {
+ printk("%s: media is %s%s\n", dev->name,
+ (lp->media == NC ? "unconnected, link down or incompatible connection" :
+ (lp->media == TP ? "TP" :
+ (lp->media == ANS ? "TP/Nway" :
+ (lp->media == BNC ? "BNC" :
+ (lp->media == AUI ? "AUI" :
+ (lp->media == BNC_AUI ? "BNC/AUI" :
+ (lp->media == EXT_SIA ? "EXT SIA" :
+ (lp->media == _100Mb ? "100Mb/s" :
+ (lp->media == _10Mb ? "10Mb/s" :
+ "???"
+ ))))))))),
+ (lp->fdx?" full duplex.":"."));
+ }
+ }
+ lp->c_media = lp->media;
}
- }
+ return;
+}
- return;
+static void de4x5_dbg_srom(struct de4x5_srom *p)
+{
+ int i;
+
+ if (de4x5_debug & DEBUG_SROM) {
+ printk("Sub-system Vendor ID: %04x\n", *((u_short *) p->sub_vendor_id));
+ printk("Sub-system ID: %04x\n", *((u_short *) p->sub_system_id));
+ printk("ID Block CRC: %02x\n", (u_char) (p->id_block_crc));
+ printk("SROM version: %02x\n", (u_char) (p->version));
+ printk("# controllers: %02x\n", (u_char) (p->num_controllers));
+
+ printk("Hardware Address: ");
+ for (i = 0; i < ETH_ALEN - 1; i++) {
+ printk("%02x:", (u_char) * (p->ieee_addr + i));
+ }
+ printk("%02x\n", (u_char) * (p->ieee_addr + i));
+ printk("CRC checksum: %04x\n", (u_short) (p->chksum));
+ for (i = 0; i < 64; i++) {
+ printk("%3d %04x\n", i << 1, (u_short) * ((u_short *) p + i));
+ }
+ }
+ return;
}
-static void
-de4x5_dbg_rx(struct sk_buff *skb, int len)
+static void de4x5_dbg_rx(struct sk_buff *skb, int len)
{
- int i, j;
-
- if (de4x5_debug & DEBUG_RX) {
- printk("R: %02x:%02x:%02x:%02x:%02x:%02x <- %02x:%02x:%02x:%02x:%02x:%02x len/SAP:%02x%02x [%d]\n",
- (u_char)skb->data[0],
- (u_char)skb->data[1],
- (u_char)skb->data[2],
- (u_char)skb->data[3],
- (u_char)skb->data[4],
- (u_char)skb->data[5],
- (u_char)skb->data[6],
- (u_char)skb->data[7],
- (u_char)skb->data[8],
- (u_char)skb->data[9],
- (u_char)skb->data[10],
- (u_char)skb->data[11],
- (u_char)skb->data[12],
- (u_char)skb->data[13],
- len);
+ int i, j;
+
if (de4x5_debug & DEBUG_RX) {
- for (j=0; len>0;j+=16, len-=16) {
- printk(" %03x: ",j);
- for (i=0; i<16 && i<len; i++) {
- printk("%02x ",(u_char)skb->data[i+j]);
+ printk("R: %02x:%02x:%02x:%02x:%02x:%02x <- %02x:%02x:%02x:%02x:%02x:%02x len/SAP:%02x%02x [%d]\n",
+ (u_char) skb->data[0],
+ (u_char) skb->data[1],
+ (u_char) skb->data[2],
+ (u_char) skb->data[3],
+ (u_char) skb->data[4],
+ (u_char) skb->data[5],
+ (u_char) skb->data[6],
+ (u_char) skb->data[7],
+ (u_char) skb->data[8],
+ (u_char) skb->data[9],
+ (u_char) skb->data[10],
+ (u_char) skb->data[11],
+ (u_char) skb->data[12],
+ (u_char) skb->data[13],
+ len);
+ if (de4x5_debug & DEBUG_RX) {
+ for (j = 0; len > 0; j += 16, len -= 16) {
+ printk(" %03x: ", j);
+ for (i = 0; i < 16 && i < len; i++) {
+ printk("%02x ", (u_char) skb->data[i + j]);
+ }
+ printk("\n");
+ }
}
- printk("\n");
- }
}
- }
-
- return;
+ return;
}
/*
** Perform IOCTL call functions here. Some are privileged operations and the
-** effective uid is checked in those cases.
+** effective uid is checked in those cases. In the normal course of events
+** this function is only used for my testing.
*/
-static int
-de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+static int de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd)
{
- struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
- struct de4x5_ioctl *ioc = (struct de4x5_ioctl *) &rq->ifr_data;
- u_long iobase = dev->base_addr;
- int i, j, status = 0;
- s32 omr;
- union {
- u8 addr[144];
- u16 sval[72];
- u32 lval[36];
- } tmp;
-
- switch(ioc->cmd) {
- case DE4X5_GET_HWADDR: /* Get the hardware address */
- ioc->len = ETH_ALEN;
- status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len);
- if (status)
- break;
- for (i=0; i<ETH_ALEN; i++) {
- tmp.addr[i] = dev->dev_addr[i];
- }
- copy_to_user(ioc->data, tmp.addr, ioc->len);
-
- break;
- case DE4X5_SET_HWADDR: /* Set the hardware address */
- status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN);
- if (status)
- break;
- status = -EPERM;
- if (!suser())
- break;
- status = 0;
- copy_from_user(tmp.addr, ioc->data, ETH_ALEN);
- for (i=0; i<ETH_ALEN; i++) {
- dev->dev_addr[i] = tmp.addr[i];
- }
- build_setup_frame(dev, PHYS_ADDR_ONLY);
- /* Set up the descriptor and give ownership to the card */
- while (test_and_set_bit(0, (void *)&dev->tbusy) != 0);/* Wait for lock to free*/
- load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET |
- SETUP_FRAME_LEN, NULL);
- lp->tx_new = (++lp->tx_new) % lp->txRingSize;
- outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */
- dev->tbusy = 0; /* Unlock the TX ring */
-
- break;
- case DE4X5_SET_PROM: /* Set Promiscuous Mode */
- if (suser()) {
- omr = inl(DE4X5_OMR);
- omr |= OMR_PR;
- outl(omr, DE4X5_OMR);
- } else {
- status = -EPERM;
- }
-
- break;
- case DE4X5_CLR_PROM: /* Clear Promiscuous Mode */
- if (suser()) {
- omr = inl(DE4X5_OMR);
- omr &= ~OMR_PR;
- outb(omr, DE4X5_OMR);
- } else {
- status = -EPERM;
- }
-
- break;
- case DE4X5_SAY_BOO: /* Say "Boo!" to the kernel log file */
- printk("%s: Boo!\n", dev->name);
-
- break;
- case DE4X5_GET_MCA: /* Get the multicast address table */
- break;
- case DE4X5_SET_MCA: /* Set a multicast address */
- break;
- case DE4X5_CLR_MCA: /* Clear all multicast addresses */
- break;
- case DE4X5_MCA_EN: /* Enable pass all multicast addresses*/
- if (suser()) {
- omr = inl(DE4X5_OMR);
- omr |= OMR_PM;
- outl(omr, DE4X5_OMR);
- } else {
- status = -EPERM;
- }
-
- break;
- case DE4X5_GET_STATS: /* Get the driver statistics */
- ioc->len = sizeof(lp->pktStats);
- status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len);
- if (status)
- break;
-
- cli();
- copy_to_user(ioc->data, &lp->pktStats, ioc->len);
- sti();
-
- break;
- case DE4X5_CLR_STATS: /* Zero out the driver statistics */
- if (suser()) {
- cli();
- memset(&lp->pktStats, 0, sizeof(lp->pktStats));
- sti();
- } else {
- status = -EPERM;
- }
-
- break;
- case DE4X5_GET_OMR: /* Get the OMR Register contents */
- tmp.addr[0] = inl(DE4X5_OMR);
- if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, 1))) {
- copy_to_user(ioc->data, tmp.addr, 1);
- }
-
- break;
- case DE4X5_SET_OMR: /* Set the OMR Register contents */
- if (suser()) {
- if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, 1))) {
- copy_from_user(tmp.addr, ioc->data, 1);
- outl(tmp.addr[0], DE4X5_OMR);
- }
- } else {
- status = -EPERM;
- }
-
- break;
- case DE4X5_GET_REG: /* Get the DE4X5 Registers */
- j = 0;
- tmp.lval[0] = inl(DE4X5_STS); j+=4;
- tmp.lval[1] = inl(DE4X5_BMR); j+=4;
- tmp.lval[2] = inl(DE4X5_IMR); j+=4;
- tmp.lval[3] = inl(DE4X5_OMR); j+=4;
- tmp.lval[4] = inl(DE4X5_SISR); j+=4;
- tmp.lval[5] = inl(DE4X5_SICR); j+=4;
- tmp.lval[6] = inl(DE4X5_STRR); j+=4;
- tmp.lval[7] = inl(DE4X5_SIGR); j+=4;
- ioc->len = j;
- if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
- copy_to_user(ioc->data, tmp.addr, ioc->len);
- }
- break;
-
-/*
-#define DE4X5_DUMP 0x0f / * Dump the DE4X5 Status * /
-
- case DE4X5_DUMP:
- j = 0;
- tmp.addr[j++] = dev->irq;
- for (i=0; i<ETH_ALEN; i++) {
- tmp.addr[j++] = dev->dev_addr[i];
- }
- tmp.addr[j++] = lp->rxRingSize;
- tmp.lval[j>>2] = (long)lp->rx_ring; j+=4;
- tmp.lval[j>>2] = (long)lp->tx_ring; j+=4;
-
- for (i=0;i<lp->rxRingSize-1;i++){
- if (i < 3) {
- tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4;
- }
- }
- tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4;
- for (i=0;i<lp->txRingSize-1;i++){
- if (i < 3) {
- tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4;
- }
- }
- tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4;
-
- for (i=0;i<lp->rxRingSize-1;i++){
- if (i < 3) {
- tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4;
- }
- }
- tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4;
- for (i=0;i<lp->txRingSize-1;i++){
- if (i < 3) {
- tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4;
- }
- }
- tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4;
-
- for (i=0;i<lp->rxRingSize;i++){
- tmp.lval[j>>2] = le32_to_cpu(lp->rx_ring[i].status); j+=4;
- }
- for (i=0;i<lp->txRingSize;i++){
- tmp.lval[j>>2] = le32_to_cpu(lp->tx_ring[i].status); j+=4;
- }
-
- tmp.lval[j>>2] = inl(DE4X5_BMR); j+=4;
- tmp.lval[j>>2] = inl(DE4X5_TPD); j+=4;
- tmp.lval[j>>2] = inl(DE4X5_RPD); j+=4;
- tmp.lval[j>>2] = inl(DE4X5_RRBA); j+=4;
- tmp.lval[j>>2] = inl(DE4X5_TRBA); j+=4;
- tmp.lval[j>>2] = inl(DE4X5_STS); j+=4;
- tmp.lval[j>>2] = inl(DE4X5_OMR); j+=4;
- tmp.lval[j>>2] = inl(DE4X5_IMR); j+=4;
- tmp.lval[j>>2] = lp->chipset; j+=4;
- if (lp->chipset == DC21140) {
- tmp.lval[j>>2] = inl(DE4X5_GEP); j+=4;
- } else {
- tmp.lval[j>>2] = inl(DE4X5_SISR); j+=4;
- tmp.lval[j>>2] = inl(DE4X5_SICR); j+=4;
- tmp.lval[j>>2] = inl(DE4X5_STRR); j+=4;
- tmp.lval[j>>2] = inl(DE4X5_SIGR); j+=4;
- }
- tmp.lval[j>>2] = lp->phy[lp->active].id; j+=4;
- if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
- tmp.lval[j>>2] = lp->active; j+=4;
- tmp.lval[j>>2]=mii_rd(MII_CR,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
- tmp.lval[j>>2]=mii_rd(MII_SR,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
- tmp.lval[j>>2]=mii_rd(MII_ID0,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
- tmp.lval[j>>2]=mii_rd(MII_ID1,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
- if (lp->phy[lp->active].id != BROADCOM_T4) {
- tmp.lval[j>>2]=mii_rd(MII_ANA,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
- tmp.lval[j>>2]=mii_rd(MII_ANLPA,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
- }
- tmp.lval[j>>2]=mii_rd(0x10,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
- if (lp->phy[lp->active].id != BROADCOM_T4) {
- tmp.lval[j>>2]=mii_rd(0x11,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
- tmp.lval[j>>2]=mii_rd(0x12,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
- } else {
- tmp.lval[j>>2]=mii_rd(0x14,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
- }
- }
-
- tmp.addr[j++] = lp->txRingSize;
- tmp.addr[j++] = dev->tbusy;
-
- ioc->len = j;
- if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
- copy_to_user(ioc->data, tmp.addr, ioc->len);
+ struct de4x5_private *lp = (struct de4x5_private *) dev->priv;
+ struct de4x5_ioctl *ioc = (struct de4x5_ioctl *) &rq->ifr_data;
+ u_long iobase = dev->base_addr;
+ int i, j, status = 0;
+ s32 omr;
+ union {
+ u8 addr[144];
+ u16 sval[72];
+ u32 lval[36];
+ } tmp;
+
+ switch (ioc->cmd) {
+ case DE4X5_GET_HWADDR: /* Get the hardware address */
+ ioc->len = ETH_ALEN;
+ status = verify_area(VERIFY_WRITE, (void *) ioc->data, ioc->len);
+ if (status)
+ break;
+ for (i = 0; i < ETH_ALEN; i++) {
+ tmp.addr[i] = dev->dev_addr[i];
+ }
+ copy_to_user(ioc->data, tmp.addr, ioc->len);
+
+ break;
+ case DE4X5_SET_HWADDR: /* Set the hardware address */
+ status = verify_area(VERIFY_READ, (void *) ioc->data, ETH_ALEN);
+ if (status)
+ break;
+ status = -EPERM;
+ if (!suser())
+ break;
+ status = 0;
+ copy_from_user(tmp.addr, ioc->data, ETH_ALEN);
+ for (i = 0; i < ETH_ALEN; i++) {
+ dev->dev_addr[i] = tmp.addr[i];
+ }
+ build_setup_frame(dev, PHYS_ADDR_ONLY);
+ /* Set up the descriptor and give ownership to the card */
+ while (test_and_set_bit(0, (void *)&dev->tbusy) != 0);
+ load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET |
+ SETUP_FRAME_LEN, NULL);
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+ outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */
+ dev->tbusy = 0; /* Unlock the TX ring */
+
+ break;
+ case DE4X5_SET_PROM: /* Set Promiscuous Mode */
+ if (suser()) {
+ omr = inl(DE4X5_OMR);
+ omr |= OMR_PR;
+ outl(omr, DE4X5_OMR);
+ dev->flags |= IFF_PROMISC;
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_CLR_PROM: /* Clear Promiscuous Mode */
+ if (suser()) {
+ omr = inl(DE4X5_OMR);
+ omr &= ~OMR_PR;
+ outb(omr, DE4X5_OMR);
+ dev->flags &= ~IFF_PROMISC;
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_SAY_BOO: /* Say "Boo!" to the kernel log file */
+ printk("%s: Boo!\n", dev->name);
+
+ break;
+ case DE4X5_MCA_EN: /* Enable pass all multicast addressing */
+ if (suser()) {
+ omr = inl(DE4X5_OMR);
+ omr |= OMR_PM;
+ outl(omr, DE4X5_OMR);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_GET_STATS: /* Get the driver statistics */
+ ioc->len = sizeof(lp->pktStats);
+ status = verify_area(VERIFY_WRITE, (void *) ioc->data, ioc->len);
+ if (status)
+ break;
+
+ cli();
+ copy_to_user(ioc->data, &lp->pktStats, ioc->len);
+ sti();
+
+ break;
+ case DE4X5_CLR_STATS: /* Zero out the driver statistics */
+ if (suser()) {
+ cli();
+ memset(&lp->pktStats, 0, sizeof(lp->pktStats));
+ sti();
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_GET_OMR: /* Get the OMR Register contents */
+ tmp.addr[0] = inl(DE4X5_OMR);
+ if (!(status = verify_area(VERIFY_WRITE, (void *) ioc->data, 1))) {
+ copy_to_user(ioc->data, tmp.addr, 1);
+ }
+ break;
+ case DE4X5_SET_OMR: /* Set the OMR Register contents */
+ if (suser()) {
+ if (!(status = verify_area(VERIFY_READ, (void *) ioc->data, 1))) {
+ copy_from_user(tmp.addr, ioc->data, 1);
+ outl(tmp.addr[0], DE4X5_OMR);
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_GET_REG: /* Get the DE4X5 Registers */
+ j = 0;
+ tmp.lval[0] = inl(DE4X5_STS);
+ j += 4;
+ tmp.lval[1] = inl(DE4X5_BMR);
+ j += 4;
+ tmp.lval[2] = inl(DE4X5_IMR);
+ j += 4;
+ tmp.lval[3] = inl(DE4X5_OMR);
+ j += 4;
+ tmp.lval[4] = inl(DE4X5_SISR);
+ j += 4;
+ tmp.lval[5] = inl(DE4X5_SICR);
+ j += 4;
+ tmp.lval[6] = inl(DE4X5_STRR);
+ j += 4;
+ tmp.lval[7] = inl(DE4X5_SIGR);
+ j += 4;
+ ioc->len = j;
+ if (!(status = verify_area(VERIFY_WRITE, (void *) ioc->data, ioc->len))) {
+ copy_to_user(ioc->data, tmp.addr, ioc->len);
+ }
+ break;
+
+#define DE4X5_DUMP 0x0f /* Dump the DE4X5 Status */
+/*
+ case DE4X5_DUMP:
+ j = 0;
+ tmp.addr[j++] = dev->irq;
+ for (i=0; i<ETH_ALEN; i++) {
+ tmp.addr[j++] = dev->dev_addr[i];
+ }
+ tmp.addr[j++] = lp->rxRingSize;
+ tmp.lval[j>>2] = (long)lp->rx_ring; j+=4;
+ tmp.lval[j>>2] = (long)lp->tx_ring; j+=4;
+
+ for (i=0;i<lp->rxRingSize-1;i++){
+ if (i < 3) {
+ tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4;
+ }
+ }
+ tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4;
+ for (i=0;i<lp->txRingSize-1;i++){
+ if (i < 3) {
+ tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4;
+ }
+ }
+ tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4;
+
+ for (i=0;i<lp->rxRingSize-1;i++){
+ if (i < 3) {
+ tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4;
+ }
+ }
+ tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4;
+ for (i=0;i<lp->txRingSize-1;i++){
+ if (i < 3) {
+ tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4;
+ }
+ }
+ tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4;
+
+ for (i=0;i<lp->rxRingSize;i++){
+ tmp.lval[j>>2] = le32_to_cpu(lp->rx_ring[i].status); j+=4;
+ }
+ for (i=0;i<lp->txRingSize;i++){
+ tmp.lval[j>>2] = le32_to_cpu(lp->tx_ring[i].status); j+=4;
+ }
+
+ tmp.lval[j>>2] = inl(DE4X5_BMR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_TPD); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_RPD); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_RRBA); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_TRBA); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_STS); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_OMR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_IMR); j+=4;
+ tmp.lval[j>>2] = lp->chipset; j+=4;
+ if (lp->chipset == DC21140) {
+ tmp.lval[j>>2] = gep_rd(dev); j+=4;
+ } else {
+ tmp.lval[j>>2] = inl(DE4X5_SISR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_SICR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_STRR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_SIGR); j+=4;
+ }
+ tmp.lval[j>>2] = lp->phy[lp->active].id; j+=4;
+ if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
+ tmp.lval[j>>2] = lp->active; j+=4;
+ tmp.lval[j>>2]=mii_rd(MII_CR,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
+ tmp.lval[j>>2]=mii_rd(MII_SR,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
+ tmp.lval[j>>2]=mii_rd(MII_ID0,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
+ tmp.lval[j>>2]=mii_rd(MII_ID1,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
+ if (lp->phy[lp->active].id != BROADCOM_T4) {
+ tmp.lval[j>>2]=mii_rd(MII_ANA,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
+ tmp.lval[j>>2]=mii_rd(MII_ANLPA,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
+ }
+ tmp.lval[j>>2]=mii_rd(0x10,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
+ if (lp->phy[lp->active].id != BROADCOM_T4) {
+ tmp.lval[j>>2]=mii_rd(0x11,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
+ tmp.lval[j>>2]=mii_rd(0x12,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
+ } else {
+ tmp.lval[j>>2]=mii_rd(0x14,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
+ }
+ }
+
+ tmp.addr[j++] = lp->txRingSize;
+ tmp.addr[j++] = dev->tbusy;
+
+ ioc->len = j;
+ if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
+ copy_to_user(ioc->data, tmp.addr, ioc->len);
+ }
+
+ break;
+ */
+ default:
+ status = -EOPNOTSUPP;
}
-
- break;
-*/
- default:
- status = -EOPNOTSUPP;
- }
-
- return status;
+
+ return status;
}
#ifdef MODULE
@@ -5003,60 +5496,128 @@ de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd)
*/
#define LP(a) ((struct de4x5_private *)(a))
static struct device *mdev = NULL;
-static int io=0x0;/* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED */
+static int io = 0x0; /* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED */
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+MODULE_PARM(io, "i");
+#endif /* LINUX_VERSION_CODE */
-int
-init_module(void)
+int init_module(void)
{
- struct device *p;
+ int i, num, status = -EIO;
+ struct device *p;
+
+ num = count_adapters();
+
+ for (i=0; i<num; i++) {
+ if ((p = insert_device(NULL, io, de4x5_probe)) == NULL)
+ return -ENOMEM;
- if ((mdev = insert_device(NULL, io, de4x5_probe)) == NULL)
- return -ENOMEM;
+ if (!mdev)
+ mdev = p;
- for (p = mdev; p != NULL; p = LP(p->priv)->next_module) {
- if (register_netdev(p) != 0)
- return -EIO;
- }
+ if (register_netdev(p) != 0) {
+ kfree(p);
+ } else {
+ status = 0; /* At least one adapter will work */
+ lastModule = p;
+ }
+ }
- return 0;
+ return status;
}
-void
-cleanup_module(void)
+void cleanup_module(void)
{
- while (mdev != NULL) {
- mdev = unlink_modules(mdev);
- }
+ while (mdev != NULL) {
+ mdev = unlink_modules(mdev);
+ }
- return;
+ return;
}
-static struct device *
-unlink_modules(struct device *p)
+static struct device *unlink_modules(struct device *p)
{
- struct device *next = NULL;
-
- if (p->priv) { /* Private areas allocated? */
- struct de4x5_private *lp = (struct de4x5_private *)p->priv;
-
- next = lp->next_module;
- if (lp->cache.buf) { /* MAC buffers allocated? */
- kfree(lp->cache.buf); /* Free the MAC buffers */
- }
- kfree(lp->cache.priv); /* Free the private area */
- release_region(p->base_addr, (lp->bus == PCI ?
- DE4X5_PCI_TOTAL_SIZE :
- DE4X5_EISA_TOTAL_SIZE));
- }
- unregister_netdev(p);
- kfree(p); /* Free the device structure */
-
- return next;
+ struct device *next = NULL;
+
+ if (p->priv) { /* Private areas allocated? */
+ struct de4x5_private *lp = (struct de4x5_private *) p->priv;
+
+ next = lp->next_module;
+ if (lp->cache.buf) { /* MAC buffers allocated? */
+ kfree(lp->cache.buf); /* Free the MAC buffers */
+ }
+ kfree(lp->cache.priv); /* Free the private area */
+ release_region(p->base_addr, (lp->bus == PCI ?
+ DE4X5_PCI_TOTAL_SIZE :
+ DE4X5_EISA_TOTAL_SIZE));
+ }
+ unregister_netdev(p);
+ kfree(p); /* Free the device structure */
+
+ return next;
}
-#endif /* MODULE */
+static int count_adapters(void)
+{
+ int i, j = 0;
+ char name[DE4X5_STRLEN];
+ u_char pb, dev_fn, dev_num;
+ u_short dev_id, vendor;
+ u_int class = DE4X5_CLASS_CODE;
+ u_int device;
+#ifndef __sparc_v9__
+ u_int iobase = 0x1000;
+
+ for (i=1; i<MAX_EISA_SLOTS; i++, iobase+=EISA_SLOT_INC) {
+ if (EISA_signature(name, EISA_ID))
+ j++;
+ }
+#endif
+ if (!pcibios_present())
+ return j;
+
+ for (i=0;
+ (pcibios_find_class(class, i, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
+ i++) {
+ dev_num = PCI_SLOT(dev_fn);
+ device = 0;
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id);
+ device = dev_id;
+ device <<= 8;
+ if (is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)
+ j++;
+ }
+ return j;
+}
+
+/*
+** If at end of eth device list and can't use current entry, malloc
+** one up. If memory could not be allocated, print an error message.
+*/
+__initfunc(static struct device *
+insert_device(struct device *dev, u_long iobase, int (*init)(struct device *)))
+{
+ struct device *new;
+
+ new = (struct device *)kmalloc(sizeof(struct device)+8, GFP_KERNEL);
+ if (new == NULL) {
+ printk("de4x5.c: Device not initialised, insufficient memory\n");
+ return NULL;
+ } else {
+ memset((char *)new, 0, sizeof(struct device)+8);
+ new->name = (char *)(new + 1);
+ new->base_addr = iobase; /* assign the io address */
+ new->init = init; /* initialisation routine */
+ }
+
+ return new;
+}
+
+#endif /* MODULE */
+
/*
* Local variables:
* compile-command: "gcc -D__KERNEL__ -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -c de4x5.c"
@@ -5064,3 +5625,10 @@ unlink_modules(struct device *p)
* compile-command: "gcc -D__KERNEL__ -DMODULE -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -c de4x5.c"
* End:
*/
+
+
+
+
+
+
+
diff --git a/drivers/net/de4x5.h b/drivers/net/de4x5.h
index 525e0122d..c3a98e06a 100644
--- a/drivers/net/de4x5.h
+++ b/drivers/net/de4x5.h
@@ -120,7 +120,7 @@
#define DC21140 DC21140_DID
#define DC2114x DC2114x_DID
#define DC21142 (DC2114x_DID | 0x0010)
-#define DC21143 (DC2114x_DID | 0x0020)
+#define DC21143 (DC2114x_DID | 0x0030)
#define is_DC21040 ((vendor == DC21040_VID) && (device == DC21040_DID))
#define is_DC21041 ((vendor == DC21041_VID) && (device == DC21041_DID))
@@ -166,7 +166,11 @@
/*
** PCI Configuration Base I/O Address Register (PCI_CBIO)
*/
+#ifdef __sparc_v9__
+#define CBIO_MASK 0xffffffffffffff80 /* Base I/O Address Mask */
+#else
#define CBIO_MASK 0xffffff80 /* Base I/O Address Mask */
+#endif
#define CBIO_IOSI 0x00000001 /* I/O Space Indicator (RO, value is 1) */
/*
@@ -360,6 +364,12 @@
#define TR_128 0x00008000 /* Threshold set to 128 (512) bytes */
#define TR_160 0x0000c000 /* Threshold set to 160 (1024) bytes */
+#define OMR_DEF (OMR_SDP)
+#define OMR_SIA (OMR_SDP | OMR_TTM)
+#define OMR_SYM (OMR_SDP | OMR_SCR | OMR_PCS | OMR_HBD | OMR_PS)
+#define OMR_MII_10 (OMR_SDP | OMR_TTM | OMR_PS)
+#define OMR_MII_100 (OMR_SDP | OMR_SCR | OMR_HBD | OMR_PS)
+
/*
** DC21040 Interrupt Mask Register (DE4X5_IMR)
*/
@@ -606,48 +616,60 @@
** DC21140 General Purpose Register (DE4X5_GEP) (hardware dependent bits)
*/
/* Valid ONLY for DE500 hardware */
-#define GEP_LNP 0x00000080 /* Link Pass (input) */
-#define GEP_SLNK 0x00000040 /* SYM LINK (input) */
-#define GEP_SDET 0x00000020 /* Signal Detect (input) */
-#define GEP_HRST 0x00000010 /* Hard RESET (to PHY) (output) */
-#define GEP_FDXD 0x00000008 /* Full Duplex Disable (output) */
-#define GEP_PHYL 0x00000004 /* PHY Loopback (output) */
-#define GEP_FLED 0x00000002 /* Force Activity LED on (output) */
-#define GEP_MODE 0x00000001 /* 0: 10Mb/s, 1: 100Mb/s */
-#define GEP_INIT 0x0000011f /* Setup inputs (0) and outputs (1) */
-#define GEP_CTRL 0x00000100 /* GEP control bit */
+#define GEP_LNP 0x00000080 /* Link Pass (input) */
+#define GEP_SLNK 0x00000040 /* SYM LINK (input) */
+#define GEP_SDET 0x00000020 /* Signal Detect (input) */
+#define GEP_HRST 0x00000010 /* Hard RESET (to PHY) (output) */
+#define GEP_FDXD 0x00000008 /* Full Duplex Disable (output) */
+#define GEP_PHYL 0x00000004 /* PHY Loopback (output) */
+#define GEP_FLED 0x00000002 /* Force Activity LED on (output) */
+#define GEP_MODE 0x00000001 /* 0: 10Mb/s, 1: 100Mb/s */
+#define GEP_INIT 0x0000011f /* Setup inputs (0) and outputs (1) */
+#define GEP_CTRL 0x00000100 /* GEP control bit */
+
+/*
+** SIA Register Defaults
+*/
+#define CSR13 0x00000001
+#define CSR14 0x0003ff7f /* Autonegotiation disabled */
+#define CSR15 0x00000008
/*
** SIA Status Register (DE4X5_SISR)
*/
-#define SISR_LPC 0xffff0000 /* Link Partner's Code Word */
-#define SISR_LPN 0x00008000 /* Link Partner Negotiable */
-#define SISR_ANS 0x00007000 /* Auto Negotiation Arbitration State */
-#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected (DC21041) */
-#define SISR_TRF 0x00000800 /* Transmit Remote Fault */
-#define SISR_NSND 0x00000400 /* Non Stable NLPs Detected (DC21142) */
+#define SISR_LPC 0xffff0000 /* Link Partner's Code Word */
+#define SISR_LPN 0x00008000 /* Link Partner Negotiable */
+#define SISR_ANS 0x00007000 /* Auto Negotiation Arbitration State */
+#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected (DC21041) */
+#define SISR_TRF 0x00000800 /* Transmit Remote Fault */
+#define SISR_NSND 0x00000400 /* Non Stable NLPs Detected (DC21142) */
#define SISR_ANR_FDS 0x00000400 /* Auto Negotiate Restart/Full Duplex Sel.*/
-#define SISR_TRA 0x00000200 /* 10BASE-T Receive Port Activity */
-#define SISR_NRA 0x00000200 /* Non Selected Port Receive Activity */
-#define SISR_ARA 0x00000100 /* AUI Receive Port Activity */
-#define SISR_SRA 0x00000100 /* Selected Port Receive Activity */
-#define SISR_DAO 0x00000080 /* PLL All One */
-#define SISR_DAZ 0x00000040 /* PLL All Zero */
-#define SISR_DSP 0x00000020 /* PLL Self-Test Pass */
-#define SISR_DSD 0x00000010 /* PLL Self-Test Done */
-#define SISR_APS 0x00000008 /* Auto Polarity State */
-#define SISR_LKF 0x00000004 /* Link Fail Status */
-#define SISR_NCR 0x00000002 /* Network Connection Error */
-#define SISR_PAUI 0x00000001 /* AUI_TP Indication */
-#define SISR_MRA 0x00000001 /* MII Receive Port Activity */
-
-#define ANS_NDIS 0x00000000 /* Nway disable */
-#define ANS_TDIS 0x00001000 /* Transmit Disable */
-#define ANS_ADET 0x00002000 /* Ability Detect */
-#define ANS_ACK 0x00003000 /* Acknowledge */
-#define ANS_CACK 0x00004000 /* Complete Acknowledge */
-#define ANS_NWOK 0x00005000 /* Nway OK - FLP Link Good */
-#define ANS_LCHK 0x00006000 /* Link Check */
+#define SISR_TRA 0x00000200 /* 10BASE-T Receive Port Activity */
+#define SISR_NRA 0x00000200 /* Non Selected Port Receive Activity */
+#define SISR_ARA 0x00000100 /* AUI Receive Port Activity */
+#define SISR_SRA 0x00000100 /* Selected Port Receive Activity */
+#define SISR_DAO 0x00000080 /* PLL All One */
+#define SISR_DAZ 0x00000040 /* PLL All Zero */
+#define SISR_DSP 0x00000020 /* PLL Self-Test Pass */
+#define SISR_DSD 0x00000010 /* PLL Self-Test Done */
+#define SISR_APS 0x00000008 /* Auto Polarity State */
+#define SISR_LKF 0x00000004 /* Link Fail Status */
+#define SISR_LS10 0x00000004 /* 10Mb/s Link Fail Status */
+#define SISR_NCR 0x00000002 /* Network Connection Error */
+#define SISR_LS100 0x00000002 /* 100Mb/s Link Fail Status */
+#define SISR_PAUI 0x00000001 /* AUI_TP Indication */
+#define SISR_MRA 0x00000001 /* MII Receive Port Activity */
+
+#define ANS_NDIS 0x00000000 /* Nway disable */
+#define ANS_TDIS 0x00001000 /* Transmit Disable */
+#define ANS_ADET 0x00002000 /* Ability Detect */
+#define ANS_ACK 0x00003000 /* Acknowledge */
+#define ANS_CACK 0x00004000 /* Complete Acknowledge */
+#define ANS_NWOK 0x00005000 /* Nway OK - FLP Link Good */
+#define ANS_LCHK 0x00006000 /* Link Check */
+
+#define SISR_RST 0x00000301 /* CSR12 reset */
+#define SISR_ANR 0x00001301 /* Autonegotiation restart */
/*
** SIA Connectivity Register (DE4X5_SICR)
@@ -828,6 +850,7 @@
#define DEBUG_OPEN 0x0040 /* Print de4x5_open() messages */
#define DEBUG_CLOSE 0x0080 /* Print de4x5_close() messages */
#define DEBUG_PCICFG 0x0100
+#define DEBUG_ALL 0x01ff
/*
** Miscellaneous
@@ -884,6 +907,11 @@
#define OPEN 2 /* Running */
/*
+*/
+#define PDET_LINK_WAIT 1200 /* msecs to wait for link detect bits */
+#define ANS_FINISH_WAIT 1000 /* msecs to wait for link detect bits */
+
+/*
** IEEE OUIs for various PHY vendor/chip combos - Reg 2 values only. Since
** the vendors seem split 50-50 on how to calculate the OUI register values
** anyway, just reading Reg2 seems reasonable for now [see de4x5_get_oui()].
@@ -936,7 +964,7 @@
} else if (lp->useSROM && !lp->useMII) {\
omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
omr |= (lp->fdx ? OMR_FDX : 0);\
- outl(omr | lp->infoblock_csr6 | OMR_HBD, DE4X5_OMR);\
+ outl(omr | (lp->infoblock_csr6 & ~(OMR_SCR | OMR_HBD)), DE4X5_OMR);\
} else {\
omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
omr |= (lp->fdx ? OMR_FDX : 0);\
@@ -953,7 +981,7 @@
outl(omr, DE4X5_OMR);\
} else if (lp->useSROM && !lp->useMII) {\
omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
- outl(omr, DE4X5_OMR);\
+ outl(omr | lp->infoblock_csr6, DE4X5_OMR);\
} else {\
omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
outl(omr | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\
@@ -991,3 +1019,5 @@ struct de4x5_ioctl {
#define DE4X5_GET_OMR 0x0c /* Get the OMR Register contents */
#define DE4X5_SET_OMR 0x0d /* Set the OMR Register contents */
#define DE4X5_GET_REG 0x0e /* Get the DE4X5 Registers */
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
diff --git a/drivers/net/de600.c b/drivers/net/de600.c
index 700c95a75..f73939830 100644
--- a/drivers/net/de600.c
+++ b/drivers/net/de600.c
@@ -338,11 +338,10 @@ de600_read_byte(unsigned char type, struct device *dev) { /* dev used by macros
static int
de600_open(struct device *dev)
{
- if (request_irq(DE600_IRQ, de600_interrupt, 0, "de600", NULL)) {
+ if (request_irq(DE600_IRQ, de600_interrupt, 0, "de600", dev)) {
printk ("%s: unable to get IRQ %d\n", dev->name, DE600_IRQ);
return 1;
}
- irq2dev_map[DE600_IRQ] = dev;
MOD_INC_USE_COUNT;
dev->start = 1;
@@ -367,8 +366,7 @@ de600_close(struct device *dev)
select_prn();
if (dev->start) {
- free_irq(DE600_IRQ, NULL);
- irq2dev_map[DE600_IRQ] = NULL;
+ free_irq(DE600_IRQ, dev);
dev->start = 0;
MOD_DEC_USE_COUNT;
}
@@ -481,7 +479,7 @@ de600_start_xmit(struct sk_buff *skb, struct device *dev)
static void
de600_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
- struct device *dev = irq2dev_map[irq];
+ struct device *dev = dev_id;
byte irq_status;
int retrig = 0;
int boguscount = 0;
diff --git a/drivers/net/de620.c b/drivers/net/de620.c
index 60bc9229d..ff2251c5f 100644
--- a/drivers/net/de620.c
+++ b/drivers/net/de620.c
@@ -439,11 +439,10 @@ de620_get_register(struct device *dev, byte reg)
static int
de620_open(struct device *dev)
{
- if (request_irq(dev->irq, de620_interrupt, 0, "de620", NULL)) {
+ if (request_irq(dev->irq, de620_interrupt, 0, "de620", dev)) {
printk ("%s: unable to get IRQ %d\n", dev->name, dev->irq);
return 1;
}
- irq2dev_map[dev->irq] = dev;
MOD_INC_USE_COUNT;
if (adapter_init(dev)) {
@@ -464,8 +463,7 @@ de620_close(struct device *dev)
/* disable recv */
de620_set_register(dev, W_TCR, RXOFF);
- free_irq(dev->irq, NULL);
- irq2dev_map[dev->irq] = NULL;
+ free_irq(dev->irq, dev);
dev->start = 0;
MOD_DEC_USE_COUNT;
@@ -595,7 +593,7 @@ de620_start_xmit(struct sk_buff *skb, struct device *dev)
static void
de620_interrupt(int irq_in, void *dev_id, struct pt_regs *regs)
{
- struct device *dev = irq2dev_map[irq_in];
+ struct device *dev = dev_id;
byte irq_status;
int bogus_count = 0;
int again = 0;
diff --git a/drivers/net/defxx.c b/drivers/net/defxx.c
index 03250092b..4ecb8a5f3 100644
--- a/drivers/net/defxx.c
+++ b/drivers/net/defxx.c
@@ -3208,7 +3208,7 @@ int dfx_xmt_queue_pkt(
printk("%s: Invalid packet length - %u bytes\n",
dev->name, skb->len);
bp->xmt_length_errors++; /* bump error counter */
- dev_tint(dev); /* dequeue packets from xmt queue and send them */
+ mark_bh(NET_BH);
dev_kfree_skb(skb, FREE_WRITE);
return(0); /* return "success" */
}
diff --git a/drivers/net/depca.c b/drivers/net/depca.c
index 74246fa7a..fa2435219 100644
--- a/drivers/net/depca.c
+++ b/drivers/net/depca.c
@@ -1,210 +1,210 @@
/* depca.c: A DIGITAL DEPCA & EtherWORKS ethernet driver for linux.
- Written 1994, 1995 by David C. Davies.
-
-
- Copyright 1994 David C. Davies
- and
- United States Government
- (as represented by the Director, National Security Agency).
-
- Copyright 1995 Digital Equipment Corporation.
-
-
- This software may be used and distributed according to the terms of
- the GNU Public License, incorporated herein by reference.
-
- This driver is written for the Digital Equipment Corporation series
- of DEPCA and EtherWORKS ethernet cards:
-
- DEPCA (the original)
- DE100
- DE101
- DE200 Turbo
- DE201 Turbo
- DE202 Turbo (TP BNC)
- DE210
- DE422 (EISA)
-
- The driver has been tested on DE100, DE200 and DE202 cards in a
- relatively busy network. The DE422 has been tested a little.
-
- This driver will NOT work for the DE203, DE204 and DE205 series of
- cards, since they have a new custom ASIC in place of the AMD LANCE
- chip. See the 'ewrk3.c' driver in the Linux source tree for running
- those cards.
-
- I have benchmarked the driver with a DE100 at 595kB/s to (542kB/s from)
- a DECstation 5000/200.
-
- The author may be reached at davies@maniac.ultranet.com
-
- =========================================================================
-
- The driver was originally based on the 'lance.c' driver from Donald
- Becker which is included with the standard driver distribution for
- linux. V0.4 is a complete re-write with only the kernel interface
- remaining from the original code.
-
- 1) Lance.c code in /linux/drivers/net/
- 2) "Ethernet/IEEE 802.3 Family. 1992 World Network Data Book/Handbook",
- AMD, 1992 [(800) 222-9323].
- 3) "Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE)",
- AMD, Pub. #17881, May 1993.
- 4) "Am79C960 PCnet-ISA(tm), Single-Chip Ethernet Controller for ISA",
- AMD, Pub. #16907, May 1992
- 5) "DEC EtherWORKS LC Ethernet Controller Owners Manual",
- Digital Equipment corporation, 1990, Pub. #EK-DE100-OM.003
- 6) "DEC EtherWORKS Turbo Ethernet Controller Owners Manual",
- Digital Equipment corporation, 1990, Pub. #EK-DE200-OM.003
- 7) "DEPCA Hardware Reference Manual", Pub. #EK-DEPCA-PR
- Digital Equipment Corporation, 1989
- 8) "DEC EtherWORKS Turbo_(TP BNC) Ethernet Controller Owners Manual",
- Digital Equipment corporation, 1991, Pub. #EK-DE202-OM.001
-
-
- Peter Bauer's depca.c (V0.5) was referred to when debugging V0.1 of this
- driver.
-
- The original DEPCA card requires that the ethernet ROM address counter
- be enabled to count and has an 8 bit NICSR. The ROM counter enabling is
- only done when a 0x08 is read as the first address octet (to minimise
- the chances of writing over some other hardware's I/O register). The
- NICSR accesses have been changed to byte accesses for all the cards
- supported by this driver, since there is only one useful bit in the MSB
- (remote boot timeout) and it is not used. Also, there is a maximum of
- only 48kB network RAM for this card. My thanks to Torbjorn Lindh for
- help debugging all this (and holding my feet to the fire until I got it
- right).
-
- The DE200 series boards have on-board 64kB RAM for use as a shared
- memory network buffer. Only the DE100 cards make use of a 2kB buffer
- mode which has not been implemented in this driver (only the 32kB and
- 64kB modes are supported [16kB/48kB for the original DEPCA]).
-
- At the most only 2 DEPCA cards can be supported on the ISA bus because
- there is only provision for two I/O base addresses on each card (0x300
- and 0x200). The I/O address is detected by searching for a byte sequence
- in the Ethernet station address PROM at the expected I/O address for the
- Ethernet PROM. The shared memory base address is 'autoprobed' by
- looking for the self test PROM and detecting the card name. When a
- second DEPCA is detected, information is placed in the base_addr
- variable of the next device structure (which is created if necessary),
- thus enabling ethif_probe initialization for the device. More than 2
- EISA cards can be supported, but care will be needed assigning the
- shared memory to ensure that each slot has the correct IRQ, I/O address
- and shared memory address assigned.
-
- ************************************************************************
-
- NOTE: If you are using two ISA DEPCAs, it is important that you assign
- the base memory addresses correctly. The driver autoprobes I/O 0x300
- then 0x200. The base memory address for the first device must be less
- than that of the second so that the auto probe will correctly assign the
- I/O and memory addresses on the same card. I can't think of a way to do
- this unambiguously at the moment, since there is nothing on the cards to
- tie I/O and memory information together.
-
- I am unable to test 2 cards together for now, so this code is
- unchecked. All reports, good or bad, are welcome.
-
- ************************************************************************
-
- The board IRQ setting must be at an unused IRQ which is auto-probed
- using Donald Becker's autoprobe routines. DEPCA and DE100 board IRQs are
- {2,3,4,5,7}, whereas the DE200 is at {5,9,10,11,15}. Note that IRQ2 is
- really IRQ9 in machines with 16 IRQ lines.
-
- No 16MB memory limitation should exist with this driver as DMA is not
- used and the common memory area is in low memory on the network card (my
- current system has 20MB and I've not had problems yet).
-
- The ability to load this driver as a loadable module has been added. To
- utilise this ability, you have to do <8 things:
-
- 0) have a copy of the loadable modules code installed on your system.
- 1) copy depca.c from the /linux/drivers/net directory to your favourite
- temporary directory.
- 2) if you wish, edit the source code near line 1530 to reflect the I/O
- address and IRQ you're using (see also 5).
- 3) compile depca.c, but include -DMODULE in the command line to ensure
- that the correct bits are compiled (see end of source code).
- 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a
- kernel with the depca configuration turned off and reboot.
- 5) insmod depca.o [irq=7] [io=0x200] [mem=0xd0000] [adapter_name=DE100]
- [Alan Cox: Changed the code to allow command line irq/io assignments]
- [Dave Davies: Changed the code to allow command line mem/name
- assignments]
- 6) run the net startup bits for your eth?? interface manually
- (usually /etc/rc.inet[12] at boot time).
- 7) enjoy!
-
- Note that autoprobing is not allowed in loadable modules - the system is
- already up and running and you're messing with interrupts.
-
- To unload a module, turn off the associated interface
- 'ifconfig eth?? down' then 'rmmod depca'.
-
- To assign a base memory address for the shared memory when running as a
- loadable module, see 5 above. To include the adapter name (if you have
- no PROM but know the card name) also see 5 above. Note that this last
- option will not work with kernel built-in depca's.
-
- The shared memory assignment for a loadable module makes sense to avoid
- the 'memory autoprobe' picking the wrong shared memory (for the case of
- 2 depca's in a PC).
-
-
- TO DO:
- ------
-
-
- Revision History
- ----------------
-
- Version Date Description
-
- 0.1 25-jan-94 Initial writing.
- 0.2 27-jan-94 Added LANCE TX hardware buffer chaining.
- 0.3 1-feb-94 Added multiple DEPCA support.
- 0.31 4-feb-94 Added DE202 recognition.
- 0.32 19-feb-94 Tidy up. Improve multi-DEPCA support.
- 0.33 25-feb-94 Fix DEPCA ethernet ROM counter enable.
- Add jabber packet fix from murf@perftech.com
- and becker@super.org
- 0.34 7-mar-94 Fix DEPCA max network memory RAM & NICSR access.
- 0.35 8-mar-94 Added DE201 recognition. Tidied up.
- 0.351 30-apr-94 Added EISA support. Added DE422 recognition.
- 0.36 16-may-94 DE422 fix released.
- 0.37 22-jul-94 Added MODULE support
- 0.38 15-aug-94 Added DBR ROM switch in depca_close().
- Multi DEPCA bug fix.
- 0.38axp 15-sep-94 Special version for Alpha AXP Linux V1.0.
- 0.381 12-dec-94 Added DE101 recognition, fix multicast bug.
- 0.382 9-feb-95 Fix recognition bug reported by <bkm@star.rl.ac.uk>.
- 0.383 22-feb-95 Fix for conflict with VESA SCSI reported by
- <stromain@alf.dec.com>
- 0.384 17-mar-95 Fix a ring full bug reported by <bkm@star.rl.ac.uk>
- 0.385 3-apr-95 Fix a recognition bug reported by
- <ryan.niemi@lastfrontier.com>
- 0.386 21-apr-95 Fix the last fix...sorry, must be galloping senility
- 0.40 25-May-95 Rewrite for portability & updated.
- ALPHA support from <jestabro@amt.tay1.dec.com>
- 0.41 26-Jun-95 Added verify_area() calls in depca_ioctl() from
- suggestion by <heiko@colossus.escape.de>
- 0.42 27-Dec-95 Add 'mem' shared memory assignment for loadable
- modules.
- Add 'adapter_name' for loadable modules when no PROM.
- Both above from a suggestion by
- <pchen@woodruffs121.residence.gatech.edu>.
- Add new multicasting code.
- 0.421 22-Apr-96 Fix alloc_device() bug <jari@markkus2.fimr.fi>
- 0.422 29-Apr-96 Fix depca_hw_init() bug <jari@markkus2.fimr.fi>
- 0.423 7-Jun-96 Fix module load bug <kmg@barco.be>
- 0.43 16-Aug-96 Update alloc_device() to conform to de4x5.c
-
- =========================================================================
-*/
+ Written 1994, 1995 by David C. Davies.
+
+
+ Copyright 1994 David C. Davies
+ and
+ United States Government
+ (as represented by the Director, National Security Agency).
+
+ Copyright 1995 Digital Equipment Corporation.
+
+
+ This software may be used and distributed according to the terms of
+ the GNU Public License, incorporated herein by reference.
+
+ This driver is written for the Digital Equipment Corporation series
+ of DEPCA and EtherWORKS ethernet cards:
+
+ DEPCA (the original)
+ DE100
+ DE101
+ DE200 Turbo
+ DE201 Turbo
+ DE202 Turbo (TP BNC)
+ DE210
+ DE422 (EISA)
+
+ The driver has been tested on DE100, DE200 and DE202 cards in a
+ relatively busy network. The DE422 has been tested a little.
+
+ This driver will NOT work for the DE203, DE204 and DE205 series of
+ cards, since they have a new custom ASIC in place of the AMD LANCE
+ chip. See the 'ewrk3.c' driver in the Linux source tree for running
+ those cards.
+
+ I have benchmarked the driver with a DE100 at 595kB/s to (542kB/s from)
+ a DECstation 5000/200.
+
+ The author may be reached at davies@maniac.ultranet.com
+
+ =========================================================================
+
+ The driver was originally based on the 'lance.c' driver from Donald
+ Becker which is included with the standard driver distribution for
+ linux. V0.4 is a complete re-write with only the kernel interface
+ remaining from the original code.
+
+ 1) Lance.c code in /linux/drivers/net/
+ 2) "Ethernet/IEEE 802.3 Family. 1992 World Network Data Book/Handbook",
+ AMD, 1992 [(800) 222-9323].
+ 3) "Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE)",
+ AMD, Pub. #17881, May 1993.
+ 4) "Am79C960 PCnet-ISA(tm), Single-Chip Ethernet Controller for ISA",
+ AMD, Pub. #16907, May 1992
+ 5) "DEC EtherWORKS LC Ethernet Controller Owners Manual",
+ Digital Equipment corporation, 1990, Pub. #EK-DE100-OM.003
+ 6) "DEC EtherWORKS Turbo Ethernet Controller Owners Manual",
+ Digital Equipment corporation, 1990, Pub. #EK-DE200-OM.003
+ 7) "DEPCA Hardware Reference Manual", Pub. #EK-DEPCA-PR
+ Digital Equipment Corporation, 1989
+ 8) "DEC EtherWORKS Turbo_(TP BNC) Ethernet Controller Owners Manual",
+ Digital Equipment corporation, 1991, Pub. #EK-DE202-OM.001
+
+
+ Peter Bauer's depca.c (V0.5) was referred to when debugging V0.1 of this
+ driver.
+
+ The original DEPCA card requires that the ethernet ROM address counter
+ be enabled to count and has an 8 bit NICSR. The ROM counter enabling is
+ only done when a 0x08 is read as the first address octet (to minimise
+ the chances of writing over some other hardware's I/O register). The
+ NICSR accesses have been changed to byte accesses for all the cards
+ supported by this driver, since there is only one useful bit in the MSB
+ (remote boot timeout) and it is not used. Also, there is a maximum of
+ only 48kB network RAM for this card. My thanks to Torbjorn Lindh for
+ help debugging all this (and holding my feet to the fire until I got it
+ right).
+
+ The DE200 series boards have on-board 64kB RAM for use as a shared
+ memory network buffer. Only the DE100 cards make use of a 2kB buffer
+ mode which has not been implemented in this driver (only the 32kB and
+ 64kB modes are supported [16kB/48kB for the original DEPCA]).
+
+ At the most only 2 DEPCA cards can be supported on the ISA bus because
+ there is only provision for two I/O base addresses on each card (0x300
+ and 0x200). The I/O address is detected by searching for a byte sequence
+ in the Ethernet station address PROM at the expected I/O address for the
+ Ethernet PROM. The shared memory base address is 'autoprobed' by
+ looking for the self test PROM and detecting the card name. When a
+ second DEPCA is detected, information is placed in the base_addr
+ variable of the next device structure (which is created if necessary),
+ thus enabling ethif_probe initialization for the device. More than 2
+ EISA cards can be supported, but care will be needed assigning the
+ shared memory to ensure that each slot has the correct IRQ, I/O address
+ and shared memory address assigned.
+
+ ************************************************************************
+
+ NOTE: If you are using two ISA DEPCAs, it is important that you assign
+ the base memory addresses correctly. The driver autoprobes I/O 0x300
+ then 0x200. The base memory address for the first device must be less
+ than that of the second so that the auto probe will correctly assign the
+ I/O and memory addresses on the same card. I can't think of a way to do
+ this unambiguously at the moment, since there is nothing on the cards to
+ tie I/O and memory information together.
+
+ I am unable to test 2 cards together for now, so this code is
+ unchecked. All reports, good or bad, are welcome.
+
+ ************************************************************************
+
+ The board IRQ setting must be at an unused IRQ which is auto-probed
+ using Donald Becker's autoprobe routines. DEPCA and DE100 board IRQs are
+ {2,3,4,5,7}, whereas the DE200 is at {5,9,10,11,15}. Note that IRQ2 is
+ really IRQ9 in machines with 16 IRQ lines.
+
+ No 16MB memory limitation should exist with this driver as DMA is not
+ used and the common memory area is in low memory on the network card (my
+ current system has 20MB and I've not had problems yet).
+
+ The ability to load this driver as a loadable module has been added. To
+ utilise this ability, you have to do <8 things:
+
+ 0) have a copy of the loadable modules code installed on your system.
+ 1) copy depca.c from the /linux/drivers/net directory to your favourite
+ temporary directory.
+ 2) if you wish, edit the source code near line 1530 to reflect the I/O
+ address and IRQ you're using (see also 5).
+ 3) compile depca.c, but include -DMODULE in the command line to ensure
+ that the correct bits are compiled (see end of source code).
+ 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a
+ kernel with the depca configuration turned off and reboot.
+ 5) insmod depca.o [irq=7] [io=0x200] [mem=0xd0000] [adapter_name=DE100]
+ [Alan Cox: Changed the code to allow command line irq/io assignments]
+ [Dave Davies: Changed the code to allow command line mem/name
+ assignments]
+ 6) run the net startup bits for your eth?? interface manually
+ (usually /etc/rc.inet[12] at boot time).
+ 7) enjoy!
+
+ Note that autoprobing is not allowed in loadable modules - the system is
+ already up and running and you're messing with interrupts.
+
+ To unload a module, turn off the associated interface
+ 'ifconfig eth?? down' then 'rmmod depca'.
+
+ To assign a base memory address for the shared memory when running as a
+ loadable module, see 5 above. To include the adapter name (if you have
+ no PROM but know the card name) also see 5 above. Note that this last
+ option will not work with kernel built-in depca's.
+
+ The shared memory assignment for a loadable module makes sense to avoid
+ the 'memory autoprobe' picking the wrong shared memory (for the case of
+ 2 depca's in a PC).
+
+
+ TO DO:
+ ------
+
+
+ Revision History
+ ----------------
+
+ Version Date Description
+
+ 0.1 25-jan-94 Initial writing.
+ 0.2 27-jan-94 Added LANCE TX hardware buffer chaining.
+ 0.3 1-feb-94 Added multiple DEPCA support.
+ 0.31 4-feb-94 Added DE202 recognition.
+ 0.32 19-feb-94 Tidy up. Improve multi-DEPCA support.
+ 0.33 25-feb-94 Fix DEPCA ethernet ROM counter enable.
+ Add jabber packet fix from murf@perftech.com
+ and becker@super.org
+ 0.34 7-mar-94 Fix DEPCA max network memory RAM & NICSR access.
+ 0.35 8-mar-94 Added DE201 recognition. Tidied up.
+ 0.351 30-apr-94 Added EISA support. Added DE422 recognition.
+ 0.36 16-may-94 DE422 fix released.
+ 0.37 22-jul-94 Added MODULE support
+ 0.38 15-aug-94 Added DBR ROM switch in depca_close().
+ Multi DEPCA bug fix.
+ 0.38axp 15-sep-94 Special version for Alpha AXP Linux V1.0.
+ 0.381 12-dec-94 Added DE101 recognition, fix multicast bug.
+ 0.382 9-feb-95 Fix recognition bug reported by <bkm@star.rl.ac.uk>.
+ 0.383 22-feb-95 Fix for conflict with VESA SCSI reported by
+ <stromain@alf.dec.com>
+ 0.384 17-mar-95 Fix a ring full bug reported by <bkm@star.rl.ac.uk>
+ 0.385 3-apr-95 Fix a recognition bug reported by
+ <ryan.niemi@lastfrontier.com>
+ 0.386 21-apr-95 Fix the last fix...sorry, must be galloping senility
+ 0.40 25-May-95 Rewrite for portability & updated.
+ ALPHA support from <jestabro@amt.tay1.dec.com>
+ 0.41 26-Jun-95 Added verify_area() calls in depca_ioctl() from
+ suggestion by <heiko@colossus.escape.de>
+ 0.42 27-Dec-95 Add 'mem' shared memory assignment for loadable
+ modules.
+ Add 'adapter_name' for loadable modules when no PROM.
+ Both above from a suggestion by
+ <pchen@woodruffs121.residence.gatech.edu>.
+ Add new multicasting code.
+ 0.421 22-Apr-96 Fix alloc_device() bug <jari@markkus2.fimr.fi>
+ 0.422 29-Apr-96 Fix depca_hw_init() bug <jari@markkus2.fimr.fi>
+ 0.423 7-Jun-96 Fix module load bug <kmg@barco.be>
+ 0.43 16-Aug-96 Update alloc_device() to conform to de4x5.c
+
+ =========================================================================
+ */
static const char *version = "depca.c:v0.43 96/8/16 davies@maniac.ultranet.com\n";
@@ -242,1647 +242,1650 @@ static int depca_debug = DEPCA_DEBUG;
static int depca_debug = 1;
#endif
-#define DEPCA_NDA 0xffe0 /* No Device Address */
+#define DEPCA_NDA 0xffe0 /* No Device Address */
/*
-** Ethernet PROM defines
-*/
+ ** Ethernet PROM defines
+ */
#define PROBE_LENGTH 32
#define ETH_PROM_SIG 0xAA5500FFUL
/*
-** Set the number of Tx and Rx buffers. Ensure that the memory requested
-** here is <= to the amount of shared memory set up by the board switches.
-** The number of descriptors MUST BE A POWER OF 2.
-**
-** total_memory = NUM_RX_DESC*(8+RX_BUFF_SZ) + NUM_TX_DESC*(8+TX_BUFF_SZ)
-*/
-#define NUM_RX_DESC 8 /* Number of RX descriptors */
-#define NUM_TX_DESC 8 /* Number of TX descriptors */
-#define RX_BUFF_SZ 1536 /* Buffer size for each Rx buffer */
-#define TX_BUFF_SZ 1536 /* Buffer size for each Tx buffer */
-
-#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */
-#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */
+ ** Set the number of Tx and Rx buffers. Ensure that the memory requested
+ ** here is <= to the amount of shared memory set up by the board switches.
+ ** The number of descriptors MUST BE A POWER OF 2.
+ **
+ ** total_memory = NUM_RX_DESC*(8+RX_BUFF_SZ) + NUM_TX_DESC*(8+TX_BUFF_SZ)
+ */
+#define NUM_RX_DESC 8 /* Number of RX descriptors */
+#define NUM_TX_DESC 8 /* Number of TX descriptors */
+#define RX_BUFF_SZ 1536 /* Buffer size for each Rx buffer */
+#define TX_BUFF_SZ 1536 /* Buffer size for each Tx buffer */
+
+#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */
+#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */
/*
-** EISA bus defines
-*/
-#define DEPCA_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */
+ ** EISA bus defines
+ */
+#define DEPCA_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */
#define MAX_EISA_SLOTS 16
#define EISA_SLOT_INC 0x1000
/*
-** ISA Bus defines
-*/
+ ** ISA Bus defines
+ */
#define DEPCA_RAM_BASE_ADDRESSES {0xc0000,0xd0000,0xe0000,0x00000}
#define DEPCA_IO_PORTS {0x300, 0x200, 0}
#define DEPCA_TOTAL_SIZE 0x10
static short mem_chkd = 0;
/*
-** Name <-> Adapter mapping
-*/
+ ** Name <-> Adapter mapping
+ */
#define DEPCA_SIGNATURE {"DEPCA",\
"DE100","DE101",\
"DE200","DE201","DE202",\
"DE210",\
"DE422",\
""}
-static enum {DEPCA, de100, de101, de200, de201, de202, de210, de422, unknown} adapter;
+static enum {
+ DEPCA, de100, de101, de200, de201, de202, de210, de422, unknown
+} adapter;
/*
-** Miscellaneous info...
-*/
+ ** Miscellaneous info...
+ */
#define DEPCA_STRLEN 16
#define MAX_NUM_DEPCAS 2
/*
-** Memory Alignment. Each descriptor is 4 longwords long. To force a
-** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and
-** DESC_ALIGN. ALIGN aligns the start address of the private memory area
-** and hence the RX descriptor ring's first entry.
-*/
-#define ALIGN4 ((u_long)4 - 1) /* 1 longword align */
-#define ALIGN8 ((u_long)8 - 1) /* 2 longword (quadword) align */
-#define ALIGN ALIGN8 /* Keep the LANCE happy... */
+ ** Memory Alignment. Each descriptor is 4 longwords long. To force a
+ ** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and
+ ** DESC_ALIGN. ALIGN aligns the start address of the private memory area
+ ** and hence the RX descriptor ring's first entry.
+ */
+#define ALIGN4 ((u_long)4 - 1) /* 1 longword align */
+#define ALIGN8 ((u_long)8 - 1) /* 2 longword (quadword) align */
+#define ALIGN ALIGN8 /* Keep the LANCE happy... */
/*
-** The DEPCA Rx and Tx ring descriptors.
-*/
+ ** The DEPCA Rx and Tx ring descriptors.
+ */
struct depca_rx_desc {
- volatile s32 base;
- s16 buf_length; /* This length is negative 2's complement! */
- s16 msg_length; /* This length is "normal". */
+ volatile s32 base;
+ s16 buf_length; /* This length is negative 2's complement! */
+ s16 msg_length; /* This length is "normal". */
};
struct depca_tx_desc {
- volatile s32 base;
- s16 length; /* This length is negative 2's complement! */
- s16 misc; /* Errors and TDR info */
+ volatile s32 base;
+ s16 length; /* This length is negative 2's complement! */
+ s16 misc; /* Errors and TDR info */
};
-#define LA_MASK 0x0000ffff /* LANCE address mask for mapping network RAM
+#define LA_MASK 0x0000ffff /* LANCE address mask for mapping network RAM
to LANCE memory address space */
/*
-** The Lance initialization block, described in databook, in common memory.
-*/
+ ** The Lance initialization block, described in databook, in common memory.
+ */
struct depca_init {
- u16 mode; /* Mode register */
- u8 phys_addr[ETH_ALEN]; /* Physical ethernet address */
- u8 mcast_table[8]; /* Multicast Hash Table. */
- u32 rx_ring; /* Rx ring base pointer & ring length */
- u32 tx_ring; /* Tx ring base pointer & ring length */
+ u16 mode; /* Mode register */
+ u8 phys_addr[ETH_ALEN]; /* Physical ethernet address */
+ u8 mcast_table[8]; /* Multicast Hash Table. */
+ u32 rx_ring; /* Rx ring base pointer & ring length */
+ u32 tx_ring; /* Tx ring base pointer & ring length */
};
#define DEPCA_PKT_STAT_SZ 16
-#define DEPCA_PKT_BIN_SZ 128 /* Should be >=100 unless you
- increase DEPCA_PKT_STAT_SZ */
+#define DEPCA_PKT_BIN_SZ 128 /* Should be >=100 unless you
+ increase DEPCA_PKT_STAT_SZ */
struct depca_private {
- char devname[DEPCA_STRLEN]; /* Device Product String */
- char adapter_name[DEPCA_STRLEN];/* /proc/ioports string */
- char adapter; /* Adapter type */
- struct depca_rx_desc *rx_ring; /* Pointer to start of RX descriptor ring */
- struct depca_tx_desc *tx_ring; /* Pointer to start of TX descriptor ring */
- struct depca_init init_block;/* Shadow Initialization block */
- char *rx_memcpy[NUM_RX_DESC]; /* CPU virt address of sh'd memory buffs */
- char *tx_memcpy[NUM_TX_DESC]; /* CPU virt address of sh'd memory buffs */
- u_long bus_offset; /* (E)ISA bus address offset vs LANCE */
- u_long sh_mem; /* Physical start addr of shared mem area */
- u_long dma_buffs; /* LANCE Rx and Tx buffers start address. */
- int rx_new, tx_new; /* The next free ring entry */
- int rx_old, tx_old; /* The ring entries to be free()ed. */
- struct net_device_stats stats;
- struct { /* Private stats counters */
- u32 bins[DEPCA_PKT_STAT_SZ];
- u32 unicast;
- u32 multicast;
- u32 broadcast;
- u32 excessive_collisions;
- u32 tx_underruns;
- u32 excessive_underruns;
- } pktStats;
- int txRingMask; /* TX ring mask */
- int rxRingMask; /* RX ring mask */
- s32 rx_rlen; /* log2(rxRingMask+1) for the descriptors */
- s32 tx_rlen; /* log2(txRingMask+1) for the descriptors */
+ char devname[DEPCA_STRLEN]; /* Device Product String */
+ char adapter_name[DEPCA_STRLEN]; /* /proc/ioports string */
+ char adapter; /* Adapter type */
+ struct depca_rx_desc *rx_ring; /* Pointer to start of RX descriptor ring */
+ struct depca_tx_desc *tx_ring; /* Pointer to start of TX descriptor ring */
+ struct depca_init init_block; /* Shadow Initialization block */
+ char *rx_memcpy[NUM_RX_DESC]; /* CPU virt address of sh'd memory buffs */
+ char *tx_memcpy[NUM_TX_DESC]; /* CPU virt address of sh'd memory buffs */
+ u_long bus_offset; /* (E)ISA bus address offset vs LANCE */
+ u_long sh_mem; /* Physical start addr of shared mem area */
+ u_long dma_buffs; /* LANCE Rx and Tx buffers start address. */
+ int rx_new, tx_new; /* The next free ring entry */
+ int rx_old, tx_old; /* The ring entries to be free()ed. */
+ struct net_device_stats stats;
+ struct { /* Private stats counters */
+ u32 bins[DEPCA_PKT_STAT_SZ];
+ u32 unicast;
+ u32 multicast;
+ u32 broadcast;
+ u32 excessive_collisions;
+ u32 tx_underruns;
+ u32 excessive_underruns;
+ } pktStats;
+ int txRingMask; /* TX ring mask */
+ int rxRingMask; /* RX ring mask */
+ s32 rx_rlen; /* log2(rxRingMask+1) for the descriptors */
+ s32 tx_rlen; /* log2(txRingMask+1) for the descriptors */
};
/*
-** The transmit ring full condition is described by the tx_old and tx_new
-** pointers by:
-** tx_old = tx_new Empty ring
-** tx_old = tx_new+1 Full ring
-** tx_old+txRingMask = tx_new Full ring (wrapped condition)
-*/
+ ** The transmit ring full condition is described by the tx_old and tx_new
+ ** pointers by:
+ ** tx_old = tx_new Empty ring
+ ** tx_old = tx_new+1 Full ring
+ ** tx_old+txRingMask = tx_new Full ring (wrapped condition)
+ */
#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
lp->tx_old+lp->txRingMask-lp->tx_new:\
lp->tx_old -lp->tx_new-1)
/*
-** Public Functions
-*/
-static int depca_open(struct device *dev);
-static int depca_start_xmit(struct sk_buff *skb, struct device *dev);
-static void depca_interrupt(int irq, void *dev_id, struct pt_regs * regs);
-static int depca_close(struct device *dev);
-static int depca_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+ ** Public Functions
+ */
+static int depca_open(struct device *dev);
+static int depca_start_xmit(struct sk_buff *skb, struct device *dev);
+static void depca_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static int depca_close(struct device *dev);
+static int depca_ioctl(struct device *dev, struct ifreq *rq, int cmd);
static struct net_device_stats *depca_get_stats(struct device *dev);
-static void set_multicast_list(struct device *dev);
+static void set_multicast_list(struct device *dev);
/*
-** Private functions
-*/
-static int depca_hw_init(struct device *dev, u_long ioaddr);
-static void depca_init_ring(struct device *dev);
-static int depca_rx(struct device *dev);
-static int depca_tx(struct device *dev);
-
-static void LoadCSRs(struct device *dev);
-static int InitRestartDepca(struct device *dev);
-static void DepcaSignature(char *name, u_long paddr);
-static int DevicePresent(u_long ioaddr);
-static int get_hw_addr(struct device *dev);
-static int EISA_signature(char *name, s32 eisa_id);
-static void SetMulticastFilter(struct device *dev);
-static void isa_probe(struct device *dev, u_long iobase);
-static void eisa_probe(struct device *dev, u_long iobase);
+ ** Private functions
+ */
+static int depca_hw_init(struct device *dev, u_long ioaddr);
+static void depca_init_ring(struct device *dev);
+static int depca_rx(struct device *dev);
+static int depca_tx(struct device *dev);
+
+static void LoadCSRs(struct device *dev);
+static int InitRestartDepca(struct device *dev);
+static void DepcaSignature(char *name, u_long paddr);
+static int DevicePresent(u_long ioaddr);
+static int get_hw_addr(struct device *dev);
+static int EISA_signature(char *name, s32 eisa_id);
+static void SetMulticastFilter(struct device *dev);
+static void isa_probe(struct device *dev, u_long iobase);
+static void eisa_probe(struct device *dev, u_long iobase);
static struct device *alloc_device(struct device *dev, u_long iobase);
-static int depca_dev_index(char *s);
-static struct device *insert_device(struct device *dev, u_long iobase, int (*init)(struct device *));
-static int load_packet(struct device *dev, struct sk_buff *skb);
-static void depca_dbg_open(struct device *dev);
+static int depca_dev_index(char *s);
+static struct device *insert_device(struct device *dev, u_long iobase, int (*init) (struct device *));
+static int load_packet(struct device *dev, struct sk_buff *skb);
+static void depca_dbg_open(struct device *dev);
#ifdef MODULE
-int init_module(void);
-void cleanup_module(void);
-static int autoprobed = 1, loading_module = 1;
-# else
-static u_char de1xx_irq[] __initdata = {2,3,4,5,7,9,0};
-static u_char de2xx_irq[] __initdata = {5,9,10,11,15,0};
-static u_char de422_irq[] __initdata = {5,9,10,11,0};
+int init_module(void);
+void cleanup_module(void);
+static int autoprobed = 1, loading_module = 1;
+#else
+static u_char de1xx_irq[] __initdata =
+{2, 3, 4, 5, 7, 9, 0};
+static u_char de2xx_irq[] __initdata =
+{5, 9, 10, 11, 15, 0};
+static u_char de422_irq[] __initdata =
+{5, 9, 10, 11, 0};
static u_char *depca_irq;
-static int autoprobed = 0, loading_module = 0;
-#endif /* MODULE */
-
-static char name[DEPCA_STRLEN];
-static int num_depcas = 0, num_eth = 0;
-static int mem=0; /* For loadable module assignment
- use insmod mem=0x????? .... */
-static char *adapter_name = '\0'; /* If no PROM when loadable module
- use insmod adapter_name=DE??? ...
- */
+static int autoprobed = 0, loading_module = 0;
+#endif /* MODULE */
+
+static char name[DEPCA_STRLEN];
+static int num_depcas = 0, num_eth = 0;
+static int mem = 0; /* For loadable module assignment
+ use insmod mem=0x????? .... */
+static char *adapter_name = '\0'; /* If no PROM when loadable module
+ use insmod adapter_name=DE??? ...
+ */
/*
-** Miscellaneous defines...
-*/
+ ** Miscellaneous defines...
+ */
#define STOP_DEPCA \
outw(CSR0, DEPCA_ADDR);\
outw(STOP, DEPCA_DATA)
-
+
__initfunc(int depca_probe(struct device *dev))
{
- int tmp = num_depcas, status = -ENODEV;
- u_long iobase = dev->base_addr;
-
- if ((iobase == 0) && loading_module){
- printk("Autoprobing is not supported when loading a module based driver.\n");
- status = -EIO;
- } else {
- isa_probe(dev, iobase);
- eisa_probe(dev, iobase);
-
- if ((tmp == num_depcas) && (iobase != 0) && loading_module) {
- printk("%s: depca_probe() cannot find device at 0x%04lx.\n", dev->name,
- iobase);
- }
-
- /*
- ** Walk the device list to check that at least one device
- ** initialised OK
- */
- for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next);
-
- if (dev->priv) status = 0;
- if (iobase == 0) autoprobed = 1;
- }
-
- return status;
+ int tmp = num_depcas, status = -ENODEV;
+ u_long iobase = dev->base_addr;
+
+ if ((iobase == 0) && loading_module) {
+ printk("Autoprobing is not supported when loading a module based driver.\n");
+ status = -EIO;
+ } else {
+ isa_probe(dev, iobase);
+ eisa_probe(dev, iobase);
+
+ if ((tmp == num_depcas) && (iobase != 0) && loading_module) {
+ printk("%s: depca_probe() cannot find device at 0x%04lx.\n", dev->name,
+ iobase);
+ }
+ /*
+ ** Walk the device list to check that at least one device
+ ** initialised OK
+ */
+ for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next);
+
+ if (dev->priv)
+ status = 0;
+ if (iobase == 0)
+ autoprobed = 1;
+ }
+
+ return status;
}
__initfunc(static int
-depca_hw_init(struct device *dev, u_long ioaddr))
+ depca_hw_init(struct device *dev, u_long ioaddr))
{
- struct depca_private *lp;
- int i, j, offset, netRAM, mem_len, status=0;
- s16 nicsr;
- u_long mem_start=0, mem_base[] = DEPCA_RAM_BASE_ADDRESSES;
-
- STOP_DEPCA;
-
- nicsr = inb(DEPCA_NICSR);
- nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | IM);
- outb(nicsr, DEPCA_NICSR);
-
- if (inw(DEPCA_DATA) == STOP) {
- do {
- strcpy(name, (adapter_name ? adapter_name : ""));
- mem_start = (mem ? mem & 0xf0000 : mem_base[mem_chkd++]);
- DepcaSignature(name, mem_start);
- } while (!mem && mem_base[mem_chkd] && (adapter == unknown));
-
- if ((adapter != unknown) && mem_start) { /* found a DEPCA device */
- dev->base_addr = ioaddr;
-
- if ((ioaddr&0x0fff)==DEPCA_EISA_IO_PORTS) {/* EISA slot address */
- printk("%s: %s at 0x%04lx (EISA slot %d)",
- dev->name, name, ioaddr, (int)((ioaddr>>12)&0x0f));
- } else { /* ISA port address */
- printk("%s: %s at 0x%04lx", dev->name, name, ioaddr);
- }
-
- printk(", h/w address ");
- status = get_hw_addr(dev);
- for (i=0; i<ETH_ALEN - 1; i++) { /* get the ethernet address */
- printk("%2.2x:", dev->dev_addr[i]);
- }
- printk("%2.2x", dev->dev_addr[i]);
-
- if (status == 0) {
- /* Set up the maximum amount of network RAM(kB) */
- netRAM = ((adapter != DEPCA) ? 64 : 48);
- if ((nicsr & _128KB) && (adapter == de422)) netRAM = 128;
- offset = 0x0000;
-
- /* Shared Memory Base Address */
- if (nicsr & BUF) {
- offset = 0x8000; /* 32kbyte RAM offset*/
- nicsr &= ~BS; /* DEPCA RAM in top 32k */
- netRAM -= 32;
- }
- mem_start += offset; /* (E)ISA start address */
- if ((mem_len = (NUM_RX_DESC*(sizeof(struct depca_rx_desc)+RX_BUFF_SZ) +
- NUM_TX_DESC*(sizeof(struct depca_tx_desc)+TX_BUFF_SZ) +
- sizeof(struct depca_init))) <=
- (netRAM<<10)) {
- printk(",\n has %dkB RAM at 0x%.5lx", netRAM, mem_start);
-
- /* Enable the shadow RAM. */
- if (adapter != DEPCA) {
- nicsr |= SHE;
- outb(nicsr, DEPCA_NICSR);
- }
-
- /* Define the device private memory */
- dev->priv = (void *) kmalloc(sizeof(struct depca_private), GFP_KERNEL);
- if (dev->priv == NULL)
- return -ENOMEM;
- lp = (struct depca_private *)dev->priv;
- memset((char *)dev->priv, 0, sizeof(struct depca_private));
- lp->adapter = adapter;
- sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
- request_region(ioaddr, DEPCA_TOTAL_SIZE, lp->adapter_name);
-
- /* Initialisation Block */
- lp->sh_mem = mem_start;
- mem_start += sizeof(struct depca_init);
-
- /* Tx & Rx descriptors (aligned to a quadword boundary) */
- mem_start = (mem_start + ALIGN) & ~ALIGN;
- lp->rx_ring = (struct depca_rx_desc *)mem_start;
-
- mem_start += (sizeof(struct depca_rx_desc) * NUM_RX_DESC);
- lp->tx_ring = (struct depca_tx_desc *)mem_start;
-
- mem_start += (sizeof(struct depca_tx_desc) * NUM_TX_DESC);
- lp->bus_offset = mem_start & 0x00ff0000;
- mem_start &= LA_MASK; /* LANCE re-mapped start address */
-
- lp->dma_buffs = mem_start;
-
- /* Finish initialising the ring information. */
- lp->rxRingMask = NUM_RX_DESC - 1;
- lp->txRingMask = NUM_TX_DESC - 1;
-
- /* Calculate Tx/Rx RLEN size for the descriptors. */
- for (i=0, j = lp->rxRingMask; j>0; i++) {
- j >>= 1;
- }
- lp->rx_rlen = (s32)(i << 29);
- for (i=0, j = lp->txRingMask; j>0; i++) {
- j >>= 1;
- }
- lp->tx_rlen = (s32)(i << 29);
-
- /* Load the initialisation block */
- depca_init_ring(dev);
-
- /* Initialise the control and status registers */
- LoadCSRs(dev);
-
- /* Enable DEPCA board interrupts for autoprobing */
- nicsr = ((nicsr & ~IM)|IEN);
- outb(nicsr, DEPCA_NICSR);
-
- /* To auto-IRQ we enable the initialization-done and DMA err,
- interrupts. For now we will always get a DMA error. */
- if (dev->irq < 2) {
+ struct depca_private *lp;
+ int i, j, offset, netRAM, mem_len, status = 0;
+ s16 nicsr;
+ u_long mem_start = 0, mem_base[] = DEPCA_RAM_BASE_ADDRESSES;
+
+ STOP_DEPCA;
+
+ nicsr = inb(DEPCA_NICSR);
+ nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | IM);
+ outb(nicsr, DEPCA_NICSR);
+
+ if (inw(DEPCA_DATA) == STOP) {
+ do {
+ strcpy(name, (adapter_name ? adapter_name : ""));
+ mem_start = (mem ? mem & 0xf0000 : mem_base[mem_chkd++]);
+ DepcaSignature(name, mem_start);
+ } while (!mem && mem_base[mem_chkd] && (adapter == unknown));
+
+ if ((adapter != unknown) && mem_start) { /* found a DEPCA device */
+ dev->base_addr = ioaddr;
+
+ if ((ioaddr & 0x0fff) == DEPCA_EISA_IO_PORTS) { /* EISA slot address */
+ printk("%s: %s at 0x%04lx (EISA slot %d)",
+ dev->name, name, ioaddr, (int) ((ioaddr >> 12) & 0x0f));
+ } else { /* ISA port address */
+ printk("%s: %s at 0x%04lx", dev->name, name, ioaddr);
+ }
+
+ printk(", h/w address ");
+ status = get_hw_addr(dev);
+ for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet address */
+ printk("%2.2x:", dev->dev_addr[i]);
+ }
+ printk("%2.2x", dev->dev_addr[i]);
+
+ if (status == 0) {
+ /* Set up the maximum amount of network RAM(kB) */
+ netRAM = ((adapter != DEPCA) ? 64 : 48);
+ if ((nicsr & _128KB) && (adapter == de422))
+ netRAM = 128;
+ offset = 0x0000;
+
+ /* Shared Memory Base Address */
+ if (nicsr & BUF) {
+ offset = 0x8000; /* 32kbyte RAM offset */
+ nicsr &= ~BS; /* DEPCA RAM in top 32k */
+ netRAM -= 32;
+ }
+ mem_start += offset; /* (E)ISA start address */
+ if ((mem_len = (NUM_RX_DESC * (sizeof(struct depca_rx_desc) + RX_BUFF_SZ) +
+ NUM_TX_DESC * (sizeof(struct depca_tx_desc) + TX_BUFF_SZ) +
+ sizeof(struct depca_init))) <=
+ (netRAM << 10)) {
+ printk(",\n has %dkB RAM at 0x%.5lx", netRAM, mem_start);
+
+ /* Enable the shadow RAM. */
+ if (adapter != DEPCA) {
+ nicsr |= SHE;
+ outb(nicsr, DEPCA_NICSR);
+ }
+ /* Define the device private memory */
+ dev->priv = (void *) kmalloc(sizeof(struct depca_private), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ lp = (struct depca_private *) dev->priv;
+ memset((char *) dev->priv, 0, sizeof(struct depca_private));
+ lp->adapter = adapter;
+ sprintf(lp->adapter_name, "%s (%s)", name, dev->name);
+ request_region(ioaddr, DEPCA_TOTAL_SIZE, lp->adapter_name);
+
+ /* Initialisation Block */
+ lp->sh_mem = mem_start;
+ mem_start += sizeof(struct depca_init);
+
+ /* Tx & Rx descriptors (aligned to a quadword boundary) */
+ mem_start = (mem_start + ALIGN) & ~ALIGN;
+ lp->rx_ring = (struct depca_rx_desc *) mem_start;
+
+ mem_start += (sizeof(struct depca_rx_desc) * NUM_RX_DESC);
+ lp->tx_ring = (struct depca_tx_desc *) mem_start;
+
+ mem_start += (sizeof(struct depca_tx_desc) * NUM_TX_DESC);
+ lp->bus_offset = mem_start & 0x00ff0000;
+ mem_start &= LA_MASK; /* LANCE re-mapped start address */
+
+ lp->dma_buffs = mem_start;
+
+ /* Finish initialising the ring information. */
+ lp->rxRingMask = NUM_RX_DESC - 1;
+ lp->txRingMask = NUM_TX_DESC - 1;
+
+ /* Calculate Tx/Rx RLEN size for the descriptors. */
+ for (i = 0, j = lp->rxRingMask; j > 0; i++) {
+ j >>= 1;
+ }
+ lp->rx_rlen = (s32) (i << 29);
+ for (i = 0, j = lp->txRingMask; j > 0; i++) {
+ j >>= 1;
+ }
+ lp->tx_rlen = (s32) (i << 29);
+
+ /* Load the initialisation block */
+ depca_init_ring(dev);
+
+ /* Initialise the control and status registers */
+ LoadCSRs(dev);
+
+ /* Enable DEPCA board interrupts for autoprobing */
+ nicsr = ((nicsr & ~IM) | IEN);
+ outb(nicsr, DEPCA_NICSR);
+
+ /* To auto-IRQ we enable the initialization-done and DMA err,
+ interrupts. For now we will always get a DMA error. */
+ if (dev->irq < 2) {
#ifndef MODULE
- unsigned char irqnum;
- autoirq_setup(0);
-
- /* Assign the correct irq list */
- switch (lp->adapter) {
- case DEPCA:
- case de100:
- case de101:
- depca_irq = de1xx_irq;
- break;
- case de200:
- case de201:
- case de202:
- case de210:
- depca_irq = de2xx_irq;
- break;
- case de422:
- depca_irq = de422_irq;
- break;
- }
-
- /* Trigger an initialization just for the interrupt. */
- outw(INEA | INIT, DEPCA_DATA);
-
- irqnum = autoirq_report(1);
- if (!irqnum) {
- printk(" and failed to detect IRQ line.\n");
- status = -ENXIO;
- } else {
- for (dev->irq=0,i=0; (depca_irq[i]) && (!dev->irq); i++) {
- if (irqnum == depca_irq[i]) {
- dev->irq = irqnum;
- printk(" and uses IRQ%d.\n", dev->irq);
+ unsigned char irqnum;
+ autoirq_setup(0);
+
+ /* Assign the correct irq list */
+ switch (lp->adapter) {
+ case DEPCA:
+ case de100:
+ case de101:
+ depca_irq = de1xx_irq;
+ break;
+ case de200:
+ case de201:
+ case de202:
+ case de210:
+ depca_irq = de2xx_irq;
+ break;
+ case de422:
+ depca_irq = de422_irq;
+ break;
+ }
+
+ /* Trigger an initialization just for the interrupt. */
+ outw(INEA | INIT, DEPCA_DATA);
+
+ irqnum = autoirq_report(1);
+ if (!irqnum) {
+ printk(" and failed to detect IRQ line.\n");
+ status = -ENXIO;
+ } else {
+ for (dev->irq = 0, i = 0; (depca_irq[i]) && (!dev->irq); i++) {
+ if (irqnum == depca_irq[i]) {
+ dev->irq = irqnum;
+ printk(" and uses IRQ%d.\n", dev->irq);
+ }
+ }
+
+ if (!dev->irq) {
+ printk(" but incorrect IRQ line detected.\n");
+ status = -ENXIO;
+ }
+ }
+#endif /* MODULE */
+ } else {
+ printk(" and assigned IRQ%d.\n", dev->irq);
+ }
+ if (status)
+ release_region(ioaddr, DEPCA_TOTAL_SIZE);
+ } else {
+ printk(",\n requests %dkB RAM: only %dkB is available!\n",
+ (mem_len >> 10), netRAM);
+ status = -ENXIO;
+ }
+ } else {
+ printk(" which has an Ethernet PROM CRC error.\n");
+ status = -ENXIO;
+ }
+ } else {
+ status = -ENXIO;
+ }
+ if (!status) {
+ if (depca_debug > 1) {
+ printk(version);
+ }
+ /* The DEPCA-specific entries in the device structure. */
+ dev->open = &depca_open;
+ dev->hard_start_xmit = &depca_start_xmit;
+ dev->stop = &depca_close;
+ dev->get_stats = &depca_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+ dev->do_ioctl = &depca_ioctl;
+
+ dev->mem_start = 0;
+
+ /* Fill in the generic field of the device structure. */
+ ether_setup(dev);
+ } else { /* Incorrectly initialised hardware */
+ if (dev->priv) {
+ kfree_s(dev->priv, sizeof(struct depca_private));
+ dev->priv = NULL;
+ }
}
- }
-
- if (!dev->irq) {
- printk(" but incorrect IRQ line detected.\n");
- status = -ENXIO;
- }
- }
-#endif /* MODULE */
- } else {
- printk(" and assigned IRQ%d.\n", dev->irq);
- }
- if (status) release_region(ioaddr, DEPCA_TOTAL_SIZE);
} else {
- printk(",\n requests %dkB RAM: only %dkB is available!\n",
- (mem_len>>10), netRAM);
- status = -ENXIO;
+ status = -ENXIO;
}
- } else {
- printk(" which has an Ethernet PROM CRC error.\n");
- status = -ENXIO;
- }
- } else {
- status = -ENXIO;
- }
- if (!status) {
- if (depca_debug > 1) {
- printk(version);
- }
-
- /* The DEPCA-specific entries in the device structure. */
- dev->open = &depca_open;
- dev->hard_start_xmit = &depca_start_xmit;
- dev->stop = &depca_close;
- dev->get_stats = &depca_get_stats;
- dev->set_multicast_list = &set_multicast_list;
- dev->do_ioctl = &depca_ioctl;
-
- dev->mem_start = 0;
-
- /* Fill in the generic field of the device structure. */
- ether_setup(dev);
- } else { /* Incorrectly initialised hardware */
- if (dev->priv) {
- kfree_s(dev->priv, sizeof(struct depca_private));
- dev->priv = NULL;
- }
- }
- } else {
- status = -ENXIO;
- }
-
- return status;
-}
+ return status;
+}
-static int
-depca_open(struct device *dev)
-{
- struct depca_private *lp = (struct depca_private *)dev->priv;
- u_long ioaddr = dev->base_addr;
- s16 nicsr;
- int status = 0;
- irq2dev_map[dev->irq] = dev;
- STOP_DEPCA;
- nicsr = inb(DEPCA_NICSR);
-
- /* Make sure the shadow RAM is enabled */
- if (adapter != DEPCA) {
- nicsr |= SHE;
- outb(nicsr, DEPCA_NICSR);
- }
-
- /* Re-initialize the DEPCA... */
- depca_init_ring(dev);
- LoadCSRs(dev);
+static int depca_open(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *) dev->priv;
+ u_long ioaddr = dev->base_addr;
+ s16 nicsr;
+ int status = 0;
+
+ STOP_DEPCA;
+ nicsr = inb(DEPCA_NICSR);
+
+ /* Make sure the shadow RAM is enabled */
+ if (adapter != DEPCA) {
+ nicsr |= SHE;
+ outb(nicsr, DEPCA_NICSR);
+ }
+ /* Re-initialize the DEPCA... */
+ depca_init_ring(dev);
+ LoadCSRs(dev);
- depca_dbg_open(dev);
+ depca_dbg_open(dev);
- if (request_irq(dev->irq, &depca_interrupt, 0, lp->adapter_name, NULL)) {
- printk("depca_open(): Requested IRQ%d is busy\n",dev->irq);
- status = -EAGAIN;
- } else {
+ if (request_irq(dev->irq, &depca_interrupt, 0, lp->adapter_name, dev)) {
+ printk("depca_open(): Requested IRQ%d is busy\n", dev->irq);
+ status = -EAGAIN;
+ } else {
- /* Enable DEPCA board interrupts and turn off LED */
- nicsr = ((nicsr & ~IM & ~LED)|IEN);
- outb(nicsr, DEPCA_NICSR);
- outw(CSR0,DEPCA_ADDR);
+ /* Enable DEPCA board interrupts and turn off LED */
+ nicsr = ((nicsr & ~IM & ~LED) | IEN);
+ outb(nicsr, DEPCA_NICSR);
+ outw(CSR0, DEPCA_ADDR);
- dev->tbusy = 0;
- dev->interrupt = 0;
- dev->start = 1;
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
- status = InitRestartDepca(dev);
+ status = InitRestartDepca(dev);
- if (depca_debug > 1){
- printk("CSR0: 0x%4.4x\n",inw(DEPCA_DATA));
- printk("nicsr: 0x%02x\n",inb(DEPCA_NICSR));
- }
- }
+ if (depca_debug > 1) {
+ printk("CSR0: 0x%4.4x\n", inw(DEPCA_DATA));
+ printk("nicsr: 0x%02x\n", inb(DEPCA_NICSR));
+ }
+ }
- MOD_INC_USE_COUNT;
+ MOD_INC_USE_COUNT;
- return status;
+ return status;
}
/* Initialize the lance Rx and Tx descriptor rings. */
-static void
-depca_init_ring(struct device *dev)
+static void depca_init_ring(struct device *dev)
{
- struct depca_private *lp = (struct depca_private *)dev->priv;
- u_int i;
- u_long p;
+ struct depca_private *lp = (struct depca_private *) dev->priv;
+ u_int i;
+ u_long p;
- /* Lock out other processes whilst setting up the hardware */
- set_bit(0, (void *)&dev->tbusy);
+ /* Lock out other processes whilst setting up the hardware */
+ set_bit(0, (void *) &dev->tbusy);
- lp->rx_new = lp->tx_new = 0;
- lp->rx_old = lp->tx_old = 0;
+ lp->rx_new = lp->tx_new = 0;
+ lp->rx_old = lp->tx_old = 0;
- /* Initialize the base addresses and length of each buffer in the ring */
- for (i = 0; i <= lp->rxRingMask; i++) {
- writel((p=lp->dma_buffs+i*RX_BUFF_SZ) | R_OWN, &lp->rx_ring[i].base);
- writew(-RX_BUFF_SZ, &lp->rx_ring[i].buf_length);
- lp->rx_memcpy[i]=(char *)(p+lp->bus_offset);
- }
- for (i = 0; i <= lp->txRingMask; i++) {
- writel((p=lp->dma_buffs+(i+lp->txRingMask+1)*TX_BUFF_SZ) & 0x00ffffff,
- &lp->tx_ring[i].base);
- lp->tx_memcpy[i]=(char *)(p+lp->bus_offset);
- }
+ /* Initialize the base addresses and length of each buffer in the ring */
+ for (i = 0; i <= lp->rxRingMask; i++) {
+ writel((p = lp->dma_buffs + i * RX_BUFF_SZ) | R_OWN, &lp->rx_ring[i].base);
+ writew(-RX_BUFF_SZ, &lp->rx_ring[i].buf_length);
+ lp->rx_memcpy[i] = (char *) (p + lp->bus_offset);
+ }
+ for (i = 0; i <= lp->txRingMask; i++) {
+ writel((p = lp->dma_buffs + (i + lp->txRingMask + 1) * TX_BUFF_SZ) & 0x00ffffff,
+ &lp->tx_ring[i].base);
+ lp->tx_memcpy[i] = (char *) (p + lp->bus_offset);
+ }
- /* Set up the initialization block */
- lp->init_block.rx_ring = ((u32)((u_long)lp->rx_ring)&LA_MASK) | lp->rx_rlen;
- lp->init_block.tx_ring = ((u32)((u_long)lp->tx_ring)&LA_MASK) | lp->tx_rlen;
+ /* Set up the initialization block */
+ lp->init_block.rx_ring = ((u32) ((u_long) lp->rx_ring) & LA_MASK) | lp->rx_rlen;
+ lp->init_block.tx_ring = ((u32) ((u_long) lp->tx_ring) & LA_MASK) | lp->tx_rlen;
- SetMulticastFilter(dev);
+ SetMulticastFilter(dev);
- for (i = 0; i < ETH_ALEN; i++) {
- lp->init_block.phys_addr[i] = dev->dev_addr[i];
- }
+ for (i = 0; i < ETH_ALEN; i++) {
+ lp->init_block.phys_addr[i] = dev->dev_addr[i];
+ }
- lp->init_block.mode = 0x0000; /* Enable the Tx and Rx */
+ lp->init_block.mode = 0x0000; /* Enable the Tx and Rx */
- return;
+ return;
}
/*
-** Writes a socket buffer to TX descriptor ring and starts transmission
-*/
-static int
-depca_start_xmit(struct sk_buff *skb, struct device *dev)
+ ** Writes a socket buffer to TX descriptor ring and starts transmission
+ */
+static int depca_start_xmit(struct sk_buff *skb, struct device *dev)
{
- struct depca_private *lp = (struct depca_private *)dev->priv;
- u_long ioaddr = dev->base_addr;
- int status = 0;
-
- /* Transmitter timeout, serious problems. */
- if (dev->tbusy) {
- int tickssofar = jiffies - dev->trans_start;
- if (tickssofar < 1*HZ) {
- status = -1;
- } else {
- printk("%s: transmit timed out, status %04x, resetting.\n",
- dev->name, inw(DEPCA_DATA));
-
- STOP_DEPCA;
- depca_init_ring(dev);
- LoadCSRs(dev);
- dev->interrupt = UNMASK_INTERRUPTS;
- dev->start = 1;
- dev->tbusy=0;
- dev->trans_start = jiffies;
- InitRestartDepca(dev);
- }
- return status;
- } else if (skb == NULL) {
- dev_tint(dev);
- } else if (skb->len > 0) {
- /* Enforce 1 process per h/w access */
- if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
- printk("%s: Transmitter access conflict.\n", dev->name);
- status = -1;
- } else {
- if (TX_BUFFS_AVAIL) { /* Fill in a Tx ring entry */
- status = load_packet(dev, skb);
-
- if (!status) {
- /* Trigger an immediate send demand. */
- outw(CSR0, DEPCA_ADDR);
- outw(INEA | TDMD, DEPCA_DATA);
-
- dev->trans_start = jiffies;
- dev_kfree_skb(skb, FREE_WRITE);
+ struct depca_private *lp = (struct depca_private *) dev->priv;
+ u_long ioaddr = dev->base_addr;
+ int status = 0;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 1 * HZ) {
+ status = -1;
+ } else {
+ printk("%s: transmit timed out, status %04x, resetting.\n",
+ dev->name, inw(DEPCA_DATA));
+
+ STOP_DEPCA;
+ depca_init_ring(dev);
+ LoadCSRs(dev);
+ dev->interrupt = UNMASK_INTERRUPTS;
+ dev->start = 1;
+ dev->tbusy = 0;
+ dev->trans_start = jiffies;
+ InitRestartDepca(dev);
+ }
+ return status;
}
- if (TX_BUFFS_AVAIL) {
- dev->tbusy=0;
+ else if (skb->len > 0)
+ {
+ /* Enforce 1 process per h/w access */
+ if (test_and_set_bit(0, (void *) &dev->tbusy) != 0) {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ status = -1;
+ } else {
+ if (TX_BUFFS_AVAIL) { /* Fill in a Tx ring entry */
+ status = load_packet(dev, skb);
+
+ if (!status) {
+ /* Trigger an immediate send demand. */
+ outw(CSR0, DEPCA_ADDR);
+ outw(INEA | TDMD, DEPCA_DATA);
+
+ dev->trans_start = jiffies;
+ dev_kfree_skb(skb, FREE_WRITE);
+ }
+ if (TX_BUFFS_AVAIL) {
+ dev->tbusy = 0;
+ }
+ } else {
+ status = -1;
+ }
+ }
}
- } else {
- status = -1;
- }
- }
- }
-
- return status;
+ return status;
}
/*
-** The DEPCA interrupt handler.
-*/
-static void
-depca_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+ ** The DEPCA interrupt handler.
+ */
+static void depca_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- struct device *dev = (struct device *)(irq2dev_map[irq]);
- struct depca_private *lp;
- s16 csr0, nicsr;
- u_long ioaddr;
-
- if (dev == NULL) {
- printk ("depca_interrupt(): irq %d for unknown device.\n", irq);
- } else {
- lp = (struct depca_private *)dev->priv;
- ioaddr = dev->base_addr;
+ struct device *dev = dev_id;
+ struct depca_private *lp;
+ s16 csr0, nicsr;
+ u_long ioaddr;
- if (dev->interrupt)
- printk("%s: Re-entering the interrupt handler.\n", dev->name);
+ if (dev == NULL) {
+ printk("depca_interrupt(): irq %d for unknown device.\n", irq);
+ } else {
+ lp = (struct depca_private *) dev->priv;
+ ioaddr = dev->base_addr;
- dev->interrupt = MASK_INTERRUPTS;
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
- /* mask the DEPCA board interrupts and turn on the LED */
- nicsr = inb(DEPCA_NICSR);
- nicsr |= (IM|LED);
- outb(nicsr, DEPCA_NICSR);
+ dev->interrupt = MASK_INTERRUPTS;
- outw(CSR0, DEPCA_ADDR);
- csr0 = inw(DEPCA_DATA);
+ /* mask the DEPCA board interrupts and turn on the LED */
+ nicsr = inb(DEPCA_NICSR);
+ nicsr |= (IM | LED);
+ outb(nicsr, DEPCA_NICSR);
- /* Acknowledge all of the current interrupt sources ASAP. */
- outw(csr0 & INTE, DEPCA_DATA);
+ outw(CSR0, DEPCA_ADDR);
+ csr0 = inw(DEPCA_DATA);
- if (csr0 & RINT) /* Rx interrupt (packet arrived) */
- depca_rx(dev);
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ outw(csr0 & INTE, DEPCA_DATA);
- if (csr0 & TINT) /* Tx interrupt (packet sent) */
- depca_tx(dev);
+ if (csr0 & RINT) /* Rx interrupt (packet arrived) */
+ depca_rx(dev);
- if ((TX_BUFFS_AVAIL >= 0) && dev->tbusy) { /* any resources available? */
- dev->tbusy = 0; /* clear TX busy flag */
- mark_bh(NET_BH);
- }
+ if (csr0 & TINT) /* Tx interrupt (packet sent) */
+ depca_tx(dev);
- /* Unmask the DEPCA board interrupts and turn off the LED */
- nicsr = (nicsr & ~IM & ~LED);
- outb(nicsr, DEPCA_NICSR);
+ if ((TX_BUFFS_AVAIL >= 0) && dev->tbusy) { /* any resources available? */
+ dev->tbusy = 0; /* clear TX busy flag */
+ mark_bh(NET_BH);
+ }
+ /* Unmask the DEPCA board interrupts and turn off the LED */
+ nicsr = (nicsr & ~IM & ~LED);
+ outb(nicsr, DEPCA_NICSR);
- dev->interrupt = UNMASK_INTERRUPTS;
- }
+ dev->interrupt = UNMASK_INTERRUPTS;
+ }
- return;
+ return;
}
-static int
-depca_rx(struct device *dev)
+static int depca_rx(struct device *dev)
{
- struct depca_private *lp = (struct depca_private *)dev->priv;
- int i, entry;
- s32 status;
-
- for (entry=lp->rx_new;
- !(readl(&lp->rx_ring[entry].base) & R_OWN);
- entry=lp->rx_new){
- status = readl(&lp->rx_ring[entry].base) >> 16 ;
- if (status & R_STP) { /* Remember start of frame */
- lp->rx_old = entry;
- }
- if (status & R_ENP) { /* Valid frame status */
- if (status & R_ERR) { /* There was an error. */
- lp->stats.rx_errors++; /* Update the error stats. */
- if (status & R_FRAM) lp->stats.rx_frame_errors++;
- if (status & R_OFLO) lp->stats.rx_over_errors++;
- if (status & R_CRC) lp->stats.rx_crc_errors++;
- if (status & R_BUFF) lp->stats.rx_fifo_errors++;
- } else {
- short len, pkt_len = readw(&lp->rx_ring[entry].msg_length);
- struct sk_buff *skb;
-
- skb = dev_alloc_skb(pkt_len+2);
- if (skb != NULL) {
- unsigned char *buf;
- skb_reserve(skb,2); /* 16 byte align the IP header */
- buf = skb_put(skb,pkt_len);
- skb->dev = dev;
- if (entry < lp->rx_old) { /* Wrapped buffer */
- len = (lp->rxRingMask - lp->rx_old + 1) * RX_BUFF_SZ;
- memcpy_fromio(buf, lp->rx_memcpy[lp->rx_old], len);
- memcpy_fromio(buf + len, lp->rx_memcpy[0], pkt_len-len);
- } else { /* Linear buffer */
- memcpy_fromio(buf, lp->rx_memcpy[lp->rx_old], pkt_len);
- }
-
- /*
- ** Notify the upper protocol layers that there is another
- ** packet to handle
- */
- skb->protocol=eth_type_trans(skb,dev);
- netif_rx(skb);
-
- /*
- ** Update stats
- */
- lp->stats.rx_packets++;
- for (i=1; i<DEPCA_PKT_STAT_SZ-1; i++) {
- if (pkt_len < (i*DEPCA_PKT_BIN_SZ)) {
- lp->pktStats.bins[i]++;
- i = DEPCA_PKT_STAT_SZ;
- }
- }
- if (buf[0] & 0x01) { /* Multicast/Broadcast */
- if ((*(s16 *)&buf[0] == -1) &&
- (*(s16 *)&buf[2] == -1) &&
- (*(s16 *)&buf[4] == -1)) {
- lp->pktStats.broadcast++;
- } else {
- lp->pktStats.multicast++;
- }
- } else if ((*(s16 *)&buf[0] == *(s16 *)&dev->dev_addr[0]) &&
- (*(s16 *)&buf[2] == *(s16 *)&dev->dev_addr[2]) &&
- (*(s16 *)&buf[4] == *(s16 *)&dev->dev_addr[4])) {
- lp->pktStats.unicast++;
- }
-
- lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */
- if (lp->pktStats.bins[0] == 0) { /* Reset counters */
- memset((char *)&lp->pktStats, 0, sizeof(lp->pktStats));
- }
- } else {
- printk("%s: Memory squeeze, deferring packet.\n", dev->name);
- lp->stats.rx_dropped++; /* Really, deferred. */
- break;
+ struct depca_private *lp = (struct depca_private *) dev->priv;
+ int i, entry;
+ s32 status;
+
+ for (entry = lp->rx_new;
+ !(readl(&lp->rx_ring[entry].base) & R_OWN);
+ entry = lp->rx_new) {
+ status = readl(&lp->rx_ring[entry].base) >> 16;
+ if (status & R_STP) { /* Remember start of frame */
+ lp->rx_old = entry;
+ }
+ if (status & R_ENP) { /* Valid frame status */
+ if (status & R_ERR) { /* There was an error. */
+ lp->stats.rx_errors++; /* Update the error stats. */
+ if (status & R_FRAM)
+ lp->stats.rx_frame_errors++;
+ if (status & R_OFLO)
+ lp->stats.rx_over_errors++;
+ if (status & R_CRC)
+ lp->stats.rx_crc_errors++;
+ if (status & R_BUFF)
+ lp->stats.rx_fifo_errors++;
+ } else {
+ short len, pkt_len = readw(&lp->rx_ring[entry].msg_length);
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len + 2);
+ if (skb != NULL) {
+ unsigned char *buf;
+ skb_reserve(skb, 2); /* 16 byte align the IP header */
+ buf = skb_put(skb, pkt_len);
+ skb->dev = dev;
+ if (entry < lp->rx_old) { /* Wrapped buffer */
+ len = (lp->rxRingMask - lp->rx_old + 1) * RX_BUFF_SZ;
+ memcpy_fromio(buf, lp->rx_memcpy[lp->rx_old], len);
+ memcpy_fromio(buf + len, lp->rx_memcpy[0], pkt_len - len);
+ } else { /* Linear buffer */
+ memcpy_fromio(buf, lp->rx_memcpy[lp->rx_old], pkt_len);
+ }
+
+ /*
+ ** Notify the upper protocol layers that there is another
+ ** packet to handle
+ */
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+
+ /*
+ ** Update stats
+ */
+ lp->stats.rx_packets++;
+ for (i = 1; i < DEPCA_PKT_STAT_SZ - 1; i++) {
+ if (pkt_len < (i * DEPCA_PKT_BIN_SZ)) {
+ lp->pktStats.bins[i]++;
+ i = DEPCA_PKT_STAT_SZ;
+ }
+ }
+ if (buf[0] & 0x01) { /* Multicast/Broadcast */
+ if ((*(s16 *) & buf[0] == -1) &&
+ (*(s16 *) & buf[2] == -1) &&
+ (*(s16 *) & buf[4] == -1)) {
+ lp->pktStats.broadcast++;
+ } else {
+ lp->pktStats.multicast++;
+ }
+ } else if ((*(s16 *) & buf[0] == *(s16 *) & dev->dev_addr[0]) &&
+ (*(s16 *) & buf[2] == *(s16 *) & dev->dev_addr[2]) &&
+ (*(s16 *) & buf[4] == *(s16 *) & dev->dev_addr[4])) {
+ lp->pktStats.unicast++;
+ }
+ lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */
+ if (lp->pktStats.bins[0] == 0) { /* Reset counters */
+ memset((char *) &lp->pktStats, 0, sizeof(lp->pktStats));
+ }
+ } else {
+ printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+ lp->stats.rx_dropped++; /* Really, deferred. */
+ break;
+ }
+ }
+ /* Change buffer ownership for this last frame, back to the adapter */
+ for (; lp->rx_old != entry; lp->rx_old = (++lp->rx_old) & lp->rxRingMask) {
+ writel(readl(&lp->rx_ring[lp->rx_old].base) | R_OWN,
+ &lp->rx_ring[lp->rx_old].base);
+ }
+ writel(readl(&lp->rx_ring[entry].base) | R_OWN, &lp->rx_ring[entry].base);
+ }
+ /*
+ ** Update entry information
+ */
+ lp->rx_new = (++lp->rx_new) & lp->rxRingMask;
}
- }
- /* Change buffer ownership for this last frame, back to the adapter */
- for (; lp->rx_old!=entry; lp->rx_old=(++lp->rx_old)&lp->rxRingMask) {
- writel(readl(&lp->rx_ring[lp->rx_old].base) | R_OWN,
- &lp->rx_ring[lp->rx_old].base);
- }
- writel(readl(&lp->rx_ring[entry].base) | R_OWN, &lp->rx_ring[entry].base);
- }
-
- /*
- ** Update entry information
- */
- lp->rx_new = (++lp->rx_new) & lp->rxRingMask;
- }
-
- return 0;
+
+ return 0;
}
/*
-** Buffer sent - check for buffer errors.
-*/
-static int
-depca_tx(struct device *dev)
+ ** Buffer sent - check for buffer errors.
+ */
+static int depca_tx(struct device *dev)
{
- struct depca_private *lp = (struct depca_private *)dev->priv;
- int entry;
- s32 status;
- u_long ioaddr = dev->base_addr;
-
- for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) {
- status = readl(&lp->tx_ring[entry].base) >> 16 ;
-
- if (status < 0) { /* Packet not yet sent! */
- break;
- } else if (status & T_ERR) { /* An error occurred. */
- status = readl(&lp->tx_ring[entry].misc);
- lp->stats.tx_errors++;
- if (status & TMD3_RTRY) lp->stats.tx_aborted_errors++;
- if (status & TMD3_LCAR) lp->stats.tx_carrier_errors++;
- if (status & TMD3_LCOL) lp->stats.tx_window_errors++;
- if (status & TMD3_UFLO) lp->stats.tx_fifo_errors++;
- if (status & (TMD3_BUFF | TMD3_UFLO)) {
- /* Trigger an immediate send demand. */
- outw(CSR0, DEPCA_ADDR);
- outw(INEA | TDMD, DEPCA_DATA);
- }
- } else if (status & (T_MORE | T_ONE)) {
- lp->stats.collisions++;
- } else {
- lp->stats.tx_packets++;
- }
-
- /* Update all the pointers */
- lp->tx_old = (++lp->tx_old) & lp->txRingMask;
- }
-
- return 0;
+ struct depca_private *lp = (struct depca_private *) dev->priv;
+ int entry;
+ s32 status;
+ u_long ioaddr = dev->base_addr;
+
+ for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) {
+ status = readl(&lp->tx_ring[entry].base) >> 16;
+
+ if (status < 0) { /* Packet not yet sent! */
+ break;
+ } else if (status & T_ERR) { /* An error occurred. */
+ status = readl(&lp->tx_ring[entry].misc);
+ lp->stats.tx_errors++;
+ if (status & TMD3_RTRY)
+ lp->stats.tx_aborted_errors++;
+ if (status & TMD3_LCAR)
+ lp->stats.tx_carrier_errors++;
+ if (status & TMD3_LCOL)
+ lp->stats.tx_window_errors++;
+ if (status & TMD3_UFLO)
+ lp->stats.tx_fifo_errors++;
+ if (status & (TMD3_BUFF | TMD3_UFLO)) {
+ /* Trigger an immediate send demand. */
+ outw(CSR0, DEPCA_ADDR);
+ outw(INEA | TDMD, DEPCA_DATA);
+ }
+ } else if (status & (T_MORE | T_ONE)) {
+ lp->stats.collisions++;
+ } else {
+ lp->stats.tx_packets++;
+ }
+
+ /* Update all the pointers */
+ lp->tx_old = (++lp->tx_old) & lp->txRingMask;
+ }
+
+ return 0;
}
-static int
-depca_close(struct device *dev)
+static int depca_close(struct device *dev)
{
- struct depca_private *lp = (struct depca_private *)dev->priv;
- s16 nicsr;
- u_long ioaddr = dev->base_addr;
-
- dev->start = 0;
- dev->tbusy = 1;
-
- outw(CSR0, DEPCA_ADDR);
-
- if (depca_debug > 1) {
- printk("%s: Shutting down ethercard, status was %2.2x.\n",
- dev->name, inw(DEPCA_DATA));
- }
-
- /*
- ** We stop the DEPCA here -- it occasionally polls
- ** memory if we don't.
- */
- outw(STOP, DEPCA_DATA);
-
- /*
- ** Give back the ROM in case the user wants to go to DOS
- */
- if (lp->adapter != DEPCA) {
- nicsr = inb(DEPCA_NICSR);
- nicsr &= ~SHE;
- outb(nicsr, DEPCA_NICSR);
- }
-
- /*
- ** Free the associated irq
- */
- free_irq(dev->irq, NULL);
- irq2dev_map[dev->irq] = NULL;
-
- MOD_DEC_USE_COUNT;
-
- return 0;
+ struct depca_private *lp = (struct depca_private *) dev->priv;
+ s16 nicsr;
+ u_long ioaddr = dev->base_addr;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ outw(CSR0, DEPCA_ADDR);
+
+ if (depca_debug > 1) {
+ printk("%s: Shutting down ethercard, status was %2.2x.\n",
+ dev->name, inw(DEPCA_DATA));
+ }
+ /*
+ ** We stop the DEPCA here -- it occasionally polls
+ ** memory if we don't.
+ */
+ outw(STOP, DEPCA_DATA);
+
+ /*
+ ** Give back the ROM in case the user wants to go to DOS
+ */
+ if (lp->adapter != DEPCA) {
+ nicsr = inb(DEPCA_NICSR);
+ nicsr &= ~SHE;
+ outb(nicsr, DEPCA_NICSR);
+ }
+ /*
+ ** Free the associated irq
+ */
+ free_irq(dev->irq, dev);
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
}
static void LoadCSRs(struct device *dev)
{
- struct depca_private *lp = (struct depca_private *)dev->priv;
- u_long ioaddr = dev->base_addr;
+ struct depca_private *lp = (struct depca_private *) dev->priv;
+ u_long ioaddr = dev->base_addr;
- outw(CSR1, DEPCA_ADDR); /* initialisation block address LSW */
- outw((u16)(lp->sh_mem & LA_MASK), DEPCA_DATA);
- outw(CSR2, DEPCA_ADDR); /* initialisation block address MSW */
- outw((u16)((lp->sh_mem & LA_MASK) >> 16), DEPCA_DATA);
- outw(CSR3, DEPCA_ADDR); /* ALE control */
- outw(ACON, DEPCA_DATA);
+ outw(CSR1, DEPCA_ADDR); /* initialisation block address LSW */
+ outw((u16) (lp->sh_mem & LA_MASK), DEPCA_DATA);
+ outw(CSR2, DEPCA_ADDR); /* initialisation block address MSW */
+ outw((u16) ((lp->sh_mem & LA_MASK) >> 16), DEPCA_DATA);
+ outw(CSR3, DEPCA_ADDR); /* ALE control */
+ outw(ACON, DEPCA_DATA);
- outw(CSR0, DEPCA_ADDR); /* Point back to CSR0 */
+ outw(CSR0, DEPCA_ADDR); /* Point back to CSR0 */
- return;
+ return;
}
static int InitRestartDepca(struct device *dev)
{
- struct depca_private *lp = (struct depca_private *)dev->priv;
- u_long ioaddr = dev->base_addr;
- int i, status=0;
-
- /* Copy the shadow init_block to shared memory */
- memcpy_toio((char *)lp->sh_mem, &lp->init_block, sizeof(struct depca_init));
-
- outw(CSR0, DEPCA_ADDR); /* point back to CSR0 */
- outw(INIT, DEPCA_DATA); /* initialize DEPCA */
-
- /* wait for lance to complete initialisation */
- for (i=0;(i<100) && !(inw(DEPCA_DATA) & IDON); i++);
-
- if (i!=100) {
- /* clear IDON by writing a "1", enable interrupts and start lance */
- outw(IDON | INEA | STRT, DEPCA_DATA);
- if (depca_debug > 2) {
- printk("%s: DEPCA open after %d ticks, init block 0x%08lx csr0 %4.4x.\n",
- dev->name, i, lp->sh_mem, inw(DEPCA_DATA));
- }
- } else {
- printk("%s: DEPCA unopen after %d ticks, init block 0x%08lx csr0 %4.4x.\n",
- dev->name, i, lp->sh_mem, inw(DEPCA_DATA));
- status = -1;
- }
-
- return status;
+ struct depca_private *lp = (struct depca_private *) dev->priv;
+ u_long ioaddr = dev->base_addr;
+ int i, status = 0;
+
+ /* Copy the shadow init_block to shared memory */
+ memcpy_toio((char *) lp->sh_mem, &lp->init_block, sizeof(struct depca_init));
+
+ outw(CSR0, DEPCA_ADDR); /* point back to CSR0 */
+ outw(INIT, DEPCA_DATA); /* initialize DEPCA */
+
+ /* wait for lance to complete initialisation */
+ for (i = 0; (i < 100) && !(inw(DEPCA_DATA) & IDON); i++);
+
+ if (i != 100) {
+ /* clear IDON by writing a "1", enable interrupts and start lance */
+ outw(IDON | INEA | STRT, DEPCA_DATA);
+ if (depca_debug > 2) {
+ printk("%s: DEPCA open after %d ticks, init block 0x%08lx csr0 %4.4x.\n",
+ dev->name, i, lp->sh_mem, inw(DEPCA_DATA));
+ }
+ } else {
+ printk("%s: DEPCA unopen after %d ticks, init block 0x%08lx csr0 %4.4x.\n",
+ dev->name, i, lp->sh_mem, inw(DEPCA_DATA));
+ status = -1;
+ }
+
+ return status;
}
static struct net_device_stats *
-depca_get_stats(struct device *dev)
+ depca_get_stats(struct device *dev)
{
- struct depca_private *lp = (struct depca_private *)dev->priv;
+ struct depca_private *lp = (struct depca_private *) dev->priv;
- /* Null body since there is no framing error counter */
+ /* Null body since there is no framing error counter */
- return &lp->stats;
+ return &lp->stats;
}
/*
-** Set or clear the multicast filter for this adaptor.
-*/
-static void
-set_multicast_list(struct device *dev)
+ ** Set or clear the multicast filter for this adaptor.
+ */
+static void set_multicast_list(struct device *dev)
{
- struct depca_private *lp = (struct depca_private *)dev->priv;
- u_long ioaddr = dev->base_addr;
-
- if (irq2dev_map[dev->irq] != NULL) {
- while(dev->tbusy); /* Stop ring access */
- set_bit(0, (void*)&dev->tbusy);
- while(lp->tx_old != lp->tx_new); /* Wait for the ring to empty */
-
- STOP_DEPCA; /* Temporarily stop the depca. */
- depca_init_ring(dev); /* Initialize the descriptor rings */
-
- if (dev->flags & IFF_PROMISC) { /* Set promiscuous mode */
- lp->init_block.mode |= PROM;
- } else {
- SetMulticastFilter(dev);
- lp->init_block.mode &= ~PROM; /* Unset promiscuous mode */
- }
-
- LoadCSRs(dev); /* Reload CSR3 */
- InitRestartDepca(dev); /* Resume normal operation. */
- dev->tbusy = 0; /* Unlock the TX ring */
- }
+ struct depca_private *lp = (struct depca_private *) dev->priv;
+ u_long ioaddr = dev->base_addr;
+
+ while (dev->tbusy); /* Stop ring access */
+ set_bit(0, (void *) &dev->tbusy);
+ while (lp->tx_old != lp->tx_new); /* Wait for the ring to empty */
+
+ STOP_DEPCA; /* Temporarily stop the depca. */
+ depca_init_ring(dev); /* Initialize the descriptor rings */
+
+ if (dev->flags & IFF_PROMISC) { /* Set promiscuous mode */
+ lp->init_block.mode |= PROM;
+ } else {
+ SetMulticastFilter(dev);
+ lp->init_block.mode &= ~PROM; /* Unset promiscuous mode */
+ }
+
+ LoadCSRs(dev); /* Reload CSR3 */
+ InitRestartDepca(dev); /* Resume normal operation. */
+ dev->tbusy = 0; /* Unlock the TX ring */
}
/*
-** Calculate the hash code and update the logical address filter
-** from a list of ethernet multicast addresses.
-** Big endian crc one liner is mine, all mine, ha ha ha ha!
-** LANCE calculates its hash codes big endian.
-*/
+ ** Calculate the hash code and update the logical address filter
+ ** from a list of ethernet multicast addresses.
+ ** Big endian crc one liner is mine, all mine, ha ha ha ha!
+ ** LANCE calculates its hash codes big endian.
+ */
static void SetMulticastFilter(struct device *dev)
{
- struct depca_private *lp = (struct depca_private *)dev->priv;
- struct dev_mc_list *dmi=dev->mc_list;
- char *addrs;
- int i, j, bit, byte;
- u16 hashcode;
- s32 crc, poly = CRC_POLYNOMIAL_BE;
-
- if (dev->flags & IFF_ALLMULTI) { /* Set all multicast bits */
- for (i=0; i<(HASH_TABLE_LEN>>3); i++) {
- lp->init_block.mcast_table[i] = (char)0xff;
- }
- } else {
- for (i=0; i<(HASH_TABLE_LEN>>3); i++){ /* Clear the multicast table */
- lp->init_block.mcast_table[i]=0;
- }
- /* Add multicast addresses */
- for (i=0;i<dev->mc_count;i++) { /* for each address in the list */
- addrs=dmi->dmi_addr;
- dmi=dmi->next;
- if ((*addrs & 0x01) == 1) { /* multicast address? */
- crc = 0xffffffff; /* init CRC for each address */
- for (byte=0;byte<ETH_ALEN;byte++) {/* for each address byte */
- /* process each address bit */
- for (bit = *addrs++,j=0;j<8;j++, bit>>=1) {
- crc = (crc << 1) ^ ((((crc<0?1:0) ^ bit) & 0x01) ? poly : 0);
- }
- }
- hashcode = (crc & 1); /* hashcode is 6 LSb of CRC ... */
- for (j=0;j<5;j++) { /* ... in reverse order. */
- hashcode = (hashcode << 1) | ((crc>>=1) & 1);
+ struct depca_private *lp = (struct depca_private *) dev->priv;
+ struct dev_mc_list *dmi = dev->mc_list;
+ char *addrs;
+ int i, j, bit, byte;
+ u16 hashcode;
+ s32 crc, poly = CRC_POLYNOMIAL_BE;
+
+ if (dev->flags & IFF_ALLMULTI) { /* Set all multicast bits */
+ for (i = 0; i < (HASH_TABLE_LEN >> 3); i++) {
+ lp->init_block.mcast_table[i] = (char) 0xff;
+ }
+ } else {
+ for (i = 0; i < (HASH_TABLE_LEN >> 3); i++) { /* Clear the multicast table */
+ lp->init_block.mcast_table[i] = 0;
+ }
+ /* Add multicast addresses */
+ for (i = 0; i < dev->mc_count; i++) { /* for each address in the list */
+ addrs = dmi->dmi_addr;
+ dmi = dmi->next;
+ if ((*addrs & 0x01) == 1) { /* multicast address? */
+ crc = 0xffffffff; /* init CRC for each address */
+ for (byte = 0; byte < ETH_ALEN; byte++) { /* for each address byte */
+ /* process each address bit */
+ for (bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) {
+ crc = (crc << 1) ^ ((((crc < 0 ? 1 : 0) ^ bit) & 0x01) ? poly : 0);
+ }
+ }
+ hashcode = (crc & 1); /* hashcode is 6 LSb of CRC ... */
+ for (j = 0; j < 5; j++) { /* ... in reverse order. */
+ hashcode = (hashcode << 1) | ((crc >>= 1) & 1);
+ }
+
+
+ byte = hashcode >> 3; /* bit[3-5] -> byte in filter */
+ bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */
+ lp->init_block.mcast_table[byte] |= bit;
+ }
+ }
}
-
- byte = hashcode >> 3; /* bit[3-5] -> byte in filter */
- bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */
- lp->init_block.mcast_table[byte] |= bit;
- }
- }
- }
-
- return;
+ return;
}
/*
-** ISA bus I/O device probe
-*/
+ ** ISA bus I/O device probe
+ */
__initfunc(static void isa_probe(struct device *dev, u_long ioaddr))
{
- int i = num_depcas, maxSlots;
- s32 ports[] = DEPCA_IO_PORTS;
-
- if (!ioaddr && autoprobed) return ; /* Been here before ! */
- if (ioaddr > 0x400) return; /* EISA Address */
- if (i >= MAX_NUM_DEPCAS) return; /* Too many ISA adapters */
-
- if (ioaddr == 0) { /* Autoprobing */
- maxSlots = MAX_NUM_DEPCAS;
- } else { /* Probe a specific location */
- ports[i] = ioaddr;
- maxSlots = i + 1;
- }
-
- for (; (i<maxSlots) && (dev!=NULL) && ports[i]; i++) {
- if (DevicePresent(ports[i]) == 0) {
- if (check_region(ports[i], DEPCA_TOTAL_SIZE) == 0) {
- if ((dev = alloc_device(dev, ports[i])) != NULL) {
- if (depca_hw_init(dev, ports[i]) == 0) {
- num_depcas++;
- }
- num_eth++;
+ int i = num_depcas, maxSlots;
+ s32 ports[] = DEPCA_IO_PORTS;
+
+ if (!ioaddr && autoprobed)
+ return; /* Been here before ! */
+ if (ioaddr > 0x400)
+ return; /* EISA Address */
+ if (i >= MAX_NUM_DEPCAS)
+ return; /* Too many ISA adapters */
+
+ if (ioaddr == 0) { /* Autoprobing */
+ maxSlots = MAX_NUM_DEPCAS;
+ } else { /* Probe a specific location */
+ ports[i] = ioaddr;
+ maxSlots = i + 1;
+ }
+
+ for (; (i < maxSlots) && (dev != NULL) && ports[i]; i++) {
+ if (DevicePresent(ports[i]) == 0) {
+ if (check_region(ports[i], DEPCA_TOTAL_SIZE) == 0) {
+ if ((dev = alloc_device(dev, ports[i])) != NULL) {
+ if (depca_hw_init(dev, ports[i]) == 0) {
+ num_depcas++;
+ }
+ num_eth++;
+ }
+ } else if (autoprobed) {
+ printk("%s: region already allocated at 0x%04x.\n", dev->name, ports[i]);
+ }
+ }
}
- } else if (autoprobed) {
- printk("%s: region already allocated at 0x%04x.\n", dev->name,ports[i]);
- }
- }
- }
- return;
+ return;
}
/*
-** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually
-** the motherboard. Upto 15 EISA devices are supported.
-*/
+ ** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually
+ ** the motherboard. Upto 15 EISA devices are supported.
+ */
__initfunc(static void eisa_probe(struct device *dev, u_long ioaddr))
{
- int i, maxSlots;
- u_long iobase;
- char name[DEPCA_STRLEN];
-
- if (!ioaddr && autoprobed) return ; /* Been here before ! */
- if ((ioaddr < 0x400) && (ioaddr > 0)) return; /* ISA Address */
-
- if (ioaddr == 0) { /* Autoprobing */
- iobase = EISA_SLOT_INC; /* Get the first slot address */
- i = 1;
- maxSlots = MAX_EISA_SLOTS;
- } else { /* Probe a specific location */
- iobase = ioaddr;
- i = (ioaddr >> 12);
- maxSlots = i + 1;
- }
- if ((iobase & 0x0fff) == 0) iobase += DEPCA_EISA_IO_PORTS;
-
- for (; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) {
- if (EISA_signature(name, EISA_ID)) {
- if (DevicePresent(iobase) == 0) {
- if (check_region(iobase, DEPCA_TOTAL_SIZE) == 0) {
- if ((dev = alloc_device(dev, iobase)) != NULL) {
- if (depca_hw_init(dev, iobase) == 0) {
- num_depcas++;
- }
- num_eth++;
- }
- } else if (autoprobed) {
- printk("%s: region already allocated at 0x%04lx.\n",dev->name,iobase);
+ int i, maxSlots;
+ u_long iobase;
+ char name[DEPCA_STRLEN];
+
+ if (!ioaddr && autoprobed)
+ return; /* Been here before ! */
+ if ((ioaddr < 0x400) && (ioaddr > 0))
+ return; /* ISA Address */
+
+ if (ioaddr == 0) { /* Autoprobing */
+ iobase = EISA_SLOT_INC; /* Get the first slot address */
+ i = 1;
+ maxSlots = MAX_EISA_SLOTS;
+ } else { /* Probe a specific location */
+ iobase = ioaddr;
+ i = (ioaddr >> 12);
+ maxSlots = i + 1;
+ }
+ if ((iobase & 0x0fff) == 0)
+ iobase += DEPCA_EISA_IO_PORTS;
+
+ for (; (i < maxSlots) && (dev != NULL); i++, iobase += EISA_SLOT_INC) {
+ if (EISA_signature(name, EISA_ID)) {
+ if (DevicePresent(iobase) == 0) {
+ if (check_region(iobase, DEPCA_TOTAL_SIZE) == 0) {
+ if ((dev = alloc_device(dev, iobase)) != NULL) {
+ if (depca_hw_init(dev, iobase) == 0) {
+ num_depcas++;
+ }
+ num_eth++;
+ }
+ } else if (autoprobed) {
+ printk("%s: region already allocated at 0x%04lx.\n", dev->name, iobase);
+ }
+ }
+ }
}
- }
- }
- }
- return;
+ return;
}
/*
-** Search the entire 'eth' device list for a fixed probe. If a match isn't
-** found then check for an autoprobe or unused device location. If they
-** are not available then insert a new device structure at the end of
-** the current list.
-*/
+ ** Search the entire 'eth' device list for a fixed probe. If a match isn't
+ ** found then check for an autoprobe or unused device location. If they
+ ** are not available then insert a new device structure at the end of
+ ** the current list.
+ */
__initfunc(static struct device *
-alloc_device(struct device *dev, u_long iobase))
+ alloc_device(struct device *dev, u_long iobase))
{
- struct device *adev = NULL;
- int fixed = 0, new_dev = 0;
+ struct device *adev = NULL;
+ int fixed = 0, new_dev = 0;
- num_eth = depca_dev_index(dev->name);
- if (loading_module) return dev;
-
- while (1) {
- if (((dev->base_addr == DEPCA_NDA) || (dev->base_addr==0)) && !adev) {
- adev=dev;
- } else if ((dev->priv == NULL) && (dev->base_addr==iobase)) {
- fixed = 1;
- } else {
- if (dev->next == NULL) {
- new_dev = 1;
- } else if (strncmp(dev->next->name, "eth", 3) != 0) {
- new_dev = 1;
- }
- }
- if ((dev->next == NULL) || new_dev || fixed) break;
- dev = dev->next;
- num_eth++;
- }
- if (adev && !fixed) {
- dev = adev;
num_eth = depca_dev_index(dev->name);
- new_dev = 0;
- }
-
- if (((dev->next == NULL) &&
- ((dev->base_addr != DEPCA_NDA) && (dev->base_addr != 0)) && !fixed) ||
- new_dev) {
- num_eth++; /* New device */
- dev = insert_device(dev, iobase, depca_probe);
- }
-
- return dev;
+ if (loading_module)
+ return dev;
+
+ while (1) {
+ if (((dev->base_addr == DEPCA_NDA) || (dev->base_addr == 0)) && !adev) {
+ adev = dev;
+ } else if ((dev->priv == NULL) && (dev->base_addr == iobase)) {
+ fixed = 1;
+ } else {
+ if (dev->next == NULL) {
+ new_dev = 1;
+ } else if (strncmp(dev->next->name, "eth", 3) != 0) {
+ new_dev = 1;
+ }
+ }
+ if ((dev->next == NULL) || new_dev || fixed)
+ break;
+ dev = dev->next;
+ num_eth++;
+ }
+ if (adev && !fixed) {
+ dev = adev;
+ num_eth = depca_dev_index(dev->name);
+ new_dev = 0;
+ }
+ if (((dev->next == NULL) &&
+ ((dev->base_addr != DEPCA_NDA) && (dev->base_addr != 0)) && !fixed) ||
+ new_dev) {
+ num_eth++; /* New device */
+ dev = insert_device(dev, iobase, depca_probe);
+ }
+ return dev;
}
/*
-** If at end of eth device list and can't use current entry, malloc
-** one up. If memory could not be allocated, print an error message.
-*/
+ ** If at end of eth device list and can't use current entry, malloc
+ ** one up. If memory could not be allocated, print an error message.
+ */
__initfunc(static struct device *
-insert_device(struct device *dev, u_long iobase, int (*init)(struct device *)))
+ insert_device(struct device *dev, u_long iobase, int (*init) (struct device *)))
{
- struct device *new;
-
- new = (struct device *)kmalloc(sizeof(struct device)+8, GFP_KERNEL);
- if (new == NULL) {
- printk("eth%d: Device not initialised, insufficient memory\n",num_eth);
- return NULL;
- } else {
- new->next = dev->next;
- dev->next = new;
- dev = dev->next; /* point to the new device */
- dev->name = (char *)(dev + 1);
- if (num_eth > 9999) {
- sprintf(dev->name,"eth????");/* New device name */
+ struct device *new;
+
+ new = (struct device *) kmalloc(sizeof(struct device) + 8, GFP_KERNEL);
+ if (new == NULL) {
+ printk("eth%d: Device not initialised, insufficient memory\n", num_eth);
+ return NULL;
} else {
- sprintf(dev->name,"eth%d", num_eth);/* New device name */
+ new->next = dev->next;
+ dev->next = new;
+ dev = dev->next; /* point to the new device */
+ dev->name = (char *) (dev + 1);
+ if (num_eth > 9999) {
+ sprintf(dev->name, "eth????"); /* New device name */
+ } else {
+ sprintf(dev->name, "eth%d", num_eth); /* New device name */
+ }
+ dev->base_addr = iobase; /* assign the io address */
+ dev->init = init; /* initialisation routine */
}
- dev->base_addr = iobase; /* assign the io address */
- dev->init = init; /* initialisation routine */
- }
- return dev;
+ return dev;
}
__initfunc(static int
-depca_dev_index(char *s))
+ depca_dev_index(char *s))
{
- int i=0, j=0;
-
- for (;*s; s++) {
- if (isdigit(*s)) {
- j=1;
- i = (i * 10) + (*s - '0');
- } else if (j) break;
- }
+ int i = 0, j = 0;
+
+ for (; *s; s++) {
+ if (isdigit(*s)) {
+ j = 1;
+ i = (i * 10) + (*s - '0');
+ } else if (j)
+ break;
+ }
- return i;
+ return i;
}
/*
-** Look for a particular board name in the on-board Remote Diagnostics
-** and Boot (readb) ROM. This will also give us a clue to the network RAM
-** base address.
-*/
+ ** Look for a particular board name in the on-board Remote Diagnostics
+ ** and Boot (readb) ROM. This will also give us a clue to the network RAM
+ ** base address.
+ */
__initfunc(static void DepcaSignature(char *name, u_long paddr))
{
- u_int i,j,k;
- const char *signatures[] = DEPCA_SIGNATURE;
- char tmpstr[16];
-
- /* Copy the first 16 bytes of ROM */
- for (i=0;i<16;i++) {
- tmpstr[i] = readb(paddr+0xc000+i);
- }
-
- /* Check if PROM contains a valid string */
- for (i=0;*signatures[i]!='\0';i++) {
- for (j=0,k=0;j<16 && k<strlen(signatures[i]);j++) {
- if (signatures[i][k] == tmpstr[j]) { /* track signature */
- k++;
- } else { /* lost signature; begin search again */
- k=0;
- }
- }
- if (k == strlen(signatures[i])) break;
- }
-
- /* Check if name string is valid, provided there's no PROM */
- if (*name && (i == unknown)) {
- for (i=0;*signatures[i]!='\0';i++) {
- if (strcmp(name,signatures[i]) == 0) break;
- }
- }
-
- /* Update search results */
- strcpy(name,signatures[i]);
- adapter = i;
-
- return;
+ u_int i, j, k;
+ const char *signatures[] = DEPCA_SIGNATURE;
+ char tmpstr[16];
+
+ /* Copy the first 16 bytes of ROM */
+ for (i = 0; i < 16; i++) {
+ tmpstr[i] = readb(paddr + 0xc000 + i);
+ }
+
+ /* Check if PROM contains a valid string */
+ for (i = 0; *signatures[i] != '\0'; i++) {
+ for (j = 0, k = 0; j < 16 && k < strlen(signatures[i]); j++) {
+ if (signatures[i][k] == tmpstr[j]) { /* track signature */
+ k++;
+ } else { /* lost signature; begin search again */
+ k = 0;
+ }
+ }
+ if (k == strlen(signatures[i]))
+ break;
+ }
+
+ /* Check if name string is valid, provided there's no PROM */
+ if (*name && (i == unknown)) {
+ for (i = 0; *signatures[i] != '\0'; i++) {
+ if (strcmp(name, signatures[i]) == 0)
+ break;
+ }
+ }
+ /* Update search results */
+ strcpy(name, signatures[i]);
+ adapter = i;
+
+ return;
}
/*
-** Look for a special sequence in the Ethernet station address PROM that
-** is common across all DEPCA products. Note that the original DEPCA needs
-** its ROM address counter to be initialized and enabled. Only enable
-** if the first address octet is a 0x08 - this minimises the chances of
-** messing around with some other hardware, but it assumes that this DEPCA
-** card initialized itself correctly.
-**
-** Search the Ethernet address ROM for the signature. Since the ROM address
-** counter can start at an arbitrary point, the search must include the entire
-** probe sequence length plus the (length_of_the_signature - 1).
-** Stop the search IMMEDIATELY after the signature is found so that the
-** PROM address counter is correctly positioned at the start of the
-** ethernet address for later read out.
-*/
+ ** Look for a special sequence in the Ethernet station address PROM that
+ ** is common across all DEPCA products. Note that the original DEPCA needs
+ ** its ROM address counter to be initialized and enabled. Only enable
+ ** if the first address octet is a 0x08 - this minimises the chances of
+ ** messing around with some other hardware, but it assumes that this DEPCA
+ ** card initialized itself correctly.
+ **
+ ** Search the Ethernet address ROM for the signature. Since the ROM address
+ ** counter can start at an arbitrary point, the search must include the entire
+ ** probe sequence length plus the (length_of_the_signature - 1).
+ ** Stop the search IMMEDIATELY after the signature is found so that the
+ ** PROM address counter is correctly positioned at the start of the
+ ** ethernet address for later read out.
+ */
__initfunc(static int DevicePresent(u_long ioaddr))
{
- union {
- struct {
- u32 a;
- u32 b;
- } llsig;
- char Sig[sizeof(u32) << 1];
- } dev;
- short sigLength=0;
- s8 data;
- s16 nicsr;
- int i, j, status = 0;
-
- data = inb(DEPCA_PROM); /* clear counter on DEPCA */
- data = inb(DEPCA_PROM); /* read data */
-
- if (data == 0x08) { /* Enable counter on DEPCA */
- nicsr = inb(DEPCA_NICSR);
- nicsr |= AAC;
- outb(nicsr, DEPCA_NICSR);
- }
-
- dev.llsig.a = ETH_PROM_SIG;
- dev.llsig.b = ETH_PROM_SIG;
- sigLength = sizeof(u32) << 1;
-
- for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) {
- data = inb(DEPCA_PROM);
- if (dev.Sig[j] == data) { /* track signature */
- j++;
- } else { /* lost signature; begin search again */
- if (data == dev.Sig[0]) { /* rare case.... */
- j=1;
- } else {
- j=0;
- }
- }
- }
-
- if (j!=sigLength) {
- status = -ENODEV; /* search failed */
- }
-
- return status;
+ union {
+ struct {
+ u32 a;
+ u32 b;
+ } llsig;
+ char Sig[sizeof(u32) << 1];
+ }
+ dev;
+ short sigLength = 0;
+ s8 data;
+ s16 nicsr;
+ int i, j, status = 0;
+
+ data = inb(DEPCA_PROM); /* clear counter on DEPCA */
+ data = inb(DEPCA_PROM); /* read data */
+
+ if (data == 0x08) { /* Enable counter on DEPCA */
+ nicsr = inb(DEPCA_NICSR);
+ nicsr |= AAC;
+ outb(nicsr, DEPCA_NICSR);
+ }
+ dev.llsig.a = ETH_PROM_SIG;
+ dev.llsig.b = ETH_PROM_SIG;
+ sigLength = sizeof(u32) << 1;
+
+ for (i = 0, j = 0; j < sigLength && i < PROBE_LENGTH + sigLength - 1; i++) {
+ data = inb(DEPCA_PROM);
+ if (dev.Sig[j] == data) { /* track signature */
+ j++;
+ } else { /* lost signature; begin search again */
+ if (data == dev.Sig[0]) { /* rare case.... */
+ j = 1;
+ } else {
+ j = 0;
+ }
+ }
+ }
+
+ if (j != sigLength) {
+ status = -ENODEV; /* search failed */
+ }
+ return status;
}
/*
-** The DE100 and DE101 PROM accesses were made non-standard for some bizarre
-** reason: access the upper half of the PROM with x=0; access the lower half
-** with x=1.
-*/
+ ** The DE100 and DE101 PROM accesses were made non-standard for some bizarre
+ ** reason: access the upper half of the PROM with x=0; access the lower half
+ ** with x=1.
+ */
__initfunc(static int get_hw_addr(struct device *dev))
{
- u_long ioaddr = dev->base_addr;
- int i, k, tmp, status = 0;
- u_short j, x, chksum;
+ u_long ioaddr = dev->base_addr;
+ int i, k, tmp, status = 0;
+ u_short j, x, chksum;
- x = (((adapter == de100) || (adapter == de101)) ? 1 : 0);
+ x = (((adapter == de100) || (adapter == de101)) ? 1 : 0);
- for (i=0,k=0,j=0;j<3;j++) {
- k <<= 1 ;
- if (k > 0xffff) k-=0xffff;
+ for (i = 0, k = 0, j = 0; j < 3; j++) {
+ k <<= 1;
+ if (k > 0xffff)
+ k -= 0xffff;
- k += (u_char) (tmp = inb(DEPCA_PROM + x));
- dev->dev_addr[i++] = (u_char) tmp;
- k += (u_short) ((tmp = inb(DEPCA_PROM + x)) << 8);
- dev->dev_addr[i++] = (u_char) tmp;
+ k += (u_char) (tmp = inb(DEPCA_PROM + x));
+ dev->dev_addr[i++] = (u_char) tmp;
+ k += (u_short) ((tmp = inb(DEPCA_PROM + x)) << 8);
+ dev->dev_addr[i++] = (u_char) tmp;
- if (k > 0xffff) k-=0xffff;
- }
- if (k == 0xffff) k=0;
+ if (k > 0xffff)
+ k -= 0xffff;
+ }
+ if (k == 0xffff)
+ k = 0;
- chksum = (u_char) inb(DEPCA_PROM + x);
- chksum |= (u_short) (inb(DEPCA_PROM + x) << 8);
- if (k != chksum) status = -1;
+ chksum = (u_char) inb(DEPCA_PROM + x);
+ chksum |= (u_short) (inb(DEPCA_PROM + x) << 8);
+ if (k != chksum)
+ status = -1;
- return status;
+ return status;
}
/*
-** Load a packet into the shared memory
-*/
+ ** Load a packet into the shared memory
+ */
static int load_packet(struct device *dev, struct sk_buff *skb)
{
- struct depca_private *lp = (struct depca_private *)dev->priv;
- int i, entry, end, len, status = 0;
-
- entry = lp->tx_new; /* Ring around buffer number. */
- end = (entry + (skb->len - 1) / TX_BUFF_SZ) & lp->txRingMask;
- if (!(readl(&lp->tx_ring[end].base) & T_OWN)) {/* Enough room? */
- /*
- ** Caution: the write order is important here... don't set up the
- ** ownership rights until all the other information is in place.
- */
- if (end < entry) { /* wrapped buffer */
- len = (lp->txRingMask - entry + 1) * TX_BUFF_SZ;
- memcpy_toio(lp->tx_memcpy[entry], skb->data, len);
- memcpy_toio(lp->tx_memcpy[0], skb->data + len, skb->len - len);
- } else { /* linear buffer */
- memcpy_toio(lp->tx_memcpy[entry], skb->data, skb->len);
- }
-
- /* set up the buffer descriptors */
- len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len;
- for (i = entry; i != end; i = (++i) & lp->txRingMask) {
- /* clean out flags */
- writel(readl(&lp->tx_ring[i].base) & ~T_FLAGS, &lp->tx_ring[i].base);
- writew(0x0000, &lp->tx_ring[i].misc); /* clears other error flags */
- writew(-TX_BUFF_SZ, &lp->tx_ring[i].length);/* packet length in buffer */
- len -= TX_BUFF_SZ;
- }
- /* clean out flags */
- writel(readl(&lp->tx_ring[end].base) & ~T_FLAGS, &lp->tx_ring[end].base);
- writew(0x0000, &lp->tx_ring[end].misc); /* clears other error flags */
- writew(-len, &lp->tx_ring[end].length); /* packet length in last buff */
-
- /* start of packet */
- writel(readl(&lp->tx_ring[entry].base) | T_STP, &lp->tx_ring[entry].base);
- /* end of packet */
- writel(readl(&lp->tx_ring[end].base) | T_ENP, &lp->tx_ring[end].base);
-
- for (i=end; i!=entry; --i) {
- /* ownership of packet */
- writel(readl(&lp->tx_ring[i].base) | T_OWN, &lp->tx_ring[i].base);
- if (i == 0) i=lp->txRingMask+1;
- }
- writel(readl(&lp->tx_ring[entry].base) | T_OWN, &lp->tx_ring[entry].base);
-
- lp->tx_new = (++end) & lp->txRingMask; /* update current pointers */
- } else {
- status = -1;
- }
-
- return status;
+ struct depca_private *lp = (struct depca_private *) dev->priv;
+ int i, entry, end, len, status = 0;
+
+ entry = lp->tx_new; /* Ring around buffer number. */
+ end = (entry + (skb->len - 1) / TX_BUFF_SZ) & lp->txRingMask;
+ if (!(readl(&lp->tx_ring[end].base) & T_OWN)) { /* Enough room? */
+ /*
+ ** Caution: the write order is important here... don't set up the
+ ** ownership rights until all the other information is in place.
+ */
+ if (end < entry) { /* wrapped buffer */
+ len = (lp->txRingMask - entry + 1) * TX_BUFF_SZ;
+ memcpy_toio(lp->tx_memcpy[entry], skb->data, len);
+ memcpy_toio(lp->tx_memcpy[0], skb->data + len, skb->len - len);
+ } else { /* linear buffer */
+ memcpy_toio(lp->tx_memcpy[entry], skb->data, skb->len);
+ }
+
+ /* set up the buffer descriptors */
+ len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len;
+ for (i = entry; i != end; i = (++i) & lp->txRingMask) {
+ /* clean out flags */
+ writel(readl(&lp->tx_ring[i].base) & ~T_FLAGS, &lp->tx_ring[i].base);
+ writew(0x0000, &lp->tx_ring[i].misc); /* clears other error flags */
+ writew(-TX_BUFF_SZ, &lp->tx_ring[i].length); /* packet length in buffer */
+ len -= TX_BUFF_SZ;
+ }
+ /* clean out flags */
+ writel(readl(&lp->tx_ring[end].base) & ~T_FLAGS, &lp->tx_ring[end].base);
+ writew(0x0000, &lp->tx_ring[end].misc); /* clears other error flags */
+ writew(-len, &lp->tx_ring[end].length); /* packet length in last buff */
+
+ /* start of packet */
+ writel(readl(&lp->tx_ring[entry].base) | T_STP, &lp->tx_ring[entry].base);
+ /* end of packet */
+ writel(readl(&lp->tx_ring[end].base) | T_ENP, &lp->tx_ring[end].base);
+
+ for (i = end; i != entry; --i) {
+ /* ownership of packet */
+ writel(readl(&lp->tx_ring[i].base) | T_OWN, &lp->tx_ring[i].base);
+ if (i == 0)
+ i = lp->txRingMask + 1;
+ }
+ writel(readl(&lp->tx_ring[entry].base) | T_OWN, &lp->tx_ring[entry].base);
+
+ lp->tx_new = (++end) & lp->txRingMask; /* update current pointers */
+ } else {
+ status = -1;
+ }
+
+ return status;
}
/*
-** Look for a particular board name in the EISA configuration space
-*/
+ ** Look for a particular board name in the EISA configuration space
+ */
__initfunc(static int EISA_signature(char *name, s32 eisa_id))
{
- u_int i;
- const char *signatures[] = DEPCA_SIGNATURE;
- char ManCode[DEPCA_STRLEN];
- union {
- s32 ID;
- char Id[4];
- } Eisa;
- int status = 0;
-
- *name = '\0';
- Eisa.ID = inl(eisa_id);
-
- ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40);
- ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40);
- ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30);
- ManCode[3]=(( Eisa.Id[2]&0x0f)+0x30);
- ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30);
- ManCode[5]='\0';
-
- for (i=0;(*signatures[i] != '\0') && (*name == '\0');i++) {
- if (strstr(ManCode, signatures[i]) != NULL) {
- strcpy(name,ManCode);
- status = 1;
- }
- }
-
- return status;
+ u_int i;
+ const char *signatures[] = DEPCA_SIGNATURE;
+ char ManCode[DEPCA_STRLEN];
+ union {
+ s32 ID;
+ char Id[4];
+ } Eisa;
+ int status = 0;
+
+ *name = '\0';
+ Eisa.ID = inl(eisa_id);
+
+ ManCode[0] = (((Eisa.Id[0] >> 2) & 0x1f) + 0x40);
+ ManCode[1] = (((Eisa.Id[1] & 0xe0) >> 5) + ((Eisa.Id[0] & 0x03) << 3) + 0x40);
+ ManCode[2] = (((Eisa.Id[2] >> 4) & 0x0f) + 0x30);
+ ManCode[3] = ((Eisa.Id[2] & 0x0f) + 0x30);
+ ManCode[4] = (((Eisa.Id[3] >> 4) & 0x0f) + 0x30);
+ ManCode[5] = '\0';
+
+ for (i = 0; (*signatures[i] != '\0') && (*name == '\0'); i++) {
+ if (strstr(ManCode, signatures[i]) != NULL) {
+ strcpy(name, ManCode);
+ status = 1;
+ }
+ }
+
+ return status;
}
static void depca_dbg_open(struct device *dev)
{
- struct depca_private *lp = (struct depca_private *)dev->priv;
- u_long ioaddr = dev->base_addr;
- struct depca_init *p = (struct depca_init *)lp->sh_mem;
- int i;
-
- if (depca_debug > 1){
- /* Copy the shadow init_block to shared memory */
- memcpy_toio((char *)lp->sh_mem,&lp->init_block,sizeof(struct depca_init));
-
- printk("%s: depca open with irq %d\n",dev->name,dev->irq);
- printk("Descriptor head addresses:\n");
- printk("\t0x%lx 0x%lx\n",(u_long)lp->rx_ring, (u_long)lp->tx_ring);
- printk("Descriptor addresses:\nRX: ");
- for (i=0;i<lp->rxRingMask;i++){
- if (i < 3) {
- printk("0x%8.8lx ", (long) &lp->rx_ring[i].base);
- }
- }
- printk("...0x%8.8lx\n", (long) &lp->rx_ring[i].base);
- printk("TX: ");
- for (i=0;i<lp->txRingMask;i++){
- if (i < 3) {
- printk("0x%8.8lx ", (long) &lp->tx_ring[i].base);
- }
- }
- printk("...0x%8.8lx\n", (long) &lp->tx_ring[i].base);
- printk("\nDescriptor buffers:\nRX: ");
- for (i=0;i<lp->rxRingMask;i++){
- if (i < 3) {
- printk("0x%8.8x ", readl(&lp->rx_ring[i].base));
- }
- }
- printk("...0x%8.8x\n", readl(&lp->rx_ring[i].base));
- printk("TX: ");
- for (i=0;i<lp->txRingMask;i++){
- if (i < 3) {
- printk("0x%8.8x ", readl(&lp->tx_ring[i].base));
- }
- }
- printk("...0x%8.8x\n", readl(&lp->tx_ring[i].base));
- printk("Initialisation block at 0x%8.8lx\n",lp->sh_mem);
- printk("\tmode: 0x%4.4x\n",readw(&p->mode));
- printk("\tphysical address: ");
- for (i=0;i<ETH_ALEN-1;i++){
- printk("%2.2x:",(u_char)readb(&p->phys_addr[i]));
- }
- printk("%2.2x\n",(u_char)readb(&p->phys_addr[i]));
- printk("\tmulticast hash table: ");
- for (i=0;i<(HASH_TABLE_LEN >> 3)-1;i++){
- printk("%2.2x:",(u_char)readb(&p->mcast_table[i]));
- }
- printk("%2.2x\n",(u_char)readb(&p->mcast_table[i]));
- printk("\trx_ring at: 0x%8.8x\n",readl(&p->rx_ring));
- printk("\ttx_ring at: 0x%8.8x\n",readl(&p->tx_ring));
- printk("dma_buffs: 0x%8.8lx\n",lp->dma_buffs);
- printk("Ring size:\nRX: %d Log2(rxRingMask): 0x%8.8x\n",
- (int)lp->rxRingMask + 1,
- lp->rx_rlen);
- printk("TX: %d Log2(txRingMask): 0x%8.8x\n",
- (int)lp->txRingMask + 1,
- lp->tx_rlen);
- outw(CSR2,DEPCA_ADDR);
- printk("CSR2&1: 0x%4.4x",inw(DEPCA_DATA));
- outw(CSR1,DEPCA_ADDR);
- printk("%4.4x\n",inw(DEPCA_DATA));
- outw(CSR3,DEPCA_ADDR);
- printk("CSR3: 0x%4.4x\n",inw(DEPCA_DATA));
- }
-
- return;
+ struct depca_private *lp = (struct depca_private *) dev->priv;
+ u_long ioaddr = dev->base_addr;
+ struct depca_init *p = (struct depca_init *) lp->sh_mem;
+ int i;
+
+ if (depca_debug > 1) {
+ /* Copy the shadow init_block to shared memory */
+ memcpy_toio((char *) lp->sh_mem, &lp->init_block, sizeof(struct depca_init));
+
+ printk("%s: depca open with irq %d\n", dev->name, dev->irq);
+ printk("Descriptor head addresses:\n");
+ printk("\t0x%lx 0x%lx\n", (u_long) lp->rx_ring, (u_long) lp->tx_ring);
+ printk("Descriptor addresses:\nRX: ");
+ for (i = 0; i < lp->rxRingMask; i++) {
+ if (i < 3) {
+ printk("0x%8.8lx ", (long) &lp->rx_ring[i].base);
+ }
+ }
+ printk("...0x%8.8lx\n", (long) &lp->rx_ring[i].base);
+ printk("TX: ");
+ for (i = 0; i < lp->txRingMask; i++) {
+ if (i < 3) {
+ printk("0x%8.8lx ", (long) &lp->tx_ring[i].base);
+ }
+ }
+ printk("...0x%8.8lx\n", (long) &lp->tx_ring[i].base);
+ printk("\nDescriptor buffers:\nRX: ");
+ for (i = 0; i < lp->rxRingMask; i++) {
+ if (i < 3) {
+ printk("0x%8.8x ", readl(&lp->rx_ring[i].base));
+ }
+ }
+ printk("...0x%8.8x\n", readl(&lp->rx_ring[i].base));
+ printk("TX: ");
+ for (i = 0; i < lp->txRingMask; i++) {
+ if (i < 3) {
+ printk("0x%8.8x ", readl(&lp->tx_ring[i].base));
+ }
+ }
+ printk("...0x%8.8x\n", readl(&lp->tx_ring[i].base));
+ printk("Initialisation block at 0x%8.8lx\n", lp->sh_mem);
+ printk("\tmode: 0x%4.4x\n", readw(&p->mode));
+ printk("\tphysical address: ");
+ for (i = 0; i < ETH_ALEN - 1; i++) {
+ printk("%2.2x:", (u_char) readb(&p->phys_addr[i]));
+ }
+ printk("%2.2x\n", (u_char) readb(&p->phys_addr[i]));
+ printk("\tmulticast hash table: ");
+ for (i = 0; i < (HASH_TABLE_LEN >> 3) - 1; i++) {
+ printk("%2.2x:", (u_char) readb(&p->mcast_table[i]));
+ }
+ printk("%2.2x\n", (u_char) readb(&p->mcast_table[i]));
+ printk("\trx_ring at: 0x%8.8x\n", readl(&p->rx_ring));
+ printk("\ttx_ring at: 0x%8.8x\n", readl(&p->tx_ring));
+ printk("dma_buffs: 0x%8.8lx\n", lp->dma_buffs);
+ printk("Ring size:\nRX: %d Log2(rxRingMask): 0x%8.8x\n",
+ (int) lp->rxRingMask + 1,
+ lp->rx_rlen);
+ printk("TX: %d Log2(txRingMask): 0x%8.8x\n",
+ (int) lp->txRingMask + 1,
+ lp->tx_rlen);
+ outw(CSR2, DEPCA_ADDR);
+ printk("CSR2&1: 0x%4.4x", inw(DEPCA_DATA));
+ outw(CSR1, DEPCA_ADDR);
+ printk("%4.4x\n", inw(DEPCA_DATA));
+ outw(CSR3, DEPCA_ADDR);
+ printk("CSR3: 0x%4.4x\n", inw(DEPCA_DATA));
+ }
+ return;
}
/*
-** Perform IOCTL call functions here. Some are privileged operations and the
-** effective uid is checked in those cases.
-** All MCA IOCTLs will not work here and are for testing purposes only.
-*/
+ ** Perform IOCTL call functions here. Some are privileged operations and the
+ ** effective uid is checked in those cases.
+ ** All MCA IOCTLs will not work here and are for testing purposes only.
+ */
static int depca_ioctl(struct device *dev, struct ifreq *rq, int cmd)
{
- struct depca_private *lp = (struct depca_private *)dev->priv;
- struct depca_ioctl *ioc = (struct depca_ioctl *) &rq->ifr_data;
- int i, status = 0;
- u_long ioaddr = dev->base_addr;
- union {
- u8 addr[(HASH_TABLE_LEN * ETH_ALEN)];
- u16 sval[(HASH_TABLE_LEN * ETH_ALEN) >> 1];
- u32 lval[(HASH_TABLE_LEN * ETH_ALEN) >> 2];
- } tmp;
-
- switch(ioc->cmd) {
- case DEPCA_GET_HWADDR: /* Get the hardware address */
- for (i=0; i<ETH_ALEN; i++) {
- tmp.addr[i] = dev->dev_addr[i];
- }
- ioc->len = ETH_ALEN;
- if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
- copy_to_user(ioc->data, tmp.addr, ioc->len);
- }
-
- break;
- case DEPCA_SET_HWADDR: /* Set the hardware address */
- if (suser()) {
- if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN))) {
- copy_from_user(tmp.addr,ioc->data,ETH_ALEN);
- for (i=0; i<ETH_ALEN; i++) {
- dev->dev_addr[i] = tmp.addr[i];
+ struct depca_private *lp = (struct depca_private *) dev->priv;
+ struct depca_ioctl *ioc = (struct depca_ioctl *) &rq->ifr_data;
+ int i, status = 0;
+ u_long ioaddr = dev->base_addr;
+ union {
+ u8 addr[(HASH_TABLE_LEN * ETH_ALEN)];
+ u16 sval[(HASH_TABLE_LEN * ETH_ALEN) >> 1];
+ u32 lval[(HASH_TABLE_LEN * ETH_ALEN) >> 2];
+ } tmp;
+
+ switch (ioc->cmd) {
+ case DEPCA_GET_HWADDR: /* Get the hardware address */
+ for (i = 0; i < ETH_ALEN; i++) {
+ tmp.addr[i] = dev->dev_addr[i];
+ }
+ ioc->len = ETH_ALEN;
+ if (!(status = verify_area(VERIFY_WRITE, (void *) ioc->data, ioc->len))) {
+ copy_to_user(ioc->data, tmp.addr, ioc->len);
+ }
+ break;
+ case DEPCA_SET_HWADDR: /* Set the hardware address */
+ if (suser()) {
+ if (!(status = verify_area(VERIFY_READ, (void *) ioc->data, ETH_ALEN))) {
+ copy_from_user(tmp.addr, ioc->data, ETH_ALEN);
+ for (i = 0; i < ETH_ALEN; i++) {
+ dev->dev_addr[i] = tmp.addr[i];
+ }
+ while (dev->tbusy); /* Stop ring access */
+ set_bit(0, (void *) &dev->tbusy);
+ while (lp->tx_old != lp->tx_new); /* Wait for the ring to empty */
+
+ STOP_DEPCA; /* Temporarily stop the depca. */
+ depca_init_ring(dev); /* Initialize the descriptor rings */
+ LoadCSRs(dev); /* Reload CSR3 */
+ InitRestartDepca(dev); /* Resume normal operation. */
+ dev->tbusy = 0; /* Unlock the TX ring */
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_SET_PROM: /* Set Promiscuous Mode */
+ if (suser()) {
+ while (dev->tbusy); /* Stop ring access */
+ set_bit(0, (void *) &dev->tbusy);
+ while (lp->tx_old != lp->tx_new); /* Wait for the ring to empty */
+
+ STOP_DEPCA; /* Temporarily stop the depca. */
+ depca_init_ring(dev); /* Initialize the descriptor rings */
+ lp->init_block.mode |= PROM; /* Set promiscuous mode */
+
+ LoadCSRs(dev); /* Reload CSR3 */
+ InitRestartDepca(dev); /* Resume normal operation. */
+ dev->tbusy = 0; /* Unlock the TX ring */
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_CLR_PROM: /* Clear Promiscuous Mode */
+ if (suser()) {
+ while (dev->tbusy); /* Stop ring access */
+ set_bit(0, (void *) &dev->tbusy);
+ while (lp->tx_old != lp->tx_new); /* Wait for the ring to empty */
+
+ STOP_DEPCA; /* Temporarily stop the depca. */
+ depca_init_ring(dev); /* Initialize the descriptor rings */
+ lp->init_block.mode &= ~PROM; /* Clear promiscuous mode */
+
+ LoadCSRs(dev); /* Reload CSR3 */
+ InitRestartDepca(dev); /* Resume normal operation. */
+ dev->tbusy = 0; /* Unlock the TX ring */
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_SAY_BOO: /* Say "Boo!" to the kernel log file */
+ printk("%s: Boo!\n", dev->name);
+
+ break;
+ case DEPCA_GET_MCA: /* Get the multicast address table */
+ ioc->len = (HASH_TABLE_LEN >> 3);
+ if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ copy_to_user(ioc->data, lp->init_block.mcast_table, ioc->len);
+ }
+ break;
+ case DEPCA_SET_MCA: /* Set a multicast address */
+ if (suser()) {
+ if (!(status = verify_area(VERIFY_READ, ioc->data, ETH_ALEN * ioc->len))) {
+ copy_from_user(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
+ set_multicast_list(dev);
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_CLR_MCA: /* Clear all multicast addresses */
+ if (suser()) {
+ set_multicast_list(dev);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_MCA_EN: /* Enable pass all multicast addressing */
+ if (suser()) {
+ set_multicast_list(dev);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_GET_STATS: /* Get the driver statistics */
+ cli();
+ ioc->len = sizeof(lp->pktStats);
+ if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ copy_to_user(ioc->data, &lp->pktStats, ioc->len);
+ }
+ sti();
+
+ break;
+ case DEPCA_CLR_STATS: /* Zero out the driver statistics */
+ if (suser()) {
+ cli();
+ memset(&lp->pktStats, 0, sizeof(lp->pktStats));
+ sti();
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_GET_REG: /* Get the DEPCA Registers */
+ i = 0;
+ tmp.sval[i++] = inw(DEPCA_NICSR);
+ outw(CSR0, DEPCA_ADDR); /* status register */
+ tmp.sval[i++] = inw(DEPCA_DATA);
+ memcpy(&tmp.sval[i], &lp->init_block, sizeof(struct depca_init));
+ ioc->len = i + sizeof(struct depca_init);
+ if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ copy_to_user(ioc->data, tmp.addr, ioc->len);
+ }
+ break;
+ default:
+ status = -EOPNOTSUPP;
}
- while(dev->tbusy); /* Stop ring access */
- set_bit(0, (void*)&dev->tbusy);
- while(lp->tx_old != lp->tx_new);/* Wait for the ring to empty */
-
- STOP_DEPCA; /* Temporarily stop the depca. */
- depca_init_ring(dev); /* Initialize the descriptor rings */
- LoadCSRs(dev); /* Reload CSR3 */
- InitRestartDepca(dev); /* Resume normal operation. */
- dev->tbusy = 0; /* Unlock the TX ring */
- }
- } else {
- status = -EPERM;
- }
-
- break;
- case DEPCA_SET_PROM: /* Set Promiscuous Mode */
- if (suser()) {
- while(dev->tbusy); /* Stop ring access */
- set_bit(0, (void*)&dev->tbusy);
- while(lp->tx_old != lp->tx_new); /* Wait for the ring to empty */
-
- STOP_DEPCA; /* Temporarily stop the depca. */
- depca_init_ring(dev); /* Initialize the descriptor rings */
- lp->init_block.mode |= PROM; /* Set promiscuous mode */
-
- LoadCSRs(dev); /* Reload CSR3 */
- InitRestartDepca(dev); /* Resume normal operation. */
- dev->tbusy = 0; /* Unlock the TX ring */
- } else {
- status = -EPERM;
- }
-
- break;
- case DEPCA_CLR_PROM: /* Clear Promiscuous Mode */
- if (suser()) {
- while(dev->tbusy); /* Stop ring access */
- set_bit(0, (void*)&dev->tbusy);
- while(lp->tx_old != lp->tx_new); /* Wait for the ring to empty */
-
- STOP_DEPCA; /* Temporarily stop the depca. */
- depca_init_ring(dev); /* Initialize the descriptor rings */
- lp->init_block.mode &= ~PROM; /* Clear promiscuous mode */
-
- LoadCSRs(dev); /* Reload CSR3 */
- InitRestartDepca(dev); /* Resume normal operation. */
- dev->tbusy = 0; /* Unlock the TX ring */
- } else {
- status = -EPERM;
- }
-
- break;
- case DEPCA_SAY_BOO: /* Say "Boo!" to the kernel log file */
- printk("%s: Boo!\n", dev->name);
-
- break;
- case DEPCA_GET_MCA: /* Get the multicast address table */
- ioc->len = (HASH_TABLE_LEN >> 3);
- if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
- copy_to_user(ioc->data, lp->init_block.mcast_table, ioc->len);
- }
-
- break;
- case DEPCA_SET_MCA: /* Set a multicast address */
- if (suser()) {
- if (!(status=verify_area(VERIFY_READ, ioc->data, ETH_ALEN*ioc->len))) {
- copy_from_user(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
- set_multicast_list(dev);
- }
- } else {
- status = -EPERM;
- }
-
- break;
- case DEPCA_CLR_MCA: /* Clear all multicast addresses */
- if (suser()) {
- set_multicast_list(dev);
- } else {
- status = -EPERM;
- }
-
- break;
- case DEPCA_MCA_EN: /* Enable pass all multicast addressing */
- if (suser()) {
- set_multicast_list(dev);
- } else {
- status = -EPERM;
- }
-
- break;
- case DEPCA_GET_STATS: /* Get the driver statistics */
- cli();
- ioc->len = sizeof(lp->pktStats);
- if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
- copy_to_user(ioc->data, &lp->pktStats, ioc->len);
- }
- sti();
-
- break;
- case DEPCA_CLR_STATS: /* Zero out the driver statistics */
- if (suser()) {
- cli();
- memset(&lp->pktStats, 0, sizeof(lp->pktStats));
- sti();
- } else {
- status = -EPERM;
- }
-
- break;
- case DEPCA_GET_REG: /* Get the DEPCA Registers */
- i=0;
- tmp.sval[i++] = inw(DEPCA_NICSR);
- outw(CSR0, DEPCA_ADDR); /* status register */
- tmp.sval[i++] = inw(DEPCA_DATA);
- memcpy(&tmp.sval[i], &lp->init_block, sizeof(struct depca_init));
- ioc->len = i+sizeof(struct depca_init);
- if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
- copy_to_user(ioc->data, tmp.addr, ioc->len);
- }
-
- break;
- default:
- status = -EOPNOTSUPP;
- }
-
- return status;
+
+ return status;
}
#ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device thisDepca = {
- devicename, /* device name is inserted by /linux/drivers/net/net_init.c */
- 0, 0, 0, 0,
- 0x200, 7, /* I/O address, IRQ */
- 0, 0, 0, NULL, depca_probe };
-
-static int irq=7; /* EDIT THESE LINE FOR YOUR CONFIGURATION */
-static int io=0x200; /* Or use the irq= io= options to insmod */
+static char devicename[9] =
+{0,};
+static struct device thisDepca =
+{
+ devicename, /* device name is inserted by /linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0x200, 7, /* I/O address, IRQ */
+ 0, 0, 0, NULL, depca_probe};
+
+static int irq = 7; /* EDIT THESE LINE FOR YOUR CONFIGURATION */
+static int io = 0x200; /* Or use the irq= io= options to insmod */
MODULE_PARM(irq, "i");
MODULE_PARM(io, "i");
/* See depca_probe() for autoprobe messages when a module */
-int
-init_module(void)
+int init_module(void)
{
- thisDepca.irq=irq;
- thisDepca.base_addr=io;
+ thisDepca.irq = irq;
+ thisDepca.base_addr = io;
- if (register_netdev(&thisDepca) != 0)
- return -EIO;
+ if (register_netdev(&thisDepca) != 0)
+ return -EIO;
- return 0;
+ return 0;
}
-void
-cleanup_module(void)
+void cleanup_module(void)
{
- if (thisDepca.priv) {
- kfree(thisDepca.priv);
- thisDepca.priv = NULL;
- }
- thisDepca.irq=0;
-
- unregister_netdev(&thisDepca);
- release_region(thisDepca.base_addr, DEPCA_TOTAL_SIZE);
-}
-#endif /* MODULE */
+ if (thisDepca.priv) {
+ kfree(thisDepca.priv);
+ thisDepca.priv = NULL;
+ }
+ thisDepca.irq = 0;
+ unregister_netdev(&thisDepca);
+ release_region(thisDepca.base_addr, DEPCA_TOTAL_SIZE);
+}
+#endif /* MODULE */
+
/*
* Local variables:
* compile-command: "gcc -D__KERNEL__ -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -c depca.c"
diff --git a/drivers/net/dgrs.c b/drivers/net/dgrs.c
index 17b4f5387..9ed7b2c8d 100644
--- a/drivers/net/dgrs.c
+++ b/drivers/net/dgrs.c
@@ -176,7 +176,7 @@ int dgrs_spantree = -1;
int dgrs_hashexpire = -1;
uchar dgrs_ipaddr[4] = { 0xff, 0xff, 0xff, 0xff};
uchar dgrs_iptrap[4] = { 0xff, 0xff, 0xff, 0xff};
-long dgrs_ipxnet = -1;
+__u32 dgrs_ipxnet = -1;
int dgrs_nicmode = 0;
/*
@@ -1507,7 +1507,7 @@ static int hashexpire = -1;
static int spantree = -1;
static int ipaddr[4] = { -1 };
static int iptrap[4] = { -1 };
-static long ipxnet = -1;
+static __u32 ipxnet = -1;
static int nicmode = -1;
MODULE_PARM(debug, "i");
diff --git a/drivers/net/dlci.c b/drivers/net/dlci.c
index 08d9901ba..c623dbd91 100644
--- a/drivers/net/dlci.c
+++ b/drivers/net/dlci.c
@@ -591,17 +591,10 @@ int dlci_init(struct device *dev)
dlp->receive = dlci_receive;
dev->type = ARPHRD_DLCI;
- dev->family = AF_INET;
dev->hard_header_len = sizeof(struct frhdr);
- dev->pa_alen = 4;
dev->addr_len = sizeof(short);
memset(dev->dev_addr, 0, sizeof(dev->dev_addr));
- dev->pa_addr = 0;
- dev->pa_dstaddr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
-
dev_init_buffers(dev);
return(0);
diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c
index 877e2256e..d5ba0e429 100644
--- a/drivers/net/dummy.c
+++ b/drivers/net/dummy.c
@@ -82,7 +82,7 @@ __initfunc(int dummy_init(struct device *dev))
if (dev->priv == NULL)
return -ENOMEM;
memset(dev->priv, 0, sizeof(struct net_device_stats));
- dev->get_stats = dummy_get_stats;
+ dev->get_stats = dummy_get_stats;
dev->open = dummy_open;
dev->stop = dummy_close;
@@ -92,6 +92,7 @@ __initfunc(int dummy_init(struct device *dev))
ether_setup(dev);
dev->tx_queue_len = 0;
dev->flags |= IFF_NOARP;
+ dev->flags &= ~IFF_BROADCAST;
return 0;
}
diff --git a/drivers/net/e2100.c b/drivers/net/e2100.c
index 4b6e67daf..3c2288f6a 100644
--- a/drivers/net/e2100.c
+++ b/drivers/net/e2100.c
@@ -254,10 +254,9 @@ e21_open(struct device *dev)
{
short ioaddr = dev->base_addr;
- if (request_irq(dev->irq, ei_interrupt, 0, "e2100", NULL)) {
+ if (request_irq(dev->irq, ei_interrupt, 0, "e2100", dev)) {
return EBUSY;
}
- irq2dev_map[dev->irq] = dev;
/* Set the interrupt line and memory base on the hardware. */
inb(ioaddr + E21_IRQ_LOW);
@@ -353,7 +352,7 @@ e21_close(struct device *dev)
if (ei_debug > 1)
printk("%s: Shutting down ethercard.\n", dev->name);
- free_irq(dev->irq, NULL);
+ free_irq(dev->irq, dev);
dev->irq = ei_status.saved_irq;
/* Shut off the interrupt line and secondary interface. */
@@ -362,8 +361,6 @@ e21_close(struct device *dev)
inb(ioaddr + E21_IRQ_HIGH); /* High IRQ bit, and if_port. */
outb(0, ioaddr + E21_ASIC);
- irq2dev_map[dev->irq] = NULL;
-
ei_close(dev);
/* Double-check that the memory has been turned off, because really
@@ -442,7 +439,7 @@ cleanup_module(void)
for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) {
struct device *dev = &dev_e21[this_dev];
if (dev->priv != NULL) {
- /* NB: e21_close() handles free_irq + irq2dev map */
+ /* NB: e21_close() handles free_irq */
kfree(dev->priv);
dev->priv = NULL;
release_region(dev->base_addr, E21_IO_EXTENT);
diff --git a/drivers/net/eepro.c b/drivers/net/eepro.c
index 25167fa0b..bd568c2ab 100644
--- a/drivers/net/eepro.c
+++ b/drivers/net/eepro.c
@@ -420,7 +420,7 @@ __initfunc(int eepro_probe1(struct device *dev, short ioaddr))
if (dev->irq > 2) {
printk(", IRQ %d, %s.\n", dev->irq,
ifmap[dev->if_port]);
- if (request_irq(dev->irq, &eepro_interrupt, 0, "eepro", NULL)) {
+ if (request_irq(dev->irq, &eepro_interrupt, 0, "eepro", dev)) {
printk("%s: unable to get IRQ %d.\n", dev->name, dev->irq);
return -EAGAIN;
}
@@ -513,7 +513,7 @@ static int eepro_grab_irq(struct device *dev)
outb(DIAGNOSE_CMD, ioaddr); /* RESET the 82595 */
if (*irqp == autoirq_report(2) && /* It's a good IRQ line */
- (request_irq(dev->irq = *irqp, &eepro_interrupt, 0, "eepro", NULL) == 0))
+ (request_irq(dev->irq = *irqp, &eepro_interrupt, 0, "eepro", dev) == 0))
break;
/* clear all interrupts */
@@ -560,10 +560,6 @@ eepro_open(struct device *dev)
return -EAGAIN;
}
- if (irq2dev_map[dev->irq] != 0
- || (irq2dev_map[dev->irq] = dev) == 0)
- return -EAGAIN;
-
/* Initialize the 82595. */
outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
@@ -741,7 +737,7 @@ eepro_send_packet(struct sk_buff *skb, struct device *dev)
static void
eepro_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
- struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct device *dev = dev_id;
int ioaddr, status, boguscount = 20;
if (net_debug > 5)
@@ -823,9 +819,7 @@ eepro_close(struct device *dev)
outb(RESET_CMD, ioaddr);
/* release the interrupt */
- free_irq(dev->irq, NULL);
-
- irq2dev_map[dev->irq] = 0;
+ free_irq(dev->irq, dev);
/* Update the statistics here. What statistics? */
diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c
index ce89c7b47..0f30de74c 100644
--- a/drivers/net/eepro100.c
+++ b/drivers/net/eepro100.c
@@ -19,7 +19,7 @@
*/
static const char *version =
-"eepro100.c:v0.34 8/30/97 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n";
+"eepro100.c:v0.36 10/20/97 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n";
/* A few user-configurable values that apply to all boards.
First set are undocumented and spelled per Intel recommendations. */
@@ -27,13 +27,16 @@ static const char *version =
static int congenb = 0; /* Enable congestion control in the DP83840. */
static int txfifo = 8; /* Tx FIFO threshold in 4 byte units, 0-15 */
static int rxfifo = 8; /* Rx FIFO threshold, default 32 bytes. */
-static int txdmacount = 0; /* Tx DMA burst length, 0-127, default 0. */
-static int rxdmacount = 0; /* Rx DMA length, 0 means no preemption. */
+/* Tx/Rx DMA burst length, 0-127, 0 == no preemption, tx==128 -> disabled. */
+static int txdmacount = 128;
+static int rxdmacount = 0;
-/* If defined use the copy-only-tiny-buffer scheme for higher performance.
- The value sets the copy breakpoint. Lower uses more memory, but is
- faster. */
-#define SKBUFF_RX_COPYBREAK 256
+/* Set the copy breakpoint for the copy-only-tiny-buffer Rx method.
+ Lower values use more memory, but are faster. */
+static int rx_copybreak = 200;
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 20;
#include <linux/config.h>
#ifdef MODULE
@@ -161,7 +164,7 @@ also has simplified Tx and Rx buffer modes. This driver uses the "flexible"
Tx mode, but in a simplified lower-overhead manner: it associates only a
single buffer descriptor with each frame descriptor.
-Despite the extra space overhead in each recieve skbuff, the driver must use
+Despite the extra space overhead in each receive skbuff, the driver must use
the simplified Rx buffer mode to assure that only a single data buffer is
associated with each RxFD. The driver implements this by reserving space
for the Rx descriptor at the head of each Rx skbuff
@@ -273,9 +276,6 @@ having to sign an Intel NDA when I'm helping Intel sell their own product!
/* Time in jiffies before concluding the transmitter is hung. */
#define TX_TIMEOUT ((400*HZ)/1000)
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-#define INTR_WORK 16
-
/* How to wait for the command unit to accept a command.
Typically this takes 0 ticks. */
static inline void wait_for_cmd_done(int cmd_ioaddr)
@@ -447,13 +447,12 @@ static void speedo_timer(unsigned long data);
static void speedo_init_rx_ring(struct device *dev);
static int speedo_start_xmit(struct sk_buff *skb, struct device *dev);
static int speedo_rx(struct device *dev);
-#ifdef SA_SHIRQ
static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
-#else
-static void speedo_interrupt(int irq, struct pt_regs *regs);
-#endif
static int speedo_close(struct device *dev);
static struct enet_statistics *speedo_get_stats(struct device *dev);
+#ifdef HAVE_PRIVATE_IOCTL
+static int speedo_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+#endif
static void set_rx_mode(struct device *dev);
@@ -462,24 +461,21 @@ static void set_rx_mode(struct device *dev);
/* 'options' is used to pass a transceiver override or full-duplex flag
e.g. "options=16" for FD, "options=32" for 100mbps-only. */
static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-
#ifdef MODULE
-
static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
static int debug = -1; /* The debug level */
+#endif
/* A list of all installed Speedo devices, for removing the driver module. */
static struct device *root_speedo_dev = NULL;
-#endif
-
int eepro100_init(struct device *dev)
{
int cards_found = 0;
if (pcibios_present()) {
- int pci_index;
- for (pci_index = 0; pci_index < 8; pci_index++) {
+ static int pci_index = 0;
+ for (; pci_index < 8; pci_index++) {
unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency;
#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
int pci_ioaddr;
@@ -543,6 +539,7 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
{
static int did_version = 0; /* Already printed version info. */
struct speedo_private *sp;
+ char *product;
int i;
u16 eeprom[0x40];
@@ -562,7 +559,7 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
u16 sum = 0;
int j;
for (j = 0, i = 0; i < 0x40; i++) {
- unsigned short value = read_eeprom(ioaddr, i);
+ u16 value = read_eeprom(ioaddr, i);
eeprom[i] = value;
sum += value;
if (i < 3) {
@@ -583,8 +580,13 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
action. */
outl(0, ioaddr + SCBPort);
- printk(KERN_INFO "%s: Intel EtherExpress Pro 10/100 at %#3x, ",
- dev->name, ioaddr);
+ if (eeprom[3] & 0x0100)
+ product = "OEM i82557/i82558 10/100 Ethernet";
+ else
+ product = "Intel EtherExpress Pro 10/100";
+
+ printk(KERN_INFO "%s: %s at %#3x, ", dev->name, product, ioaddr);
+
for (i = 0; i < 5; i++)
printk("%2.2X:", dev->dev_addr[i]);
printk("%2.2X, IRQ %d.\n", dev->dev_addr[i], irq);
@@ -596,7 +598,7 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
{
const char *connectors[] = {" RJ45", " BNC", " AUI", " MII"};
/* The self-test results must be paragraph aligned. */
- int str[6], *volatile self_test_results;
+ s32 str[6], *volatile self_test_results;
int boguscnt = 16000; /* Timeout for set-test. */
if (eeprom[3] & 0x03)
printk(KERN_INFO " Receiver lock-up bug exists -- enabling"
@@ -642,7 +644,7 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
}
/* Perform a system self-test. */
- self_test_results = (int*) ((((int) str) + 15) & ~0xf);
+ self_test_results = (s32*) ((((long) str) + 15) & ~0xf);
self_test_results[0] = 0;
self_test_results[1] = -1;
outl(virt_to_bus(self_test_results) | 1, ioaddr + SCBPort);
@@ -673,6 +675,8 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
}
#endif /* kernel_bloat */
+ outl(0, ioaddr + SCBPort);
+
/* We do a request_region() only to register /proc/ioports info. */
request_region(ioaddr, SPEEDO3_TOTAL_SIZE, "Intel Speedo3 Ethernet");
@@ -683,10 +687,8 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
dev->priv = kmalloc(sizeof(*sp), GFP_KERNEL);
sp = dev->priv;
memset(sp, 0, sizeof(*sp));
-#ifdef MODULE
sp->next_module = root_speedo_dev;
root_speedo_dev = dev;
-#endif
if (card_idx >= 0) {
if (full_duplex[card_idx] >= 0)
@@ -699,10 +701,8 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
sp->phy[1] = eeprom[7];
sp->rx_bug = (eeprom[3] & 0x03) == 3 ? 0 : 1;
- printk(KERN_INFO " Operating in %s duplex mode.\n",
- sp->full_duplex ? "full" : "half");
if (sp->rx_bug)
- printk(KERN_INFO " Reciever lock-up workaround activated.\n");
+ printk(KERN_INFO " Receiver lock-up workaround activated.\n");
/* The Speedo-specific entries in the device structure. */
dev->open = &speedo_open;
@@ -712,6 +712,9 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
#ifdef NEW_MULTICAST
dev->set_multicast_list = &set_rx_mode;
#endif
+#ifdef HAVE_PRIVATE_IOCTL
+ dev->do_ioctl = &speedo_ioctl;
+#endif
return;
}
@@ -730,7 +733,11 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
/* Delay between EEPROM clock transitions.
This is a "nasty" timing loop, but PC compatible machines are defined
to delay an ISA compatible period for the SLOW_DOWN_IO macro. */
+#ifdef _LINUX_DELAY_H
+#define eeprom_delay(nanosec) udelay(1);
+#else
#define eeprom_delay(nanosec) do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+#endif
/* The EEPROM commands include the alway-set leading bit. */
#define EE_WRITE_CMD (5 << 6)
@@ -823,26 +830,10 @@ speedo_open(struct device *dev)
SLOW_DOWN_IO; /* At least 250ns */
#endif
-#ifdef SA_SHIRQ
if (request_irq(dev->irq, &speedo_interrupt, SA_SHIRQ,
"Intel EtherExpress Pro 10/100 Ethernet", dev)) {
return -EAGAIN;
}
-#else
-#ifdef USE_SHARED_IRQ
- if (request_shared_irq(dev->irq, &speedo_interrupt, dev,
- "Intel EtherExpress Pro 10/100 Ethernet"))
- return -EAGAIN;
-#else
- if (dev->irq < 2 || dev->irq > 15 || irq2dev_map[dev->irq] != NULL)
- return -EAGAIN;
- irq2dev_map[dev->irq] = dev;
- if (request_irq(dev->irq, &speedo_interrupt, 0, "Intel EtherExpress Pro 10/100 Ethernet")) {
- irq2dev_map[dev->irq] = NULL;
- return -EAGAIN;
- }
-#endif
-#endif
if (speedo_debug > 1)
printk(KERN_DEBUG "%s: speedo_open() irq %d.\n", dev->name, dev->irq);
@@ -850,11 +841,13 @@ speedo_open(struct device *dev)
MOD_INC_USE_COUNT;
/* Load the statistics block address. */
+ wait_for_cmd_done(ioaddr + SCBCmd);
outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer);
outw(INT_MASK | CU_STATSADDR, ioaddr + SCBCmd);
sp->lstats.done_marker = 0;
speedo_init_rx_ring(dev);
+ wait_for_cmd_done(ioaddr + SCBCmd);
outl(0, ioaddr + SCBPointer);
outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd);
@@ -865,8 +858,8 @@ speedo_open(struct device *dev)
/* Fill the first command with our physical address. */
{
- unsigned short *eaddrs = (unsigned short *)dev->dev_addr;
- unsigned short *setup_frm = (short *)&(sp->tx_ring[0].tx_desc_addr);
+ u16 *eaddrs = (u16 *)dev->dev_addr;
+ u16 *setup_frm = (u16 *)&(sp->tx_ring[0].tx_desc_addr);
/* Avoid a bug(?!) here by marking the command already completed. */
sp->tx_ring[0].status = ((CmdSuspend | CmdIASetup) << 16) | 0xa000;
@@ -880,6 +873,7 @@ speedo_open(struct device *dev)
sp->dirty_tx = 0;
sp->tx_full = 0;
+ wait_for_cmd_done(ioaddr + SCBCmd);
outl(0, ioaddr + SCBPointer);
outw(INT_MASK | CU_CMD_BASE, ioaddr + SCBCmd);
@@ -916,6 +910,7 @@ speedo_open(struct device *dev)
sp->timer.function = &speedo_timer; /* timer handler */
add_timer(&sp->timer);
+ wait_for_cmd_done(ioaddr + SCBCmd);
outw(CU_DUMPSTATS, ioaddr + SCBCmd);
return 0;
}
@@ -1045,13 +1040,6 @@ speedo_start_xmit(struct sk_buff *skb, struct device *dev)
int ioaddr = dev->base_addr;
int entry;
- if (skb == NULL || skb->len <= 0) {
- printk(KERN_ERR "%s: Obsolete driver layer request made: skbuff==NULL.\n",
- dev->name);
- dev_tint(dev);
- return 0;
- }
-
/* Block a timer-based transmit from overlapping. This could better be
done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
If this ever occurs the queue layer is doing something evil! */
@@ -1099,6 +1087,7 @@ speedo_start_xmit(struct sk_buff *skb, struct device *dev)
sp->last_cmd->command &= ~(CmdSuspend | CmdIntr);
sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
/* Trigger the command unit resume. */
+ wait_for_cmd_done(ioaddr + SCBCmd);
outw(CU_RESUME, ioaddr + SCBCmd);
restore_flags(flags);
}
@@ -1116,23 +1105,11 @@ speedo_start_xmit(struct sk_buff *skb, struct device *dev)
/* The interrupt handler does all of the Rx thread work and cleans up
after the Tx thread. */
-#ifdef SA_SHIRQ
static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
-#else
-static void speedo_interrupt(int irq, struct pt_regs *regs)
-#endif
{
-#ifdef SA_SHIRQ
struct device *dev = (struct device *)dev_instance;
-#else
-#ifdef USE_SHARED_IRQ
- struct device *dev = (struct device *)(irq == 0 ? regs : irq2dev_map[irq]);
-#else
- struct device *dev = (struct device *)(irq2dev_map[irq]);
-#endif
-#endif
struct speedo_private *sp;
- int ioaddr, boguscnt = INTR_WORK;
+ int ioaddr, boguscnt = max_interrupt_work;
unsigned short status;
#ifndef final_version
@@ -1201,7 +1178,7 @@ static void speedo_interrupt(int irq, struct pt_regs *regs)
int status = sp->tx_ring[entry].status;
if (speedo_debug > 5)
- printk(KERN_DEBUG " scavenge canidate %d status %4.4x.\n",
+ printk(KERN_DEBUG " scavenge candidate %d status %4.4x.\n",
entry, status);
if ((status & 0x8000) == 0)
break; /* It still hasn't been processed. */
@@ -1254,11 +1231,7 @@ static void speedo_interrupt(int irq, struct pt_regs *regs)
if (dev->start == 0 && --stopit < 0) {
printk(KERN_ALERT "%s: Emergency stop, interrupt is stuck.\n",
dev->name);
-#ifdef SA_SHIRQ
free_irq(irq, dev);
-#else
- free_irq(irq);
-#endif
}
}
#endif
@@ -1292,13 +1265,13 @@ speedo_rx(struct device *dev)
dev->name, status);
} else {
/* Malloc up new buffer, compatible with net-2e. */
- short pkt_len = sp->rx_ringp[entry]->count & 0x3fff;
+ int pkt_len = sp->rx_ringp[entry]->count & 0x3fff;
struct sk_buff *skb;
int rx_in_place = 0;
/* Check if the packet is long enough to just accept without
copying to a properly sized skbuff. */
- if (pkt_len > SKBUFF_RX_COPYBREAK) {
+ if (pkt_len > rx_copybreak) {
struct sk_buff *newskb;
char *temp;
@@ -1375,8 +1348,14 @@ speedo_rx(struct device *dev)
#if (LINUX_VERSION_CODE >= VERSION(1,3,44))
if (! rx_in_place) {
skb_reserve(skb, 2); /* 16 byte align the data fields */
+#if defined(__i386) && notyet
+ /* Packet is in one chunk -- we can copy + cksum. */
+ eth_io_copy_and_sum(skb, bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
+ pkt_len, 0);
+#else
memcpy(skb_put(skb, pkt_len),
bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len);
+#endif
}
skb->protocol = eth_type_trans(skb, dev);
#else
@@ -1438,12 +1417,7 @@ speedo_close(struct device *dev)
outw(INT_MASK, ioaddr + SCBCmd);
outw(INT_MASK | RX_ABORT, ioaddr + SCBCmd);
-#ifdef SA_SHIRQ
free_irq(dev->irq, dev);
-#else
- free_irq(dev->irq);
- irq2dev_map[dev->irq] = 0;
-#endif
/* Free all the skbuffs in the Rx and Tx queues. */
for (i = 0; i < RX_RING_SIZE; i++) {
@@ -1519,12 +1493,39 @@ speedo_get_stats(struct device *dev)
sp->stats.rx_fifo_errors += sp->lstats.rx_overrun_errs;
sp->stats.rx_length_errors += sp->lstats.rx_runt_errs;
sp->lstats.done_marker = 0x0000;
- if (dev->start)
+ if (dev->start) {
+ wait_for_cmd_done(ioaddr + SCBCmd);
outw(CU_DUMPSTATS, ioaddr + SCBCmd);
+ }
}
return &sp->stats;
}
+#ifdef HAVE_PRIVATE_IOCTL
+static int speedo_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+ struct speedo_private *sp = (struct speedo_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ u16 *data = (u16 *)&rq->ifr_data;
+ int phy = sp->phy[0] & 0x1f;
+
+ switch(cmd) {
+ case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
+ data[0] = phy;
+ case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
+ data[3] = mdio_read(ioaddr, data[0], data[1]);
+ return 0;
+ case SIOCDEVPRIVATE+2: /* Write the specified MII register */
+ if (!suser())
+ return -EPERM;
+ mdio_write(ioaddr, data[0], data[1], data[2]);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+#endif /* HAVE_PRIVATE_IOCTL */
+
/* Set or clear the multicast filter for this adaptor.
This is very ugly with Intel chips -- we usually have to execute an
entire configuration command, plus process a multicast command.
@@ -1583,6 +1584,7 @@ set_rx_mode(struct device *dev)
virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE]));
sp->last_cmd->command &= ~CmdSuspend;
/* Immediately trigger the command unit resume. */
+ wait_for_cmd_done(ioaddr + SCBCmd);
outw(CU_RESUME, ioaddr + SCBCmd);
sp->last_cmd = &sp->config_cmd;
restore_flags(flags);
@@ -1598,8 +1600,7 @@ set_rx_mode(struct device *dev)
if (new_rx_mode == 0 && dev->mc_count < 3) {
/* The simple case of 0-2 multicast list entries occurs often, and
fits within one tx_ring[] entry. */
- u16 *setup_params;
- unsigned short *eaddrs;
+ u16 *setup_params, *eaddrs;
struct dev_mc_list *mclist;
save_flags(flags);
@@ -1610,12 +1611,12 @@ set_rx_mode(struct device *dev)
sp->tx_ring[entry].link =
virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]);
sp->tx_ring[entry].tx_desc_addr = 0; /* Really MC list count. */
- setup_params = (short *)&sp->tx_ring[entry].tx_desc_addr;
+ setup_params = (u16 *)&sp->tx_ring[entry].tx_desc_addr;
*setup_params++ = dev->mc_count*6;
/* Fill in the multicast addresses. */
for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
i++, mclist = mclist->next) {
- eaddrs = (unsigned short *)mclist->dmi_addr;
+ eaddrs = (u16 *)mclist->dmi_addr;
*setup_params++ = *eaddrs++;
*setup_params++ = *eaddrs++;
*setup_params++ = *eaddrs++;
@@ -1623,15 +1624,16 @@ set_rx_mode(struct device *dev)
sp->last_cmd->command &= ~CmdSuspend;
/* Immediately trigger the command unit resume. */
+ wait_for_cmd_done(ioaddr + SCBCmd);
outw(CU_RESUME, ioaddr + SCBCmd);
sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
restore_flags(flags);
} else if (new_rx_mode == 0) {
/* This does not work correctly, but why not? */
struct dev_mc_list *mclist;
- unsigned short *eaddrs;
+ u16 *eaddrs;
struct descriptor *mc_setup_frm = sp->mc_setup_frm;
- u16 *setup_params = (short *)mc_setup_frm->params;
+ u16 *setup_params = (u16 *)mc_setup_frm->params;
int i;
if (sp->mc_setup_frm_len < 10 + dev->mc_count*6
@@ -1657,12 +1659,12 @@ set_rx_mode(struct device *dev)
mc_setup_frm->status = 0;
mc_setup_frm->command = CmdSuspend | CmdIntr | CmdMulticastList;
/* Link set below. */
- setup_params = (short *)mc_setup_frm->params;
+ setup_params = (u16 *)mc_setup_frm->params;
*setup_params++ = dev->mc_count*6;
/* Fill in the multicast addresses. */
for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
i++, mclist = mclist->next) {
- eaddrs = (unsigned short *)mclist->dmi_addr;
+ eaddrs = (u16 *)mclist->dmi_addr;
*setup_params++ = *eaddrs++;
*setup_params++ = *eaddrs++;
*setup_params++ = *eaddrs++;
@@ -1688,6 +1690,7 @@ set_rx_mode(struct device *dev)
sp->last_cmd->command &= ~CmdSuspend;
/* Immediately trigger the command unit resume. */
+ wait_for_cmd_done(ioaddr + SCBCmd);
outw(CU_RESUME, ioaddr + SCBCmd);
sp->last_cmd = mc_setup_frm;
restore_flags(flags);
@@ -1704,6 +1707,21 @@ set_rx_mode(struct device *dev)
char kernel_version[] = UTS_RELEASE;
#endif
+#if LINUX_VERSION_CODE > 0x20118
+MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
+MODULE_DESCRIPTION("Intel i82557/i82558 EtherExpressPro driver");
+MODULE_PARM(debug, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(congenb, "i");
+MODULE_PARM(txfifo, "i");
+MODULE_PARM(rxfifo, "i");
+MODULE_PARM(txdmacount, "i");
+MODULE_PARM(rxdmacount, "i");
+MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(max_interrupt_work, "i");
+#endif
+
int
init_module(void)
{
diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c
index eb0117eb4..c78e798dd 100644
--- a/drivers/net/eexpress.c
+++ b/drivers/net/eexpress.c
@@ -346,9 +346,7 @@ static int eexp_open(struct device *dev)
if (!irq || !irqrmap[irq])
return -ENXIO;
- if (irq2dev_map[irq] ||
- ((irq2dev_map[irq]=dev),0) ||
- request_irq(irq,&eexp_irq,0,"EtherExpress",NULL))
+ if (request_irq(irq,&eexp_irq,0,"EtherExpress",dev))
return -EAGAIN;
request_region(ioaddr, EEXP_IO_EXTENT, "EtherExpress");
@@ -390,8 +388,7 @@ static int eexp_close(struct device *dev)
lp->started = 0;
scb_command(dev, SCB_CUsuspend|SCB_RUsuspend);
outb(0,ioaddr+SIGNAL_CA);
- free_irq(irq,NULL);
- irq2dev_map[irq] = NULL;
+ free_irq(irq,dev);
outb(i586_RST,ioaddr+EEPROM_Ctrl);
release_region(ioaddr,16);
@@ -628,7 +625,7 @@ static void eexp_cmd_clear(struct device *dev)
static void eexp_irq(int irq, void *dev_info, struct pt_regs *regs)
{
- struct device *dev = irq2dev_map[irq];
+ struct device *dev = dev_info;
struct net_local *lp;
unsigned short ioaddr,status,ack_cmd;
unsigned short old_read_ptr, old_write_ptr;
diff --git a/drivers/net/eql.c b/drivers/net/eql.c
index 820d5b428..386708670 100644
--- a/drivers/net/eql.c
+++ b/drivers/net/eql.c
@@ -262,12 +262,6 @@ __initfunc(int eql_init(struct device *dev))
dev->mtu = EQL_DEFAULT_MTU; /* set to 576 in eql.h */
dev->flags = IFF_MASTER;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = 4;
-
dev->type = ARPHRD_SLIP;
dev->tx_queue_len = 5; /* Hands them off fast */
diff --git a/drivers/net/es3210.c b/drivers/net/es3210.c
index 96ffd43de..b7e0cae06 100644
--- a/drivers/net/es3210.c
+++ b/drivers/net/es3210.c
@@ -214,7 +214,7 @@ __initfunc(int es_probe1(struct device *dev, int ioaddr))
printk(" assigning IRQ %d", dev->irq);
}
- if (request_irq(dev->irq, ei_interrupt, 0, "es3210", NULL)) {
+ if (request_irq(dev->irq, ei_interrupt, 0, "es3210", dev)) {
printk (" unable to get IRQ %d.\n", dev->irq);
return EAGAIN;
}
@@ -225,7 +225,7 @@ __initfunc(int es_probe1(struct device *dev, int ioaddr))
if (mem_enabled != 0x80) {
printk(" shared mem disabled - giving up\n");
- free_irq(dev->irq, NULL);
+ free_irq(dev->irq, dev);
return -ENXIO;
}
dev->mem_start = 0xC0000 + mem_bits*0x4000;
@@ -243,7 +243,7 @@ __initfunc(int es_probe1(struct device *dev, int ioaddr))
/* Allocate dev->priv and fill in 8390 specific dev fields. */
if (ethdev_init(dev)) {
printk (" unable to allocate memory for dev->priv.\n");
- free_irq(dev->irq, NULL);
+ free_irq(dev->irq, dev);
return -ENOMEM;
}
@@ -432,8 +432,7 @@ cleanup_module(void)
if (dev->priv != NULL) {
kfree(dev->priv);
dev->priv = NULL;
- free_irq(dev->irq, NULL);
- irq2dev_map[dev->irq] = NULL;
+ free_irq(dev->irq, dev);
release_region(dev->base_addr, ES_IO_EXTENT);
unregister_netdev(dev);
}
diff --git a/drivers/net/eth16i.c b/drivers/net/eth16i.c
index 74cf38d50..fa5ae74e4 100644
--- a/drivers/net/eth16i.c
+++ b/drivers/net/eth16i.c
@@ -434,7 +434,7 @@ __initfunc(static int eth16i_probe1(struct device *dev, short ioaddr))
dev->irq = irq;
/* Try to obtain interrupt vector */
- if(request_irq(dev->irq, &eth16i_interrupt, 0, "eth16i", NULL)) {
+ if(request_irq(dev->irq, &eth16i_interrupt, 0, "eth16i", dev)) {
printk("%s: %s at %#3x, but is unusable due
conflict on IRQ %d.\n", dev->name, cardname, ioaddr, irq);
return EAGAIN;
@@ -807,8 +807,6 @@ static int eth16i_open(struct device *dev)
struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
int ioaddr = dev->base_addr;
- irq2dev_map[dev->irq] = dev;
-
/* Powerup the chip */
outb(0xc0 | POWERUP, ioaddr + CONFIG_REG_1);
@@ -1106,7 +1104,7 @@ static void eth16i_rx(struct device *dev)
static void eth16i_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct device *dev = dev_id;
struct eth16i_local *lp;
int ioaddr = 0,
status;
@@ -1227,8 +1225,7 @@ int init_module(void)
void cleanup_module(void)
{
unregister_netdev( &dev_eth16i );
- free_irq( dev_eth16i.irq, NULL );
- irq2dev_map[ dev_eth16i.irq ] = NULL;
+ free_irq( dev_eth16i.irq, &dev_eth16i );
release_region( dev_eth16i.base_addr, ETH16I_IO_EXTENT );
}
diff --git a/drivers/net/ethertap.c b/drivers/net/ethertap.c
index 1ae6a636e..b9bc5253a 100644
--- a/drivers/net/ethertap.c
+++ b/drivers/net/ethertap.c
@@ -20,11 +20,12 @@
#include <linux/errno.h>
#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/init.h>
-#include <net/netlink.h>
+#include <linux/netlink.h>
/*
* Index to functions.
@@ -98,14 +99,25 @@ __initfunc(int ethertap_probe(struct device *dev))
static int ethertap_open(struct device *dev)
{
+ struct in_device *in_dev;
if (ethertap_debug > 2)
printk("%s: Doing ethertap_open()...", dev->name);
netlink_attach(dev->base_addr, ethertap_rx);
dev->start = 1;
dev->tbusy = 0;
+
/* Fill in the MAC based on the IP address. We do the same thing
here as PLIP does */
- memcpy(dev->dev_addr+2,&dev->pa_addr,4);
+
+ if((in_dev=dev->ip_ptr)!=NULL)
+ {
+ /*
+ * Any address wil do - we take the first
+ */
+ struct in_ifaddr *ifa=in_dev->ifa_list;
+ if(ifa!=NULL)
+ memcpy(dev->dev_addr+2,&ifa->ifa_local,4);
+ }
MOD_INC_USE_COUNT;
return 0;
}
@@ -147,7 +159,7 @@ static int ethertap_rx(int id, struct sk_buff *skb)
if(dev==NULL)
{
- printk("%s: bad unit!\n",dev->name);
+ printk("ethertap: bad unit!\n");
kfree_skb(skb, FREE_WRITE);
return -ENXIO;
}
diff --git a/drivers/net/ewrk3.c b/drivers/net/ewrk3.c
index 75492e1c1..ee0581e1d 100644
--- a/drivers/net/ewrk3.c
+++ b/drivers/net/ewrk3.c
@@ -1,140 +1,140 @@
/* ewrk3.c: A DIGITAL EtherWORKS 3 ethernet driver for Linux.
- Written 1994 by David C. Davies.
-
- Copyright 1994 Digital Equipment Corporation.
-
- This software may be used and distributed according to the terms of
- the GNU Public License, incorporated herein by reference.
+ Written 1994 by David C. Davies.
+
+ Copyright 1994 Digital Equipment Corporation.
+
+ This software may be used and distributed according to the terms of
+ the GNU Public License, incorporated herein by reference.
- This driver is written for the Digital Equipment Corporation series
- of EtherWORKS ethernet cards:
-
- DE203 Turbo (BNC)
- DE204 Turbo (TP)
- DE205 Turbo (TP BNC)
-
- The driver has been tested on a relatively busy network using the DE205
- card and benchmarked with 'ttcp': it transferred 16M of data at 975kB/s
- (7.8Mb/s) to a DECstation 5000/200.
-
- The author may be reached at davies@maniac.ultranet.com.
-
- =========================================================================
- This driver has been written substantially from scratch, although its
- inheritance of style and stack interface from 'depca.c' and in turn from
- Donald Becker's 'lance.c' should be obvious.
-
- The DE203/4/5 boards all use a new proprietary chip in place of the
- LANCE chip used in prior cards (DEPCA, DE100, DE200/1/2, DE210, DE422).
- Use the depca.c driver in the standard distribution for the LANCE based
- cards from DIGITAL; this driver will not work with them.
-
- The DE203/4/5 cards have 2 main modes: shared memory and I/O only. I/O
- only makes all the card accesses through I/O transactions and no high
- (shared) memory is used. This mode provides a >48% performance penalty
- and is deprecated in this driver, although allowed to provide initial
- setup when hardstrapped.
-
- The shared memory mode comes in 3 flavours: 2kB, 32kB and 64kB. There is
- no point in using any mode other than the 2kB mode - their performances
- are virtually identical, although the driver has been tested in the 2kB
- and 32kB modes. I would suggest you uncomment the line:
-
- FORCE_2K_MODE;
-
- to allow the driver to configure the card as a 2kB card at your current
- base address, thus leaving more room to clutter your system box with
- other memory hungry boards.
-
- As many ISA and EISA cards can be supported under this driver as you
- wish, limited primarily by the available IRQ lines, rather than by the
- available I/O addresses (24 ISA, 16 EISA). I have checked different
- configurations of multiple depca cards and ewrk3 cards and have not
- found a problem yet (provided you have at least depca.c v0.38) ...
-
- The board IRQ setting must be at an unused IRQ which is auto-probed
- using Donald Becker's autoprobe routines. All these cards are at
- {5,10,11,15}.
-
- No 16MB memory limitation should exist with this driver as DMA is not
- used and the common memory area is in low memory on the network card (my
- current system has 20MB and I've not had problems yet).
-
- The ability to load this driver as a loadable module has been included
- and used extensively during the driver development (to save those long
- reboot sequences). To utilise this ability, you have to do 8 things:
-
- 0) have a copy of the loadable modules code installed on your system.
- 1) copy ewrk3.c from the /linux/drivers/net directory to your favourite
- temporary directory.
- 2) edit the source code near line 1898 to reflect the I/O address and
- IRQ you're using.
- 3) compile ewrk3.c, but include -DMODULE in the command line to ensure
- that the correct bits are compiled (see end of source code).
- 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a
- kernel with the ewrk3 configuration turned off and reboot.
- 5) insmod ewrk3.o
- [Alan Cox: Changed this so you can insmod ewrk3.o irq=x io=y]
- 6) run the net startup bits for your new eth?? interface manually
- (usually /etc/rc.inet[12] at boot time).
- 7) enjoy!
-
- Note that autoprobing is not allowed in loadable modules - the system is
- already up and running and you're messing with interrupts.
-
- To unload a module, turn off the associated interface
- 'ifconfig eth?? down' then 'rmmod ewrk3'.
-
- Promiscuous mode has been turned off in this driver, but all the
- multicast address bits have been turned on. This improved the send
- performance on a busy network by about 13%.
-
- Ioctl's have now been provided (primarily because I wanted to grab some
- packet size statistics). They are patterned after 'plipconfig.c' from a
- suggestion by Alan Cox. Using these ioctls, you can enable promiscuous
- mode, add/delete multicast addresses, change the hardware address, get
- packet size distribution statistics and muck around with the control and
- status register. I'll add others if and when the need arises.
-
- TO DO:
- ------
-
-
- Revision History
- ----------------
-
- Version Date Description
-
- 0.1 26-aug-94 Initial writing. ALPHA code release.
- 0.11 31-aug-94 Fixed: 2k mode memory base calc.,
- LeMAC version calc.,
- IRQ vector assignments during autoprobe.
- 0.12 31-aug-94 Tested working on LeMAC2 (DE20[345]-AC) card.
- Fixed up MCA hash table algorithm.
- 0.20 4-sep-94 Added IOCTL functionality.
- 0.21 14-sep-94 Added I/O mode.
- 0.21axp 15-sep-94 Special version for ALPHA AXP Linux V1.0.
- 0.22 16-sep-94 Added more IOCTLs & tidied up.
- 0.23 21-sep-94 Added transmit cut through.
- 0.24 31-oct-94 Added uid checks in some ioctls.
- 0.30 1-nov-94 BETA code release.
- 0.31 5-dec-94 Added check/allocate region code.
- 0.32 16-jan-95 Broadcast packet fix.
- 0.33 10-Feb-95 Fix recognition bug reported by <bkm@star.rl.ac.uk>.
- 0.40 27-Dec-95 Rationalise MODULE and autoprobe code.
- Rewrite for portability & updated.
- ALPHA support from <jestabro@amt.tay1.dec.com>
- Added verify_area() calls in ewrk3_ioctl() from
- suggestion by <heiko@colossus.escape.de>.
- Add new multicasting code.
- 0.41 20-Jan-96 Fix IRQ set up problem reported by
- <kenneth@bbs.sas.ntu.ac.sg>.
- 0.42 22-Apr-96 Fix alloc_device() bug <jari@markkus2.fimr.fi>
- 0.43 16-Aug-96 Update alloc_device() to conform to de4x5.c
-
- =========================================================================
-*/
+ This driver is written for the Digital Equipment Corporation series
+ of EtherWORKS ethernet cards:
+
+ DE203 Turbo (BNC)
+ DE204 Turbo (TP)
+ DE205 Turbo (TP BNC)
+
+ The driver has been tested on a relatively busy network using the DE205
+ card and benchmarked with 'ttcp': it transferred 16M of data at 975kB/s
+ (7.8Mb/s) to a DECstation 5000/200.
+
+ The author may be reached at davies@maniac.ultranet.com.
+
+ =========================================================================
+ This driver has been written substantially from scratch, although its
+ inheritance of style and stack interface from 'depca.c' and in turn from
+ Donald Becker's 'lance.c' should be obvious.
+
+ The DE203/4/5 boards all use a new proprietary chip in place of the
+ LANCE chip used in prior cards (DEPCA, DE100, DE200/1/2, DE210, DE422).
+ Use the depca.c driver in the standard distribution for the LANCE based
+ cards from DIGITAL; this driver will not work with them.
+
+ The DE203/4/5 cards have 2 main modes: shared memory and I/O only. I/O
+ only makes all the card accesses through I/O transactions and no high
+ (shared) memory is used. This mode provides a >48% performance penalty
+ and is deprecated in this driver, although allowed to provide initial
+ setup when hardstrapped.
+
+ The shared memory mode comes in 3 flavours: 2kB, 32kB and 64kB. There is
+ no point in using any mode other than the 2kB mode - their performances
+ are virtually identical, although the driver has been tested in the 2kB
+ and 32kB modes. I would suggest you uncomment the line:
+
+ FORCE_2K_MODE;
+
+ to allow the driver to configure the card as a 2kB card at your current
+ base address, thus leaving more room to clutter your system box with
+ other memory hungry boards.
+
+ As many ISA and EISA cards can be supported under this driver as you
+ wish, limited primarily by the available IRQ lines, rather than by the
+ available I/O addresses (24 ISA, 16 EISA). I have checked different
+ configurations of multiple depca cards and ewrk3 cards and have not
+ found a problem yet (provided you have at least depca.c v0.38) ...
+
+ The board IRQ setting must be at an unused IRQ which is auto-probed
+ using Donald Becker's autoprobe routines. All these cards are at
+ {5,10,11,15}.
+
+ No 16MB memory limitation should exist with this driver as DMA is not
+ used and the common memory area is in low memory on the network card (my
+ current system has 20MB and I've not had problems yet).
+
+ The ability to load this driver as a loadable module has been included
+ and used extensively during the driver development (to save those long
+ reboot sequences). To utilise this ability, you have to do 8 things:
+
+ 0) have a copy of the loadable modules code installed on your system.
+ 1) copy ewrk3.c from the /linux/drivers/net directory to your favourite
+ temporary directory.
+ 2) edit the source code near line 1898 to reflect the I/O address and
+ IRQ you're using.
+ 3) compile ewrk3.c, but include -DMODULE in the command line to ensure
+ that the correct bits are compiled (see end of source code).
+ 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a
+ kernel with the ewrk3 configuration turned off and reboot.
+ 5) insmod ewrk3.o
+ [Alan Cox: Changed this so you can insmod ewrk3.o irq=x io=y]
+ 6) run the net startup bits for your new eth?? interface manually
+ (usually /etc/rc.inet[12] at boot time).
+ 7) enjoy!
+
+ Note that autoprobing is not allowed in loadable modules - the system is
+ already up and running and you're messing with interrupts.
+
+ To unload a module, turn off the associated interface
+ 'ifconfig eth?? down' then 'rmmod ewrk3'.
+
+ Promiscuous mode has been turned off in this driver, but all the
+ multicast address bits have been turned on. This improved the send
+ performance on a busy network by about 13%.
+
+ Ioctl's have now been provided (primarily because I wanted to grab some
+ packet size statistics). They are patterned after 'plipconfig.c' from a
+ suggestion by Alan Cox. Using these ioctls, you can enable promiscuous
+ mode, add/delete multicast addresses, change the hardware address, get
+ packet size distribution statistics and muck around with the control and
+ status register. I'll add others if and when the need arises.
+
+ TO DO:
+ ------
+
+
+ Revision History
+ ----------------
+
+ Version Date Description
+
+ 0.1 26-aug-94 Initial writing. ALPHA code release.
+ 0.11 31-aug-94 Fixed: 2k mode memory base calc.,
+ LeMAC version calc.,
+ IRQ vector assignments during autoprobe.
+ 0.12 31-aug-94 Tested working on LeMAC2 (DE20[345]-AC) card.
+ Fixed up MCA hash table algorithm.
+ 0.20 4-sep-94 Added IOCTL functionality.
+ 0.21 14-sep-94 Added I/O mode.
+ 0.21axp 15-sep-94 Special version for ALPHA AXP Linux V1.0.
+ 0.22 16-sep-94 Added more IOCTLs & tidied up.
+ 0.23 21-sep-94 Added transmit cut through.
+ 0.24 31-oct-94 Added uid checks in some ioctls.
+ 0.30 1-nov-94 BETA code release.
+ 0.31 5-dec-94 Added check/allocate region code.
+ 0.32 16-jan-95 Broadcast packet fix.
+ 0.33 10-Feb-95 Fix recognition bug reported by <bkm@star.rl.ac.uk>.
+ 0.40 27-Dec-95 Rationalise MODULE and autoprobe code.
+ Rewrite for portability & updated.
+ ALPHA support from <jestabro@amt.tay1.dec.com>
+ Added verify_area() calls in ewrk3_ioctl() from
+ suggestion by <heiko@colossus.escape.de>.
+ Add new multicasting code.
+ 0.41 20-Jan-96 Fix IRQ set up problem reported by
+ <kenneth@bbs.sas.ntu.ac.sg>.
+ 0.42 22-Apr-96 Fix alloc_device() bug <jari@markkus2.fimr.fi>
+ 0.43 16-Aug-96 Update alloc_device() to conform to de4x5.c
+
+ =========================================================================
+ */
static const char *version = "ewrk3.c:v0.43 96/8/16 davies@maniac.ultranet.com\n";
@@ -172,7 +172,7 @@ static int ewrk3_debug = EWRK3_DEBUG;
static int ewrk3_debug = 1;
#endif
-#define EWRK3_NDA 0xffe0 /* No Device Address */
+#define EWRK3_NDA 0xffe0 /* No Device Address */
#define PROBE_LENGTH 32
#define ETH_PROM_SIG 0xAA5500FFUL
@@ -187,18 +187,18 @@ static int ewrk3_debug = 1;
#endif
/*
-** Sets up the I/O area for the autoprobe.
-*/
-#define EWRK3_IO_BASE 0x100 /* Start address for probe search */
-#define EWRK3_IOP_INC 0x20 /* I/O address increment */
-#define EWRK3_TOTAL_SIZE 0x20 /* required I/O address length */
+ ** Sets up the I/O area for the autoprobe.
+ */
+#define EWRK3_IO_BASE 0x100 /* Start address for probe search */
+#define EWRK3_IOP_INC 0x20 /* I/O address increment */
+#define EWRK3_TOTAL_SIZE 0x20 /* required I/O address length */
#ifndef MAX_NUM_EWRK3S
#define MAX_NUM_EWRK3S 21
#endif
#ifndef EWRK3_EISA_IO_PORTS
-#define EWRK3_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */
+#define EWRK3_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */
#endif
#ifndef MAX_EISA_SLOTS
@@ -206,22 +206,22 @@ static int ewrk3_debug = 1;
#define EISA_SLOT_INC 0x1000
#endif
-#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */
-#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */
+#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */
+#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */
-#define QUEUE_PKT_TIMEOUT (1*HZ) /* Jiffies */
+#define QUEUE_PKT_TIMEOUT (1*HZ) /* Jiffies */
/*
-** EtherWORKS 3 shared memory window sizes
-*/
+ ** EtherWORKS 3 shared memory window sizes
+ */
#define IO_ONLY 0x00
#define SHMEM_2K 0x800
#define SHMEM_32K 0x8000
#define SHMEM_64K 0x10000
/*
-** EtherWORKS 3 IRQ ENABLE/DISABLE
-*/
+ ** EtherWORKS 3 IRQ ENABLE/DISABLE
+ */
#define ENABLE_IRQs { \
icr |= lp->irq_mask;\
outb(icr, EWRK3_ICR); /* Enable the IRQs */\
@@ -234,8 +234,8 @@ static int ewrk3_debug = 1;
}
/*
-** EtherWORKS 3 START/STOP
-*/
+ ** EtherWORKS 3 START/STOP
+ */
#define START_EWRK3 { \
csr = inb(EWRK3_CSR);\
csr &= ~(CSR_TXD|CSR_RXD);\
@@ -248,1668 +248,1674 @@ static int ewrk3_debug = 1;
}
/*
-** The EtherWORKS 3 private structure
-*/
+ ** The EtherWORKS 3 private structure
+ */
#define EWRK3_PKT_STAT_SZ 16
-#define EWRK3_PKT_BIN_SZ 128 /* Should be >=100 unless you
- increase EWRK3_PKT_STAT_SZ */
+#define EWRK3_PKT_BIN_SZ 128 /* Should be >=100 unless you
+ increase EWRK3_PKT_STAT_SZ */
struct ewrk3_private {
- char adapter_name[80]; /* Name exported to /proc/ioports */
- u_long shmem_base; /* Shared memory start address */
- u_long shmem_length; /* Shared memory window length */
- struct net_device_stats stats; /* Public stats */
- struct {
- u32 bins[EWRK3_PKT_STAT_SZ]; /* Private stats counters */
- u32 unicast;
- u32 multicast;
- u32 broadcast;
- u32 excessive_collisions;
- u32 tx_underruns;
- u32 excessive_underruns;
- } pktStats;
- u_char irq_mask; /* Adapter IRQ mask bits */
- u_char mPage; /* Maximum 2kB Page number */
- u_char lemac; /* Chip rev. level */
- u_char hard_strapped; /* Don't allow a full open */
- u_char lock; /* Lock the page register */
- u_char txc; /* Transmit cut through */
- u_char *mctbl; /* Pointer to the multicast table */
+ char adapter_name[80]; /* Name exported to /proc/ioports */
+ u_long shmem_base; /* Shared memory start address */
+ u_long shmem_length; /* Shared memory window length */
+ struct net_device_stats stats; /* Public stats */
+ struct {
+ u32 bins[EWRK3_PKT_STAT_SZ]; /* Private stats counters */
+ u32 unicast;
+ u32 multicast;
+ u32 broadcast;
+ u32 excessive_collisions;
+ u32 tx_underruns;
+ u32 excessive_underruns;
+ } pktStats;
+ u_char irq_mask; /* Adapter IRQ mask bits */
+ u_char mPage; /* Maximum 2kB Page number */
+ u_char lemac; /* Chip rev. level */
+ u_char hard_strapped; /* Don't allow a full open */
+ u_char lock; /* Lock the page register */
+ u_char txc; /* Transmit cut through */
+ u_char *mctbl; /* Pointer to the multicast table */
};
/*
-** Force the EtherWORKS 3 card to be in 2kB MODE
-*/
+ ** Force the EtherWORKS 3 card to be in 2kB MODE
+ */
#define FORCE_2K_MODE { \
shmem_length = SHMEM_2K;\
outb(((mem_start - 0x80000) >> 11), EWRK3_MBR);\
}
/*
-** Public Functions
-*/
-static int ewrk3_open(struct device *dev);
-static int ewrk3_queue_pkt(struct sk_buff *skb, struct device *dev);
-static void ewrk3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static int ewrk3_close(struct device *dev);
+ ** Public Functions
+ */
+static int ewrk3_open(struct device *dev);
+static int ewrk3_queue_pkt(struct sk_buff *skb, struct device *dev);
+static void ewrk3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static int ewrk3_close(struct device *dev);
static struct net_device_stats *ewrk3_get_stats(struct device *dev);
-static void set_multicast_list(struct device *dev);
-static int ewrk3_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+static void set_multicast_list(struct device *dev);
+static int ewrk3_ioctl(struct device *dev, struct ifreq *rq, int cmd);
/*
-** Private functions
-*/
-static int ewrk3_hw_init(struct device *dev, u_long iobase);
-static void ewrk3_init(struct device *dev);
-static int ewrk3_rx(struct device *dev);
-static int ewrk3_tx(struct device *dev);
-
-static void EthwrkSignature(char * name, char *eeprom_image);
-static int DevicePresent(u_long iobase);
-static void SetMulticastFilter(struct device *dev);
-static int EISA_signature(char *name, s32 eisa_id);
-
-static int Read_EEPROM(u_long iobase, u_char eaddr);
-static int Write_EEPROM(short data, u_long iobase, u_char eaddr);
-static u_char get_hw_addr (struct device *dev, u_char *eeprom_image, char chipType);
-
-static void isa_probe(struct device *dev, u_long iobase);
-static void eisa_probe(struct device *dev, u_long iobase);
+ ** Private functions
+ */
+static int ewrk3_hw_init(struct device *dev, u_long iobase);
+static void ewrk3_init(struct device *dev);
+static int ewrk3_rx(struct device *dev);
+static int ewrk3_tx(struct device *dev);
+
+static void EthwrkSignature(char *name, char *eeprom_image);
+static int DevicePresent(u_long iobase);
+static void SetMulticastFilter(struct device *dev);
+static int EISA_signature(char *name, s32 eisa_id);
+
+static int Read_EEPROM(u_long iobase, u_char eaddr);
+static int Write_EEPROM(short data, u_long iobase, u_char eaddr);
+static u_char get_hw_addr(struct device *dev, u_char * eeprom_image, char chipType);
+
+static void isa_probe(struct device *dev, u_long iobase);
+static void eisa_probe(struct device *dev, u_long iobase);
static struct device *alloc_device(struct device *dev, u_long iobase);
-static int ewrk3_dev_index(char *s);
-static struct device *insert_device(struct device *dev, u_long iobase, int (*init)(struct device *));
+static int ewrk3_dev_index(char *s);
+static struct device *insert_device(struct device *dev, u_long iobase, int (*init) (struct device *));
#ifdef MODULE
-int init_module(void);
+int init_module(void);
void cleanup_module(void);
static int autoprobed = 1, loading_module = 1;
-# else
-static u_char irq[] = {5,0,10,3,11,9,15,12};
+#else
+static u_char irq[] =
+{5, 0, 10, 3, 11, 9, 15, 12};
static int autoprobed = 0, loading_module = 0;
-#endif /* MODULE */
+#endif /* MODULE */
static char name[EWRK3_STRLEN + 1];
static int num_ewrk3s = 0, num_eth = 0;
/*
-** Miscellaneous defines...
-*/
+ ** Miscellaneous defines...
+ */
#define INIT_EWRK3 {\
outb(EEPROM_INIT, EWRK3_IOPR);\
udelay(1000);\
}
+
-
__initfunc(int ewrk3_probe(struct device *dev))
{
- int tmp = num_ewrk3s, status = -ENODEV;
- u_long iobase = dev->base_addr;
-
- if ((iobase == 0) && loading_module){
- printk("Autoprobing is not supported when loading a module based driver.\n");
- status = -EIO;
- } else { /* First probe for the Ethernet */
- /* Address PROM pattern */
- isa_probe(dev, iobase);
- eisa_probe(dev, iobase);
-
- if ((tmp == num_ewrk3s) && (iobase != 0) && loading_module) {
- printk("%s: ewrk3_probe() cannot find device at 0x%04lx.\n", dev->name,
- iobase);
- }
-
- /*
- ** Walk the device list to check that at least one device
- ** initialised OK
- */
- for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next);
-
- if (dev->priv) status = 0;
- if (iobase == 0) autoprobed = 1;
- }
-
- return status;
+ int tmp = num_ewrk3s, status = -ENODEV;
+ u_long iobase = dev->base_addr;
+
+ if ((iobase == 0) && loading_module) {
+ printk("Autoprobing is not supported when loading a module based driver.\n");
+ status = -EIO;
+ } else { /* First probe for the Ethernet */
+ /* Address PROM pattern */
+ isa_probe(dev, iobase);
+ eisa_probe(dev, iobase);
+
+ if ((tmp == num_ewrk3s) && (iobase != 0) && loading_module) {
+ printk("%s: ewrk3_probe() cannot find device at 0x%04lx.\n", dev->name,
+ iobase);
+ }
+ /*
+ ** Walk the device list to check that at least one device
+ ** initialised OK
+ */
+ for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next);
+
+ if (dev->priv)
+ status = 0;
+ if (iobase == 0)
+ autoprobed = 1;
+ }
+
+ return status;
}
__initfunc(static int
-ewrk3_hw_init(struct device *dev, u_long iobase))
+ ewrk3_hw_init(struct device *dev, u_long iobase))
{
- struct ewrk3_private *lp;
- int i, status=0;
- u_long mem_start, shmem_length;
- u_char cr, cmr, icr, nicsr, lemac, hard_strapped = 0;
- u_char eeprom_image[EEPROM_MAX], chksum, eisa_cr = 0;
-
- /*
- ** Stop the EWRK3. Enable the DBR ROM. Disable interrupts and remote boot.
- ** This also disables the EISA_ENABLE bit in the EISA Control Register.
- */
- if (iobase > 0x400) eisa_cr = inb(EISA_CR);
- INIT_EWRK3;
-
- nicsr = inb(EWRK3_CSR);
-
- icr = inb(EWRK3_ICR);
- icr &= 0x70;
- outb(icr, EWRK3_ICR); /* Disable all the IRQs */
-
- if (nicsr == (CSR_TXD|CSR_RXD)) {
-
- /* Check that the EEPROM is alive and well and not living on Pluto... */
- for (chksum=0, i=0; i<EEPROM_MAX; i+=2) {
- union {
- short val;
- char c[2];
- } tmp;
-
- tmp.val = (short)Read_EEPROM(iobase, (i>>1));
- eeprom_image[i] = tmp.c[0];
- eeprom_image[i+1] = tmp.c[1];
- chksum += eeprom_image[i] + eeprom_image[i+1];
- }
-
- if (chksum != 0) { /* Bad EEPROM Data! */
- printk("%s: Device has a bad on-board EEPROM.\n", dev->name);
- status = -ENXIO;
- } else {
- EthwrkSignature(name, eeprom_image);
- if (*name != '\0') { /* found a EWRK3 device */
- dev->base_addr = iobase;
-
- if (iobase > 0x400) {
- outb(eisa_cr, EISA_CR); /* Rewrite the EISA CR */
- }
-
- lemac = eeprom_image[EEPROM_CHIPVER];
- cmr = inb(EWRK3_CMR);
-
- if (((lemac == LeMAC) && ((cmr & CMR_NO_EEPROM) != CMR_NO_EEPROM)) ||
- ((lemac == LeMAC2) && !(cmr & CMR_HS))) {
- printk("%s: %s at %#4lx", dev->name, name, iobase);
- hard_strapped = 1;
- } else if ((iobase&0x0fff)==EWRK3_EISA_IO_PORTS) {
- /* EISA slot address */
- printk("%s: %s at %#4lx (EISA slot %ld)",
- dev->name, name, iobase, ((iobase>>12)&0x0f));
- } else { /* ISA port address */
- printk("%s: %s at %#4lx", dev->name, name, iobase);
- }
-
- if (!status) {
- printk(", h/w address ");
- if (lemac!=LeMAC2) DevicePresent(iobase);/* need after EWRK3_INIT */
- status = get_hw_addr(dev, eeprom_image, lemac);
- for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet addr. */
- printk("%2.2x:", dev->dev_addr[i]);
- }
- printk("%2.2x,\n", dev->dev_addr[i]);
-
- if (status) {
- printk(" which has an EEPROM CRC error.\n");
- status = -ENXIO;
- } else {
- if (lemac == LeMAC2) { /* Special LeMAC2 CMR things */
- cmr &= ~(CMR_RA | CMR_WB | CMR_LINK | CMR_POLARITY | CMR_0WS);
- if (eeprom_image[EEPROM_MISC0] & READ_AHEAD) cmr |= CMR_RA;
- if (eeprom_image[EEPROM_MISC0] & WRITE_BEHIND) cmr |= CMR_WB;
- if (eeprom_image[EEPROM_NETMAN0] & NETMAN_POL) cmr |= CMR_POLARITY;
- if (eeprom_image[EEPROM_NETMAN0] & NETMAN_LINK) cmr |= CMR_LINK;
- if (eeprom_image[EEPROM_MISC0] & _0WS_ENA) cmr |= CMR_0WS;
- }
- if (eeprom_image[EEPROM_SETUP] & SETUP_DRAM) cmr |= CMR_DRAM;
- outb(cmr, EWRK3_CMR);
-
- cr = inb(EWRK3_CR); /* Set up the Control Register */
- cr |= eeprom_image[EEPROM_SETUP] & SETUP_APD;
- if (cr & SETUP_APD) cr |= eeprom_image[EEPROM_SETUP] & SETUP_PS;
- cr |= eeprom_image[EEPROM_MISC0] & FAST_BUS;
- cr |= eeprom_image[EEPROM_MISC0] & ENA_16;
- outb(cr, EWRK3_CR);
-
- /*
- ** Determine the base address and window length for the EWRK3
- ** RAM from the memory base register.
- */
- mem_start = inb(EWRK3_MBR);
- shmem_length = 0;
- if (mem_start != 0) {
- if ((mem_start >= 0x0a) && (mem_start <= 0x0f)) {
- mem_start *= SHMEM_64K;
- shmem_length = SHMEM_64K;
- } else if ((mem_start >= 0x14) && (mem_start <= 0x1f)) {
- mem_start *= SHMEM_32K;
- shmem_length = SHMEM_32K;
- } else if ((mem_start >= 0x40) && (mem_start <= 0xff)) {
- mem_start = mem_start * SHMEM_2K + 0x80000;
- shmem_length = SHMEM_2K;
- } else {
- status = -ENXIO;
- }
- }
-
- /*
- ** See the top of this source code for comments about
- ** uncommenting this line.
- */
-/* FORCE_2K_MODE;*/
-
- if (!status) {
- if (hard_strapped) {
- printk(" is hard strapped.\n");
- } else if (mem_start) {
- printk(" has a %dk RAM window", (int)(shmem_length >> 10));
- printk(" at 0x%.5lx", mem_start);
- } else {
- printk(" is in I/O only mode");
- }
-
- /* private area & initialise */
- dev->priv = (void *) kmalloc(sizeof(struct ewrk3_private),
- GFP_KERNEL);
- if (dev->priv == NULL) {
- return -ENOMEM;
- }
- lp = (struct ewrk3_private *)dev->priv;
- memset(dev->priv, 0, sizeof(struct ewrk3_private));
- lp->shmem_base = mem_start;
- lp->shmem_length = shmem_length;
- lp->lemac = lemac;
- lp->hard_strapped = hard_strapped;
-
- lp->mPage = 64;
- if (cmr & CMR_DRAM) lp->mPage <<= 1 ;/* 2 DRAMS on module */
-
- sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
- request_region(iobase, EWRK3_TOTAL_SIZE, lp->adapter_name);
-
- lp->irq_mask = ICR_TNEM|ICR_TXDM|ICR_RNEM|ICR_RXDM;
-
- if (!hard_strapped) {
- /*
- ** Enable EWRK3 board interrupts for autoprobing
- */
- icr |= ICR_IE; /* Enable interrupts */
- outb(icr, EWRK3_ICR);
+ struct ewrk3_private *lp;
+ int i, status = 0;
+ u_long mem_start, shmem_length;
+ u_char cr, cmr, icr, nicsr, lemac, hard_strapped = 0;
+ u_char eeprom_image[EEPROM_MAX], chksum, eisa_cr = 0;
- /* The DMA channel may be passed in on this parameter. */
- dev->dma = 0;
+ /*
+ ** Stop the EWRK3. Enable the DBR ROM. Disable interrupts and remote boot.
+ ** This also disables the EISA_ENABLE bit in the EISA Control Register.
+ */
+ if (iobase > 0x400)
+ eisa_cr = inb(EISA_CR);
+ INIT_EWRK3;
+
+ nicsr = inb(EWRK3_CSR);
+
+ icr = inb(EWRK3_ICR);
+ icr &= 0x70;
+ outb(icr, EWRK3_ICR); /* Disable all the IRQs */
+
+ if (nicsr == (CSR_TXD | CSR_RXD)) {
+
+ /* Check that the EEPROM is alive and well and not living on Pluto... */
+ for (chksum = 0, i = 0; i < EEPROM_MAX; i += 2) {
+ union {
+ short val;
+ char c[2];
+ } tmp;
+
+ tmp.val = (short) Read_EEPROM(iobase, (i >> 1));
+ eeprom_image[i] = tmp.c[0];
+ eeprom_image[i + 1] = tmp.c[1];
+ chksum += eeprom_image[i] + eeprom_image[i + 1];
+ }
- /* To auto-IRQ we enable the initialization-done and DMA err,
- interrupts. For now we will always get a DMA error. */
- if (dev->irq < 2) {
-#ifndef MODULE
- u_char irqnum;
-
- autoirq_setup(0);
-
- /*
- ** Trigger a TNE interrupt.
- */
- icr |=ICR_TNEM;
- outb(1,EWRK3_TDQ); /* Write to the TX done queue */
- outb(icr, EWRK3_ICR); /* Unmask the TXD interrupt */
-
- irqnum = irq[((icr & IRQ_SEL) >> 4)];
-
- dev->irq = autoirq_report(1);
- if ((dev->irq) && (irqnum == dev->irq)) {
- printk(" and uses IRQ%d.\n", dev->irq);
- } else {
- if (!dev->irq) {
- printk(" and failed to detect IRQ line.\n");
- } else if ((irqnum == 1) && (lemac == LeMAC2)) {
- printk(" and an illegal IRQ line detected.\n");
- } else {
- printk(", but incorrect IRQ line detected.\n");
- }
- status = -ENXIO;
- }
-
- DISABLE_IRQs; /* Mask all interrupts */
-
-#endif /* MODULE */
+ if (chksum != 0) { /* Bad EEPROM Data! */
+ printk("%s: Device has a bad on-board EEPROM.\n", dev->name);
+ status = -ENXIO;
} else {
- printk(" and requires IRQ%d.\n", dev->irq);
+ EthwrkSignature(name, eeprom_image);
+ if (*name != '\0') { /* found a EWRK3 device */
+ dev->base_addr = iobase;
+
+ if (iobase > 0x400) {
+ outb(eisa_cr, EISA_CR); /* Rewrite the EISA CR */
+ }
+ lemac = eeprom_image[EEPROM_CHIPVER];
+ cmr = inb(EWRK3_CMR);
+
+ if (((lemac == LeMAC) && ((cmr & CMR_NO_EEPROM) != CMR_NO_EEPROM)) ||
+ ((lemac == LeMAC2) && !(cmr & CMR_HS))) {
+ printk("%s: %s at %#4lx", dev->name, name, iobase);
+ hard_strapped = 1;
+ } else if ((iobase & 0x0fff) == EWRK3_EISA_IO_PORTS) {
+ /* EISA slot address */
+ printk("%s: %s at %#4lx (EISA slot %ld)",
+ dev->name, name, iobase, ((iobase >> 12) & 0x0f));
+ } else { /* ISA port address */
+ printk("%s: %s at %#4lx", dev->name, name, iobase);
+ }
+
+ if (!status) {
+ printk(", h/w address ");
+ if (lemac != LeMAC2)
+ DevicePresent(iobase); /* need after EWRK3_INIT */
+ status = get_hw_addr(dev, eeprom_image, lemac);
+ for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet addr. */
+ printk("%2.2x:", dev->dev_addr[i]);
+ }
+ printk("%2.2x,\n", dev->dev_addr[i]);
+
+ if (status) {
+ printk(" which has an EEPROM CRC error.\n");
+ status = -ENXIO;
+ } else {
+ if (lemac == LeMAC2) { /* Special LeMAC2 CMR things */
+ cmr &= ~(CMR_RA | CMR_WB | CMR_LINK | CMR_POLARITY | CMR_0WS);
+ if (eeprom_image[EEPROM_MISC0] & READ_AHEAD)
+ cmr |= CMR_RA;
+ if (eeprom_image[EEPROM_MISC0] & WRITE_BEHIND)
+ cmr |= CMR_WB;
+ if (eeprom_image[EEPROM_NETMAN0] & NETMAN_POL)
+ cmr |= CMR_POLARITY;
+ if (eeprom_image[EEPROM_NETMAN0] & NETMAN_LINK)
+ cmr |= CMR_LINK;
+ if (eeprom_image[EEPROM_MISC0] & _0WS_ENA)
+ cmr |= CMR_0WS;
+ }
+ if (eeprom_image[EEPROM_SETUP] & SETUP_DRAM)
+ cmr |= CMR_DRAM;
+ outb(cmr, EWRK3_CMR);
+
+ cr = inb(EWRK3_CR); /* Set up the Control Register */
+ cr |= eeprom_image[EEPROM_SETUP] & SETUP_APD;
+ if (cr & SETUP_APD)
+ cr |= eeprom_image[EEPROM_SETUP] & SETUP_PS;
+ cr |= eeprom_image[EEPROM_MISC0] & FAST_BUS;
+ cr |= eeprom_image[EEPROM_MISC0] & ENA_16;
+ outb(cr, EWRK3_CR);
+
+ /*
+ ** Determine the base address and window length for the EWRK3
+ ** RAM from the memory base register.
+ */
+ mem_start = inb(EWRK3_MBR);
+ shmem_length = 0;
+ if (mem_start != 0) {
+ if ((mem_start >= 0x0a) && (mem_start <= 0x0f)) {
+ mem_start *= SHMEM_64K;
+ shmem_length = SHMEM_64K;
+ } else if ((mem_start >= 0x14) && (mem_start <= 0x1f)) {
+ mem_start *= SHMEM_32K;
+ shmem_length = SHMEM_32K;
+ } else if ((mem_start >= 0x40) && (mem_start <= 0xff)) {
+ mem_start = mem_start * SHMEM_2K + 0x80000;
+ shmem_length = SHMEM_2K;
+ } else {
+ status = -ENXIO;
+ }
+ }
+ /*
+ ** See the top of this source code for comments about
+ ** uncommenting this line.
+ */
+/* FORCE_2K_MODE; */
+
+ if (!status) {
+ if (hard_strapped) {
+ printk(" is hard strapped.\n");
+ } else if (mem_start) {
+ printk(" has a %dk RAM window", (int) (shmem_length >> 10));
+ printk(" at 0x%.5lx", mem_start);
+ } else {
+ printk(" is in I/O only mode");
+ }
+
+ /* private area & initialise */
+ dev->priv = (void *) kmalloc(sizeof(struct ewrk3_private),
+ GFP_KERNEL);
+ if (dev->priv == NULL) {
+ return -ENOMEM;
+ }
+ lp = (struct ewrk3_private *) dev->priv;
+ memset(dev->priv, 0, sizeof(struct ewrk3_private));
+ lp->shmem_base = mem_start;
+ lp->shmem_length = shmem_length;
+ lp->lemac = lemac;
+ lp->hard_strapped = hard_strapped;
+
+ lp->mPage = 64;
+ if (cmr & CMR_DRAM)
+ lp->mPage <<= 1; /* 2 DRAMS on module */
+
+ sprintf(lp->adapter_name, "%s (%s)", name, dev->name);
+ request_region(iobase, EWRK3_TOTAL_SIZE, lp->adapter_name);
+
+ lp->irq_mask = ICR_TNEM | ICR_TXDM | ICR_RNEM | ICR_RXDM;
+
+ if (!hard_strapped) {
+ /*
+ ** Enable EWRK3 board interrupts for autoprobing
+ */
+ icr |= ICR_IE; /* Enable interrupts */
+ outb(icr, EWRK3_ICR);
+
+ /* The DMA channel may be passed in on this parameter. */
+ dev->dma = 0;
+
+ /* To auto-IRQ we enable the initialization-done and DMA err,
+ interrupts. For now we will always get a DMA error. */
+ if (dev->irq < 2) {
+#ifndef MODULE
+ u_char irqnum;
+
+ autoirq_setup(0);
+
+ /*
+ ** Trigger a TNE interrupt.
+ */
+ icr |= ICR_TNEM;
+ outb(1, EWRK3_TDQ); /* Write to the TX done queue */
+ outb(icr, EWRK3_ICR); /* Unmask the TXD interrupt */
+
+ irqnum = irq[((icr & IRQ_SEL) >> 4)];
+
+ dev->irq = autoirq_report(1);
+ if ((dev->irq) && (irqnum == dev->irq)) {
+ printk(" and uses IRQ%d.\n", dev->irq);
+ } else {
+ if (!dev->irq) {
+ printk(" and failed to detect IRQ line.\n");
+ } else if ((irqnum == 1) && (lemac == LeMAC2)) {
+ printk(" and an illegal IRQ line detected.\n");
+ } else {
+ printk(", but incorrect IRQ line detected.\n");
+ }
+ status = -ENXIO;
+ }
+
+ DISABLE_IRQs; /* Mask all interrupts */
+
+#endif /* MODULE */
+ } else {
+ printk(" and requires IRQ%d.\n", dev->irq);
+ }
+ }
+ if (status)
+ release_region(iobase, EWRK3_TOTAL_SIZE);
+ } else {
+ status = -ENXIO;
+ }
+ }
+ }
+ } else {
+ status = -ENXIO;
+ }
+ }
+
+ if (!status) {
+ if (ewrk3_debug > 1) {
+ printk(version);
+ }
+ /* The EWRK3-specific entries in the device structure. */
+ dev->open = &ewrk3_open;
+ dev->hard_start_xmit = &ewrk3_queue_pkt;
+ dev->stop = &ewrk3_close;
+ dev->get_stats = &ewrk3_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+ dev->do_ioctl = &ewrk3_ioctl;
+
+ dev->mem_start = 0;
+
+ /* Fill in the generic field of the device structure. */
+ ether_setup(dev);
}
- }
- if (status) release_region(iobase, EWRK3_TOTAL_SIZE);
- } else {
- status = -ENXIO;
- }
- }
+ } else {
+ status = -ENXIO;
}
- } else {
- status = -ENXIO;
- }
- }
-
- if (!status) {
- if (ewrk3_debug > 1) {
- printk(version);
- }
-
- /* The EWRK3-specific entries in the device structure. */
- dev->open = &ewrk3_open;
- dev->hard_start_xmit = &ewrk3_queue_pkt;
- dev->stop = &ewrk3_close;
- dev->get_stats = &ewrk3_get_stats;
- dev->set_multicast_list = &set_multicast_list;
- dev->do_ioctl = &ewrk3_ioctl;
-
- dev->mem_start = 0;
-
- /* Fill in the generic field of the device structure. */
- ether_setup(dev);
- }
- } else {
- status = -ENXIO;
- }
-
- return status;
-}
+ return status;
+}
-static int
-ewrk3_open(struct device *dev)
+
+static int ewrk3_open(struct device *dev)
{
- struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
- u_long iobase = dev->base_addr;
- int i, status = 0;
- u_char icr, csr;
-
- /*
- ** Stop the TX and RX...
- */
- STOP_EWRK3;
-
- if (!lp->hard_strapped) {
- irq2dev_map[dev->irq] = dev; /* For latched interrupts */
-
- if (request_irq(dev->irq, (void *)ewrk3_interrupt, 0, "ewrk3", NULL)) {
- printk("ewrk3_open(): Requested IRQ%d is busy\n",dev->irq);
- status = -EAGAIN;
- } else {
-
- /*
- ** Re-initialize the EWRK3...
- */
- ewrk3_init(dev);
-
- if (ewrk3_debug > 1){
- printk("%s: ewrk3 open with irq %d\n",dev->name,dev->irq);
- printk(" physical address: ");
- for (i=0;i<5;i++){
- printk("%2.2x:",(u_char)dev->dev_addr[i]);
- }
- printk("%2.2x\n",(u_char)dev->dev_addr[i]);
- if (lp->shmem_length == 0) {
- printk(" no shared memory, I/O only mode\n");
+ struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ int i, status = 0;
+ u_char icr, csr;
+
+ /*
+ ** Stop the TX and RX...
+ */
+ STOP_EWRK3;
+
+ if (!lp->hard_strapped) {
+ if (request_irq(dev->irq, (void *) ewrk3_interrupt, 0, "ewrk3", dev)) {
+ printk("ewrk3_open(): Requested IRQ%d is busy\n", dev->irq);
+ status = -EAGAIN;
+ } else {
+
+ /*
+ ** Re-initialize the EWRK3...
+ */
+ ewrk3_init(dev);
+
+ if (ewrk3_debug > 1) {
+ printk("%s: ewrk3 open with irq %d\n", dev->name, dev->irq);
+ printk(" physical address: ");
+ for (i = 0; i < 5; i++) {
+ printk("%2.2x:", (u_char) dev->dev_addr[i]);
+ }
+ printk("%2.2x\n", (u_char) dev->dev_addr[i]);
+ if (lp->shmem_length == 0) {
+ printk(" no shared memory, I/O only mode\n");
+ } else {
+ printk(" start of shared memory: 0x%08lx\n", lp->shmem_base);
+ printk(" window length: 0x%04lx\n", lp->shmem_length);
+ }
+ printk(" # of DRAMS: %d\n", ((inb(EWRK3_CMR) & 0x02) ? 2 : 1));
+ printk(" csr: 0x%02x\n", inb(EWRK3_CSR));
+ printk(" cr: 0x%02x\n", inb(EWRK3_CR));
+ printk(" icr: 0x%02x\n", inb(EWRK3_ICR));
+ printk(" cmr: 0x%02x\n", inb(EWRK3_CMR));
+ printk(" fmqc: 0x%02x\n", inb(EWRK3_FMQC));
+ }
+ dev->tbusy = 0;
+ dev->start = 1;
+ dev->interrupt = UNMASK_INTERRUPTS;
+
+ /*
+ ** Unmask EWRK3 board interrupts
+ */
+ icr = inb(EWRK3_ICR);
+ ENABLE_IRQs;
+
+ }
} else {
- printk(" start of shared memory: 0x%08lx\n",lp->shmem_base);
- printk(" window length: 0x%04lx\n",lp->shmem_length);
+ dev->start = 0;
+ dev->tbusy = 1;
+ printk("%s: ewrk3 available for hard strapped set up only.\n", dev->name);
+ printk(" Run the 'ewrk3setup' utility or remove the hard straps.\n");
}
- printk(" # of DRAMS: %d\n",((inb(EWRK3_CMR) & 0x02) ? 2 : 1));
- printk(" csr: 0x%02x\n", inb(EWRK3_CSR));
- printk(" cr: 0x%02x\n", inb(EWRK3_CR));
- printk(" icr: 0x%02x\n", inb(EWRK3_ICR));
- printk(" cmr: 0x%02x\n", inb(EWRK3_CMR));
- printk(" fmqc: 0x%02x\n", inb(EWRK3_FMQC));
- }
-
- dev->tbusy = 0;
- dev->start = 1;
- dev->interrupt = UNMASK_INTERRUPTS;
-
- /*
- ** Unmask EWRK3 board interrupts
- */
- icr = inb(EWRK3_ICR);
- ENABLE_IRQs;
-
- }
- } else {
- dev->start = 0;
- dev->tbusy = 1;
- printk("%s: ewrk3 available for hard strapped set up only.\n", dev->name);
- printk(" Run the 'ewrk3setup' utility or remove the hard straps.\n");
- }
-
- MOD_INC_USE_COUNT;
-
- return status;
-}
-/*
-** Initialize the EtherWORKS 3 operating conditions
-*/
-static void
-ewrk3_init(struct device *dev)
-{
- struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
- u_char csr, page;
- u_long iobase = dev->base_addr;
-
- /*
- ** Enable any multicasts
- */
- set_multicast_list(dev);
-
- /*
- ** Clean out any remaining entries in all the queues here
- */
- while (inb(EWRK3_TQ));
- while (inb(EWRK3_TDQ));
- while (inb(EWRK3_RQ));
- while (inb(EWRK3_FMQ));
-
- /*
- ** Write a clean free memory queue
- */
- for (page=1;page<lp->mPage;page++) { /* Write the free page numbers */
- outb(page, EWRK3_FMQ); /* to the Free Memory Queue */
- }
-
- lp->lock = 0; /* Ensure there are no locks */
-
- START_EWRK3; /* Enable the TX and/or RX */
+ MOD_INC_USE_COUNT;
+
+ return status;
}
/*
-** Writes a socket buffer to the free page queue
-*/
-static int
-ewrk3_queue_pkt(struct sk_buff *skb, struct device *dev)
+ ** Initialize the EtherWORKS 3 operating conditions
+ */
+static void ewrk3_init(struct device *dev)
{
- struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
- u_long iobase = dev->base_addr;
- int status = 0;
- u_char icr, csr;
-
- /* Transmitter timeout, serious problems. */
- if (dev->tbusy || lp->lock) {
- int tickssofar = jiffies - dev->trans_start;
- if (tickssofar < QUEUE_PKT_TIMEOUT) {
- status = -1;
- } else if (!lp->hard_strapped) {
- printk("%s: transmit timed/locked out, status %04x, resetting.\n",
- dev->name, inb(EWRK3_CSR));
-
- /*
- ** Mask all board interrupts
- */
- DISABLE_IRQs;
-
- /*
- ** Stop the TX and RX...
- */
- STOP_EWRK3;
-
- ewrk3_init(dev);
-
- /*
- ** Unmask EWRK3 board interrupts
- */
- ENABLE_IRQs;
-
- dev->tbusy=0;
- dev->trans_start = jiffies;
- }
- } else if (skb == NULL) {
- dev_tint(dev);
- } else if (skb->len > 0) {
-
- /*
- ** Block a timer-based transmit from overlapping. This could better be
- ** done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
- */
- if (test_and_set_bit(0, (void*)&dev->tbusy) != 0)
- printk("%s: Transmitter access conflict.\n", dev->name);
-
- DISABLE_IRQs; /* So that the page # remains correct */
-
- /*
- ** Get a free page from the FMQ when resources are available
- */
- if (inb(EWRK3_FMQC) > 0) {
- u_long buf = 0;
- u_char page;
-
- if ((page = inb(EWRK3_FMQ)) < lp->mPage) {
+ struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv;
+ u_char csr, page;
+ u_long iobase = dev->base_addr;
+
/*
- ** Set up shared memory window and pointer into the window
- */
- while (test_and_set_bit(0, (void *)&lp->lock) != 0); /* Wait for lock to free */
- if (lp->shmem_length == IO_ONLY) {
- outb(page, EWRK3_IOPR);
- } else if (lp->shmem_length == SHMEM_2K) {
- buf = lp->shmem_base;
- outb(page, EWRK3_MPR);
- } else if (lp->shmem_length == SHMEM_32K) {
- buf = ((((short)page << 11) & 0x7800) + lp->shmem_base);
- outb((page >> 4), EWRK3_MPR);
- } else if (lp->shmem_length == SHMEM_64K) {
- buf = ((((short)page << 11) & 0xf800) + lp->shmem_base);
- outb((page >> 5), EWRK3_MPR);
- } else {
- status = -1;
- printk("%s: Oops - your private data area is hosed!\n",dev->name);
- }
+ ** Enable any multicasts
+ */
+ set_multicast_list(dev);
+
+ /*
+ ** Clean out any remaining entries in all the queues here
+ */
+ while (inb(EWRK3_TQ));
+ while (inb(EWRK3_TDQ));
+ while (inb(EWRK3_RQ));
+ while (inb(EWRK3_FMQ));
- if (!status) {
-
- /*
- ** Set up the buffer control structures and copy the data from
- ** the socket buffer to the shared memory .
- */
-
- if (lp->shmem_length == IO_ONLY) {
- int i;
- u_char *p = skb->data;
-
- outb((char)(TCR_QMODE | TCR_PAD | TCR_IFC), EWRK3_DATA);
- outb((char)(skb->len & 0xff), EWRK3_DATA);
- outb((char)((skb->len >> 8) & 0xff), EWRK3_DATA);
- outb((char)0x04, EWRK3_DATA);
- for (i=0; i<skb->len; i++) {
- outb(*p++, EWRK3_DATA);
- }
- outb(page, EWRK3_TQ); /* Start sending pkt */
- } else {
- writeb((char)(TCR_QMODE|TCR_PAD|TCR_IFC), (char *)buf);/* ctrl byte*/
- buf+=1;
- writeb((char)(skb->len & 0xff), (char *)buf);/* length (16 bit xfer)*/
- buf+=1;
- if (lp->txc) {
- writeb((char)(((skb->len >> 8) & 0xff) | XCT), (char *)buf);
- buf+=1;
- writeb(0x04, (char *)buf); /* index byte */
- buf+=1;
- writeb(0x00, (char *)(buf + skb->len)); /* Write the XCT flag */
- memcpy_toio(buf, skb->data, PRELOAD);/* Write PRELOAD bytes*/
- outb(page, EWRK3_TQ); /* Start sending pkt */
- memcpy_toio(buf+PRELOAD, skb->data+PRELOAD, skb->len-PRELOAD);
- writeb(0xff, (char *)(buf + skb->len)); /* Write the XCT flag */
- } else {
- writeb((char)((skb->len >> 8) & 0xff), (char *)buf);
- buf+=1;
- writeb(0x04, (char *)buf); /* index byte */
- buf+=1;
- memcpy_toio((char *)buf, skb->data, skb->len);/* Write data bytes */
- outb(page, EWRK3_TQ); /* Start sending pkt */
- }
- }
-
- dev->trans_start = jiffies;
- dev_kfree_skb (skb, FREE_WRITE);
-
- } else { /* return unused page to the free memory queue */
- outb(page, EWRK3_FMQ);
+ /*
+ ** Write a clean free memory queue
+ */
+ for (page = 1; page < lp->mPage; page++) { /* Write the free page numbers */
+ outb(page, EWRK3_FMQ); /* to the Free Memory Queue */
}
- lp->lock = 0; /* unlock the page register */
- } else {
- printk("ewrk3_queue_pkt(): Invalid free memory page (%d).\n",
- (u_char) page);
- }
- } else {
- printk("ewrk3_queue_pkt(): No free resources...\n");
- printk("ewrk3_queue_pkt(): CSR: %02x ICR: %02x FMQC: %02x\n",inb(EWRK3_CSR),inb(EWRK3_ICR),inb(EWRK3_FMQC));
- }
-
- /* Check for free resources: clear 'tbusy' if there are some */
- if (inb(EWRK3_FMQC) > 0) {
- dev->tbusy = 0;
- }
-
- ENABLE_IRQs;
- }
-
- return status;
+
+ lp->lock = 0; /* Ensure there are no locks */
+
+ START_EWRK3; /* Enable the TX and/or RX */
}
/*
-** The EWRK3 interrupt handler.
-*/
-static void
-ewrk3_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+ ** Writes a socket buffer to the free page queue
+ */
+static int ewrk3_queue_pkt(struct sk_buff *skb, struct device *dev)
{
- struct device *dev = (struct device *)(irq2dev_map[irq]);
- struct ewrk3_private *lp;
- u_long iobase;
- u_char icr, cr, csr;
-
- if (dev == NULL) {
- printk ("ewrk3_interrupt(): irq %d for unknown device.\n", irq);
- } else {
- lp = (struct ewrk3_private *)dev->priv;
- iobase = dev->base_addr;
-
- if (dev->interrupt)
- printk("%s: Re-entering the interrupt handler.\n", dev->name);
-
- dev->interrupt = MASK_INTERRUPTS;
-
- /* get the interrupt information */
- csr = inb(EWRK3_CSR);
-
- /*
- ** Mask the EWRK3 board interrupts and turn on the LED
- */
- DISABLE_IRQs;
-
- cr = inb(EWRK3_CR);
- cr |= CR_LED;
- outb(cr, EWRK3_CR);
-
- if (csr & CSR_RNE) /* Rx interrupt (packet[s] arrived) */
- ewrk3_rx(dev);
-
- if (csr & CSR_TNE) /* Tx interrupt (packet sent) */
- ewrk3_tx(dev);
-
- /*
- ** Now deal with the TX/RX disable flags. These are set when there
- ** are no more resources. If resources free up then enable these
- ** interrupts, otherwise mask them - failure to do this will result
- ** in the system hanging in an interrupt loop.
- */
- if (inb(EWRK3_FMQC)) { /* any resources available? */
- lp->irq_mask |= ICR_TXDM|ICR_RXDM;/* enable the interrupt source */
- csr &= ~(CSR_TXD|CSR_RXD);/* ensure restart of a stalled TX or RX */
- outb(csr, EWRK3_CSR);
- dev->tbusy = 0; /* clear TX busy flag */
- mark_bh(NET_BH);
- } else {
- lp->irq_mask &= ~(ICR_TXDM|ICR_RXDM);/* disable the interrupt source */
- }
-
- /* Unmask the EWRK3 board interrupts and turn off the LED */
- cr &= ~CR_LED;
- outb(cr, EWRK3_CR);
-
- dev->interrupt = UNMASK_INTERRUPTS;
- ENABLE_IRQs;
- }
-
- return;
+ struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ int status = 0;
+ u_char icr, csr;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy || lp->lock) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < QUEUE_PKT_TIMEOUT) {
+ status = -1;
+ } else if (!lp->hard_strapped) {
+ printk("%s: transmit timed/locked out, status %04x, resetting.\n",
+ dev->name, inb(EWRK3_CSR));
+
+ /*
+ ** Mask all board interrupts
+ */
+ DISABLE_IRQs;
+
+ /*
+ ** Stop the TX and RX...
+ */
+ STOP_EWRK3;
+
+ ewrk3_init(dev);
+
+ /*
+ ** Unmask EWRK3 board interrupts
+ */
+ ENABLE_IRQs;
+
+ dev->tbusy = 0;
+ dev->trans_start = jiffies;
+ }
+ } else if (skb->len > 0) {
+
+ /*
+ ** Block a timer-based transmit from overlapping. This could better be
+ ** done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+ */
+ if (test_and_set_bit(0, (void *) &dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+
+ DISABLE_IRQs; /* So that the page # remains correct */
+
+ /*
+ ** Get a free page from the FMQ when resources are available
+ */
+ if (inb(EWRK3_FMQC) > 0) {
+ u_long buf = 0;
+ u_char page;
+
+ if ((page = inb(EWRK3_FMQ)) < lp->mPage) {
+ /*
+ ** Set up shared memory window and pointer into the window
+ */
+ while (test_and_set_bit(0, (void *) &lp->lock) != 0); /* Wait for lock to free */
+ if (lp->shmem_length == IO_ONLY) {
+ outb(page, EWRK3_IOPR);
+ } else if (lp->shmem_length == SHMEM_2K) {
+ buf = lp->shmem_base;
+ outb(page, EWRK3_MPR);
+ } else if (lp->shmem_length == SHMEM_32K) {
+ buf = ((((short) page << 11) & 0x7800) + lp->shmem_base);
+ outb((page >> 4), EWRK3_MPR);
+ } else if (lp->shmem_length == SHMEM_64K) {
+ buf = ((((short) page << 11) & 0xf800) + lp->shmem_base);
+ outb((page >> 5), EWRK3_MPR);
+ } else {
+ status = -1;
+ printk("%s: Oops - your private data area is hosed!\n", dev->name);
+ }
+
+ if (!status) {
+
+ /*
+ ** Set up the buffer control structures and copy the data from
+ ** the socket buffer to the shared memory .
+ */
+
+ if (lp->shmem_length == IO_ONLY) {
+ int i;
+ u_char *p = skb->data;
+
+ outb((char) (TCR_QMODE | TCR_PAD | TCR_IFC), EWRK3_DATA);
+ outb((char) (skb->len & 0xff), EWRK3_DATA);
+ outb((char) ((skb->len >> 8) & 0xff), EWRK3_DATA);
+ outb((char) 0x04, EWRK3_DATA);
+ for (i = 0; i < skb->len; i++) {
+ outb(*p++, EWRK3_DATA);
+ }
+ outb(page, EWRK3_TQ); /* Start sending pkt */
+ } else {
+ writeb((char) (TCR_QMODE | TCR_PAD | TCR_IFC), (char *) buf); /* ctrl byte */
+ buf += 1;
+ writeb((char) (skb->len & 0xff), (char *) buf); /* length (16 bit xfer) */
+ buf += 1;
+ if (lp->txc) {
+ writeb((char) (((skb->len >> 8) & 0xff) | XCT), (char *) buf);
+ buf += 1;
+ writeb(0x04, (char *) buf); /* index byte */
+ buf += 1;
+ writeb(0x00, (char *) (buf + skb->len)); /* Write the XCT flag */
+ memcpy_toio(buf, skb->data, PRELOAD); /* Write PRELOAD bytes */
+ outb(page, EWRK3_TQ); /* Start sending pkt */
+ memcpy_toio(buf + PRELOAD, skb->data + PRELOAD, skb->len - PRELOAD);
+ writeb(0xff, (char *) (buf + skb->len)); /* Write the XCT flag */
+ } else {
+ writeb((char) ((skb->len >> 8) & 0xff), (char *) buf);
+ buf += 1;
+ writeb(0x04, (char *) buf); /* index byte */
+ buf += 1;
+ memcpy_toio((char *) buf, skb->data, skb->len); /* Write data bytes */
+ outb(page, EWRK3_TQ); /* Start sending pkt */
+ }
+ }
+
+ dev->trans_start = jiffies;
+ dev_kfree_skb(skb, FREE_WRITE);
+
+ } else { /* return unused page to the free memory queue */
+ outb(page, EWRK3_FMQ);
+ }
+ lp->lock = 0; /* unlock the page register */
+ } else {
+ printk("ewrk3_queue_pkt(): Invalid free memory page (%d).\n",
+ (u_char) page);
+ }
+ } else {
+ printk("ewrk3_queue_pkt(): No free resources...\n");
+ printk("ewrk3_queue_pkt(): CSR: %02x ICR: %02x FMQC: %02x\n", inb(EWRK3_CSR), inb(EWRK3_ICR), inb(EWRK3_FMQC));
+ }
+
+ /* Check for free resources: clear 'tbusy' if there are some */
+ if (inb(EWRK3_FMQC) > 0) {
+ dev->tbusy = 0;
+ }
+ ENABLE_IRQs;
+ }
+ return status;
}
-static int
-ewrk3_rx(struct device *dev)
+/*
+ ** The EWRK3 interrupt handler.
+ */
+static void ewrk3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
- u_long iobase = dev->base_addr;
- int i, status = 0;
- u_char page, tmpPage = 0, tmpLock = 0;
- u_long buf = 0;
-
- while (inb(EWRK3_RQC) && !status) { /* Whilst there's incoming data */
- if ((page = inb(EWRK3_RQ)) < lp->mPage) {/* Get next entry's buffer page */
- /*
- ** Preempt any process using the current page register. Check for
- ** an existing lock to reduce time taken in I/O transactions.
- */
- if ((tmpLock = test_and_set_bit(0, (void *)&lp->lock)) == 1) { /* Assert lock */
- if (lp->shmem_length == IO_ONLY) { /* Get existing page */
- tmpPage = inb(EWRK3_IOPR);
- } else {
- tmpPage = inb(EWRK3_MPR);
- }
- }
-
- /*
- ** Set up shared memory window and pointer into the window
- */
- if (lp->shmem_length == IO_ONLY) {
- outb(page, EWRK3_IOPR);
- } else if (lp->shmem_length == SHMEM_2K) {
- buf = lp->shmem_base;
- outb(page, EWRK3_MPR);
- } else if (lp->shmem_length == SHMEM_32K) {
- buf = ((((short)page << 11) & 0x7800) + lp->shmem_base);
- outb((page >> 4), EWRK3_MPR);
- } else if (lp->shmem_length == SHMEM_64K) {
- buf = ((((short)page << 11) & 0xf800) + lp->shmem_base);
- outb((page >> 5), EWRK3_MPR);
- } else {
- status = -1;
- printk("%s: Oops - your private data area is hosed!\n",dev->name);
- }
-
- if (!status) {
- char rx_status;
- int pkt_len;
+ struct device *dev = dev_id;
+ struct ewrk3_private *lp;
+ u_long iobase;
+ u_char icr, cr, csr;
- if (lp->shmem_length == IO_ONLY) {
- rx_status = inb(EWRK3_DATA);
- pkt_len = inb(EWRK3_DATA);
- pkt_len |= ((u_short)inb(EWRK3_DATA) << 8);
+ if (dev == NULL) {
+ printk("ewrk3_interrupt(): irq %d for unknown device.\n", irq);
} else {
- rx_status = readb(buf);
- buf+=1;
- pkt_len = readw(buf);
- buf+=3;
+ lp = (struct ewrk3_private *) dev->priv;
+ iobase = dev->base_addr;
+
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+ dev->interrupt = MASK_INTERRUPTS;
+
+ /* get the interrupt information */
+ csr = inb(EWRK3_CSR);
+
+ /*
+ ** Mask the EWRK3 board interrupts and turn on the LED
+ */
+ DISABLE_IRQs;
+
+ cr = inb(EWRK3_CR);
+ cr |= CR_LED;
+ outb(cr, EWRK3_CR);
+
+ if (csr & CSR_RNE) /* Rx interrupt (packet[s] arrived) */
+ ewrk3_rx(dev);
+
+ if (csr & CSR_TNE) /* Tx interrupt (packet sent) */
+ ewrk3_tx(dev);
+
+ /*
+ ** Now deal with the TX/RX disable flags. These are set when there
+ ** are no more resources. If resources free up then enable these
+ ** interrupts, otherwise mask them - failure to do this will result
+ ** in the system hanging in an interrupt loop.
+ */
+ if (inb(EWRK3_FMQC)) { /* any resources available? */
+ lp->irq_mask |= ICR_TXDM | ICR_RXDM; /* enable the interrupt source */
+ csr &= ~(CSR_TXD | CSR_RXD); /* ensure restart of a stalled TX or RX */
+ outb(csr, EWRK3_CSR);
+ dev->tbusy = 0; /* clear TX busy flag */
+ mark_bh(NET_BH);
+ } else {
+ lp->irq_mask &= ~(ICR_TXDM | ICR_RXDM); /* disable the interrupt source */
+ }
+
+ /* Unmask the EWRK3 board interrupts and turn off the LED */
+ cr &= ~CR_LED;
+ outb(cr, EWRK3_CR);
+
+ dev->interrupt = UNMASK_INTERRUPTS;
+ ENABLE_IRQs;
}
- if (!(rx_status & R_ROK)) { /* There was an error. */
- lp->stats.rx_errors++; /* Update the error stats. */
- if (rx_status & R_DBE) lp->stats.rx_frame_errors++;
- if (rx_status & R_CRC) lp->stats.rx_crc_errors++;
- if (rx_status & R_PLL) lp->stats.rx_fifo_errors++;
- } else {
- struct sk_buff *skb;
-
- if ((skb = dev_alloc_skb(pkt_len+2)) != NULL) {
- unsigned char *p;
- skb->dev = dev;
- skb_reserve(skb,2); /* Align to 16 bytes */
- p = skb_put(skb,pkt_len);
-
- if (lp->shmem_length == IO_ONLY) {
- *p = inb(EWRK3_DATA); /* dummy read */
- for (i=0; i<pkt_len; i++) {
- *p++ = inb(EWRK3_DATA);
- }
- } else {
- memcpy_fromio(p, buf, pkt_len);
- }
-
- /*
- ** Notify the upper protocol layers that there is another
- ** packet to handle
- */
- skb->protocol=eth_type_trans(skb,dev);
- netif_rx(skb);
-
- /*
- ** Update stats
- */
- lp->stats.rx_packets++;
- for (i=1; i<EWRK3_PKT_STAT_SZ-1; i++) {
- if (pkt_len < i*EWRK3_PKT_BIN_SZ) {
- lp->pktStats.bins[i]++;
- i = EWRK3_PKT_STAT_SZ;
- }
- }
- p = skb->data; /* Look at the dest addr */
- if (p[0] & 0x01) { /* Multicast/Broadcast */
- if ((*(s32 *)&p[0] == -1) && (*(s16 *)&p[4] == -1)) {
- lp->pktStats.broadcast++;
- } else {
- lp->pktStats.multicast++;
- }
- } else if ((*(s32 *)&p[0] == *(s32 *)&dev->dev_addr[0]) &&
- (*(s16 *)&p[4] == *(s16 *)&dev->dev_addr[4])) {
- lp->pktStats.unicast++;
- }
-
- lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */
- if (lp->pktStats.bins[0] == 0) { /* Reset counters */
- memset(&lp->pktStats, 0, sizeof(lp->pktStats));
- }
- } else {
- printk("%s: Insufficient memory; nuking packet.\n", dev->name);
- lp->stats.rx_dropped++; /* Really, deferred. */
- break;
- }
- }
- }
- /*
- ** Return the received buffer to the free memory queue
- */
- outb(page, EWRK3_FMQ);
-
- if (tmpLock) { /* If a lock was preempted */
- if (lp->shmem_length == IO_ONLY) { /* Replace old page */
- outb(tmpPage, EWRK3_IOPR);
- } else {
- outb(tmpPage, EWRK3_MPR);
+ return;
+}
+
+static int ewrk3_rx(struct device *dev)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ int i, status = 0;
+ u_char page, tmpPage = 0, tmpLock = 0;
+ u_long buf = 0;
+
+ while (inb(EWRK3_RQC) && !status) { /* Whilst there's incoming data */
+ if ((page = inb(EWRK3_RQ)) < lp->mPage) { /* Get next entry's buffer page */
+ /*
+ ** Preempt any process using the current page register. Check for
+ ** an existing lock to reduce time taken in I/O transactions.
+ */
+ if ((tmpLock = test_and_set_bit(0, (void *) &lp->lock)) == 1) { /* Assert lock */
+ if (lp->shmem_length == IO_ONLY) { /* Get existing page */
+ tmpPage = inb(EWRK3_IOPR);
+ } else {
+ tmpPage = inb(EWRK3_MPR);
+ }
+ }
+ /*
+ ** Set up shared memory window and pointer into the window
+ */
+ if (lp->shmem_length == IO_ONLY) {
+ outb(page, EWRK3_IOPR);
+ } else if (lp->shmem_length == SHMEM_2K) {
+ buf = lp->shmem_base;
+ outb(page, EWRK3_MPR);
+ } else if (lp->shmem_length == SHMEM_32K) {
+ buf = ((((short) page << 11) & 0x7800) + lp->shmem_base);
+ outb((page >> 4), EWRK3_MPR);
+ } else if (lp->shmem_length == SHMEM_64K) {
+ buf = ((((short) page << 11) & 0xf800) + lp->shmem_base);
+ outb((page >> 5), EWRK3_MPR);
+ } else {
+ status = -1;
+ printk("%s: Oops - your private data area is hosed!\n", dev->name);
+ }
+
+ if (!status) {
+ char rx_status;
+ int pkt_len;
+
+ if (lp->shmem_length == IO_ONLY) {
+ rx_status = inb(EWRK3_DATA);
+ pkt_len = inb(EWRK3_DATA);
+ pkt_len |= ((u_short) inb(EWRK3_DATA) << 8);
+ } else {
+ rx_status = readb(buf);
+ buf += 1;
+ pkt_len = readw(buf);
+ buf += 3;
+ }
+
+ if (!(rx_status & R_ROK)) { /* There was an error. */
+ lp->stats.rx_errors++; /* Update the error stats. */
+ if (rx_status & R_DBE)
+ lp->stats.rx_frame_errors++;
+ if (rx_status & R_CRC)
+ lp->stats.rx_crc_errors++;
+ if (rx_status & R_PLL)
+ lp->stats.rx_fifo_errors++;
+ } else {
+ struct sk_buff *skb;
+
+ if ((skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+ unsigned char *p;
+ skb->dev = dev;
+ skb_reserve(skb, 2); /* Align to 16 bytes */
+ p = skb_put(skb, pkt_len);
+
+ if (lp->shmem_length == IO_ONLY) {
+ *p = inb(EWRK3_DATA); /* dummy read */
+ for (i = 0; i < pkt_len; i++) {
+ *p++ = inb(EWRK3_DATA);
+ }
+ } else {
+ memcpy_fromio(p, buf, pkt_len);
+ }
+
+ /*
+ ** Notify the upper protocol layers that there is another
+ ** packet to handle
+ */
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+
+ /*
+ ** Update stats
+ */
+ lp->stats.rx_packets++;
+ for (i = 1; i < EWRK3_PKT_STAT_SZ - 1; i++) {
+ if (pkt_len < i * EWRK3_PKT_BIN_SZ) {
+ lp->pktStats.bins[i]++;
+ i = EWRK3_PKT_STAT_SZ;
+ }
+ }
+ p = skb->data; /* Look at the dest addr */
+ if (p[0] & 0x01) { /* Multicast/Broadcast */
+ if ((*(s32 *) & p[0] == -1) && (*(s16 *) & p[4] == -1)) {
+ lp->pktStats.broadcast++;
+ } else {
+ lp->pktStats.multicast++;
+ }
+ } else if ((*(s32 *) & p[0] == *(s32 *) & dev->dev_addr[0]) &&
+ (*(s16 *) & p[4] == *(s16 *) & dev->dev_addr[4])) {
+ lp->pktStats.unicast++;
+ }
+ lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */
+ if (lp->pktStats.bins[0] == 0) { /* Reset counters */
+ memset(&lp->pktStats, 0, sizeof(lp->pktStats));
+ }
+ } else {
+ printk("%s: Insufficient memory; nuking packet.\n", dev->name);
+ lp->stats.rx_dropped++; /* Really, deferred. */
+ break;
+ }
+ }
+ }
+ /*
+ ** Return the received buffer to the free memory queue
+ */
+ outb(page, EWRK3_FMQ);
+
+ if (tmpLock) { /* If a lock was preempted */
+ if (lp->shmem_length == IO_ONLY) { /* Replace old page */
+ outb(tmpPage, EWRK3_IOPR);
+ } else {
+ outb(tmpPage, EWRK3_MPR);
+ }
+ }
+ lp->lock = 0; /* Unlock the page register */
+ } else {
+ printk("ewrk3_rx(): Illegal page number, page %d\n", page);
+ printk("ewrk3_rx(): CSR: %02x ICR: %02x FMQC: %02x\n", inb(EWRK3_CSR), inb(EWRK3_ICR), inb(EWRK3_FMQC));
+ }
}
- }
- lp->lock = 0; /* Unlock the page register */
- } else {
- printk("ewrk3_rx(): Illegal page number, page %d\n",page);
- printk("ewrk3_rx(): CSR: %02x ICR: %02x FMQC: %02x\n",inb(EWRK3_CSR),inb(EWRK3_ICR),inb(EWRK3_FMQC));
- }
- }
- return status;
+ return status;
}
/*
-** Buffer sent - check for TX buffer errors.
-*/
-static int
-ewrk3_tx(struct device *dev)
+ ** Buffer sent - check for TX buffer errors.
+ */
+static int ewrk3_tx(struct device *dev)
{
- struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
- u_long iobase = dev->base_addr;
- u_char tx_status;
-
- while ((tx_status = inb(EWRK3_TDQ)) > 0) { /* Whilst there's old buffers */
- if (tx_status & T_VSTS) { /* The status is valid */
- if (tx_status & T_TXE) {
- lp->stats.tx_errors++;
- if (tx_status & T_NCL) lp->stats.tx_carrier_errors++;
- if (tx_status & T_LCL) lp->stats.tx_window_errors++;
- if (tx_status & T_CTU) {
- if ((tx_status & T_COLL) ^ T_XUR) {
- lp->pktStats.tx_underruns++;
- } else {
- lp->pktStats.excessive_underruns++;
- }
- } else if (tx_status & T_COLL) {
- if ((tx_status & T_COLL) ^ T_XCOLL) {
- lp->stats.collisions++;
- } else {
- lp->pktStats.excessive_collisions++;
- }
+ struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ u_char tx_status;
+
+ while ((tx_status = inb(EWRK3_TDQ)) > 0) { /* Whilst there's old buffers */
+ if (tx_status & T_VSTS) { /* The status is valid */
+ if (tx_status & T_TXE) {
+ lp->stats.tx_errors++;
+ if (tx_status & T_NCL)
+ lp->stats.tx_carrier_errors++;
+ if (tx_status & T_LCL)
+ lp->stats.tx_window_errors++;
+ if (tx_status & T_CTU) {
+ if ((tx_status & T_COLL) ^ T_XUR) {
+ lp->pktStats.tx_underruns++;
+ } else {
+ lp->pktStats.excessive_underruns++;
+ }
+ } else if (tx_status & T_COLL) {
+ if ((tx_status & T_COLL) ^ T_XCOLL) {
+ lp->stats.collisions++;
+ } else {
+ lp->pktStats.excessive_collisions++;
+ }
+ }
+ } else {
+ lp->stats.tx_packets++;
+ }
+ }
}
- } else {
- lp->stats.tx_packets++;
- }
- }
- }
- return 0;
+ return 0;
}
-static int
-ewrk3_close(struct device *dev)
+static int ewrk3_close(struct device *dev)
{
- struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
- u_long iobase = dev->base_addr;
- u_char icr, csr;
-
- dev->start = 0;
- dev->tbusy = 1;
-
- if (ewrk3_debug > 1) {
- printk("%s: Shutting down ethercard, status was %2.2x.\n",
- dev->name, inb(EWRK3_CSR));
- }
+ struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ u_char icr, csr;
- /*
- ** We stop the EWRK3 here... mask interrupts and stop TX & RX
- */
- DISABLE_IRQs;
+ dev->start = 0;
+ dev->tbusy = 1;
- STOP_EWRK3;
-
- /*
- ** Clean out the TX and RX queues here (note that one entry
- ** may get added to either the TXD or RX queues if the the TX or RX
- ** just starts processing a packet before the STOP_EWRK3 command
- ** is received. This will be flushed in the ewrk3_open() call).
- */
- while (inb(EWRK3_TQ));
- while (inb(EWRK3_TDQ));
- while (inb(EWRK3_RQ));
-
- if (!lp->hard_strapped) {
- free_irq(dev->irq, NULL);
+ if (ewrk3_debug > 1) {
+ printk("%s: Shutting down ethercard, status was %2.2x.\n",
+ dev->name, inb(EWRK3_CSR));
+ }
+ /*
+ ** We stop the EWRK3 here... mask interrupts and stop TX & RX
+ */
+ DISABLE_IRQs;
- irq2dev_map[dev->irq] = 0;
- }
+ STOP_EWRK3;
- MOD_DEC_USE_COUNT;
+ /*
+ ** Clean out the TX and RX queues here (note that one entry
+ ** may get added to either the TXD or RX queues if the the TX or RX
+ ** just starts processing a packet before the STOP_EWRK3 command
+ ** is received. This will be flushed in the ewrk3_open() call).
+ */
+ while (inb(EWRK3_TQ));
+ while (inb(EWRK3_TDQ));
+ while (inb(EWRK3_RQ));
+
+ if (!lp->hard_strapped) {
+ free_irq(dev->irq, dev);
+ }
+ MOD_DEC_USE_COUNT;
- return 0;
+ return 0;
}
static struct net_device_stats *ewrk3_get_stats(struct device *dev)
{
- struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+ struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv;
/* Null body since there is no framing error counter */
return &lp->stats;
}
/*
-** Set or clear the multicast filter for this adapter.
-*/
+ ** Set or clear the multicast filter for this adapter.
+ */
static void set_multicast_list(struct device *dev)
{
- struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
- u_long iobase = dev->base_addr;
- u_char csr;
-
- if (irq2dev_map[dev->irq] != NULL) {
- csr = inb(EWRK3_CSR);
-
- if (lp->shmem_length == IO_ONLY) {
- lp->mctbl = (char *) PAGE0_HTE;
- } else {
- lp->mctbl = (char *)(lp->shmem_base + PAGE0_HTE);
- }
-
- csr &= ~(CSR_PME | CSR_MCE);
- if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */
- csr |= CSR_PME;
- outb(csr, EWRK3_CSR);
- } else {
- SetMulticastFilter(dev);
- csr |= CSR_MCE;
- outb(csr, EWRK3_CSR);
- }
- }
+ struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv;
+ u_long iobase = dev->base_addr;
+ u_char csr;
+
+ csr = inb(EWRK3_CSR);
+
+ if (lp->shmem_length == IO_ONLY) {
+ lp->mctbl = (char *) PAGE0_HTE;
+ } else {
+ lp->mctbl = (char *) (lp->shmem_base + PAGE0_HTE);
+ }
+
+ csr &= ~(CSR_PME | CSR_MCE);
+ if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */
+ csr |= CSR_PME;
+ outb(csr, EWRK3_CSR);
+ } else {
+ SetMulticastFilter(dev);
+ csr |= CSR_MCE;
+ outb(csr, EWRK3_CSR);
+ }
}
/*
-** Calculate the hash code and update the logical address filter
-** from a list of ethernet multicast addresses.
-** Little endian crc one liner from Matt Thomas, DEC.
-**
-** Note that when clearing the table, the broadcast bit must remain asserted
-** to receive broadcast messages.
-*/
+ ** Calculate the hash code and update the logical address filter
+ ** from a list of ethernet multicast addresses.
+ ** Little endian crc one liner from Matt Thomas, DEC.
+ **
+ ** Note that when clearing the table, the broadcast bit must remain asserted
+ ** to receive broadcast messages.
+ */
static void SetMulticastFilter(struct device *dev)
{
- struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
- struct dev_mc_list *dmi=dev->mc_list;
- u_long iobase = dev->base_addr;
- int i;
- char *addrs, j, bit, byte;
- short *p = (short *) lp->mctbl;
- u16 hashcode;
- s32 crc, poly = CRC_POLYNOMIAL_LE;
-
- while (test_and_set_bit(0, (void *)&lp->lock) != 0); /* Wait for lock to free */
-
- if (lp->shmem_length == IO_ONLY) {
- outb(0, EWRK3_IOPR);
- outw(EEPROM_OFFSET(lp->mctbl), EWRK3_PIR1);
- } else {
- outb(0, EWRK3_MPR);
- }
-
- if (dev->flags & IFF_ALLMULTI) {
- for (i=0; i<(HASH_TABLE_LEN >> 3); i++) {
- if (lp->shmem_length == IO_ONLY) {
- outb(0xff, EWRK3_DATA);
- } else { /* memset didn't work here */
- writew(0xffff, p);
- p++; i++;
- }
- }
- } else {
- /* Clear table except for broadcast bit */
- if (lp->shmem_length == IO_ONLY) {
- for (i=0; i<(HASH_TABLE_LEN >> 4) - 1; i++) {
- outb(0x00, EWRK3_DATA);
- }
- outb(0x80, EWRK3_DATA); i++; /* insert the broadcast bit */
- for (; i<(HASH_TABLE_LEN >> 3); i++) {
- outb(0x00, EWRK3_DATA);
- }
- } else {
- memset_io(lp->mctbl, 0, (HASH_TABLE_LEN >> 3));
- writeb(0x80, (char *)(lp->mctbl + (HASH_TABLE_LEN >> 4) - 1));
- }
-
- /* Update table */
- for (i=0;i<dev->mc_count;i++) { /* for each address in the list */
- addrs=dmi->dmi_addr;
- dmi=dmi->next;
- if ((*addrs & 0x01) == 1) { /* multicast address? */
- crc = 0xffffffff; /* init CRC for each address */
- for (byte=0;byte<ETH_ALEN;byte++) { /* for each address byte */
- /* process each address bit */
- for (bit = *addrs++,j=0;j<8;j++, bit>>=1) {
- crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0);
- }
- }
- hashcode = crc & ((1 << 9) - 1); /* hashcode is 9 LSb of CRC */
+ struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv;
+ struct dev_mc_list *dmi = dev->mc_list;
+ u_long iobase = dev->base_addr;
+ int i;
+ char *addrs, j, bit, byte;
+ short *p = (short *) lp->mctbl;
+ u16 hashcode;
+ s32 crc, poly = CRC_POLYNOMIAL_LE;
- byte = hashcode >> 3; /* bit[3-8] -> byte in filter */
- bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */
+ while (test_and_set_bit(0, (void *) &lp->lock) != 0); /* Wait for lock to free */
if (lp->shmem_length == IO_ONLY) {
- u_char tmp;
+ outb(0, EWRK3_IOPR);
+ outw(EEPROM_OFFSET(lp->mctbl), EWRK3_PIR1);
+ } else {
+ outb(0, EWRK3_MPR);
+ }
- outw((short)((long)lp->mctbl) + byte, EWRK3_PIR1);
- tmp = inb(EWRK3_DATA);
- tmp |= bit;
- outw((short)((long)lp->mctbl) + byte, EWRK3_PIR1);
- outb(tmp, EWRK3_DATA);
+ if (dev->flags & IFF_ALLMULTI) {
+ for (i = 0; i < (HASH_TABLE_LEN >> 3); i++) {
+ if (lp->shmem_length == IO_ONLY) {
+ outb(0xff, EWRK3_DATA);
+ } else { /* memset didn't work here */
+ writew(0xffff, p);
+ p++;
+ i++;
+ }
+ }
} else {
- writeb(readb(lp->mctbl + byte) | bit, lp->mctbl + byte);
+ /* Clear table except for broadcast bit */
+ if (lp->shmem_length == IO_ONLY) {
+ for (i = 0; i < (HASH_TABLE_LEN >> 4) - 1; i++) {
+ outb(0x00, EWRK3_DATA);
+ }
+ outb(0x80, EWRK3_DATA);
+ i++; /* insert the broadcast bit */
+ for (; i < (HASH_TABLE_LEN >> 3); i++) {
+ outb(0x00, EWRK3_DATA);
+ }
+ } else {
+ memset_io(lp->mctbl, 0, (HASH_TABLE_LEN >> 3));
+ writeb(0x80, (char *) (lp->mctbl + (HASH_TABLE_LEN >> 4) - 1));
+ }
+
+ /* Update table */
+ for (i = 0; i < dev->mc_count; i++) { /* for each address in the list */
+ addrs = dmi->dmi_addr;
+ dmi = dmi->next;
+ if ((*addrs & 0x01) == 1) { /* multicast address? */
+ crc = 0xffffffff; /* init CRC for each address */
+ for (byte = 0; byte < ETH_ALEN; byte++) { /* for each address byte */
+ /* process each address bit */
+ for (bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) {
+ crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0);
+ }
+ }
+ hashcode = crc & ((1 << 9) - 1); /* hashcode is 9 LSb of CRC */
+
+ byte = hashcode >> 3; /* bit[3-8] -> byte in filter */
+ bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */
+
+ if (lp->shmem_length == IO_ONLY) {
+ u_char tmp;
+
+ outw((short) ((long) lp->mctbl) + byte, EWRK3_PIR1);
+ tmp = inb(EWRK3_DATA);
+ tmp |= bit;
+ outw((short) ((long) lp->mctbl) + byte, EWRK3_PIR1);
+ outb(tmp, EWRK3_DATA);
+ } else {
+ writeb(readb(lp->mctbl + byte) | bit, lp->mctbl + byte);
+ }
+ }
+ }
}
- }
- }
- }
- lp->lock = 0; /* Unlock the page register */
+ lp->lock = 0; /* Unlock the page register */
- return;
+ return;
}
/*
-** ISA bus I/O device probe
-*/
+ ** ISA bus I/O device probe
+ */
__initfunc(static void isa_probe(struct device *dev, u_long ioaddr))
{
- int i = num_ewrk3s, maxSlots;
- u_long iobase;
-
- if (!ioaddr && autoprobed) return ; /* Been here before ! */
- if (ioaddr >= 0x400) return; /* Not ISA */
-
- if (ioaddr == 0) { /* Autoprobing */
- iobase = EWRK3_IO_BASE; /* Get the first slot address */
- maxSlots = 24;
- } else { /* Probe a specific location */
- iobase = ioaddr;
- maxSlots = i + 1;
- }
-
- for (; (i<maxSlots) && (dev!=NULL);iobase+=EWRK3_IOP_INC, i++) {
- if (!check_region(iobase, EWRK3_TOTAL_SIZE)) {
- if (DevicePresent(iobase) == 0) {
- if ((dev = alloc_device(dev, iobase)) != NULL) {
- if (ewrk3_hw_init(dev, iobase) == 0) {
- num_ewrk3s++;
- }
- num_eth++;
+ int i = num_ewrk3s, maxSlots;
+ u_long iobase;
+
+ if (!ioaddr && autoprobed)
+ return; /* Been here before ! */
+ if (ioaddr >= 0x400)
+ return; /* Not ISA */
+
+ if (ioaddr == 0) { /* Autoprobing */
+ iobase = EWRK3_IO_BASE; /* Get the first slot address */
+ maxSlots = 24;
+ } else { /* Probe a specific location */
+ iobase = ioaddr;
+ maxSlots = i + 1;
+ }
+
+ for (; (i < maxSlots) && (dev != NULL); iobase += EWRK3_IOP_INC, i++) {
+ if (!check_region(iobase, EWRK3_TOTAL_SIZE)) {
+ if (DevicePresent(iobase) == 0) {
+ if ((dev = alloc_device(dev, iobase)) != NULL) {
+ if (ewrk3_hw_init(dev, iobase) == 0) {
+ num_ewrk3s++;
+ }
+ num_eth++;
+ }
+ }
+ } else if (autoprobed) {
+ printk("%s: region already allocated at 0x%04lx.\n", dev->name, iobase);
+ }
}
- }
- } else if (autoprobed) {
- printk("%s: region already allocated at 0x%04lx.\n", dev->name, iobase);
- }
- }
- return;
+ return;
}
/*
-** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually
-** the motherboard.
-*/
+ ** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually
+ ** the motherboard.
+ */
__initfunc(static void eisa_probe(struct device *dev, u_long ioaddr))
{
- int i, maxSlots;
- u_long iobase;
- char name[EWRK3_STRLEN];
-
- if (!ioaddr && autoprobed) return ; /* Been here before ! */
- if (ioaddr < 0x1000) return; /* Not EISA */
-
- if (ioaddr == 0) { /* Autoprobing */
- iobase = EISA_SLOT_INC; /* Get the first slot address */
- i = 1;
- maxSlots = MAX_EISA_SLOTS;
- } else { /* Probe a specific location */
- iobase = ioaddr;
- i = (ioaddr >> 12);
- maxSlots = i + 1;
- }
-
- for (i=1; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) {
- if (EISA_signature(name, EISA_ID) == 0) {
- if (!check_region(iobase, EWRK3_TOTAL_SIZE)) {
- if (DevicePresent(iobase) == 0) {
- if ((dev = alloc_device(dev, iobase)) != NULL) {
- if (ewrk3_hw_init(dev, iobase) == 0) {
- num_ewrk3s++;
- }
- num_eth++;
- }
+ int i, maxSlots;
+ u_long iobase;
+ char name[EWRK3_STRLEN];
+
+ if (!ioaddr && autoprobed)
+ return; /* Been here before ! */
+ if (ioaddr < 0x1000)
+ return; /* Not EISA */
+
+ if (ioaddr == 0) { /* Autoprobing */
+ iobase = EISA_SLOT_INC; /* Get the first slot address */
+ i = 1;
+ maxSlots = MAX_EISA_SLOTS;
+ } else { /* Probe a specific location */
+ iobase = ioaddr;
+ i = (ioaddr >> 12);
+ maxSlots = i + 1;
}
- } else if (autoprobed) {
- printk("%s: region already allocated at 0x%04lx.\n", dev->name, iobase);
- }
- }
- }
- return;
+ for (i = 1; (i < maxSlots) && (dev != NULL); i++, iobase += EISA_SLOT_INC) {
+ if (EISA_signature(name, EISA_ID) == 0) {
+ if (!check_region(iobase, EWRK3_TOTAL_SIZE)) {
+ if (DevicePresent(iobase) == 0) {
+ if ((dev = alloc_device(dev, iobase)) != NULL) {
+ if (ewrk3_hw_init(dev, iobase) == 0) {
+ num_ewrk3s++;
+ }
+ num_eth++;
+ }
+ }
+ } else if (autoprobed) {
+ printk("%s: region already allocated at 0x%04lx.\n", dev->name, iobase);
+ }
+ }
+ }
+
+ return;
}
/*
-** Search the entire 'eth' device list for a fixed probe. If a match isn't
-** found then check for an autoprobe or unused device location. If they
-** are not available then insert a new device structure at the end of
-** the current list.
-*/
+ ** Search the entire 'eth' device list for a fixed probe. If a match isn't
+ ** found then check for an autoprobe or unused device location. If they
+ ** are not available then insert a new device structure at the end of
+ ** the current list.
+ */
__initfunc(static struct device *
-alloc_device(struct device *dev, u_long iobase))
+ alloc_device(struct device *dev, u_long iobase))
{
- struct device *adev = NULL;
- int fixed = 0, new_dev = 0;
-
- num_eth = ewrk3_dev_index(dev->name);
- if (loading_module) return dev;
+ struct device *adev = NULL;
+ int fixed = 0, new_dev = 0;
- while (1) {
- if (((dev->base_addr == EWRK3_NDA) || (dev->base_addr==0)) && !adev) {
- adev=dev;
- } else if ((dev->priv == NULL) && (dev->base_addr==iobase)) {
- fixed = 1;
- } else {
- if (dev->next == NULL) {
- new_dev = 1;
- } else if (strncmp(dev->next->name, "eth", 3) != 0) {
- new_dev = 1;
- }
- }
- if ((dev->next == NULL) || new_dev || fixed) break;
- dev = dev->next;
- num_eth++;
- }
- if (adev && !fixed) {
- dev = adev;
num_eth = ewrk3_dev_index(dev->name);
- new_dev = 0;
- }
-
- if (((dev->next == NULL) &&
- ((dev->base_addr != EWRK3_NDA) && (dev->base_addr != 0)) && !fixed) ||
- new_dev) {
- num_eth++; /* New device */
- dev = insert_device(dev, iobase, ewrk3_probe);
- }
-
- return dev;
+ if (loading_module)
+ return dev;
+
+ while (1) {
+ if (((dev->base_addr == EWRK3_NDA) || (dev->base_addr == 0)) && !adev) {
+ adev = dev;
+ } else if ((dev->priv == NULL) && (dev->base_addr == iobase)) {
+ fixed = 1;
+ } else {
+ if (dev->next == NULL) {
+ new_dev = 1;
+ } else if (strncmp(dev->next->name, "eth", 3) != 0) {
+ new_dev = 1;
+ }
+ }
+ if ((dev->next == NULL) || new_dev || fixed)
+ break;
+ dev = dev->next;
+ num_eth++;
+ }
+ if (adev && !fixed) {
+ dev = adev;
+ num_eth = ewrk3_dev_index(dev->name);
+ new_dev = 0;
+ }
+ if (((dev->next == NULL) &&
+ ((dev->base_addr != EWRK3_NDA) && (dev->base_addr != 0)) && !fixed) ||
+ new_dev) {
+ num_eth++; /* New device */
+ dev = insert_device(dev, iobase, ewrk3_probe);
+ }
+ return dev;
}
/*
-** If at end of eth device list and can't use current entry, malloc
-** one up. If memory could not be allocated, print an error message.
-*/
+ ** If at end of eth device list and can't use current entry, malloc
+ ** one up. If memory could not be allocated, print an error message.
+ */
__initfunc(static struct device *
-insert_device(struct device *dev, u_long iobase, int (*init)(struct device *)))
+ insert_device(struct device *dev, u_long iobase, int (*init) (struct device *)))
{
- struct device *new;
-
- new = (struct device *)kmalloc(sizeof(struct device)+8, GFP_KERNEL);
- if (new == NULL) {
- printk("eth%d: Device not initialised, insufficient memory\n",num_eth);
- return NULL;
- } else {
- new->next = dev->next;
- dev->next = new;
- dev = dev->next; /* point to the new device */
- dev->name = (char *)(dev + 1);
- if (num_eth > 9999) {
- sprintf(dev->name,"eth????");/* New device name */
+ struct device *new;
+
+ new = (struct device *) kmalloc(sizeof(struct device) + 8, GFP_KERNEL);
+ if (new == NULL) {
+ printk("eth%d: Device not initialised, insufficient memory\n", num_eth);
+ return NULL;
} else {
- sprintf(dev->name,"eth%d", num_eth);/* New device name */
+ new->next = dev->next;
+ dev->next = new;
+ dev = dev->next; /* point to the new device */
+ dev->name = (char *) (dev + 1);
+ if (num_eth > 9999) {
+ sprintf(dev->name, "eth????"); /* New device name */
+ } else {
+ sprintf(dev->name, "eth%d", num_eth); /* New device name */
+ }
+ dev->base_addr = iobase; /* assign the io address */
+ dev->init = init; /* initialisation routine */
}
- dev->base_addr = iobase; /* assign the io address */
- dev->init = init; /* initialisation routine */
- }
- return dev;
+ return dev;
}
__initfunc(static int
-ewrk3_dev_index(char *s))
+ ewrk3_dev_index(char *s))
{
- int i=0, j=0;
-
- for (;*s; s++) {
- if (isdigit(*s)) {
- j=1;
- i = (i * 10) + (*s - '0');
- } else if (j) break;
- }
+ int i = 0, j = 0;
+
+ for (; *s; s++) {
+ if (isdigit(*s)) {
+ j = 1;
+ i = (i * 10) + (*s - '0');
+ } else if (j)
+ break;
+ }
- return i;
+ return i;
}
/*
-** Read the EWRK3 EEPROM using this routine
-*/
+ ** Read the EWRK3 EEPROM using this routine
+ */
static int Read_EEPROM(u_long iobase, u_char eaddr)
{
- int i;
+ int i;
- outb((eaddr & 0x3f), EWRK3_PIR1); /* set up 6 bits of address info */
- outb(EEPROM_RD, EWRK3_IOPR); /* issue read command */
- for (i=0;i<5000;i++) inb(EWRK3_CSR); /* wait 1msec */
+ outb((eaddr & 0x3f), EWRK3_PIR1); /* set up 6 bits of address info */
+ outb(EEPROM_RD, EWRK3_IOPR); /* issue read command */
+ for (i = 0; i < 5000; i++)
+ inb(EWRK3_CSR); /* wait 1msec */
- return inw(EWRK3_EPROM1); /* 16 bits data return */
+ return inw(EWRK3_EPROM1); /* 16 bits data return */
}
/*
-** Write the EWRK3 EEPROM using this routine
-*/
+ ** Write the EWRK3 EEPROM using this routine
+ */
static int Write_EEPROM(short data, u_long iobase, u_char eaddr)
{
- int i;
-
- outb(EEPROM_WR_EN, EWRK3_IOPR); /* issue write enable command */
- for (i=0;i<5000;i++) inb(EWRK3_CSR); /* wait 1msec */
- outw(data, EWRK3_EPROM1); /* write data to register */
- outb((eaddr & 0x3f), EWRK3_PIR1); /* set up 6 bits of address info */
- outb(EEPROM_WR, EWRK3_IOPR); /* issue write command */
- for (i=0;i<75000;i++) inb(EWRK3_CSR); /* wait 15msec */
- outb(EEPROM_WR_DIS, EWRK3_IOPR); /* issue write disable command */
- for (i=0;i<5000;i++) inb(EWRK3_CSR); /* wait 1msec */
-
- return 0;
+ int i;
+
+ outb(EEPROM_WR_EN, EWRK3_IOPR); /* issue write enable command */
+ for (i = 0; i < 5000; i++)
+ inb(EWRK3_CSR); /* wait 1msec */
+ outw(data, EWRK3_EPROM1); /* write data to register */
+ outb((eaddr & 0x3f), EWRK3_PIR1); /* set up 6 bits of address info */
+ outb(EEPROM_WR, EWRK3_IOPR); /* issue write command */
+ for (i = 0; i < 75000; i++)
+ inb(EWRK3_CSR); /* wait 15msec */
+ outb(EEPROM_WR_DIS, EWRK3_IOPR); /* issue write disable command */
+ for (i = 0; i < 5000; i++)
+ inb(EWRK3_CSR); /* wait 1msec */
+
+ return 0;
}
/*
-** Look for a particular board name in the on-board EEPROM.
-*/
+ ** Look for a particular board name in the on-board EEPROM.
+ */
__initfunc(static void EthwrkSignature(char *name, char *eeprom_image))
{
- u_long i,j,k;
- char *signatures[] = EWRK3_SIGNATURE;
-
- strcpy(name, "");
- for (i=0;*signatures[i] != '\0' && *name == '\0';i++) {
- for (j=EEPROM_PNAME7,k=0;j<=EEPROM_PNAME0 && k<strlen(signatures[i]);j++) {
- if (signatures[i][k] == eeprom_image[j]) { /* track signature */
- k++;
- } else { /* lost signature; begin search again */
- k=0;
- }
- }
- if (k == strlen(signatures[i])) {
- for (k=0; k<EWRK3_STRLEN; k++) {
- name[k] = eeprom_image[EEPROM_PNAME7 + k];
- name[EWRK3_STRLEN] = '\0';
- }
- }
- }
-
- return; /* return the device name string */
+ u_long i, j, k;
+ char *signatures[] = EWRK3_SIGNATURE;
+
+ strcpy(name, "");
+ for (i = 0; *signatures[i] != '\0' && *name == '\0'; i++) {
+ for (j = EEPROM_PNAME7, k = 0; j <= EEPROM_PNAME0 && k < strlen(signatures[i]); j++) {
+ if (signatures[i][k] == eeprom_image[j]) { /* track signature */
+ k++;
+ } else { /* lost signature; begin search again */
+ k = 0;
+ }
+ }
+ if (k == strlen(signatures[i])) {
+ for (k = 0; k < EWRK3_STRLEN; k++) {
+ name[k] = eeprom_image[EEPROM_PNAME7 + k];
+ name[EWRK3_STRLEN] = '\0';
+ }
+ }
+ }
+
+ return; /* return the device name string */
}
/*
-** Look for a special sequence in the Ethernet station address PROM that
-** is common across all EWRK3 products.
-**
-** Search the Ethernet address ROM for the signature. Since the ROM address
-** counter can start at an arbitrary point, the search must include the entire
-** probe sequence length plus the (length_of_the_signature - 1).
-** Stop the search IMMEDIATELY after the signature is found so that the
-** PROM address counter is correctly positioned at the start of the
-** ethernet address for later read out.
-*/
+ ** Look for a special sequence in the Ethernet station address PROM that
+ ** is common across all EWRK3 products.
+ **
+ ** Search the Ethernet address ROM for the signature. Since the ROM address
+ ** counter can start at an arbitrary point, the search must include the entire
+ ** probe sequence length plus the (length_of_the_signature - 1).
+ ** Stop the search IMMEDIATELY after the signature is found so that the
+ ** PROM address counter is correctly positioned at the start of the
+ ** ethernet address for later read out.
+ */
__initfunc(static int DevicePresent(u_long iobase))
{
- union {
- struct {
- u32 a;
- u32 b;
- } llsig;
- char Sig[sizeof(u32) << 1];
- } dev;
- short sigLength;
- char data;
- int i, j, status = 0;
-
- dev.llsig.a = ETH_PROM_SIG;
- dev.llsig.b = ETH_PROM_SIG;
- sigLength = sizeof(u32) << 1;
-
- for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) {
- data = inb(EWRK3_APROM);
- if (dev.Sig[j] == data) { /* track signature */
- j++;
- } else { /* lost signature; begin search again */
- if (data == dev.Sig[0]) {
- j=1;
- } else {
- j=0;
- }
- }
- }
-
- if (j!=sigLength) {
- status = -ENODEV; /* search failed */
- }
-
- return status;
+ union {
+ struct {
+ u32 a;
+ u32 b;
+ } llsig;
+ char Sig[sizeof(u32) << 1];
+ }
+ dev;
+ short sigLength;
+ char data;
+ int i, j, status = 0;
+
+ dev.llsig.a = ETH_PROM_SIG;
+ dev.llsig.b = ETH_PROM_SIG;
+ sigLength = sizeof(u32) << 1;
+
+ for (i = 0, j = 0; j < sigLength && i < PROBE_LENGTH + sigLength - 1; i++) {
+ data = inb(EWRK3_APROM);
+ if (dev.Sig[j] == data) { /* track signature */
+ j++;
+ } else { /* lost signature; begin search again */
+ if (data == dev.Sig[0]) {
+ j = 1;
+ } else {
+ j = 0;
+ }
+ }
+ }
+
+ if (j != sigLength) {
+ status = -ENODEV; /* search failed */
+ }
+ return status;
}
-__initfunc(static u_char get_hw_addr(struct device *dev, u_char *eeprom_image, char chipType))
+__initfunc(static u_char get_hw_addr(struct device *dev, u_char * eeprom_image, char chipType))
{
- int i, j, k;
- u_short chksum;
- u_char crc, lfsr, sd, status = 0;
- u_long iobase = dev->base_addr;
- u16 tmp;
-
- if (chipType == LeMAC2) {
- for (crc=0x6a, j=0; j<ETH_ALEN; j++) {
- sd = dev->dev_addr[j] = eeprom_image[EEPROM_PADDR0 + j];
- outb(dev->dev_addr[j], EWRK3_PAR0 + j);
- for (k=0; k<8; k++, sd >>= 1) {
- lfsr = ((((crc & 0x02) >> 1) ^ (crc & 0x01)) ^ (sd & 0x01)) << 7;
- crc = (crc >> 1) + lfsr;
- }
- }
- if (crc != eeprom_image[EEPROM_PA_CRC]) status = -1;
- } else {
- for (i=0,k=0;i<ETH_ALEN;) {
- k <<= 1 ;
- if (k > 0xffff) k-=0xffff;
-
- k += (u_char) (tmp = inb(EWRK3_APROM));
- dev->dev_addr[i] = (u_char) tmp;
- outb(dev->dev_addr[i], EWRK3_PAR0 + i);
- i++;
- k += (u_short) ((tmp = inb(EWRK3_APROM)) << 8);
- dev->dev_addr[i] = (u_char) tmp;
- outb(dev->dev_addr[i], EWRK3_PAR0 + i);
- i++;
-
- if (k > 0xffff) k-=0xffff;
- }
- if (k == 0xffff) k=0;
- chksum = inb(EWRK3_APROM);
- chksum |= (inb(EWRK3_APROM)<<8);
- if (k != chksum) status = -1;
- }
-
- return status;
+ int i, j, k;
+ u_short chksum;
+ u_char crc, lfsr, sd, status = 0;
+ u_long iobase = dev->base_addr;
+ u16 tmp;
+
+ if (chipType == LeMAC2) {
+ for (crc = 0x6a, j = 0; j < ETH_ALEN; j++) {
+ sd = dev->dev_addr[j] = eeprom_image[EEPROM_PADDR0 + j];
+ outb(dev->dev_addr[j], EWRK3_PAR0 + j);
+ for (k = 0; k < 8; k++, sd >>= 1) {
+ lfsr = ((((crc & 0x02) >> 1) ^ (crc & 0x01)) ^ (sd & 0x01)) << 7;
+ crc = (crc >> 1) + lfsr;
+ }
+ }
+ if (crc != eeprom_image[EEPROM_PA_CRC])
+ status = -1;
+ } else {
+ for (i = 0, k = 0; i < ETH_ALEN;) {
+ k <<= 1;
+ if (k > 0xffff)
+ k -= 0xffff;
+
+ k += (u_char) (tmp = inb(EWRK3_APROM));
+ dev->dev_addr[i] = (u_char) tmp;
+ outb(dev->dev_addr[i], EWRK3_PAR0 + i);
+ i++;
+ k += (u_short) ((tmp = inb(EWRK3_APROM)) << 8);
+ dev->dev_addr[i] = (u_char) tmp;
+ outb(dev->dev_addr[i], EWRK3_PAR0 + i);
+ i++;
+
+ if (k > 0xffff)
+ k -= 0xffff;
+ }
+ if (k == 0xffff)
+ k = 0;
+ chksum = inb(EWRK3_APROM);
+ chksum |= (inb(EWRK3_APROM) << 8);
+ if (k != chksum)
+ status = -1;
+ }
+
+ return status;
}
/*
-** Look for a particular board name in the EISA configuration space
-*/
+ ** Look for a particular board name in the EISA configuration space
+ */
__initfunc(static int EISA_signature(char *name, s32 eisa_id))
{
- u_long i;
- char *signatures[] = EWRK3_SIGNATURE;
- char ManCode[EWRK3_STRLEN];
- union {
- s32 ID;
- char Id[4];
- } Eisa;
- int status = 0;
-
- *name = '\0';
- for (i=0; i<4; i++) {
- Eisa.Id[i] = inb(eisa_id + i);
- }
-
- ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40);
- ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40);
- ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30);
- ManCode[3]=((Eisa.Id[2]&0x0f)+0x30);
- ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30);
- ManCode[5]='\0';
-
- for (i=0;(*signatures[i] != '\0') && (*name == '\0');i++) {
- if (strstr(ManCode, signatures[i]) != NULL) {
- strcpy(name,ManCode);
- status = 1;
- }
- }
-
- return status; /* return the device name string */
+ u_long i;
+ char *signatures[] = EWRK3_SIGNATURE;
+ char ManCode[EWRK3_STRLEN];
+ union {
+ s32 ID;
+ char Id[4];
+ } Eisa;
+ int status = 0;
+
+ *name = '\0';
+ for (i = 0; i < 4; i++) {
+ Eisa.Id[i] = inb(eisa_id + i);
+ }
+
+ ManCode[0] = (((Eisa.Id[0] >> 2) & 0x1f) + 0x40);
+ ManCode[1] = (((Eisa.Id[1] & 0xe0) >> 5) + ((Eisa.Id[0] & 0x03) << 3) + 0x40);
+ ManCode[2] = (((Eisa.Id[2] >> 4) & 0x0f) + 0x30);
+ ManCode[3] = ((Eisa.Id[2] & 0x0f) + 0x30);
+ ManCode[4] = (((Eisa.Id[3] >> 4) & 0x0f) + 0x30);
+ ManCode[5] = '\0';
+
+ for (i = 0; (*signatures[i] != '\0') && (*name == '\0'); i++) {
+ if (strstr(ManCode, signatures[i]) != NULL) {
+ strcpy(name, ManCode);
+ status = 1;
+ }
+ }
+
+ return status; /* return the device name string */
}
/*
-** Perform IOCTL call functions here. Some are privileged operations and the
-** effective uid is checked in those cases.
-*/
+ ** Perform IOCTL call functions here. Some are privileged operations and the
+ ** effective uid is checked in those cases.
+ */
static int ewrk3_ioctl(struct device *dev, struct ifreq *rq, int cmd)
{
- struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
- struct ewrk3_ioctl *ioc = (struct ewrk3_ioctl *) &rq->ifr_data;
- u_long iobase = dev->base_addr;
- int i, j, status = 0;
- u_char csr;
- union {
- u_char addr[HASH_TABLE_LEN * ETH_ALEN];
- u_short val[(HASH_TABLE_LEN * ETH_ALEN) >> 1];
- } tmp;
-
- switch(ioc->cmd) {
- case EWRK3_GET_HWADDR: /* Get the hardware address */
- for (i=0; i<ETH_ALEN; i++) {
- tmp.addr[i] = dev->dev_addr[i];
- }
- ioc->len = ETH_ALEN;
- if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
- copy_to_user(ioc->data, tmp.addr, ioc->len);
- }
-
- break;
- case EWRK3_SET_HWADDR: /* Set the hardware address */
- if (suser()) {
- if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN))) {
- csr = inb(EWRK3_CSR);
- csr |= (CSR_TXD|CSR_RXD);
- outb(csr, EWRK3_CSR); /* Disable the TX and RX */
+ struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv;
+ struct ewrk3_ioctl *ioc = (struct ewrk3_ioctl *) &rq->ifr_data;
+ u_long iobase = dev->base_addr;
+ int i, j, status = 0;
+ u_char csr;
+ union {
+ u_char addr[HASH_TABLE_LEN * ETH_ALEN];
+ u_short val[(HASH_TABLE_LEN * ETH_ALEN) >> 1];
+ } tmp;
+
+ switch (ioc->cmd) {
+ case EWRK3_GET_HWADDR: /* Get the hardware address */
+ for (i = 0; i < ETH_ALEN; i++) {
+ tmp.addr[i] = dev->dev_addr[i];
+ }
+ ioc->len = ETH_ALEN;
+ if (!(status = verify_area(VERIFY_WRITE, (void *) ioc->data, ioc->len))) {
+ copy_to_user(ioc->data, tmp.addr, ioc->len);
+ }
+ break;
+ case EWRK3_SET_HWADDR: /* Set the hardware address */
+ if (suser()) {
+ if (!(status = verify_area(VERIFY_READ, (void *) ioc->data, ETH_ALEN))) {
+ csr = inb(EWRK3_CSR);
+ csr |= (CSR_TXD | CSR_RXD);
+ outb(csr, EWRK3_CSR); /* Disable the TX and RX */
+
+ copy_from_user(tmp.addr, ioc->data, ETH_ALEN);
+ for (i = 0; i < ETH_ALEN; i++) {
+ dev->dev_addr[i] = tmp.addr[i];
+ outb(tmp.addr[i], EWRK3_PAR0 + i);
+ }
+
+ csr &= ~(CSR_TXD | CSR_RXD); /* Enable the TX and RX */
+ outb(csr, EWRK3_CSR);
+ }
+ } else {
+ status = -EPERM;
+ }
- copy_from_user(tmp.addr,ioc->data,ETH_ALEN);
- for (i=0; i<ETH_ALEN; i++) {
- dev->dev_addr[i] = tmp.addr[i];
- outb(tmp.addr[i], EWRK3_PAR0 + i);
- }
+ break;
+ case EWRK3_SET_PROM: /* Set Promiscuous Mode */
+ if (suser()) {
+ csr = inb(EWRK3_CSR);
+ csr |= CSR_PME;
+ csr &= ~CSR_MCE;
+ outb(csr, EWRK3_CSR);
+ } else {
+ status = -EPERM;
+ }
- csr &= ~(CSR_TXD|CSR_RXD); /* Enable the TX and RX */
- outb(csr, EWRK3_CSR);
- }
- } else {
- status = -EPERM;
- }
-
- break;
- case EWRK3_SET_PROM: /* Set Promiscuous Mode */
- if (suser()) {
- csr = inb(EWRK3_CSR);
- csr |= CSR_PME;
- csr &= ~CSR_MCE;
- outb(csr, EWRK3_CSR);
- } else {
- status = -EPERM;
- }
-
- break;
- case EWRK3_CLR_PROM: /* Clear Promiscuous Mode */
- if (suser()) {
- csr = inb(EWRK3_CSR);
- csr &= ~CSR_PME;
- outb(csr, EWRK3_CSR);
- } else {
- status = -EPERM;
- }
-
- break;
- case EWRK3_SAY_BOO: /* Say "Boo!" to the kernel log file */
- printk("%s: Boo!\n", dev->name);
-
- break;
- case EWRK3_GET_MCA: /* Get the multicast address table */
- if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
- while (test_and_set_bit(0, (void *)&lp->lock) != 0); /* Wait for lock to free */
- if (lp->shmem_length == IO_ONLY) {
- outb(0, EWRK3_IOPR);
- outw(PAGE0_HTE, EWRK3_PIR1);
- for (i=0; i<(HASH_TABLE_LEN >> 3); i++) {
- tmp.addr[i] = inb(EWRK3_DATA);
- }
- } else {
- outb(0, EWRK3_MPR);
- memcpy_fromio(tmp.addr, (char *)(lp->shmem_base + PAGE0_HTE), (HASH_TABLE_LEN >> 3));
- }
- ioc->len = (HASH_TABLE_LEN >> 3);
- copy_to_user(ioc->data, tmp.addr, ioc->len);
- }
- lp->lock = 0; /* Unlock the page register */
-
- break;
- case EWRK3_SET_MCA: /* Set a multicast address */
- if (suser()) {
- if (!(status=verify_area(VERIFY_READ, ioc->data, ETH_ALEN*ioc->len))) {
- copy_from_user(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
- set_multicast_list(dev);
- }
- } else {
- status = -EPERM;
- }
-
- break;
- case EWRK3_CLR_MCA: /* Clear all multicast addresses */
- if (suser()) {
- set_multicast_list(dev);
- } else {
- status = -EPERM;
- }
-
- break;
- case EWRK3_MCA_EN: /* Enable multicast addressing */
- if (suser()) {
- csr = inb(EWRK3_CSR);
- csr |= CSR_MCE;
- csr &= ~CSR_PME;
- outb(csr, EWRK3_CSR);
- } else {
- status = -EPERM;
- }
-
- break;
- case EWRK3_GET_STATS: /* Get the driver statistics */
- cli();
- ioc->len = sizeof(lp->pktStats);
- if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
- copy_to_user(ioc->data, &lp->pktStats, ioc->len);
- }
- sti();
-
- break;
- case EWRK3_CLR_STATS: /* Zero out the driver statistics */
- if (suser()) {
- cli();
- memset(&lp->pktStats, 0, sizeof(lp->pktStats));
- sti();
- } else {
- status = -EPERM;
- }
-
- break;
- case EWRK3_GET_CSR: /* Get the CSR Register contents */
- tmp.addr[0] = inb(EWRK3_CSR);
- ioc->len = 1;
- if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
- copy_to_user(ioc->data, tmp.addr, ioc->len);
- }
-
- break;
- case EWRK3_SET_CSR: /* Set the CSR Register contents */
- if (suser()) {
- if (!(status=verify_area(VERIFY_READ, ioc->data, 1))) {
- copy_from_user(tmp.addr, ioc->data, 1);
- outb(tmp.addr[0], EWRK3_CSR);
- }
- } else {
- status = -EPERM;
- }
-
- break;
- case EWRK3_GET_EEPROM: /* Get the EEPROM contents */
- if (suser()) {
- for (i=0; i<(EEPROM_MAX>>1); i++) {
- tmp.val[i] = (short)Read_EEPROM(iobase, i);
- }
- i = EEPROM_MAX;
- tmp.addr[i++] = inb(EWRK3_CMR); /* Config/Management Reg. */
- for (j=0;j<ETH_ALEN;j++) {
- tmp.addr[i++] = inb(EWRK3_PAR0 + j);
- }
- ioc->len = EEPROM_MAX + 1 + ETH_ALEN;
- if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
- copy_to_user(ioc->data, tmp.addr, ioc->len);
- }
- } else {
- status = -EPERM;
- }
-
- break;
- case EWRK3_SET_EEPROM: /* Set the EEPROM contents */
- if (suser()) {
- if (!(status=verify_area(VERIFY_READ, ioc->data, EEPROM_MAX))) {
- copy_from_user(tmp.addr, ioc->data, EEPROM_MAX);
- for (i=0; i<(EEPROM_MAX>>1); i++) {
- Write_EEPROM(tmp.val[i], iobase, i);
+ break;
+ case EWRK3_CLR_PROM: /* Clear Promiscuous Mode */
+ if (suser()) {
+ csr = inb(EWRK3_CSR);
+ csr &= ~CSR_PME;
+ outb(csr, EWRK3_CSR);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_SAY_BOO: /* Say "Boo!" to the kernel log file */
+ printk("%s: Boo!\n", dev->name);
+
+ break;
+ case EWRK3_GET_MCA: /* Get the multicast address table */
+ if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ while (test_and_set_bit(0, (void *) &lp->lock) != 0); /* Wait for lock to free */
+ if (lp->shmem_length == IO_ONLY) {
+ outb(0, EWRK3_IOPR);
+ outw(PAGE0_HTE, EWRK3_PIR1);
+ for (i = 0; i < (HASH_TABLE_LEN >> 3); i++) {
+ tmp.addr[i] = inb(EWRK3_DATA);
+ }
+ } else {
+ outb(0, EWRK3_MPR);
+ memcpy_fromio(tmp.addr, (char *) (lp->shmem_base + PAGE0_HTE), (HASH_TABLE_LEN >> 3));
+ }
+ ioc->len = (HASH_TABLE_LEN >> 3);
+ copy_to_user(ioc->data, tmp.addr, ioc->len);
+ }
+ lp->lock = 0; /* Unlock the page register */
+
+ break;
+ case EWRK3_SET_MCA: /* Set a multicast address */
+ if (suser()) {
+ if (!(status = verify_area(VERIFY_READ, ioc->data, ETH_ALEN * ioc->len))) {
+ copy_from_user(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
+ set_multicast_list(dev);
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_CLR_MCA: /* Clear all multicast addresses */
+ if (suser()) {
+ set_multicast_list(dev);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_MCA_EN: /* Enable multicast addressing */
+ if (suser()) {
+ csr = inb(EWRK3_CSR);
+ csr |= CSR_MCE;
+ csr &= ~CSR_PME;
+ outb(csr, EWRK3_CSR);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_GET_STATS: /* Get the driver statistics */
+ cli();
+ ioc->len = sizeof(lp->pktStats);
+ if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ copy_to_user(ioc->data, &lp->pktStats, ioc->len);
+ }
+ sti();
+
+ break;
+ case EWRK3_CLR_STATS: /* Zero out the driver statistics */
+ if (suser()) {
+ cli();
+ memset(&lp->pktStats, 0, sizeof(lp->pktStats));
+ sti();
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_GET_CSR: /* Get the CSR Register contents */
+ tmp.addr[0] = inb(EWRK3_CSR);
+ ioc->len = 1;
+ if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ copy_to_user(ioc->data, tmp.addr, ioc->len);
+ }
+ break;
+ case EWRK3_SET_CSR: /* Set the CSR Register contents */
+ if (suser()) {
+ if (!(status = verify_area(VERIFY_READ, ioc->data, 1))) {
+ copy_from_user(tmp.addr, ioc->data, 1);
+ outb(tmp.addr[0], EWRK3_CSR);
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_GET_EEPROM: /* Get the EEPROM contents */
+ if (suser()) {
+ for (i = 0; i < (EEPROM_MAX >> 1); i++) {
+ tmp.val[i] = (short) Read_EEPROM(iobase, i);
+ }
+ i = EEPROM_MAX;
+ tmp.addr[i++] = inb(EWRK3_CMR); /* Config/Management Reg. */
+ for (j = 0; j < ETH_ALEN; j++) {
+ tmp.addr[i++] = inb(EWRK3_PAR0 + j);
+ }
+ ioc->len = EEPROM_MAX + 1 + ETH_ALEN;
+ if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ copy_to_user(ioc->data, tmp.addr, ioc->len);
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_SET_EEPROM: /* Set the EEPROM contents */
+ if (suser()) {
+ if (!(status = verify_area(VERIFY_READ, ioc->data, EEPROM_MAX))) {
+ copy_from_user(tmp.addr, ioc->data, EEPROM_MAX);
+ for (i = 0; i < (EEPROM_MAX >> 1); i++) {
+ Write_EEPROM(tmp.val[i], iobase, i);
+ }
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_GET_CMR: /* Get the CMR Register contents */
+ tmp.addr[0] = inb(EWRK3_CMR);
+ ioc->len = 1;
+ if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ copy_to_user(ioc->data, tmp.addr, ioc->len);
+ }
+ break;
+ case EWRK3_SET_TX_CUT_THRU: /* Set TX cut through mode */
+ if (suser()) {
+ lp->txc = 1;
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_CLR_TX_CUT_THRU: /* Clear TX cut through mode */
+ if (suser()) {
+ lp->txc = 0;
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ default:
+ status = -EOPNOTSUPP;
}
- }
- } else {
- status = -EPERM;
- }
-
- break;
- case EWRK3_GET_CMR: /* Get the CMR Register contents */
- tmp.addr[0] = inb(EWRK3_CMR);
- ioc->len = 1;
- if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
- copy_to_user(ioc->data, tmp.addr, ioc->len);
- }
-
- break;
- case EWRK3_SET_TX_CUT_THRU: /* Set TX cut through mode */
- if (suser()) {
- lp->txc = 1;
- } else {
- status = -EPERM;
- }
-
- break;
- case EWRK3_CLR_TX_CUT_THRU: /* Clear TX cut through mode */
- if (suser()) {
- lp->txc = 0;
- } else {
- status = -EPERM;
- }
-
- break;
- default:
- status = -EOPNOTSUPP;
- }
-
- return status;
+
+ return status;
}
#ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device thisEthwrk = {
- devicename, /* device name is inserted by /linux/drivers/net/net_init.c */
- 0, 0, 0, 0,
- 0x300, 5, /* I/O address, IRQ */
- 0, 0, 0, NULL, ewrk3_probe };
+static char devicename[9] =
+{0,};
+static struct device thisEthwrk =
+{
+ devicename, /* device name is inserted by /linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0x300, 5, /* I/O address, IRQ */
+ 0, 0, 0, NULL, ewrk3_probe};
-static int io=0x300; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
-static int irq=5; /* or use the insmod io= irq= options */
+static int io = 0x300; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
+static int irq = 5; /* or use the insmod io= irq= options */
MODULE_PARM(io, "i");
MODULE_PARM(irq, "i");
-int
-init_module(void)
+int init_module(void)
{
- thisEthwrk.base_addr=io;
- thisEthwrk.irq=irq;
- if (register_netdev(&thisEthwrk) != 0)
- return -EIO;
- return 0;
+ thisEthwrk.base_addr = io;
+ thisEthwrk.irq = irq;
+ if (register_netdev(&thisEthwrk) != 0)
+ return -EIO;
+ return 0;
}
-void
-cleanup_module(void)
+void cleanup_module(void)
{
- if (thisEthwrk.priv) {
- kfree(thisEthwrk.priv);
- thisEthwrk.priv = NULL;
- }
- thisEthwrk.irq = 0;
-
- unregister_netdev(&thisEthwrk);
- release_region(thisEthwrk.base_addr, EWRK3_TOTAL_SIZE);
-}
-#endif /* MODULE */
+ if (thisEthwrk.priv) {
+ kfree(thisEthwrk.priv);
+ thisEthwrk.priv = NULL;
+ }
+ thisEthwrk.irq = 0;
+ unregister_netdev(&thisEthwrk);
+ release_region(thisEthwrk.base_addr, EWRK3_TOTAL_SIZE);
+}
+#endif /* MODULE */
+
/*
* Local variables:
* compile-command: "gcc -D__KERNEL__ -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -c ewrk3.c"
diff --git a/drivers/net/fmv18x.c b/drivers/net/fmv18x.c
index 59362c8fe..78bf20a07 100644
--- a/drivers/net/fmv18x.c
+++ b/drivers/net/fmv18x.c
@@ -180,7 +180,7 @@ __initfunc(int fmv18x_probe1(struct device *dev, short ioaddr))
irq = irqmap[(inb(ioaddr + FJ_CONFIG0)>>6) & 0x03];
/* Snarf the interrupt vector now. */
- if (request_irq(irq, &net_interrupt, 0, "fmv18x", NULL)) {
+ if (request_irq(irq, &net_interrupt, 0, "fmv18x", dev)) {
printk ("FMV-18x found at %#3x, but it's unusable due to a conflict on"
"IRQ %d.\n", ioaddr, irq);
return EAGAIN;
@@ -199,7 +199,6 @@ __initfunc(int fmv18x_probe1(struct device *dev, short ioaddr))
dev->base_addr = ioaddr;
dev->irq = irq;
- irq2dev_map[irq] = dev;
for(i = 0; i < 6; i++) {
unsigned char val = inb(ioaddr + FJ_MACADDR + i);
@@ -399,7 +398,7 @@ net_send_packet(struct sk_buff *skb, struct device *dev)
static void
net_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct device *dev = dev_id;
struct net_local *lp;
int ioaddr, status;
@@ -642,8 +641,7 @@ cleanup_module(void)
dev_fmv18x.priv = NULL;
/* If we don't do this, we can't re-insmod it later. */
- free_irq(dev_fmv18x.irq, NULL);
- irq2dev_map[dev_fmv18x.irq] = NULL;
+ free_irq(dev_fmv18x.irq, &dev_fmv18x);
release_region(dev_fmv18x.base_addr, FMV18X_IO_EXTENT);
}
#endif /* MODULE */
diff --git a/drivers/net/hdlcdrv.c b/drivers/net/hdlcdrv.c
index 59c72f914..89f7229e7 100644
--- a/drivers/net/hdlcdrv.c
+++ b/drivers/net/hdlcdrv.c
@@ -63,7 +63,6 @@
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/tcp.h>
-#include <linux/net_alias.h>
/* --------------------------------------------------------------------- */
@@ -569,15 +568,6 @@ static int hdlcdrv_send_packet(struct sk_buff *skb, struct device *dev)
if (hdlcdrv_paranoia_check(dev, "hdlcdrv_send_packet"))
return 0;
sm = (struct hdlcdrv_state *)dev->priv;
- /*
- * If some higher layer thinks we've missed an tx-done interrupt
- * we are passed NULL. Caution: dev_tint() handles the cli()/sti()
- * itself.
- */
- if (skb == NULL) {
- dev_tint(dev);
- return 0;
- }
skb_queue_tail(&sm->send_queue, skb);
dev->trans_start = jiffies;
return 0;
@@ -908,11 +898,6 @@ static int hdlcdrv_probe(struct device *dev)
/* New style flags */
dev->flags = 0;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = sizeof(unsigned long);
return 0;
}
diff --git a/drivers/net/hp-plus.c b/drivers/net/hp-plus.c
index b7f85f735..c7f0a5e5d 100644
--- a/drivers/net/hp-plus.c
+++ b/drivers/net/hp-plus.c
@@ -259,7 +259,7 @@ hpp_open(struct device *dev)
int ioaddr = dev->base_addr - NIC_OFFSET;
int option_reg;
- if (request_irq(dev->irq, &ei_interrupt, 0, "hp-plus", NULL)) {
+ if (request_irq(dev->irq, &ei_interrupt, 0, "hp-plus", dev)) {
return -EAGAIN;
}
@@ -288,8 +288,7 @@ hpp_close(struct device *dev)
int ioaddr = dev->base_addr - NIC_OFFSET;
int option_reg = inw(ioaddr + HPP_OPTION);
- free_irq(dev->irq, NULL);
- irq2dev_map[dev->irq] = NULL;
+ free_irq(dev->irq, dev);
ei_close(dev);
outw((option_reg & ~EnableIRQ) | MemDisable | NICReset | ChipReset,
ioaddr + HPP_OPTION);
@@ -465,7 +464,7 @@ cleanup_module(void)
for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) {
struct device *dev = &dev_hpp[this_dev];
if (dev->priv != NULL) {
- /* NB: hpp_close() handles free_irq + irq2dev map */
+ /* NB: hpp_close() handles free_irq */
int ioaddr = dev->base_addr - NIC_OFFSET;
kfree(dev->priv);
dev->priv = NULL;
diff --git a/drivers/net/hp.c b/drivers/net/hp.c
index d57763e46..c31f97842 100644
--- a/drivers/net/hp.c
+++ b/drivers/net/hp.c
@@ -157,7 +157,7 @@ __initfunc(int hp_probe1(struct device *dev, int ioaddr))
outb_p(irqmap[irq] | HP_RUN, ioaddr + HP_CONFIGURE);
outb_p( 0x00 | HP_RUN, ioaddr + HP_CONFIGURE);
if (irq == autoirq_report(0) /* It's a good IRQ line! */
- && request_irq (irq, &ei_interrupt, 0, "hp", NULL) == 0) {
+ && request_irq (irq, &ei_interrupt, 0, "hp", dev) == 0) {
printk(" selecting IRQ %d.\n", irq);
dev->irq = *irqp;
break;
@@ -171,7 +171,7 @@ __initfunc(int hp_probe1(struct device *dev, int ioaddr))
} else {
if (dev->irq == 2)
dev->irq = 9;
- if (request_irq(dev->irq, ei_interrupt, 0, "hp", NULL)) {
+ if (request_irq(dev->irq, ei_interrupt, 0, "hp", dev)) {
printk (" unable to get IRQ %d.\n", dev->irq);
return EBUSY;
}
@@ -180,7 +180,7 @@ __initfunc(int hp_probe1(struct device *dev, int ioaddr))
/* Allocate dev->priv and fill in 8390 specific dev fields. */
if (ethdev_init(dev)) {
printk (" unable to get memory for dev->priv.\n");
- free_irq(dev->irq, NULL);
+ free_irq(dev->irq, dev);
return -ENOMEM;
}
@@ -435,8 +435,7 @@ cleanup_module(void)
int ioaddr = dev->base_addr - NIC_OFFSET;
kfree(dev->priv);
dev->priv = NULL;
- free_irq(dev->irq, NULL);
- irq2dev_map[dev->irq] = NULL;
+ free_irq(dev->irq, dev);
release_region(ioaddr, HP_IO_EXTENT);
unregister_netdev(dev);
}
diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c
index 0780e038f..b5eaec24f 100644
--- a/drivers/net/hp100.c
+++ b/drivers/net/hp100.c
@@ -1,51 +1,51 @@
/*
-** hp100.c
-** HP CASCADE Architecture Driver for 100VG-AnyLan Network Adapters
-**
-** $Id: hp100.c,v 1.52 1997/04/21 14:20:20 perex Exp perex $
-**
-** Based on the HP100 driver written by Jaroslav Kysela <perex@jcu.cz>
-** Extended for new busmaster capable chipsets by
-** Siegfried "Frieder" Loeffler (dg1sek) <floeff@mathematik.uni-stuttgart.de>
-**
-** Maintained by: Jaroslav Kysela <perex@jcu.cz>
-**
-** This driver has only been tested with
-** -- HP J2585B 10/100 Mbit/s PCI Busmaster
-** -- HP J2585A 10/100 Mbit/s PCI
-** -- HP J2970 10 Mbit/s PCI Combo 10base-T/BNC
-** -- HP J2973 10 Mbit/s PCI 10base-T
-** -- HP J2573 10/100 ISA
-** -- Compex ReadyLink ENET100-VG4 10/100 Mbit/s PCI / EISA
-**
-** but it should also work with the other CASCADE based adapters.
-**
-** TODO:
-** - J2573 seems to hang sometimes when in shared memory mode.
-** - Mode for Priority TX
-** - Check PCI registers, performance might be improved?
-** - To reduce interrupt load in busmaster, one could switch off
-** the interrupts that are used to refill the queues whenever the
-** queues are filled up to more than a certain threshold.
-**
-**
-** This source/code is public free; you can distribute it and/or modify
-** it under terms of the GNU General Public License (published by the
-** Free Software Foundation) either version two of this License, or any
-** later version.
-**
-*/
-
-#define HP100_DEFAULT_PRIORITY_TX 0
+ ** hp100.c
+ ** HP CASCADE Architecture Driver for 100VG-AnyLan Network Adapters
+ **
+ ** $Id: hp100.c,v 1.14 1997/11/16 13:57:28 alan Exp $
+ **
+ ** Based on the HP100 driver written by Jaroslav Kysela <perex@jcu.cz>
+ ** Extended for new busmaster capable chipsets by
+ ** Siegfried "Frieder" Loeffler (dg1sek) <floeff@mathematik.uni-stuttgart.de>
+ **
+ ** Maintained by: Jaroslav Kysela <perex@jcu.cz>
+ **
+ ** This driver has only been tested with
+ ** -- HP J2585B 10/100 Mbit/s PCI Busmaster
+ ** -- HP J2585A 10/100 Mbit/s PCI
+ ** -- HP J2970 10 Mbit/s PCI Combo 10base-T/BNC
+ ** -- HP J2973 10 Mbit/s PCI 10base-T
+ ** -- HP J2573 10/100 ISA
+ ** -- Compex ReadyLink ENET100-VG4 10/100 Mbit/s PCI / EISA
+ **
+ ** but it should also work with the other CASCADE based adapters.
+ **
+ ** TODO:
+ ** - J2573 seems to hang sometimes when in shared memory mode.
+ ** - Mode for Priority TX
+ ** - Check PCI registers, performance might be improved?
+ ** - To reduce interrupt load in busmaster, one could switch off
+ ** the interrupts that are used to refill the queues whenever the
+ ** queues are filled up to more than a certain threshold.
+ **
+ **
+ ** This source/code is public free; you can distribute it and/or modify
+ ** it under terms of the GNU General Public License (published by the
+ ** Free Software Foundation) either version two of this License, or any
+ ** later version.
+ **
+ */
+
+#define HP100_DEFAULT_PRIORITY_TX 0
#undef HP100_DEBUG
-#undef HP100_DEBUG_B /* Trace */
-#undef HP100_DEBUG_BM /* Debug busmaster code (PDL stuff) */
+#undef HP100_DEBUG_B /* Trace */
+#undef HP100_DEBUG_BM /* Debug busmaster code (PDL stuff) */
-#undef HP100_DEBUG_TRAINING /* Debug login-to-hub procedure */
-#undef HP100_DEBUG_TX
-#undef HP100_DEBUG_IRQ
-#undef HP100_DEBUG_RX
+#undef HP100_DEBUG_TRAINING /* Debug login-to-hub procedure */
+#undef HP100_DEBUG_TX
+#undef HP100_DEBUG_IRQ
+#undef HP100_DEBUG_RX
#include <linux/version.h>
#include <linux/module.h>
@@ -67,7 +67,7 @@
#include <linux/skbuff.h>
#include <linux/types.h>
-#include <linux/config.h> /* for CONFIG_PCI */
+#include <linux/config.h> /* for CONFIG_PCI */
#include <linux/delay.h>
#if LINUX_VERSION_CODE < 0x020100
@@ -99,7 +99,7 @@ typedef struct net_device_stats hp100_stats_t;
#define PCI_DEVICE_ID_COMPEX_ENET100VG4 0x0112
#endif
-#define HP100_REGION_SIZE 0x20 /* for ioports */
+#define HP100_REGION_SIZE 0x20 /* for ioports */
#define HP100_MAX_PACKET_SIZE (1536+4)
#define HP100_MIN_PACKET_SIZE 60
@@ -119,84 +119,85 @@ typedef struct net_device_stats hp100_stats_t;
*/
struct hp100_eisa_id {
- u_int id;
- const char *name;
- u_char bus;
+ u_int id;
+ const char *name;
+ u_char bus;
};
struct hp100_private {
- struct hp100_eisa_id *id;
- u_short chip;
- u_short soft_model;
- u_int memory_size;
- u_short rx_ratio; /* 1 - 99 */
- u_short priority_tx; /* != 0 - priority tx */
- u_short mode; /* PIO, Shared Mem or Busmaster */
- u_char bus;
- u_char pci_bus;
- u_char pci_device_fn;
- short mem_mapped; /* memory mapped access */
- u_int *mem_ptr_virt; /* virtual memory mapped area, maybe NULL */
- u_int *mem_ptr_phys; /* physical memory mapped area */
- short lan_type; /* 10Mb/s, 100Mb/s or -1 (error) */
- int hub_status; /* was login to hub successful? */
- u_char mac1_mode;
- u_char mac2_mode;
- hp100_stats_t stats;
-
- /* Rings for busmaster mode: */
- hp100_ring_t *rxrhead; /* Head (oldest) index into rxring */
- hp100_ring_t *rxrtail; /* Tail (newest) index into rxring */
- hp100_ring_t *txrhead; /* Head (oldest) index into txring */
- hp100_ring_t *txrtail; /* Tail (newest) index into txring */
-
- hp100_ring_t rxring[ MAX_RX_PDL ];
- hp100_ring_t txring[ MAX_TX_PDL ];
-
- u_int *page_vaddr; /* Virtual address of allocated page */
- u_int *page_vaddr_algn; /* Aligned virtual address of allocated page */
- int rxrcommit; /* # Rx PDLs commited to adapter */
- int txrcommit; /* # Tx PDLs commited to adapter */
+ struct hp100_eisa_id *id;
+ u_short chip;
+ u_short soft_model;
+ u_int memory_size;
+ u_short rx_ratio; /* 1 - 99 */
+ u_short priority_tx; /* != 0 - priority tx */
+ u_short mode; /* PIO, Shared Mem or Busmaster */
+ u_char bus;
+ u_char pci_bus;
+ u_char pci_device_fn;
+ short mem_mapped; /* memory mapped access */
+ u_int *mem_ptr_virt; /* virtual memory mapped area, maybe NULL */
+ u_int *mem_ptr_phys; /* physical memory mapped area */
+ short lan_type; /* 10Mb/s, 100Mb/s or -1 (error) */
+ int hub_status; /* was login to hub successful? */
+ u_char mac1_mode;
+ u_char mac2_mode;
+ hp100_stats_t stats;
+
+ /* Rings for busmaster mode: */
+ hp100_ring_t *rxrhead; /* Head (oldest) index into rxring */
+ hp100_ring_t *rxrtail; /* Tail (newest) index into rxring */
+ hp100_ring_t *txrhead; /* Head (oldest) index into txring */
+ hp100_ring_t *txrtail; /* Tail (newest) index into txring */
+
+ hp100_ring_t rxring[MAX_RX_PDL];
+ hp100_ring_t txring[MAX_TX_PDL];
+
+ u_int *page_vaddr; /* Virtual address of allocated page */
+ u_int *page_vaddr_algn; /* Aligned virtual address of allocated page */
+ int rxrcommit; /* # Rx PDLs commited to adapter */
+ int txrcommit; /* # Tx PDLs commited to adapter */
};
/*
* variables
*/
-static struct hp100_eisa_id hp100_eisa_ids[] = {
+static struct hp100_eisa_id hp100_eisa_ids[] =
+{
/* 10/100 EISA card with revision A Cascade chip */
- { 0x80F1F022, "HP J2577 rev A", HP100_BUS_EISA },
+ {0x80F1F022, "HP J2577 rev A", HP100_BUS_EISA},
/* 10/100 ISA card with revision A Cascade chip */
- { 0x50F1F022, "HP J2573 rev A", HP100_BUS_ISA },
+ {0x50F1F022, "HP J2573 rev A", HP100_BUS_ISA},
/* 10 only EISA card with Cascade chip */
- { 0x2019F022, "HP 27248B", HP100_BUS_EISA },
+ {0x2019F022, "HP 27248B", HP100_BUS_EISA},
/* 10/100 EISA card with Cascade chip */
- { 0x4019F022, "HP J2577", HP100_BUS_EISA },
+ {0x4019F022, "HP J2577", HP100_BUS_EISA},
/* 10/100 ISA card with Cascade chip */
- { 0x5019F022, "HP J2573", HP100_BUS_ISA },
+ {0x5019F022, "HP J2573", HP100_BUS_ISA},
/* 10/100 PCI card - old J2585A */
- { 0x1030103c, "HP J2585A", HP100_BUS_PCI },
+ {0x1030103c, "HP J2585A", HP100_BUS_PCI},
/* 10/100 PCI card - new J2585B - master capable */
- { 0x1041103c, "HP J2585B", HP100_BUS_PCI },
+ {0x1041103c, "HP J2585B", HP100_BUS_PCI},
/* 10 Mbit Combo Adapter */
- { 0x1042103c, "HP J2970", HP100_BUS_PCI },
+ {0x1042103c, "HP J2970", HP100_BUS_PCI},
/* 10 Mbit 10baseT Adapter */
- { 0x1040103c, "HP J2973", HP100_BUS_PCI },
+ {0x1040103c, "HP J2973", HP100_BUS_PCI},
/* 10/100 EISA card from Compex */
- { 0x0103180e, "ReadyLink ENET100-VG4", HP100_BUS_EISA },
+ {0x0103180e, "ReadyLink ENET100-VG4", HP100_BUS_EISA},
/* 10/100 PCI card from Compex (J2585A compatible) */
- { 0x011211f6, "ReadyLink ENET100-VG4", HP100_BUS_PCI }
+ {0x011211f6, "ReadyLink ENET100-VG4", HP100_BUS_PCI}
};
static int hp100_rx_ratio = HP100_DEFAULT_RX_RATIO;
@@ -204,49 +205,49 @@ static int hp100_priority_tx = HP100_DEFAULT_PRIORITY_TX;
static int hp100_mode = 1;
#ifdef LINUX_2_1
-MODULE_PARM( hp100_rx_ratio, "1i" );
-MODULE_PARM( hp100_priority_tx, "1i" );
-MODULE_PARM( hp100_mode, "1i" );
+MODULE_PARM(hp100_rx_ratio, "1i");
+MODULE_PARM(hp100_priority_tx, "1i");
+MODULE_PARM(hp100_mode, "1i");
#endif
/*
* prototypes
*/
-static int hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn );
-static int hp100_open( struct device *dev );
-static int hp100_close( struct device *dev );
-static int hp100_start_xmit( struct sk_buff *skb, struct device *dev );
-static int hp100_start_xmit_bm (struct sk_buff *skb, struct device *dev );
-static void hp100_rx( struct device *dev );
-static hp100_stats_t *hp100_get_stats( struct device *dev );
-static void hp100_update_stats( struct device *dev );
-static void hp100_clear_stats( int ioaddr );
-static void hp100_set_multicast_list( struct device *dev);
-static void hp100_interrupt( int irq, void *dev_id, struct pt_regs *regs );
-static void hp100_start_interface( struct device *dev );
-static void hp100_stop_interface( struct device *dev );
-static void hp100_load_eeprom( struct device *dev );
-static int hp100_sense_lan( struct device *dev );
-static int hp100_login_to_vg_hub( struct device *dev, u_short force_relogin );
-static int hp100_down_vg_link( struct device *dev );
-static void hp100_cascade_reset( struct device *dev, u_short enable );
-static void hp100_BM_shutdown( struct device *dev );
-static void hp100_mmuinit( struct device *dev );
-static void hp100_init_pdls( struct device *dev );
-static int hp100_init_rxpdl( register hp100_ring_t *ringptr, register u_int *pdlptr);
-static int hp100_init_txpdl( register hp100_ring_t *ringptr, register u_int *pdlptr);
-static void hp100_rxfill( struct device *dev );
-static void hp100_hwinit( struct device *dev );
-static void hp100_clean_txring( struct device *dev );
+static int hp100_probe1(struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn);
+static int hp100_open(struct device *dev);
+static int hp100_close(struct device *dev);
+static int hp100_start_xmit(struct sk_buff *skb, struct device *dev);
+static int hp100_start_xmit_bm(struct sk_buff *skb, struct device *dev);
+static void hp100_rx(struct device *dev);
+static hp100_stats_t *hp100_get_stats(struct device *dev);
+static void hp100_update_stats(struct device *dev);
+static void hp100_clear_stats(int ioaddr);
+static void hp100_set_multicast_list(struct device *dev);
+static void hp100_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void hp100_start_interface(struct device *dev);
+static void hp100_stop_interface(struct device *dev);
+static void hp100_load_eeprom(struct device *dev);
+static int hp100_sense_lan(struct device *dev);
+static int hp100_login_to_vg_hub(struct device *dev, u_short force_relogin);
+static int hp100_down_vg_link(struct device *dev);
+static void hp100_cascade_reset(struct device *dev, u_short enable);
+static void hp100_BM_shutdown(struct device *dev);
+static void hp100_mmuinit(struct device *dev);
+static void hp100_init_pdls(struct device *dev);
+static int hp100_init_rxpdl(register hp100_ring_t * ringptr, register u_int * pdlptr);
+static int hp100_init_txpdl(register hp100_ring_t * ringptr, register u_int * pdlptr);
+static void hp100_rxfill(struct device *dev);
+static void hp100_hwinit(struct device *dev);
+static void hp100_clean_txring(struct device *dev);
#ifdef HP100_DEBUG
-static void hp100_RegisterDump( struct device *dev );
+static void hp100_RegisterDump(struct device *dev);
#endif
/* TODO: This function should not really be needed in a good design... */
-static void wait( void )
+static void wait(void)
{
- udelay( 1000 );
+ udelay(1000);
}
/*
@@ -255,929 +256,862 @@ static void wait( void )
* since this could cause problems when the card is not installed.
*/
-__initfunc(int hp100_probe( struct device *dev ))
+__initfunc(int hp100_probe(struct device *dev))
{
- int base_addr = dev ? dev -> base_addr : 0;
- int ioaddr = 0;
+ int base_addr = dev ? dev->base_addr : 0;
+ int ioaddr = 0;
#ifdef CONFIG_PCI
- int pci_start_index = 0;
+ int pci_start_index = 0;
#endif
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4200, TRACE );
- printk( "hp100: probe\n" );
-#endif
-
- if ( base_addr > 0xff ) /* Check a single specified location. */
- {
- if ( check_region( base_addr, HP100_REGION_SIZE ) ) return -EINVAL;
- if ( base_addr < 0x400 )
- return hp100_probe1( dev, base_addr, HP100_BUS_ISA, 0, 0 );
- else
- return hp100_probe1( dev, base_addr, HP100_BUS_EISA, 0, 0 );
- }
- else
+ hp100_outw(0x4200, TRACE);
+ printk("hp100: probe\n");
+#endif
+
+ if (base_addr > 0xff) { /* Check a single specified location. */
+ if (check_region(base_addr, HP100_REGION_SIZE))
+ return -EINVAL;
+ if (base_addr < 0x400)
+ return hp100_probe1(dev, base_addr, HP100_BUS_ISA, 0, 0);
+ else
+ return hp100_probe1(dev, base_addr, HP100_BUS_EISA, 0, 0);
+ } else
#ifdef CONFIG_PCI
- if ( base_addr > 0 && base_addr < 8 + 1 )
- pci_start_index = 0x100 | ( base_addr - 1 );
- else
+ if (base_addr > 0 && base_addr < 8 + 1)
+ pci_start_index = 0x100 | (base_addr - 1);
+ else
#endif
- if ( base_addr != 0 ) return -ENXIO;
+ if (base_addr != 0)
+ return -ENXIO;
- /* at first - scan PCI bus(es) */
+ /* at first - scan PCI bus(es) */
#ifdef CONFIG_PCI
- if ( pcibios_present() )
- {
- int pci_index;
+ if (pcibios_present()) {
+ int pci_index;
#ifdef HP100_DEBUG_PCI
- printk( "hp100: PCI BIOS is present, checking for devices..\n" );
-#endif
- for ( pci_index = pci_start_index & 7; pci_index < 8; pci_index++ )
- {
- u_char pci_bus, pci_device_fn;
- u_short pci_command;
-
- if ((pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A,
- pci_index, &pci_bus,
- &pci_device_fn ) != 0 ) &&
- (pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585B,
- pci_index, &pci_bus,
- &pci_device_fn ) != 0 ) &&
- (pcibios_find_device( PCI_VENDOR_ID_COMPEX, PCI_DEVICE_ID_COMPEX_ENET100VG4,
- pci_index, &pci_bus,
- &pci_device_fn ) != 0 ) ) break;
-
- pcibios_read_config_dword( pci_bus, pci_device_fn,
- PCI_BASE_ADDRESS_0, &ioaddr );
-
- ioaddr &= ~3; /* remove I/O space marker in bit 0. */
-
- if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
-
- pcibios_read_config_word( pci_bus, pci_device_fn,
- PCI_COMMAND, &pci_command );
- if ( !( pci_command & PCI_COMMAND_MASTER ) )
- {
+ printk("hp100: PCI BIOS is present, checking for devices..\n");
+#endif
+ for (pci_index = pci_start_index & 7; pci_index < 8; pci_index++) {
+ u_char pci_bus, pci_device_fn;
+ u_short pci_command;
+
+ if ((pcibios_find_device(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A,
+ pci_index, &pci_bus,
+ &pci_device_fn) != 0) &&
+ (pcibios_find_device(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585B,
+ pci_index, &pci_bus,
+ &pci_device_fn) != 0) &&
+ (pcibios_find_device(PCI_VENDOR_ID_COMPEX, PCI_DEVICE_ID_COMPEX_ENET100VG4,
+ pci_index, &pci_bus,
+ &pci_device_fn) != 0))
+ break;
+
+ pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &ioaddr);
+
+ ioaddr &= ~3; /* remove I/O space marker in bit 0. */
+
+ if (check_region(ioaddr, HP100_REGION_SIZE))
+ continue;
+
+ pcibios_read_config_word(pci_bus, pci_device_fn,
+ PCI_COMMAND, &pci_command);
+ if (!(pci_command & PCI_COMMAND_MASTER)) {
#ifdef HP100_DEBUG
- printk( "hp100: PCI Master Bit has not been set. Setting...\n" );
+ printk("hp100: PCI Master Bit has not been set. Setting...\n");
#endif
- pci_command |= PCI_COMMAND_MASTER;
- pcibios_write_config_word( pci_bus, pci_device_fn,
- PCI_COMMAND, pci_command );
- }
+ pci_command |= PCI_COMMAND_MASTER;
+ pcibios_write_config_word(pci_bus, pci_device_fn,
+ PCI_COMMAND, pci_command);
+ }
#ifdef HP100_DEBUG
- printk( "hp100: PCI adapter found at 0x%x\n", ioaddr );
-#endif
- if ( hp100_probe1( dev, ioaddr, HP100_BUS_PCI, pci_bus, pci_device_fn ) == 0 )
- return 0;
- }
- }
- if ( pci_start_index > 0 ) return -ENODEV;
-#endif /* CONFIG_PCI */
-
- /* Second: Probe all EISA possible port regions (if EISA bus present) */
- for ( ioaddr = 0x1c38; EISA_bus && ioaddr < 0x10000; ioaddr += 0x400 )
- {
- if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
- if ( hp100_probe1( dev, ioaddr, HP100_BUS_EISA, 0, 0 ) == 0 ) return 0;
- }
-
- /* Third Probe all ISA possible port regions */
- for ( ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20 )
- {
- if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
- if ( hp100_probe1( dev, ioaddr, HP100_BUS_ISA, 0, 0 ) == 0 ) return 0;
- }
-
- return -ENODEV;
-}
+ printk("hp100: PCI adapter found at 0x%x\n", ioaddr);
+#endif
+ if (hp100_probe1(dev, ioaddr, HP100_BUS_PCI, pci_bus, pci_device_fn) == 0)
+ return 0;
+ }
+ }
+ if (pci_start_index > 0)
+ return -ENODEV;
+#endif /* CONFIG_PCI */
+
+ /* Second: Probe all EISA possible port regions (if EISA bus present) */
+ for (ioaddr = 0x1c38; EISA_bus && ioaddr < 0x10000; ioaddr += 0x400) {
+ if (check_region(ioaddr, HP100_REGION_SIZE))
+ continue;
+ if (hp100_probe1(dev, ioaddr, HP100_BUS_EISA, 0, 0) == 0)
+ return 0;
+ }
+ /* Third Probe all ISA possible port regions */
+ for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20) {
+ if (check_region(ioaddr, HP100_REGION_SIZE))
+ continue;
+ if (hp100_probe1(dev, ioaddr, HP100_BUS_ISA, 0, 0) == 0)
+ return 0;
+ }
+
+ return -ENODEV;
+}
-__initfunc(static int hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn ))
+
+__initfunc(static int hp100_probe1(struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn))
{
- int i;
+ int i;
- u_char uc, uc_1;
- u_int eisa_id;
- u_int chip;
- u_int memory_size = 0;
- short mem_mapped;
- u_int *mem_ptr_phys, *mem_ptr_virt;
- struct hp100_private *lp;
- struct hp100_eisa_id *eid;
+ u_char uc, uc_1;
+ u_int eisa_id;
+ u_int chip;
+ u_int memory_size = 0;
+ short mem_mapped;
+ u_int *mem_ptr_phys, *mem_ptr_virt;
+ struct hp100_private *lp;
+ struct hp100_eisa_id *eid;
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4201, TRACE );
- printk("hp100: probe1\n");
+ hp100_outw(0x4201, TRACE);
+ printk("hp100: probe1\n");
#endif
- if ( dev == NULL )
- {
+ if (dev == NULL) {
#ifdef HP100_DEBUG
- printk( "hp100_probe1: dev == NULL ?\n" );
-#endif
- return EIO;
- }
-
- if ( hp100_inw( HW_ID ) != HP100_HW_ID_CASCADE )
- {
- return -ENODEV;
- }
- else
- {
- chip = hp100_inw( PAGING ) & HP100_CHIPID_MASK;
-#ifdef HP100_DEBUG
- if ( chip == HP100_CHIPID_SHASTA )
- printk("hp100: Shasta Chip detected. (This is a pre 802.12 chip)\n");
- else if ( chip == HP100_CHIPID_RAINIER )
- printk("hp100: Rainier Chip detected. (This is a pre 802.12 chip)\n");
- else if ( chip == HP100_CHIPID_LASSEN )
- printk("hp100: Lassen Chip detected.\n");
- else
- printk("hp100: Warning: Unknown CASCADE chip (id=0x%.4x).\n",chip);
-#endif
- }
-
- dev->base_addr = ioaddr;
-
- hp100_page( ID_MAC_ADDR );
- for ( i = uc = eisa_id = 0; i < 4; i++ )
- {
- eisa_id >>= 8;
- uc_1 = hp100_inb( BOARD_ID + i );
- eisa_id |= uc_1 << 24;
- uc += uc_1;
- }
- uc += hp100_inb( BOARD_ID + 4 );
-
- if ( uc != 0xff ) /* bad checksum? */
- {
- printk("hp100_probe: bad EISA ID checksum at base port 0x%x\n", ioaddr );
- return -ENODEV;
- }
-
- for ( i=0; i<sizeof(hp100_eisa_ids)/sizeof(struct hp100_eisa_id); i++)
- if ( ( hp100_eisa_ids[ i ].id & 0xf0ffffff ) == ( eisa_id & 0xf0ffffff ) )
- break;
- if ( i >= sizeof( hp100_eisa_ids ) / sizeof( struct hp100_eisa_id ) )
- {
- printk( "hp100_probe1: card at port 0x%x isn't known (id = 0x%x)\n", ioaddr, eisa_id );
- return -ENODEV;
- }
- eid = &hp100_eisa_ids[ i ];
- if ( ( eid->id & 0x0f000000 ) < ( eisa_id & 0x0f000000 ) )
- {
- printk( "hp100_probe1: newer version of card %s at port 0x%x - unsupported\n",
- eid->name, ioaddr );
- return -ENODEV;
- }
-
- for ( i = uc = 0; i < 7; i++ )
- uc += hp100_inb( LAN_ADDR + i );
- if ( uc != 0xff )
- {
- printk("hp100_probe1: bad lan address checksum (card %s at port 0x%x)\n",
- eid->name, ioaddr );
- return -EIO;
- }
-
- /* Determine driver operation mode
- *
- * Use the variable "hp100_mode" upon insmod or as kernel parameter to
- * force driver modes:
- * hp100_mode=1 -> default, use busmaster mode if configured.
- * hp100_mode=2 -> enable shared memory mode
- * hp100_mode=3 -> force use of i/o mapped mode.
- * hp100_mode=4 -> same as 1, but re-set the enable bit on the card.
- */
-
- if(hp100_mode==3)
- {
- hp100_outw(HP100_MEM_EN|HP100_RESET_LB, OPTION_LSW);
- hp100_outw(HP100_IO_EN|HP100_SET_LB, OPTION_LSW);
- hp100_outw(HP100_BM_WRITE|HP100_BM_READ|HP100_RESET_HB, OPTION_LSW);
- printk("hp100: IO mapped mode forced.\n");
- }
- else if(hp100_mode==2)
- {
- hp100_outw(HP100_MEM_EN|HP100_SET_LB, OPTION_LSW);
- hp100_outw(HP100_IO_EN |HP100_SET_LB, OPTION_LSW);
- hp100_outw(HP100_BM_WRITE|HP100_BM_READ|HP100_RESET_HB, OPTION_LSW);
- printk("hp100: Shared memory mode requested.\n");
- }
- else if(hp100_mode==4)
- {
- if(chip==HP100_CHIPID_LASSEN)
- {
- hp100_outw(HP100_BM_WRITE|
- HP100_BM_READ | HP100_SET_HB, OPTION_LSW);
- hp100_outw(HP100_IO_EN |
- HP100_MEM_EN | HP100_RESET_LB, OPTION_LSW);
- printk("hp100: Busmaster mode requested.\n");
+ printk("hp100_probe1: dev == NULL ?\n");
+#endif
+ return EIO;
}
- hp100_mode=1;
- }
-
- if(hp100_mode==1) /* default behaviour */
- {
- if( (hp100_inw(OPTION_LSW)&HP100_IO_EN) &&
- (~hp100_inw(OPTION_LSW)&HP100_MEM_EN) &&
- (~hp100_inw(OPTION_LSW)&(HP100_BM_WRITE|HP100_BM_READ))
- )
- {
+ if (hp100_inw(HW_ID) != HP100_HW_ID_CASCADE) {
+ return -ENODEV;
+ } else {
+ chip = hp100_inw(PAGING) & HP100_CHIPID_MASK;
#ifdef HP100_DEBUG
- printk("hp100: IO_EN bit is set on card.\n");
+ if (chip == HP100_CHIPID_SHASTA)
+ printk("hp100: Shasta Chip detected. (This is a pre 802.12 chip)\n");
+ else if (chip == HP100_CHIPID_RAINIER)
+ printk("hp100: Rainier Chip detected. (This is a pre 802.12 chip)\n");
+ else if (chip == HP100_CHIPID_LASSEN)
+ printk("hp100: Lassen Chip detected.\n");
+ else
+ printk("hp100: Warning: Unknown CASCADE chip (id=0x%.4x).\n", chip);
#endif
- hp100_mode=3;
}
- else if( ( chip==HP100_CHIPID_LASSEN ) &&
- ( (hp100_inw(OPTION_LSW)&(HP100_BM_WRITE|HP100_BM_READ) ) ==
- (HP100_BM_WRITE|HP100_BM_READ) ) )
- {
- printk("hp100: Busmaster mode enabled.\n");
- hp100_outw(HP100_MEM_EN|HP100_IO_EN|HP100_RESET_LB, OPTION_LSW);
+
+ dev->base_addr = ioaddr;
+
+ hp100_page(ID_MAC_ADDR);
+ for (i = uc = eisa_id = 0; i < 4; i++) {
+ eisa_id >>= 8;
+ uc_1 = hp100_inb(BOARD_ID + i);
+ eisa_id |= uc_1 << 24;
+ uc += uc_1;
+ }
+ uc += hp100_inb(BOARD_ID + 4);
+
+ if (uc != 0xff) { /* bad checksum? */
+ printk("hp100_probe: bad EISA ID checksum at base port 0x%x\n", ioaddr);
+ return -ENODEV;
+ }
+ for (i = 0; i < sizeof(hp100_eisa_ids) / sizeof(struct hp100_eisa_id); i++)
+ if ((hp100_eisa_ids[i].id & 0xf0ffffff) == (eisa_id & 0xf0ffffff))
+ break;
+ if (i >= sizeof(hp100_eisa_ids) / sizeof(struct hp100_eisa_id)) {
+ printk("hp100_probe1: card at port 0x%x isn't known (id = 0x%x)\n", ioaddr, eisa_id);
+ return -ENODEV;
+ }
+ eid = &hp100_eisa_ids[i];
+ if ((eid->id & 0x0f000000) < (eisa_id & 0x0f000000)) {
+ printk("hp100_probe1: newer version of card %s at port 0x%x - unsupported\n",
+ eid->name, ioaddr);
+ return -ENODEV;
+ }
+ for (i = uc = 0; i < 7; i++)
+ uc += hp100_inb(LAN_ADDR + i);
+ if (uc != 0xff) {
+ printk("hp100_probe1: bad lan address checksum (card %s at port 0x%x)\n",
+ eid->name, ioaddr);
+ return -EIO;
}
- else
- {
+ /* Determine driver operation mode
+
+ * Use the variable "hp100_mode" upon insmod or as kernel parameter to
+ * force driver modes:
+ * hp100_mode=1 -> default, use busmaster mode if configured.
+ * hp100_mode=2 -> enable shared memory mode
+ * hp100_mode=3 -> force use of i/o mapped mode.
+ * hp100_mode=4 -> same as 1, but re-set the enable bit on the card.
+ */
+
+ if (hp100_mode == 3) {
+ hp100_outw(HP100_MEM_EN | HP100_RESET_LB, OPTION_LSW);
+ hp100_outw(HP100_IO_EN | HP100_SET_LB, OPTION_LSW);
+ hp100_outw(HP100_BM_WRITE | HP100_BM_READ | HP100_RESET_HB, OPTION_LSW);
+ printk("hp100: IO mapped mode forced.\n");
+ } else if (hp100_mode == 2) {
+ hp100_outw(HP100_MEM_EN | HP100_SET_LB, OPTION_LSW);
+ hp100_outw(HP100_IO_EN | HP100_SET_LB, OPTION_LSW);
+ hp100_outw(HP100_BM_WRITE | HP100_BM_READ | HP100_RESET_HB, OPTION_LSW);
+ printk("hp100: Shared memory mode requested.\n");
+ } else if (hp100_mode == 4) {
+ if (chip == HP100_CHIPID_LASSEN) {
+ hp100_outw(HP100_BM_WRITE |
+ HP100_BM_READ | HP100_SET_HB, OPTION_LSW);
+ hp100_outw(HP100_IO_EN |
+ HP100_MEM_EN | HP100_RESET_LB, OPTION_LSW);
+ printk("hp100: Busmaster mode requested.\n");
+ }
+ hp100_mode = 1;
+ }
+ if (hp100_mode == 1) { /* default behaviour */
+ if ((hp100_inw(OPTION_LSW) & HP100_IO_EN) &&
+ (~hp100_inw(OPTION_LSW) & HP100_MEM_EN) &&
+ (~hp100_inw(OPTION_LSW) & (HP100_BM_WRITE | HP100_BM_READ))
+ ) {
#ifdef HP100_DEBUG
- printk("hp100: Card not configured for BM or BM not supported with this card. Trying shared memory mode.\n");
+ printk("hp100: IO_EN bit is set on card.\n");
+#endif
+ hp100_mode = 3;
+ } else if ((chip == HP100_CHIPID_LASSEN) &&
+ ((hp100_inw(OPTION_LSW) & (HP100_BM_WRITE | HP100_BM_READ)) ==
+ (HP100_BM_WRITE | HP100_BM_READ))) {
+ printk("hp100: Busmaster mode enabled.\n");
+ hp100_outw(HP100_MEM_EN | HP100_IO_EN | HP100_RESET_LB, OPTION_LSW);
+ } else {
+#ifdef HP100_DEBUG
+ printk("hp100: Card not configured for BM or BM not supported with this card. Trying shared memory mode.\n");
#endif
- /* In this case, try shared memory mode */
- hp100_mode=2;
- hp100_outw(HP100_MEM_EN|HP100_SET_LB, OPTION_LSW);
- /* hp100_outw(HP100_IO_EN|HP100_RESET_LB, OPTION_LSW); */
+ /* In this case, try shared memory mode */
+ hp100_mode = 2;
+ hp100_outw(HP100_MEM_EN | HP100_SET_LB, OPTION_LSW);
+ /* hp100_outw(HP100_IO_EN|HP100_RESET_LB, OPTION_LSW); */
+ }
}
- }
-
- /* Check for shared memory on the card, eventually remap it */
- hp100_page( HW_MAP );
- mem_mapped = (( hp100_inw( OPTION_LSW ) & ( HP100_MEM_EN ) ) != 0);
- mem_ptr_phys = mem_ptr_virt = NULL;
- memory_size = (8192<<( (hp100_inb(SRAM)>>5)&0x07));
-
- /* For memory mapped or busmaster mode, we want the memory address */
- if ( mem_mapped || (hp100_mode==1))
- {
- mem_ptr_phys = (u_int *)( hp100_inw( MEM_MAP_LSW ) |
- ( hp100_inw( MEM_MAP_MSW ) << 16 ) );
- (u_int)mem_ptr_phys &= ~0x1fff; /* 8k alignment */
-
- if ( bus == HP100_BUS_ISA && ( (u_long)mem_ptr_phys & ~0xfffff ) != 0 )
- {
- printk("hp100: Can only use programmed i/o mode.\n");
- mem_ptr_phys = NULL;
- mem_mapped = 0;
- hp100_mode=3; /* Use programmed i/o */
- }
-
- /* We do not need access to shared memory in busmaster mode */
- /* However in slave mode we need to remap high (>1GB) card memory */
- if(hp100_mode!=1) /* = not busmaster */
- {
- if ( bus == HP100_BUS_PCI )
- {
- /* We try with smaller memory sizes, if ioremap fails */
- for(; memory_size>16383; memory_size=memory_size/2)
- {
- if((mem_ptr_virt=ioremap((u_long)mem_ptr_phys,memory_size))==NULL)
- {
+ /* Check for shared memory on the card, eventually remap it */
+ hp100_page(HW_MAP);
+ mem_mapped = ((hp100_inw(OPTION_LSW) & (HP100_MEM_EN)) != 0);
+ mem_ptr_phys = mem_ptr_virt = NULL;
+ memory_size = (8192 << ((hp100_inb(SRAM) >> 5) & 0x07));
+
+ /* For memory mapped or busmaster mode, we want the memory address */
+ if (mem_mapped || (hp100_mode == 1)) {
+ mem_ptr_phys = (u_int *) (hp100_inw(MEM_MAP_LSW) |
+ (hp100_inw(MEM_MAP_MSW) << 16));
+ (u_int) mem_ptr_phys &= ~0x1fff; /* 8k alignment */
+
+ if (bus == HP100_BUS_ISA && ((u_long) mem_ptr_phys & ~0xfffff) != 0) {
+ printk("hp100: Can only use programmed i/o mode.\n");
+ mem_ptr_phys = NULL;
+ mem_mapped = 0;
+ hp100_mode = 3; /* Use programmed i/o */
+ }
+ /* We do not need access to shared memory in busmaster mode */
+ /* However in slave mode we need to remap high (>1GB) card memory */
+ if (hp100_mode != 1) { /* = not busmaster */
+ if (bus == HP100_BUS_PCI) {
+ /* We try with smaller memory sizes, if ioremap fails */
+ for (; memory_size > 16383; memory_size = memory_size / 2) {
+ if ((mem_ptr_virt = ioremap((u_long) mem_ptr_phys, memory_size)) == NULL) {
#ifdef HP100_DEBUG
- printk( "hp100: ioremap for 0x%x bytes high PCI memory at 0x%lx failed\n", memory_size, (u_long)mem_ptr_phys );
+ printk("hp100: ioremap for 0x%x bytes high PCI memory at 0x%lx failed\n", memory_size, (u_long) mem_ptr_phys);
#endif
- }
- else
- {
+ } else {
#ifdef HP100_DEBUG
- printk( "hp100: remapped 0x%x bytes high PCI memory at 0x%lx to 0x%lx.\n", memory_size, (u_long)mem_ptr_phys, (u_long)mem_ptr_virt);
-#endif
- break;
- }
- }
-
- if(mem_ptr_virt==NULL) /* all ioremap tries failed */
- {
- printk("hp100: Failed to ioremap the PCI card memory. Will have to use i/o mapped mode.\n");
- hp100_mode=3;
- memory_size = (8192<<( (hp100_inb(SRAM)>>5)&0x07) );
+ printk("hp100: remapped 0x%x bytes high PCI memory at 0x%lx to 0x%lx.\n", memory_size, (u_long) mem_ptr_phys, (u_long) mem_ptr_virt);
+#endif
+ break;
+ }
+ }
+
+ if (mem_ptr_virt == NULL) { /* all ioremap tries failed */
+ printk("hp100: Failed to ioremap the PCI card memory. Will have to use i/o mapped mode.\n");
+ hp100_mode = 3;
+ memory_size = (8192 << ((hp100_inb(SRAM) >> 5) & 0x07));
+ }
+ }
}
- }
}
-
- }
-
- if(hp100_mode==3) /* io mapped forced */
- {
- mem_mapped = 0;
- mem_ptr_phys = mem_ptr_virt = NULL;
- printk("hp100: Using (slow) programmed i/o mode.\n");
- }
-
- /* Initialise the "private" data structure for this card. */
- if ( (dev->priv=kmalloc(sizeof(struct hp100_private), GFP_KERNEL)) == NULL)
- return -ENOMEM;
- memset( dev->priv, 0, sizeof(struct hp100_private) );
-
- lp = (struct hp100_private *)dev->priv;
- lp->id = eid;
- lp->chip = chip;
- lp->mode = hp100_mode;
- lp->pci_bus = pci_bus;
- lp->bus = bus;
- lp->pci_device_fn = pci_device_fn;
- lp->priority_tx = hp100_priority_tx;
- lp->rx_ratio = hp100_rx_ratio;
- lp->mem_ptr_phys = mem_ptr_phys;
- lp->mem_ptr_virt = mem_ptr_virt;
- hp100_page( ID_MAC_ADDR );
- lp->soft_model = hp100_inb( SOFT_MODEL );
- lp->mac1_mode = HP100_MAC1MODE3;
- lp->mac2_mode = HP100_MAC2MODE3;
-
- dev->base_addr = ioaddr;
-
- lp->memory_size = memory_size;
- lp->rx_ratio = hp100_rx_ratio; /* can be conf'd with insmod */
-
- /* memory region for programmed i/o */
- request_region( dev->base_addr, HP100_REGION_SIZE, eid->name );
-
- dev->open = hp100_open;
- dev->stop = hp100_close;
-
- if (lp->mode==1) /* busmaster */
- dev->hard_start_xmit = hp100_start_xmit_bm;
- else
- dev->hard_start_xmit = hp100_start_xmit;
-
- dev->get_stats = hp100_get_stats;
- dev->set_multicast_list = &hp100_set_multicast_list;
-
- /* Ask the card for which IRQ line it is configured */
- hp100_page( HW_MAP );
- dev->irq = hp100_inb( IRQ_CHANNEL ) & HP100_IRQMASK;
- if ( dev->irq == 2 )
- dev->irq = 9;
-
- if(lp->mode==1) /* busmaster */
- dev->dma=4;
-
- /* Ask the card for its MAC address and store it for later use. */
- hp100_page( ID_MAC_ADDR );
- for ( i = uc = 0; i < 6; i++ )
- dev->dev_addr[ i ] = hp100_inb( LAN_ADDR + i );
-
- /* Reset statistics (counters) */
- hp100_clear_stats( ioaddr );
-
- ether_setup( dev );
-
- /* If busmaster mode is wanted, a dma-capable memory area is needed for
- * the rx and tx PDLs
- * PCI cards can access the whole PC memory. Therefore GFP_DMA is not
- * needed for the allocation of the memory area.
- */
-
- /* TODO: We do not need this with old cards, where PDLs are stored
- * in the cards shared memory area. But currently, busmaster has been
- * implemented/tested only with the lassen chip anyway... */
- if(lp->mode==1) /* busmaster */
- {
- /* Get physically continous memory for TX & RX PDLs */
- if ( (lp->page_vaddr=kmalloc(MAX_RINGSIZE+0x0f,GFP_KERNEL) ) == NULL)
- return -ENOMEM;
- lp->page_vaddr_algn=((u_int *) ( ((u_int)(lp->page_vaddr)+0x0f) &~0x0f));
- memset(lp->page_vaddr, 0, MAX_RINGSIZE+0x0f);
+ if (hp100_mode == 3) { /* io mapped forced */
+ mem_mapped = 0;
+ mem_ptr_phys = mem_ptr_virt = NULL;
+ printk("hp100: Using (slow) programmed i/o mode.\n");
+ }
+ /* Initialise the "private" data structure for this card. */
+ if ((dev->priv = kmalloc(sizeof(struct hp100_private), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct hp100_private));
+
+ lp = (struct hp100_private *) dev->priv;
+ lp->id = eid;
+ lp->chip = chip;
+ lp->mode = hp100_mode;
+ lp->pci_bus = pci_bus;
+ lp->bus = bus;
+ lp->pci_device_fn = pci_device_fn;
+ lp->priority_tx = hp100_priority_tx;
+ lp->rx_ratio = hp100_rx_ratio;
+ lp->mem_ptr_phys = mem_ptr_phys;
+ lp->mem_ptr_virt = mem_ptr_virt;
+ hp100_page(ID_MAC_ADDR);
+ lp->soft_model = hp100_inb(SOFT_MODEL);
+ lp->mac1_mode = HP100_MAC1MODE3;
+ lp->mac2_mode = HP100_MAC2MODE3;
+
+ dev->base_addr = ioaddr;
+
+ lp->memory_size = memory_size;
+ lp->rx_ratio = hp100_rx_ratio; /* can be conf'd with insmod */
+
+ /* memory region for programmed i/o */
+ request_region(dev->base_addr, HP100_REGION_SIZE, eid->name);
+
+ dev->open = hp100_open;
+ dev->stop = hp100_close;
+
+ if (lp->mode == 1) /* busmaster */
+ dev->hard_start_xmit = hp100_start_xmit_bm;
+ else
+ dev->hard_start_xmit = hp100_start_xmit;
+
+ dev->get_stats = hp100_get_stats;
+ dev->set_multicast_list = &hp100_set_multicast_list;
+
+ /* Ask the card for which IRQ line it is configured */
+ hp100_page(HW_MAP);
+ dev->irq = hp100_inb(IRQ_CHANNEL) & HP100_IRQMASK;
+ if (dev->irq == 2)
+ dev->irq = 9;
+
+ if (lp->mode == 1) /* busmaster */
+ dev->dma = 4;
+
+ /* Ask the card for its MAC address and store it for later use. */
+ hp100_page(ID_MAC_ADDR);
+ for (i = uc = 0; i < 6; i++)
+ dev->dev_addr[i] = hp100_inb(LAN_ADDR + i);
+
+ /* Reset statistics (counters) */
+ hp100_clear_stats(ioaddr);
+
+ ether_setup(dev);
+
+ /* If busmaster mode is wanted, a dma-capable memory area is needed for
+ * the rx and tx PDLs
+ * PCI cards can access the whole PC memory. Therefore GFP_DMA is not
+ * needed for the allocation of the memory area.
+ */
+
+ /* TODO: We do not need this with old cards, where PDLs are stored
+ * in the cards shared memory area. But currently, busmaster has been
+ * implemented/tested only with the lassen chip anyway... */
+ if (lp->mode == 1) { /* busmaster */
+ /* Get physically continous memory for TX & RX PDLs */
+ if ((lp->page_vaddr = kmalloc(MAX_RINGSIZE + 0x0f, GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ lp->page_vaddr_algn = ((u_int *) (((u_int) (lp->page_vaddr) + 0x0f) & ~0x0f));
+ memset(lp->page_vaddr, 0, MAX_RINGSIZE + 0x0f);
#ifdef HP100_DEBUG_BM
- printk("hp100: Reserved DMA memory from 0x%x to 0x%x\n",
- (u_int)lp->page_vaddr_algn,
- (u_int)lp->page_vaddr_algn+MAX_RINGSIZE);
-#endif
- lp->rxrcommit = lp->txrcommit = 0;
- lp->rxrhead = lp->rxrtail = &(lp->rxring[0]);
- lp->txrhead = lp->txrtail = &(lp->txring[0]);
- }
-
- /* Initialise the card. */
- /* (I'm not really sure if it's a good idea to do this during probing, but
- * like this it's assured that the lan connection type can be sensed
- * correctly)
- */
- hp100_hwinit( dev );
-
- /* Try to find out which kind of LAN the card is connected to. */
- lp->lan_type = hp100_sense_lan( dev );
-
- /* Print out a message what about what we think we have probed. */
- printk( "hp100: %s: %s at 0x%x, IRQ %d, ",
- dev->name, lp->id->name, ioaddr, dev->irq );
- switch ( bus ) {
- case HP100_BUS_EISA: printk( "EISA" ); break;
- case HP100_BUS_PCI: printk( "PCI" ); break;
- default: printk( "ISA" ); break;
- }
- printk( " bus, %dk SRAM (rx/tx %d%%).\n",
- lp->memory_size >> 10, lp->rx_ratio );
-
- if ( lp->mode==2 ) /* memory mapped */
- {
- printk( "%s: Memory area at 0x%lx-0x%lx",
- dev->name,(u_long)mem_ptr_phys,(u_long)mem_ptr_phys+(u_long)lp->memory_size );
- if ( mem_ptr_virt )
- printk( " (virtual base 0x%lx)", (u_long)mem_ptr_virt );
- printk( ".\n" );
-
- /* Set for info when doing ifconfig */
- dev->mem_start = (u_long)mem_ptr_phys;
- dev->mem_end = (u_long)mem_ptr_phys+(u_long)lp->memory_size;
- }
- printk( "%s: ", dev->name );
- if ( lp->lan_type != HP100_LAN_ERR )
- printk( "Adapter is attached to " );
- switch ( lp->lan_type ) {
- case HP100_LAN_100:
- printk( "100Mb/s Voice Grade AnyLAN network.\n" );
- break;
- case HP100_LAN_10:
- printk( "10Mb/s network.\n" );
- break;
- default:
- printk( "Warning! Link down.\n" );
- }
- return 0;
+ printk("hp100: Reserved DMA memory from 0x%x to 0x%x\n",
+ (u_int) lp->page_vaddr_algn,
+ (u_int) lp->page_vaddr_algn + MAX_RINGSIZE);
+#endif
+ lp->rxrcommit = lp->txrcommit = 0;
+ lp->rxrhead = lp->rxrtail = &(lp->rxring[0]);
+ lp->txrhead = lp->txrtail = &(lp->txring[0]);
+ }
+ /* Initialise the card. */
+ /* (I'm not really sure if it's a good idea to do this during probing, but
+ * like this it's assured that the lan connection type can be sensed
+ * correctly)
+ */
+ hp100_hwinit(dev);
+
+ /* Try to find out which kind of LAN the card is connected to. */
+ lp->lan_type = hp100_sense_lan(dev);
+
+ /* Print out a message what about what we think we have probed. */
+ printk("hp100: %s: %s at 0x%x, IRQ %d, ",
+ dev->name, lp->id->name, ioaddr, dev->irq);
+ switch (bus) {
+ case HP100_BUS_EISA:
+ printk("EISA");
+ break;
+ case HP100_BUS_PCI:
+ printk("PCI");
+ break;
+ default:
+ printk("ISA");
+ break;
+ }
+ printk(" bus, %dk SRAM (rx/tx %d%%).\n",
+ lp->memory_size >> 10, lp->rx_ratio);
+
+ if (lp->mode == 2) { /* memory mapped */
+ printk("%s: Memory area at 0x%lx-0x%lx",
+ dev->name, (u_long) mem_ptr_phys, (u_long) mem_ptr_phys + (u_long) lp->memory_size);
+ if (mem_ptr_virt)
+ printk(" (virtual base 0x%lx)", (u_long) mem_ptr_virt);
+ printk(".\n");
+
+ /* Set for info when doing ifconfig */
+ dev->mem_start = (u_long) mem_ptr_phys;
+ dev->mem_end = (u_long) mem_ptr_phys + (u_long) lp->memory_size;
+ }
+ printk("%s: ", dev->name);
+ if (lp->lan_type != HP100_LAN_ERR)
+ printk("Adapter is attached to ");
+ switch (lp->lan_type) {
+ case HP100_LAN_100:
+ printk("100Mb/s Voice Grade AnyLAN network.\n");
+ break;
+ case HP100_LAN_10:
+ printk("10Mb/s network.\n");
+ break;
+ default:
+ printk("Warning! Link down.\n");
+ }
+ return 0;
}
-
+
/* This procedure puts the card into a stable init state */
-static void hp100_hwinit( struct device *dev )
+static void hp100_hwinit(struct device *dev)
{
- int ioaddr = dev->base_addr;
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4202, TRACE );
- printk("hp100: hwinit\n");
-#endif
-
- /* Initialise the card. -------------------------------------------- */
-
- /* Clear all pending Ints and disable Ints */
- hp100_page( PERFORMANCE );
- hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */
- hp100_outw( 0xffff, IRQ_STATUS ); /* clear all pending ints */
-
- hp100_outw( HP100_INT_EN | HP100_RESET_LB, OPTION_LSW );
- hp100_outw( HP100_TRI_INT | HP100_SET_HB, OPTION_LSW );
-
- if(lp->mode==1)
- {
- hp100_BM_shutdown( dev ); /* disables BM, puts cascade in reset */
- wait();
- }
- else
- {
- hp100_outw( HP100_INT_EN | HP100_RESET_LB, OPTION_LSW );
- hp100_cascade_reset( dev, TRUE );
- hp100_page( MAC_CTRL );
- hp100_andb( ~(HP100_RX_EN|HP100_TX_EN), MAC_CFG_1);
- }
-
- /* Initiate EEPROM reload */
- hp100_load_eeprom( dev );
-
- wait();
-
- /* Go into reset again. */
- hp100_cascade_reset( dev, TRUE );
-
- /* Set Option Registers to a safe state */
- hp100_outw( HP100_DEBUG_EN |
- HP100_RX_HDR |
- HP100_EE_EN |
- HP100_BM_WRITE |
- HP100_BM_READ | HP100_RESET_HB |
- HP100_FAKE_INT |
- HP100_INT_EN |
- HP100_MEM_EN |
- HP100_IO_EN | HP100_RESET_LB, OPTION_LSW);
-
- hp100_outw( HP100_TRI_INT |
- HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW );
-
- hp100_outb( HP100_PRIORITY_TX |
- HP100_ADV_NXT_PKT |
- HP100_TX_CMD | HP100_RESET_LB, OPTION_MSW );
-
- /* TODO: Configure MMU for Ram Test. */
- /* TODO: Ram Test. */
-
- /* Re-check if adapter is still at same i/o location */
- /* (If the base i/o in eeprom has been changed but the */
- /* registers had not been changed, a reload of the eeprom */
- /* would move the adapter to the address stored in eeprom */
-
- /* TODO: Code to implement. */
-
- /* Until here it was code from HWdiscover procedure. */
- /* Next comes code from mmuinit procedure of SCO BM driver which is
- * called from HWconfigure in the SCO driver. */
-
- /* Initialise MMU, eventually switch on Busmaster Mode, initialise
- * multicast filter...
- */
- hp100_mmuinit( dev );
-
- /* We don't turn the interrupts on here - this is done by start_interface. */
- wait(); /* TODO: Do we really need this? */
-
- /* Enable Hardware (e.g. unreset) */
- hp100_cascade_reset( dev, FALSE );
-
- /* ------- initialisation complete ----------- */
-
- /* Finally try to log in the Hub if there may be a VG connection. */
- if( lp->lan_type != HP100_LAN_10 )
- hp100_login_to_vg_hub( dev, FALSE ); /* relogin */
-}
+ hp100_outw(0x4202, TRACE);
+ printk("hp100: hwinit\n");
+#endif
+
+ /* Initialise the card. -------------------------------------------- */
+
+ /* Clear all pending Ints and disable Ints */
+ hp100_page(PERFORMANCE);
+ hp100_outw(0xfefe, IRQ_MASK); /* mask off all ints */
+ hp100_outw(0xffff, IRQ_STATUS); /* clear all pending ints */
+
+ hp100_outw(HP100_INT_EN | HP100_RESET_LB, OPTION_LSW);
+ hp100_outw(HP100_TRI_INT | HP100_SET_HB, OPTION_LSW);
+
+ if (lp->mode == 1) {
+ hp100_BM_shutdown(dev); /* disables BM, puts cascade in reset */
+ wait();
+ } else {
+ hp100_outw(HP100_INT_EN | HP100_RESET_LB, OPTION_LSW);
+ hp100_cascade_reset(dev, TRUE);
+ hp100_page(MAC_CTRL);
+ hp100_andb(~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1);
+ }
+ /* Initiate EEPROM reload */
+ hp100_load_eeprom(dev);
+
+ wait();
+
+ /* Go into reset again. */
+ hp100_cascade_reset(dev, TRUE);
+
+ /* Set Option Registers to a safe state */
+ hp100_outw(HP100_DEBUG_EN |
+ HP100_RX_HDR |
+ HP100_EE_EN |
+ HP100_BM_WRITE |
+ HP100_BM_READ | HP100_RESET_HB |
+ HP100_FAKE_INT |
+ HP100_INT_EN |
+ HP100_MEM_EN |
+ HP100_IO_EN | HP100_RESET_LB, OPTION_LSW);
+
+ hp100_outw(HP100_TRI_INT |
+ HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW);
+
+ hp100_outb(HP100_PRIORITY_TX |
+ HP100_ADV_NXT_PKT |
+ HP100_TX_CMD | HP100_RESET_LB, OPTION_MSW);
+
+ /* TODO: Configure MMU for Ram Test. */
+ /* TODO: Ram Test. */
+
+ /* Re-check if adapter is still at same i/o location */
+ /* (If the base i/o in eeprom has been changed but the */
+ /* registers had not been changed, a reload of the eeprom */
+ /* would move the adapter to the address stored in eeprom */
+
+ /* TODO: Code to implement. */
+
+ /* Until here it was code from HWdiscover procedure. */
+ /* Next comes code from mmuinit procedure of SCO BM driver which is
+ * called from HWconfigure in the SCO driver. */
+
+ /* Initialise MMU, eventually switch on Busmaster Mode, initialise
+ * multicast filter...
+ */
+ hp100_mmuinit(dev);
+
+ /* We don't turn the interrupts on here - this is done by start_interface. */
+ wait(); /* TODO: Do we really need this? */
+
+ /* Enable Hardware (e.g. unreset) */
+ hp100_cascade_reset(dev, FALSE);
+
+ /* ------- initialisation complete ----------- */
+
+ /* Finally try to log in the Hub if there may be a VG connection. */
+ if (lp->lan_type != HP100_LAN_10)
+ hp100_login_to_vg_hub(dev, FALSE); /* relogin */
+}
+
/*
* mmuinit - Reinitialise Cascade MMU and MAC settings.
* Note: Must already be in reset and leaves card in reset.
*/
-static void hp100_mmuinit( struct device *dev )
+static void hp100_mmuinit(struct device *dev)
{
- int ioaddr = dev->base_addr;
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
- int i;
+ int ioaddr = dev->base_addr;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
+ int i;
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4203, TRACE );
- printk("hp100: mmuinit\n");
+ hp100_outw(0x4203, TRACE);
+ printk("hp100: mmuinit\n");
#endif
#ifdef HP100_DEBUG
- if( 0!=(hp100_inw(OPTION_LSW)&HP100_HW_RST) )
- {
- printk("hp100: Not in reset when entering mmuinit. Fix me.\n");
- return;
- }
-#endif
-
- /* Make sure IRQs are masked off and ack'ed. */
- hp100_page( PERFORMANCE );
- hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */
- hp100_outw( 0xffff, IRQ_STATUS ); /* ack IRQ */
-
- /*
- * Enable Hardware
- * - Clear Debug En, Rx Hdr Pipe, EE En, I/O En, Fake Int and Intr En
- * - Set Tri-State Int, Bus Master Rd/Wr, and Mem Map Disable
- * - Clear Priority, Advance Pkt and Xmit Cmd
- */
-
- hp100_outw( HP100_DEBUG_EN |
- HP100_RX_HDR |
- HP100_EE_EN | HP100_RESET_HB |
- HP100_IO_EN |
- HP100_FAKE_INT |
- HP100_INT_EN | HP100_RESET_LB, OPTION_LSW );
-
- hp100_outw( HP100_TRI_INT | HP100_SET_HB, OPTION_LSW);
-
- if(lp->mode==1) /* busmaster */
- {
- hp100_outw( HP100_BM_WRITE |
- HP100_BM_READ |
- HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW );
- }
- else if(lp->mode==2) /* memory mapped */
- {
- hp100_outw( HP100_BM_WRITE |
- HP100_BM_READ | HP100_RESET_HB, OPTION_LSW );
- hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW );
- hp100_outw( HP100_MEM_EN | HP100_SET_LB, OPTION_LSW );
- hp100_outw( HP100_IO_EN | HP100_SET_LB, OPTION_LSW );
- }
- else if( lp->mode==3 ) /* i/o mapped mode */
- {
- hp100_outw( HP100_MMAP_DIS | HP100_SET_HB |
- HP100_IO_EN | HP100_SET_LB, OPTION_LSW );
- }
-
- hp100_page( HW_MAP );
- hp100_outb( 0, EARLYRXCFG );
- hp100_outw( 0, EARLYTXCFG );
-
- /*
- * Enable Bus Master mode
- */
- if(lp->mode==1) /* busmaster */
- {
- /* Experimental: Set some PCI configuration bits */
- hp100_page( HW_MAP );
- hp100_andb( ~HP100_PDL_USE3, MODECTRL1 ); /* BM engine read maximum */
- hp100_andb( ~HP100_TX_DUALQ, MODECTRL1 ); /* No Queue for Priority TX */
-
- /* PCI Bus failures should result in a Misc. Interrupt */
- hp100_orb( HP100_EN_BUS_FAIL, MODECTRL2);
-
- hp100_outw( HP100_BM_READ | HP100_BM_WRITE | HP100_SET_HB, OPTION_LSW );
- hp100_page( HW_MAP );
- /* Use Burst Mode and switch on PAGE_CK */
- hp100_orb( HP100_BM_BURST_RD |
- HP100_BM_BURST_WR, BM);
- if((lp->chip==HP100_CHIPID_RAINIER)||(lp->chip==HP100_CHIPID_SHASTA))
- hp100_orb( HP100_BM_PAGE_CK, BM );
- hp100_orb( HP100_BM_MASTER, BM );
- }
- else /* not busmaster */
- {
- hp100_page(HW_MAP);
- hp100_andb(~HP100_BM_MASTER, BM );
- }
-
- /*
- * Divide card memory into regions for Rx, Tx and, if non-ETR chip, PDLs
- */
- hp100_page( MMU_CFG );
- if(lp->mode==1) /* only needed for Busmaster */
- {
- int xmit_stop, recv_stop;
-
- if((lp->chip==HP100_CHIPID_RAINIER)||(lp->chip==HP100_CHIPID_SHASTA))
- {
- int pdl_stop;
-
- /*
- * Each pdl is 508 bytes long. (63 frags * 4 bytes for address and
- * 4 bytes for for header). We will leave NUM_RXPDLS * 508 (rounded
- * to the next higher 1k boundary) bytes for the rx-pdl's
- * Note: For non-etr chips the transmit stop register must be
- * programmed on a 1k boundary, i.e. bits 9:0 must be zero.
- */
- pdl_stop = lp->memory_size;
- xmit_stop = ( pdl_stop-508*(MAX_RX_PDL)-16 )& ~(0x03ff);
- recv_stop = ( xmit_stop * (lp->rx_ratio)/100 ) &~(0x03ff);
- hp100_outw( (pdl_stop>>4)-1, PDL_MEM_STOP );
+ if (0 != (hp100_inw(OPTION_LSW) & HP100_HW_RST)) {
+ printk("hp100: Not in reset when entering mmuinit. Fix me.\n");
+ return;
+ }
+#endif
+
+ /* Make sure IRQs are masked off and ack'ed. */
+ hp100_page(PERFORMANCE);
+ hp100_outw(0xfefe, IRQ_MASK); /* mask off all ints */
+ hp100_outw(0xffff, IRQ_STATUS); /* ack IRQ */
+
+ /*
+ * Enable Hardware
+ * - Clear Debug En, Rx Hdr Pipe, EE En, I/O En, Fake Int and Intr En
+ * - Set Tri-State Int, Bus Master Rd/Wr, and Mem Map Disable
+ * - Clear Priority, Advance Pkt and Xmit Cmd
+ */
+
+ hp100_outw(HP100_DEBUG_EN |
+ HP100_RX_HDR |
+ HP100_EE_EN | HP100_RESET_HB |
+ HP100_IO_EN |
+ HP100_FAKE_INT |
+ HP100_INT_EN | HP100_RESET_LB, OPTION_LSW);
+
+ hp100_outw(HP100_TRI_INT | HP100_SET_HB, OPTION_LSW);
+
+ if (lp->mode == 1) { /* busmaster */
+ hp100_outw(HP100_BM_WRITE |
+ HP100_BM_READ |
+ HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW);
+ } else if (lp->mode == 2) { /* memory mapped */
+ hp100_outw(HP100_BM_WRITE |
+ HP100_BM_READ | HP100_RESET_HB, OPTION_LSW);
+ hp100_outw(HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW);
+ hp100_outw(HP100_MEM_EN | HP100_SET_LB, OPTION_LSW);
+ hp100_outw(HP100_IO_EN | HP100_SET_LB, OPTION_LSW);
+ } else if (lp->mode == 3) { /* i/o mapped mode */
+ hp100_outw(HP100_MMAP_DIS | HP100_SET_HB |
+ HP100_IO_EN | HP100_SET_LB, OPTION_LSW);
+ }
+ hp100_page(HW_MAP);
+ hp100_outb(0, EARLYRXCFG);
+ hp100_outw(0, EARLYTXCFG);
+
+ /*
+ * Enable Bus Master mode
+ */
+ if (lp->mode == 1) { /* busmaster */
+ /* Experimental: Set some PCI configuration bits */
+ hp100_page(HW_MAP);
+ hp100_andb(~HP100_PDL_USE3, MODECTRL1); /* BM engine read maximum */
+ hp100_andb(~HP100_TX_DUALQ, MODECTRL1); /* No Queue for Priority TX */
+
+ /* PCI Bus failures should result in a Misc. Interrupt */
+ hp100_orb(HP100_EN_BUS_FAIL, MODECTRL2);
+
+ hp100_outw(HP100_BM_READ | HP100_BM_WRITE | HP100_SET_HB, OPTION_LSW);
+ hp100_page(HW_MAP);
+ /* Use Burst Mode and switch on PAGE_CK */
+ hp100_orb(HP100_BM_BURST_RD |
+ HP100_BM_BURST_WR, BM);
+ if ((lp->chip == HP100_CHIPID_RAINIER) || (lp->chip == HP100_CHIPID_SHASTA))
+ hp100_orb(HP100_BM_PAGE_CK, BM);
+ hp100_orb(HP100_BM_MASTER, BM);
+ } else { /* not busmaster */
+ hp100_page(HW_MAP);
+ hp100_andb(~HP100_BM_MASTER, BM);
+ }
+
+ /*
+ * Divide card memory into regions for Rx, Tx and, if non-ETR chip, PDLs
+ */
+ hp100_page(MMU_CFG);
+ if (lp->mode == 1) { /* only needed for Busmaster */
+ int xmit_stop, recv_stop;
+
+ if ((lp->chip == HP100_CHIPID_RAINIER) || (lp->chip == HP100_CHIPID_SHASTA)) {
+ int pdl_stop;
+
+ /*
+ * Each pdl is 508 bytes long. (63 frags * 4 bytes for address and
+ * 4 bytes for for header). We will leave NUM_RXPDLS * 508 (rounded
+ * to the next higher 1k boundary) bytes for the rx-pdl's
+ * Note: For non-etr chips the transmit stop register must be
+ * programmed on a 1k boundary, i.e. bits 9:0 must be zero.
+ */
+ pdl_stop = lp->memory_size;
+ xmit_stop = (pdl_stop - 508 * (MAX_RX_PDL) - 16) & ~(0x03ff);
+ recv_stop = (xmit_stop * (lp->rx_ratio) / 100) & ~(0x03ff);
+ hp100_outw((pdl_stop >> 4) - 1, PDL_MEM_STOP);
#ifdef HP100_DEBUG_BM
- printk("hp100: PDL_STOP = 0x%x\n", pdl_stop);
+ printk("hp100: PDL_STOP = 0x%x\n", pdl_stop);
#endif
- }
- else /* ETR chip (Lassen) in busmaster mode */
- {
- xmit_stop = ( lp->memory_size ) - 1;
- recv_stop = ( ( lp->memory_size * lp->rx_ratio ) / 100 ) & ~(0x03ff);
- }
+ } else { /* ETR chip (Lassen) in busmaster mode */
+ xmit_stop = (lp->memory_size) - 1;
+ recv_stop = ((lp->memory_size * lp->rx_ratio) / 100) & ~(0x03ff);
+ }
- hp100_outw( xmit_stop>>4 , TX_MEM_STOP );
- hp100_outw( recv_stop>>4 , RX_MEM_STOP );
+ hp100_outw(xmit_stop >> 4, TX_MEM_STOP);
+ hp100_outw(recv_stop >> 4, RX_MEM_STOP);
#ifdef HP100_DEBUG_BM
- printk("hp100: TX_STOP = 0x%x\n",xmit_stop>>4);
- printk("hp100: RX_STOP = 0x%x\n",recv_stop>>4);
-#endif
- }
- else /* Slave modes (memory mapped and programmed io) */
- {
- hp100_outw( (((lp->memory_size*lp->rx_ratio)/100)>>4), RX_MEM_STOP );
- hp100_outw( ((lp->memory_size - 1 )>>4), TX_MEM_STOP );
+ printk("hp100: TX_STOP = 0x%x\n", xmit_stop >> 4);
+ printk("hp100: RX_STOP = 0x%x\n", recv_stop >> 4);
+#endif
+ } else { /* Slave modes (memory mapped and programmed io) */
+ hp100_outw((((lp->memory_size * lp->rx_ratio) / 100) >> 4), RX_MEM_STOP);
+ hp100_outw(((lp->memory_size - 1) >> 4), TX_MEM_STOP);
#ifdef HP100_DEBUG
- printk("hp100: TX_MEM_STOP: 0x%x\n", hp100_inw(TX_MEM_STOP));
- printk("hp100: RX_MEM_STOP: 0x%x\n", hp100_inw(RX_MEM_STOP));
-#endif
- }
-
- /* Write MAC address into page 1 */
- hp100_page( MAC_ADDRESS );
- for ( i = 0; i < 6; i++ )
- hp100_outb( dev->dev_addr[ i ], MAC_ADDR + i );
-
- /* Zero the multicast hash registers */
- for ( i = 0; i < 8; i++ )
- hp100_outb( 0x0, HASH_BYTE0 + i );
-
- /* Set up MAC defaults */
- hp100_page( MAC_CTRL );
-
- /* Go to LAN Page and zero all filter bits */
- /* Zero accept error, accept multicast, accept broadcast and accept */
- /* all directed packet bits */
- hp100_andb( ~(HP100_RX_EN|
- HP100_TX_EN|
- HP100_ACC_ERRORED|
- HP100_ACC_MC|
- HP100_ACC_BC|
- HP100_ACC_PHY), MAC_CFG_1 );
-
- hp100_outb( 0x00, MAC_CFG_2 );
-
- /* Zero the frame format bit. This works around a training bug in the */
- /* new hubs. */
- hp100_outb( 0x00, VG_LAN_CFG_2); /* (use 802.3) */
-
- if(lp->priority_tx)
- hp100_outb( HP100_PRIORITY_TX | HP100_SET_LB, OPTION_MSW );
- else
- hp100_outb( HP100_PRIORITY_TX | HP100_RESET_LB, OPTION_MSW );
-
- hp100_outb( HP100_ADV_NXT_PKT |
- HP100_TX_CMD | HP100_RESET_LB, OPTION_MSW );
-
- /* If busmaster, initialize the PDLs */
- if(lp->mode==1)
- hp100_init_pdls( dev );
-
- /* Go to performance page and initalize isr and imr registers */
- hp100_page( PERFORMANCE );
- hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */
- hp100_outw( 0xffff, IRQ_STATUS ); /* ack IRQ */
-}
+ printk("hp100: TX_MEM_STOP: 0x%x\n", hp100_inw(TX_MEM_STOP));
+ printk("hp100: RX_MEM_STOP: 0x%x\n", hp100_inw(RX_MEM_STOP));
+#endif
+ }
+ /* Write MAC address into page 1 */
+ hp100_page(MAC_ADDRESS);
+ for (i = 0; i < 6; i++)
+ hp100_outb(dev->dev_addr[i], MAC_ADDR + i);
+
+ /* Zero the multicast hash registers */
+ for (i = 0; i < 8; i++)
+ hp100_outb(0x0, HASH_BYTE0 + i);
+
+ /* Set up MAC defaults */
+ hp100_page(MAC_CTRL);
+
+ /* Go to LAN Page and zero all filter bits */
+ /* Zero accept error, accept multicast, accept broadcast and accept */
+ /* all directed packet bits */
+ hp100_andb(~(HP100_RX_EN |
+ HP100_TX_EN |
+ HP100_ACC_ERRORED |
+ HP100_ACC_MC |
+ HP100_ACC_BC |
+ HP100_ACC_PHY), MAC_CFG_1);
+
+ hp100_outb(0x00, MAC_CFG_2);
+
+ /* Zero the frame format bit. This works around a training bug in the */
+ /* new hubs. */
+ hp100_outb(0x00, VG_LAN_CFG_2); /* (use 802.3) */
+
+ if (lp->priority_tx)
+ hp100_outb(HP100_PRIORITY_TX | HP100_SET_LB, OPTION_MSW);
+ else
+ hp100_outb(HP100_PRIORITY_TX | HP100_RESET_LB, OPTION_MSW);
+
+ hp100_outb(HP100_ADV_NXT_PKT |
+ HP100_TX_CMD | HP100_RESET_LB, OPTION_MSW);
+
+ /* If busmaster, initialize the PDLs */
+ if (lp->mode == 1)
+ hp100_init_pdls(dev);
+
+ /* Go to performance page and initalize isr and imr registers */
+ hp100_page(PERFORMANCE);
+ hp100_outw(0xfefe, IRQ_MASK); /* mask off all ints */
+ hp100_outw(0xffff, IRQ_STATUS); /* ack IRQ */
+}
+
/*
* open/close functions
*/
-static int hp100_open( struct device *dev )
+static int hp100_open(struct device *dev)
{
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
-#ifdef HP100_DEBUG_B
- int ioaddr=dev->base_addr;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
+#ifdef HP100_DEBUG_B
+ int ioaddr = dev->base_addr;
#endif
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4204, TRACE );
- printk("hp100: open\n");
-#endif
-
- /* New: if bus is PCI or EISA, interrupts might be shared interrupts */
- if((lp->bus==HP100_BUS_PCI)||(lp->bus==HP100_BUS_EISA))
- {
- if(request_irq(dev->irq,hp100_interrupt,SA_SHIRQ,lp->id->name,dev))
- {
- printk( "%s: unable to get IRQ %d\n", dev->name, dev->irq );
- return -EAGAIN;
+ hp100_outw(0x4204, TRACE);
+ printk("hp100: open\n");
+#endif
+
+ /* New: if bus is PCI or EISA, interrupts might be shared interrupts */
+ if ((lp->bus == HP100_BUS_PCI) || (lp->bus == HP100_BUS_EISA)) {
+ if (request_irq(dev->irq, hp100_interrupt, SA_SHIRQ, lp->id->name, dev)) {
+ printk("%s: unable to get IRQ %d\n", dev->name, dev->irq);
+ return -EAGAIN;
+ }
+ } else if (request_irq(dev->irq, hp100_interrupt, SA_INTERRUPT, lp->id->name, dev)) {
+ printk("%s: unable to get IRQ %d\n", dev->name, dev->irq);
+ return -EAGAIN;
}
- }
- else
- if(request_irq(dev->irq, hp100_interrupt, SA_INTERRUPT, lp->id->name, NULL))
- {
- printk( "%s: unable to get IRQ %d\n", dev->name, dev->irq );
- return -EAGAIN;
- }
-
- irq2dev_map[ dev->irq ] = dev;
-
- MOD_INC_USE_COUNT;
-
- dev->tbusy = 0;
- dev->trans_start = jiffies;
- dev->interrupt = 0;
- dev->start = 1;
-
- lp->lan_type = hp100_sense_lan( dev );
- lp->mac1_mode = HP100_MAC1MODE3;
- lp->mac2_mode = HP100_MAC2MODE3;
-
- hp100_stop_interface( dev );
-
- hp100_hwinit( dev );
-
- hp100_start_interface( dev ); /* sets mac modes, enables interrupts */
-
- return 0;
-}
+ MOD_INC_USE_COUNT;
+ dev->tbusy = 0;
+ dev->trans_start = jiffies;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ lp->lan_type = hp100_sense_lan(dev);
+ lp->mac1_mode = HP100_MAC1MODE3;
+ lp->mac2_mode = HP100_MAC2MODE3;
+
+ hp100_stop_interface(dev);
+
+ hp100_hwinit(dev);
+
+ hp100_start_interface(dev); /* sets mac modes, enables interrupts */
+
+ return 0;
+}
+
/* The close function is called when the interface is to be brought down */
-static int hp100_close( struct device *dev )
+static int hp100_close(struct device *dev)
{
- int ioaddr = dev->base_addr;
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4205, TRACE );
- printk("hp100:close\n");
+ hp100_outw(0x4205, TRACE);
+ printk("hp100:close\n");
#endif
- hp100_page( PERFORMANCE );
- hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all IRQs */
+ hp100_page(PERFORMANCE);
+ hp100_outw(0xfefe, IRQ_MASK); /* mask off all IRQs */
- hp100_stop_interface( dev );
+ hp100_stop_interface(dev);
- if ( lp->lan_type == HP100_LAN_100 )
- lp->hub_status=hp100_login_to_vg_hub( dev, FALSE );
+ if (lp->lan_type == HP100_LAN_100)
+ lp->hub_status = hp100_login_to_vg_hub(dev, FALSE);
- dev->tbusy = 1;
- dev->start = 0;
+ dev->tbusy = 1;
+ dev->start = 0;
- if ((lp->bus==HP100_BUS_PCI)||(lp->bus==HP100_BUS_EISA))
- free_irq( dev->irq, dev );
- else
- free_irq( dev->irq, NULL );
- irq2dev_map[ dev->irq ] = NULL;
- MOD_DEC_USE_COUNT;
- return 0;
+ if ((lp->bus == HP100_BUS_PCI) || (lp->bus == HP100_BUS_EISA))
+ free_irq(dev->irq, dev);
+ else
+ free_irq(dev->irq, NULL);
+ MOD_DEC_USE_COUNT;
+ return 0;
}
-
+
/*
* Configure the PDL Rx rings and LAN
*/
-static void hp100_init_pdls( struct device *dev )
+static void hp100_init_pdls(struct device *dev)
{
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
- hp100_ring_t *ringptr;
- u_int *pageptr;
- int i;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
+ hp100_ring_t *ringptr;
+ u_int *pageptr;
+ int i;
#ifdef HP100_DEBUG_B
- int ioaddr = dev->base_addr;
+ int ioaddr = dev->base_addr;
#endif
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4206, TRACE );
- printk("hp100: init pdls\n");
-#endif
-
- if(0==lp->page_vaddr_algn)
- printk("hp100: Warning: lp->page_vaddr_algn not initialised!\n");
- else
- {
- /* pageptr shall point into the DMA accessible memory region */
- /* we use this pointer to status the upper limit of allocated */
- /* memory in the allocated page. */
- /* note: align the pointers to the pci cache line size */
- memset(lp->page_vaddr_algn, 0, MAX_RINGSIZE); /* Zero Rx/Tx ring page */
- pageptr=lp->page_vaddr_algn;
-
- lp->rxrcommit =0;
- ringptr = lp->rxrhead = lp-> rxrtail = &(lp->rxring[0]);
-
- /* Initialise Rx Ring */
- for (i=MAX_RX_PDL-1; i>=0; i--)
- {
- lp->rxring[i].next = ringptr;
- ringptr=&(lp->rxring[i]);
- pageptr+=hp100_init_rxpdl(ringptr, pageptr);
- }
-
- /* Initialise Tx Ring */
- lp->txrcommit = 0;
- ringptr = lp->txrhead = lp->txrtail = &(lp->txring[0]);
- for (i=MAX_TX_PDL-1; i>=0; i--)
- {
- lp->txring[i].next = ringptr;
- ringptr=&(lp->txring[i]);
- pageptr+=hp100_init_txpdl(ringptr, pageptr);
- }
- }
-}
+ hp100_outw(0x4206, TRACE);
+ printk("hp100: init pdls\n");
+#endif
+
+ if (0 == lp->page_vaddr_algn)
+ printk("hp100: Warning: lp->page_vaddr_algn not initialised!\n");
+ else {
+ /* pageptr shall point into the DMA accessible memory region */
+ /* we use this pointer to status the upper limit of allocated */
+ /* memory in the allocated page. */
+ /* note: align the pointers to the pci cache line size */
+ memset(lp->page_vaddr_algn, 0, MAX_RINGSIZE); /* Zero Rx/Tx ring page */
+ pageptr = lp->page_vaddr_algn;
+
+ lp->rxrcommit = 0;
+ ringptr = lp->rxrhead = lp->rxrtail = &(lp->rxring[0]);
+
+ /* Initialise Rx Ring */
+ for (i = MAX_RX_PDL - 1; i >= 0; i--) {
+ lp->rxring[i].next = ringptr;
+ ringptr = &(lp->rxring[i]);
+ pageptr += hp100_init_rxpdl(ringptr, pageptr);
+ }
+ /* Initialise Tx Ring */
+ lp->txrcommit = 0;
+ ringptr = lp->txrhead = lp->txrtail = &(lp->txring[0]);
+ for (i = MAX_TX_PDL - 1; i >= 0; i--) {
+ lp->txring[i].next = ringptr;
+ ringptr = &(lp->txring[i]);
+ pageptr += hp100_init_txpdl(ringptr, pageptr);
+ }
+ }
+}
+
/* These functions "format" the entries in the pdl structure */
/* They return how much memory the fragments need. */
-static int hp100_init_rxpdl( register hp100_ring_t *ringptr, register u32 *pdlptr )
+static int hp100_init_rxpdl(register hp100_ring_t * ringptr, register u32 * pdlptr)
{
- /* pdlptr is starting adress for this pdl */
+ /* pdlptr is starting adress for this pdl */
- if( 0!=( ((unsigned)pdlptr) & 0xf) )
- printk("hp100: Init rxpdl: Unaligned pdlptr 0x%x.\n",(unsigned)pdlptr);
+ if (0 != (((unsigned) pdlptr) & 0xf))
+ printk("hp100: Init rxpdl: Unaligned pdlptr 0x%x.\n", (unsigned) pdlptr);
- ringptr->pdl = pdlptr+1;
- ringptr->pdl_paddr = virt_to_bus(pdlptr+1);
- ringptr->skb = (void *) NULL;
+ ringptr->pdl = pdlptr + 1;
+ ringptr->pdl_paddr = virt_to_bus(pdlptr + 1);
+ ringptr->skb = (void *) NULL;
- /*
- * Write address and length of first PDL Fragment (which is used for
- * storing the RX-Header
- * We use the 4 bytes _before_ the PDH in the pdl memory area to
- * store this information. (PDH is at offset 0x04)
- */
- /* Note that pdlptr+1 and not pdlptr is the pointer to the PDH */
+ /*
+ * Write address and length of first PDL Fragment (which is used for
+ * storing the RX-Header
+ * We use the 4 bytes _before_ the PDH in the pdl memory area to
+ * store this information. (PDH is at offset 0x04)
+ */
+ /* Note that pdlptr+1 and not pdlptr is the pointer to the PDH */
- *(pdlptr+2) =(u_int) virt_to_bus(pdlptr); /* Address Frag 1 */
- *(pdlptr+3) = 4; /* Length Frag 1 */
+ *(pdlptr + 2) = (u_int) virt_to_bus(pdlptr); /* Address Frag 1 */
+ *(pdlptr + 3) = 4; /* Length Frag 1 */
- return( ( ((MAX_RX_FRAG*2+2)+3) /4)*4 );
+ return ((((MAX_RX_FRAG * 2 + 2) + 3) / 4) * 4);
}
-static int hp100_init_txpdl( register hp100_ring_t *ringptr, register u32 *pdlptr )
+static int hp100_init_txpdl(register hp100_ring_t * ringptr, register u32 * pdlptr)
{
- if( 0!=( ((unsigned)pdlptr) & 0xf) )
- printk("hp100: Init txpdl: Unaligned pdlptr 0x%x.\n",(unsigned) pdlptr);
-
- ringptr->pdl = pdlptr; /* +1; */
- ringptr->pdl_paddr = virt_to_bus(pdlptr); /* +1 */
- ringptr->skb = (void *) NULL;
-
- return((((MAX_TX_FRAG*2+2)+3)/4)*4);
-}
+ if (0 != (((unsigned) pdlptr) & 0xf))
+ printk("hp100: Init txpdl: Unaligned pdlptr 0x%x.\n", (unsigned) pdlptr);
+
+ ringptr->pdl = pdlptr; /* +1; */
+ ringptr->pdl_paddr = virt_to_bus(pdlptr); /* +1 */
+ ringptr->skb = (void *) NULL;
+ return ((((MAX_TX_FRAG * 2 + 2) + 3) / 4) * 4);
+}
+
/*
* hp100_build_rx_pdl allocates an skb_buff of maximum size plus two bytes
* for possible odd word alignment rounding up to next dword and set PDL
@@ -1185,78 +1119,77 @@ static int hp100_init_txpdl( register hp100_ring_t *ringptr, register u32 *pdlpt
* Returns: 0 if unable to allocate skb_buff
* 1 if successful
*/
-int hp100_build_rx_pdl( hp100_ring_t *ringptr, struct device *dev )
+int hp100_build_rx_pdl(hp100_ring_t * ringptr, struct device *dev)
{
#ifdef HP100_DEBUG_B
- int ioaddr = dev->base_addr;
+ int ioaddr = dev->base_addr;
#endif
#ifdef HP100_DEBUG_BM
- u_int *p;
+ u_int *p;
#endif
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4207, TRACE );
- printk("hp100: build rx pdl\n");
-#endif
-
- /* Allocate skb buffer of maximum size */
- /* Note: This depends on the alloc_skb functions allocating more
- * space than requested, i.e. aligning to 16bytes */
-
- ringptr->skb = dev_alloc_skb( ((MAX_ETHER_SIZE+2+3)/4)*4 );
-
- if(NULL!=ringptr->skb)
- {
- /*
- * Reserve 2 bytes at the head of the buffer to land the IP header
- * on a long word boundary (According to the Network Driver section
- * in the Linux KHG, this should help to increase performance.)
- */
- skb_reserve(ringptr->skb, 2);
-
- ringptr->skb->dev=dev;
- ringptr->skb->data=(u_char *)skb_put(ringptr->skb, MAX_ETHER_SIZE );
-
- /* ringptr->pdl points to the beginning of the PDL, i.e. the PDH */
- /* Note: 1st Fragment is used for the 4 byte packet status
- * (receive header). Its PDL entries are set up by init_rxpdl. So
- * here we only have to set up the PDL fragment entries for the data
- * part. Those 4 bytes will be stored in the DMA memory region
- * directly before the PDL.
- */
+ hp100_outw(0x4207, TRACE);
+ printk("hp100: build rx pdl\n");
+#endif
+
+ /* Allocate skb buffer of maximum size */
+ /* Note: This depends on the alloc_skb functions allocating more
+ * space than requested, i.e. aligning to 16bytes */
+
+ ringptr->skb = dev_alloc_skb(((MAX_ETHER_SIZE + 2 + 3) / 4) * 4);
+
+ if (NULL != ringptr->skb) {
+ /*
+ * Reserve 2 bytes at the head of the buffer to land the IP header
+ * on a long word boundary (According to the Network Driver section
+ * in the Linux KHG, this should help to increase performance.)
+ */
+ skb_reserve(ringptr->skb, 2);
+
+ ringptr->skb->dev = dev;
+ ringptr->skb->data = (u_char *) skb_put(ringptr->skb, MAX_ETHER_SIZE);
+
+ /* ringptr->pdl points to the beginning of the PDL, i.e. the PDH */
+ /* Note: 1st Fragment is used for the 4 byte packet status
+ * (receive header). Its PDL entries are set up by init_rxpdl. So
+ * here we only have to set up the PDL fragment entries for the data
+ * part. Those 4 bytes will be stored in the DMA memory region
+ * directly before the PDL.
+ */
#ifdef HP100_DEBUG_BM
- printk("hp100: build_rx_pdl: PDH@0x%x, skb->data (len %d) at 0x%x\n",
- (u_int) ringptr->pdl,
- ((MAX_ETHER_SIZE+2+3)/4)*4,
- (unsigned int) ringptr->skb->data);
+ printk("hp100: build_rx_pdl: PDH@0x%x, skb->data (len %d) at 0x%x\n",
+ (u_int) ringptr->pdl,
+ ((MAX_ETHER_SIZE + 2 + 3) / 4) * 4,
+ (unsigned int) ringptr->skb->data);
#endif
- ringptr->pdl[0] = 0x00020000; /* Write PDH */
- ringptr->pdl[3] = ((u_int)virt_to_bus(ringptr->skb->data));
- ringptr->pdl[4] = MAX_ETHER_SIZE; /* Length of Data */
-
+ ringptr->pdl[0] = 0x00020000; /* Write PDH */
+ ringptr->pdl[3] = ((u_int) virt_to_bus(ringptr->skb->data));
+ ringptr->pdl[4] = MAX_ETHER_SIZE; /* Length of Data */
+
#ifdef HP100_DEBUG_BM
- for(p=(ringptr->pdl); p<(ringptr->pdl+5); p++)
- printk("Adr 0x%.8x = 0x%.8x\n",(u_int) p,(u_int) *p );
-#endif
- return(1);
- }
- /* else: */
- /* alloc_skb failed (no memory) -> still can receive the header
- * fragment into PDL memory. make PDL safe by clearing msgptr and
- * making the PDL only 1 fragment (i.e. the 4 byte packet status)
- */
+ for (p = (ringptr->pdl); p < (ringptr->pdl + 5); p++)
+ printk("Adr 0x%.8x = 0x%.8x\n", (u_int) p, (u_int) * p);
+#endif
+ return (1);
+ }
+ /* else: */
+ /* alloc_skb failed (no memory) -> still can receive the header
+ * fragment into PDL memory. make PDL safe by clearing msgptr and
+ * making the PDL only 1 fragment (i.e. the 4 byte packet status)
+ */
#ifdef HP100_DEBUG_BM
- printk("hp100: build_rx_pdl: PDH@0x%x, No space for skb.\n",
- (u_int) ringptr->pdl);
+ printk("hp100: build_rx_pdl: PDH@0x%x, No space for skb.\n",
+ (u_int) ringptr->pdl);
#endif
- ringptr->pdl[0]=0x00010000; /* PDH: Count=1 Fragment */
+ ringptr->pdl[0] = 0x00010000; /* PDH: Count=1 Fragment */
- return(0);
+ return (0);
}
-
+
/*
* hp100_rxfill - attempt to fill the Rx Ring will empty skb's
*
@@ -1267,273 +1200,246 @@ int hp100_build_rx_pdl( hp100_ring_t *ringptr, struct device *dev )
* b. Put the physical address of the buffer into the PDL.
* c. Output physical address of PDL to adapter.
*/
-static void hp100_rxfill( struct device *dev )
+static void hp100_rxfill(struct device *dev)
{
- int ioaddr=dev->base_addr;
+ int ioaddr = dev->base_addr;
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
- hp100_ring_t *ringptr;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
+ hp100_ring_t *ringptr;
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4208, TRACE );
- printk("hp100: rxfill\n");
-#endif
-
- hp100_page( PERFORMANCE );
-
- while (lp->rxrcommit < MAX_RX_PDL)
- {
- /*
- ** Attempt to get a buffer and build a Rx PDL.
- */
- ringptr = lp->rxrtail;
- if (0 == hp100_build_rx_pdl( ringptr, dev ))
- {
- return; /* None available, return */
- }
-
- /* Hand this PDL over to the card */
- /* Note: This needs performance page selected! */
+ hp100_outw(0x4208, TRACE);
+ printk("hp100: rxfill\n");
+#endif
+
+ hp100_page(PERFORMANCE);
+
+ while (lp->rxrcommit < MAX_RX_PDL) {
+ /*
+ ** Attempt to get a buffer and build a Rx PDL.
+ */
+ ringptr = lp->rxrtail;
+ if (0 == hp100_build_rx_pdl(ringptr, dev)) {
+ return; /* None available, return */
+ }
+ /* Hand this PDL over to the card */
+ /* Note: This needs performance page selected! */
#ifdef HP100_DEBUG_BM
- printk("hp100: rxfill: Hand to card: pdl #%d @0x%x phys:0x%x, buffer: 0x%x\n",
- lp->rxrcommit,
- (u_int)ringptr->pdl,
- (u_int)ringptr->pdl_paddr,
- (u_int)ringptr->pdl[3]);
-#endif
-
- hp100_outl( (u32)ringptr->pdl_paddr, RX_PDA);
-
- lp->rxrcommit += 1;
- lp->rxrtail = ringptr->next;
- }
-}
+ printk("hp100: rxfill: Hand to card: pdl #%d @0x%x phys:0x%x, buffer: 0x%x\n",
+ lp->rxrcommit,
+ (u_int) ringptr->pdl,
+ (u_int) ringptr->pdl_paddr,
+ (u_int) ringptr->pdl[3]);
+#endif
+ hp100_outl((u32) ringptr->pdl_paddr, RX_PDA);
+
+ lp->rxrcommit += 1;
+ lp->rxrtail = ringptr->next;
+ }
+}
+
/*
* BM_shutdown - shutdown bus mastering and leave chip in reset state
*/
-static void hp100_BM_shutdown( struct device *dev )
+static void hp100_BM_shutdown(struct device *dev)
{
- int ioaddr = dev->base_addr;
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
- unsigned long time;
+ int ioaddr = dev->base_addr;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
+ unsigned long time;
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4209, TRACE );
- printk("hp100: bm shutdown\n");
-#endif
-
- hp100_page( PERFORMANCE );
- hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */
- hp100_outw( 0xffff, IRQ_STATUS ); /* Ack all ints */
-
- /* Ensure Interrupts are off */
- hp100_outw( HP100_INT_EN | HP100_RESET_LB , OPTION_LSW );
-
- /* Disable all MAC activity */
- hp100_page( MAC_CTRL );
- hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); /* stop rx/tx */
-
- /* If cascade MMU is not already in reset */
- if (0 != (hp100_inw(OPTION_LSW)&HP100_HW_RST) )
- {
- /* Wait 1.3ms (10Mb max packet time) to ensure MAC is idle so
- * MMU pointers will not be reset out from underneath
- */
- hp100_page( MAC_CTRL );
- for(time=0; time<5000; time++)
- {
- if( (hp100_inb(MAC_CFG_1)&(HP100_TX_IDLE|HP100_RX_IDLE))==
- (HP100_TX_IDLE|HP100_RX_IDLE) ) break;
- }
-
- /* Shutdown algorithm depends on the generation of Cascade */
- if( lp->chip==HP100_CHIPID_LASSEN )
- { /* ETR shutdown/reset */
- /* Disable Busmaster mode and wait for bit to go to zero. */
- hp100_page(HW_MAP);
- hp100_andb( ~HP100_BM_MASTER, BM );
- /* 100 ms timeout */
- for(time=0; time<32000; time++)
- {
- if ( 0 == (hp100_inb( BM ) & HP100_BM_MASTER) ) break;
- }
- }
- else
- { /* Shasta or Rainier Shutdown/Reset */
- /* To ensure all bus master inloading activity has ceased,
- * wait for no Rx PDAs or no Rx packets on card.
- */
- hp100_page( PERFORMANCE );
- /* 100 ms timeout */
- for(time=0; time<10000; time++)
- {
- /* RX_PDL: PDLs not executed. */
- /* RX_PKT_CNT: RX'd packets on card. */
- if ( (hp100_inb( RX_PDL ) == 0) &&
- (hp100_inb( RX_PKT_CNT ) == 0) ) break;
- }
-
- if(time>=10000)
- printk("hp100: BM shutdown error.\n");
-
- /* To ensure all bus master outloading activity has ceased,
- * wait until the Tx PDA count goes to zero or no more Tx space
- * available in the Tx region of the card.
- */
- /* 100 ms timeout */
- for(time=0; time<10000; time++) {
- if ( (0 == hp100_inb( TX_PKT_CNT )) &&
- (0 != (hp100_inb( TX_MEM_FREE )&HP100_AUTO_COMPARE))) break;
- }
-
- /* Disable Busmaster mode */
- hp100_page(HW_MAP);
- hp100_andb( ~HP100_BM_MASTER, BM );
- } /* end of shutdown procedure for non-etr parts */
-
- hp100_cascade_reset( dev, TRUE );
- }
- hp100_page( PERFORMANCE );
- hp100_outw( HP100_BM_READ | HP100_BM_WRITE | HP100_RESET_HB, OPTION_LSW );
- /* Busmaster mode should be shut down now. */
+ hp100_outw(0x4209, TRACE);
+ printk("hp100: bm shutdown\n");
+#endif
+
+ hp100_page(PERFORMANCE);
+ hp100_outw(0xfefe, IRQ_MASK); /* mask off all ints */
+ hp100_outw(0xffff, IRQ_STATUS); /* Ack all ints */
+
+ /* Ensure Interrupts are off */
+ hp100_outw(HP100_INT_EN | HP100_RESET_LB, OPTION_LSW);
+
+ /* Disable all MAC activity */
+ hp100_page(MAC_CTRL);
+ hp100_andb(~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1); /* stop rx/tx */
+
+ /* If cascade MMU is not already in reset */
+ if (0 != (hp100_inw(OPTION_LSW) & HP100_HW_RST)) {
+ /* Wait 1.3ms (10Mb max packet time) to ensure MAC is idle so
+ * MMU pointers will not be reset out from underneath
+ */
+ hp100_page(MAC_CTRL);
+ for (time = 0; time < 5000; time++) {
+ if ((hp100_inb(MAC_CFG_1) & (HP100_TX_IDLE | HP100_RX_IDLE)) ==
+ (HP100_TX_IDLE | HP100_RX_IDLE))
+ break;
+ }
+
+ /* Shutdown algorithm depends on the generation of Cascade */
+ if (lp->chip == HP100_CHIPID_LASSEN) { /* ETR shutdown/reset */
+ /* Disable Busmaster mode and wait for bit to go to zero. */
+ hp100_page(HW_MAP);
+ hp100_andb(~HP100_BM_MASTER, BM);
+ /* 100 ms timeout */
+ for (time = 0; time < 32000; time++) {
+ if (0 == (hp100_inb(BM) & HP100_BM_MASTER))
+ break;
+ }
+ } else { /* Shasta or Rainier Shutdown/Reset */
+ /* To ensure all bus master inloading activity has ceased,
+ * wait for no Rx PDAs or no Rx packets on card.
+ */
+ hp100_page(PERFORMANCE);
+ /* 100 ms timeout */
+ for (time = 0; time < 10000; time++) {
+ /* RX_PDL: PDLs not executed. */
+ /* RX_PKT_CNT: RX'd packets on card. */
+ if ((hp100_inb(RX_PDL) == 0) &&
+ (hp100_inb(RX_PKT_CNT) == 0))
+ break;
+ }
+
+ if (time >= 10000)
+ printk("hp100: BM shutdown error.\n");
+
+ /* To ensure all bus master outloading activity has ceased,
+ * wait until the Tx PDA count goes to zero or no more Tx space
+ * available in the Tx region of the card.
+ */
+ /* 100 ms timeout */
+ for (time = 0; time < 10000; time++) {
+ if ((0 == hp100_inb(TX_PKT_CNT)) &&
+ (0 != (hp100_inb(TX_MEM_FREE) & HP100_AUTO_COMPARE)))
+ break;
+ }
+
+ /* Disable Busmaster mode */
+ hp100_page(HW_MAP);
+ hp100_andb(~HP100_BM_MASTER, BM);
+ } /* end of shutdown procedure for non-etr parts */
+
+ hp100_cascade_reset(dev, TRUE);
+ }
+ hp100_page(PERFORMANCE);
+ hp100_outw(HP100_BM_READ | HP100_BM_WRITE | HP100_RESET_HB, OPTION_LSW);
+ /* Busmaster mode should be shut down now. */
}
+
-
/*
* transmit functions
*/
/* tx function for busmaster mode */
-static int hp100_start_xmit_bm( struct sk_buff *skb, struct device *dev )
+static int hp100_start_xmit_bm(struct sk_buff *skb, struct device *dev)
{
- int i, ok_flag;
- int ioaddr = dev->base_addr;
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
- hp100_ring_t *ringptr;
+ int i, ok_flag;
+ int ioaddr = dev->base_addr;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
+ hp100_ring_t *ringptr;
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4210, TRACE );
- printk("hp100: start_xmit_bm\n");
-#endif
-
- if ( skb==NULL )
- {
- dev_tint( dev );
- return 0;
- }
-
- if ( skb->len <= 0 ) return 0;
-
- /* Get Tx ring tail pointer */
- if( lp->txrtail->next==lp->txrhead )
- {
- /* No memory. */
+ hp100_outw(0x4210, TRACE);
+ printk("hp100: start_xmit_bm\n");
+#endif
+
+ /* Get Tx ring tail pointer */
+ if (lp->txrtail->next == lp->txrhead) {
+ /* No memory. */
#ifdef HP100_DEBUG
- printk("hp100: start_xmit_bm: No TX PDL available.\n");
-#endif
- /* not waited long enough since last tx? */
- if ( jiffies - dev->trans_start < HZ/10 ) return -EAGAIN;
-
- if ( lp->lan_type < 0 ) /* no LAN type detected yet? */
- {
- hp100_stop_interface( dev );
- if ( ( lp->lan_type = hp100_sense_lan( dev ) ) < 0 )
- {
- printk( "%s: no connection found - check wire\n", dev->name );
- hp100_start_interface( dev ); /* 10Mb/s RX pkts maybe handled */
- return -EIO;
- }
- if ( lp->lan_type == HP100_LAN_100 )
- lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); /* relogin */
- hp100_start_interface( dev );
- }
-
- if ( lp->lan_type == HP100_LAN_100 && lp->hub_status < 0 )
- /* we have a 100Mb/s adapter but it isn't connected to hub */
- {
- printk( "%s: login to 100Mb/s hub retry\n", dev->name );
- hp100_stop_interface( dev );
- lp->hub_status = hp100_login_to_vg_hub( dev, FALSE );
- hp100_start_interface( dev );
+ printk("hp100: start_xmit_bm: No TX PDL available.\n");
+#endif
+ /* not waited long enough since last tx? */
+ if (jiffies - dev->trans_start < HZ / 10)
+ return -EAGAIN;
+
+ if (lp->lan_type < 0) { /* no LAN type detected yet? */
+ hp100_stop_interface(dev);
+ if ((lp->lan_type = hp100_sense_lan(dev)) < 0) {
+ printk("%s: no connection found - check wire\n", dev->name);
+ hp100_start_interface(dev); /* 10Mb/s RX pkts maybe handled */
+ return -EIO;
+ }
+ if (lp->lan_type == HP100_LAN_100)
+ lp->hub_status = hp100_login_to_vg_hub(dev, FALSE); /* relogin */
+ hp100_start_interface(dev);
+ }
+ if (lp->lan_type == HP100_LAN_100 && lp->hub_status < 0)
+ /* we have a 100Mb/s adapter but it isn't connected to hub */
+ {
+ printk("%s: login to 100Mb/s hub retry\n", dev->name);
+ hp100_stop_interface(dev);
+ lp->hub_status = hp100_login_to_vg_hub(dev, FALSE);
+ hp100_start_interface(dev);
+ } else {
+ hp100_ints_off();
+ i = hp100_sense_lan(dev);
+ hp100_page(PERFORMANCE);
+ hp100_ints_on();
+ if (i == HP100_LAN_ERR)
+ printk("%s: link down detected\n", dev->name);
+ else if (lp->lan_type != i) { /* cable change! */
+ /* it's very hard - all network setting must be changed!!! */
+ printk("%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name);
+ lp->lan_type = i;
+ hp100_stop_interface(dev);
+ if (lp->lan_type == HP100_LAN_100)
+ lp->hub_status = hp100_login_to_vg_hub(dev, FALSE);
+ hp100_start_interface(dev);
+ } else {
+ printk("%s: interface reset\n", dev->name);
+ hp100_stop_interface(dev);
+ hp100_start_interface(dev);
+ }
+ }
+
+ dev->trans_start = jiffies;
+ return -EAGAIN;
}
- else
- {
- hp100_ints_off();
- i = hp100_sense_lan( dev );
- hp100_page( PERFORMANCE );
- hp100_ints_on();
- if ( i == HP100_LAN_ERR )
- printk( "%s: link down detected\n", dev->name );
- else
- if ( lp->lan_type != i ) /* cable change! */
- {
- /* it's very hard - all network setting must be changed!!! */
- printk( "%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name );
- lp->lan_type = i;
- hp100_stop_interface( dev );
- if ( lp->lan_type == HP100_LAN_100 )
- lp->hub_status = hp100_login_to_vg_hub( dev, FALSE );
- hp100_start_interface( dev );
- }
- else
- {
- printk( "%s: interface reset\n", dev->name );
- hp100_stop_interface( dev );
- hp100_start_interface( dev );
- }
+ /*
+ * we have to turn int's off before modifying this, otherwise
+ * a tx_pdl_cleanup could occur at the same time
+ */
+ cli();
+ ringptr = lp->txrtail;
+ lp->txrtail = ringptr->next;
+
+ /* Check whether packet has minimal packet size */
+ ok_flag = skb->len >= HP100_MIN_PACKET_SIZE;
+ i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE;
+
+ ringptr->skb = skb;
+ ringptr->pdl[0] = ((1 << 16) | i); /* PDH: 1 Fragment & length */
+ ringptr->pdl[1] = (u32) virt_to_bus(skb->data); /* 1st Frag: Adr. of data */
+ if (lp->chip == HP100_CHIPID_SHASTA) {
+ /* TODO:Could someone who has the EISA card please check if this works? */
+ ringptr->pdl[2] = i;
+ } else { /* Lassen */
+ /* In the PDL, don't use the padded size but the real packet size: */
+ ringptr->pdl[2] = skb->len; /* 1st Frag: Length of frag */
}
- dev->trans_start = jiffies;
- return -EAGAIN;
- }
-
- /*
- * we have to turn int's off before modifying this, otherwise
- * a tx_pdl_cleanup could occur at the same time
- */
- cli();
- ringptr=lp->txrtail;
- lp->txrtail=ringptr->next;
-
- /* Check whether packet has minimal packet size */
- ok_flag = skb->len >= HP100_MIN_PACKET_SIZE;
- i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE;
-
- ringptr->skb=skb;
- ringptr->pdl[0]=((1<<16) | i); /* PDH: 1 Fragment & length */
- ringptr->pdl[1]=(u32)virt_to_bus(skb->data); /* 1st Frag: Adr. of data */
- if(lp->chip==HP100_CHIPID_SHASTA)
- {
- /* TODO:Could someone who has the EISA card please check if this works? */
- ringptr->pdl[2]=i;
- }
- else /* Lassen */
- {
- /* In the PDL, don't use the padded size but the real packet size: */
- ringptr->pdl[2]=skb->len; /* 1st Frag: Length of frag */
- }
-
- /* Hand this PDL to the card. */
- hp100_outl( ringptr->pdl_paddr, TX_PDA_L ); /* Low Prio. Queue */
-
- lp->txrcommit++;
- sti();
-
- /* Update statistics */
- lp->stats.tx_packets++;
+ /* Hand this PDL to the card. */
+ hp100_outl(ringptr->pdl_paddr, TX_PDA_L); /* Low Prio. Queue */
+
+ lp->txrcommit++;
+ sti();
+
+ /* Update statistics */
+ lp->stats.tx_packets++;
#ifdef LINUX_2_1
- lp->stats.tx_bytes += skb->len;
+ lp->stats.tx_bytes += skb->len;
#endif
- dev->trans_start = jiffies;
-
- return 0;
-}
+ dev->trans_start = jiffies;
+ return 0;
+}
+
/* clean_txring checks if packets have been sent by the card by reading
* the TX_PDL register from the performance page and comparing it to the
* number of commited packets. It then frees the skb's of the packets that
@@ -1541,188 +1447,168 @@ static int hp100_start_xmit_bm( struct sk_buff *skb, struct device *dev )
*
* Needs the PERFORMANCE page selected.
*/
-static void hp100_clean_txring( struct device *dev )
+static void hp100_clean_txring(struct device *dev)
{
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
- int ioaddr = dev->base_addr;
- int donecount;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
+ int ioaddr = dev->base_addr;
+ int donecount;
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4211, TRACE );
- printk("hp100: clean txring\n");
+ hp100_outw(0x4211, TRACE);
+ printk("hp100: clean txring\n");
#endif
- /* How many PDLs have been transmitted? */
- donecount=(lp->txrcommit)-hp100_inb(TX_PDL);
+ /* How many PDLs have been transmitted? */
+ donecount = (lp->txrcommit) - hp100_inb(TX_PDL);
#ifdef HP100_DEBUG
- if(donecount>MAX_TX_PDL)
- printk("hp100: Warning: More PDLs transmitted than commited to card???\n");
+ if (donecount > MAX_TX_PDL)
+ printk("hp100: Warning: More PDLs transmitted than commited to card???\n");
#endif
- for( ; 0!=donecount; donecount-- )
- {
+ for (; 0 != donecount; donecount--) {
#ifdef HP100_DEBUG_BM
- printk("hp100: Free skb: data @0x%.8x txrcommit=0x%x TXPDL=0x%x, done=0x%x\n",
- (u_int) lp->txrhead->skb->data,
- lp->txrcommit,
- hp100_inb(TX_PDL),
- donecount);
-#endif
- dev_kfree_skb( lp->txrhead->skb, FREE_WRITE );
- lp->txrhead->skb=(void *)NULL;
- lp->txrhead=lp->txrhead->next;
- lp->txrcommit--;
- }
+ printk("hp100: Free skb: data @0x%.8x txrcommit=0x%x TXPDL=0x%x, done=0x%x\n",
+ (u_int) lp->txrhead->skb->data,
+ lp->txrcommit,
+ hp100_inb(TX_PDL),
+ donecount);
+#endif
+ dev_kfree_skb(lp->txrhead->skb, FREE_WRITE);
+ lp->txrhead->skb = (void *) NULL;
+ lp->txrhead = lp->txrhead->next;
+ lp->txrcommit--;
+ }
}
-
+
/* tx function for slave modes */
-static int hp100_start_xmit( struct sk_buff *skb, struct device *dev )
+static int hp100_start_xmit(struct sk_buff *skb, struct device *dev)
{
- int i, ok_flag;
- int ioaddr = dev->base_addr;
- u_short val;
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
+ int i, ok_flag;
+ int ioaddr = dev->base_addr;
+ u_short val;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4212, TRACE );
- printk("hp100: start_xmit\n");
-#endif
-
- if ( lp->lan_type < 0 ) /* no LAN type detected yet? */
- {
- hp100_stop_interface( dev );
- if ( ( lp->lan_type = hp100_sense_lan( dev ) ) < 0 )
- {
- printk( "%s: no connection found - check wire\n", dev->name );
- hp100_start_interface( dev ); /* 10Mb/s RX packets maybe handled */
- return -EIO;
- }
- if ( lp->lan_type == HP100_LAN_100 )
- lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); /* relogin */
- hp100_start_interface( dev );
- }
-
- /* If there is not enough free memory on the card... */
- i=hp100_inl(TX_MEM_FREE)&0x7fffffff;
- if ( !(((i/2)-539)>(skb->len+16) && (hp100_inb(TX_PKT_CNT)<255)) )
- {
+ hp100_outw(0x4212, TRACE);
+ printk("hp100: start_xmit\n");
+#endif
+
+ if (lp->lan_type < 0) { /* no LAN type detected yet? */
+ hp100_stop_interface(dev);
+ if ((lp->lan_type = hp100_sense_lan(dev)) < 0) {
+ printk("%s: no connection found - check wire\n", dev->name);
+ hp100_start_interface(dev); /* 10Mb/s RX packets maybe handled */
+ return -EIO;
+ }
+ if (lp->lan_type == HP100_LAN_100)
+ lp->hub_status = hp100_login_to_vg_hub(dev, FALSE); /* relogin */
+ hp100_start_interface(dev);
+ }
+ /* If there is not enough free memory on the card... */
+ i = hp100_inl(TX_MEM_FREE) & 0x7fffffff;
+ if (!(((i / 2) - 539) > (skb->len + 16) && (hp100_inb(TX_PKT_CNT) < 255))) {
#ifdef HP100_DEBUG
- printk( "hp100_start_xmit: tx free mem = 0x%x\n", i );
+ printk("hp100_start_xmit: tx free mem = 0x%x\n", i);
#endif
- /* not waited long enough since last failed tx try? */
- if ( jiffies - dev->trans_start < HZ/2 )
- {
+ /* not waited long enough since last failed tx try? */
+ if (jiffies - dev->trans_start < HZ / 2) {
#ifdef HP100_DEBUG
- printk("hp100: trans_start timing problem\n");
+ printk("hp100: trans_start timing problem\n");
#endif
- return -EAGAIN;
+ return -EAGAIN;
+ }
+ if (lp->lan_type == HP100_LAN_100 && lp->hub_status < 0)
+ /* we have a 100Mb/s adapter but it isn't connected to hub */
+ {
+ printk("%s: login to 100Mb/s hub retry\n", dev->name);
+ hp100_stop_interface(dev);
+ lp->hub_status = hp100_login_to_vg_hub(dev, FALSE);
+ hp100_start_interface(dev);
+ } else {
+ hp100_ints_off();
+ i = hp100_sense_lan(dev);
+ hp100_page(PERFORMANCE);
+ hp100_ints_on();
+ if (i == HP100_LAN_ERR)
+ printk("%s: link down detected\n", dev->name);
+ else if (lp->lan_type != i) { /* cable change! */
+ /* it's very hard - all network setting must be changed!!! */
+ printk("%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name);
+ lp->lan_type = i;
+ hp100_stop_interface(dev);
+ if (lp->lan_type == HP100_LAN_100)
+ lp->hub_status = hp100_login_to_vg_hub(dev, FALSE);
+ hp100_start_interface(dev);
+ } else {
+ printk("%s: interface reset\n", dev->name);
+ hp100_stop_interface(dev);
+ hp100_start_interface(dev);
+ udelay(1000);
+ }
+ }
+ dev->trans_start = jiffies;
+ return -EAGAIN;
}
- if ( lp->lan_type == HP100_LAN_100 && lp->hub_status < 0 )
- /* we have a 100Mb/s adapter but it isn't connected to hub */
- {
- printk( "%s: login to 100Mb/s hub retry\n", dev->name );
- hp100_stop_interface( dev );
- lp->hub_status = hp100_login_to_vg_hub( dev, FALSE );
- hp100_start_interface( dev );
- }
- else
- {
- hp100_ints_off();
- i = hp100_sense_lan( dev );
- hp100_page( PERFORMANCE );
- hp100_ints_on();
- if ( i == HP100_LAN_ERR )
- printk( "%s: link down detected\n", dev->name );
- else
- if ( lp->lan_type != i ) /* cable change! */
- {
- /* it's very hard - all network setting must be changed!!! */
- printk( "%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name );
- lp->lan_type = i;
- hp100_stop_interface( dev );
- if ( lp->lan_type == HP100_LAN_100 )
- lp->hub_status = hp100_login_to_vg_hub( dev, FALSE );
- hp100_start_interface( dev );
- }
- else
- {
- printk( "%s: interface reset\n", dev->name );
- hp100_stop_interface( dev );
- hp100_start_interface( dev );
- udelay(1000);
- }
- }
- dev->trans_start = jiffies;
- return -EAGAIN;
- }
-
- for ( i=0; i<6000 && ( hp100_inb( OPTION_MSW ) & HP100_TX_CMD ); i++ )
- {
-#ifdef HP100_DEBUG_TX
- printk( "hp100_start_xmit: busy\n" );
-#endif
- }
-
- hp100_ints_off();
- val = hp100_inw( IRQ_STATUS );
- /* Ack / clear the interrupt TX_COMPLETE interrupt - this interrupt is set
- * when the current packet being transmitted on the wire is completed. */
- hp100_outw( HP100_TX_COMPLETE, IRQ_STATUS );
+ for (i = 0; i < 6000 && (hp100_inb(OPTION_MSW) & HP100_TX_CMD); i++) {
#ifdef HP100_DEBUG_TX
- printk("hp100_start_xmit: irq_status=0x%.4x, irqmask=0x%.4x, len=%d\n",val,hp100_inw(IRQ_MASK),(int)skb->len );
-#endif
-
- ok_flag = skb->len >= HP100_MIN_PACKET_SIZE;
- i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE;
-
- hp100_outw( i, DATA32 ); /* tell card the total packet length */
- hp100_outw( i, FRAGMENT_LEN ); /* and first/only fragment length */
-
- if ( lp->mode==2 ) /* memory mapped */
- {
- if ( lp->mem_ptr_virt ) /* high pci memory was remapped */
- {
- /* Note: The J2585B needs alignment to 32bits here! */
- memcpy( lp->mem_ptr_virt, skb->data, ( skb->len +3 ) & ~3 );
- if ( !ok_flag )
- memset( lp->mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb->len );
+ printk("hp100_start_xmit: busy\n");
+#endif
}
- else
- {
- memcpy_toio( lp->mem_ptr_phys, skb->data, skb->len );
- if ( !ok_flag )
- memset_io( lp->mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb->len );
+
+ hp100_ints_off();
+ val = hp100_inw(IRQ_STATUS);
+ /* Ack / clear the interrupt TX_COMPLETE interrupt - this interrupt is set
+ * when the current packet being transmitted on the wire is completed. */
+ hp100_outw(HP100_TX_COMPLETE, IRQ_STATUS);
+#ifdef HP100_DEBUG_TX
+ printk("hp100_start_xmit: irq_status=0x%.4x, irqmask=0x%.4x, len=%d\n", val, hp100_inw(IRQ_MASK), (int) skb->len);
+#endif
+
+ ok_flag = skb->len >= HP100_MIN_PACKET_SIZE;
+ i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE;
+
+ hp100_outw(i, DATA32); /* tell card the total packet length */
+ hp100_outw(i, FRAGMENT_LEN); /* and first/only fragment length */
+
+ if (lp->mode == 2) { /* memory mapped */
+ if (lp->mem_ptr_virt) { /* high pci memory was remapped */
+ /* Note: The J2585B needs alignment to 32bits here! */
+ memcpy(lp->mem_ptr_virt, skb->data, (skb->len + 3) & ~3);
+ if (!ok_flag)
+ memset(lp->mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb->len);
+ } else {
+ memcpy_toio(lp->mem_ptr_phys, skb->data, skb->len);
+ if (!ok_flag)
+ memset_io(lp->mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb->len);
+ }
+ } else { /* programmed i/o */
+ outsl(ioaddr + HP100_REG_DATA32, skb->data, (skb->len + 3) >> 2);
+ if (!ok_flag)
+ for (i = (skb->len + 3) & ~3; i < HP100_MIN_PACKET_SIZE; i += 4)
+ hp100_outl(0, DATA32);
}
- }
- else /* programmed i/o */
- {
- outsl( ioaddr + HP100_REG_DATA32, skb->data, ( skb->len + 3 ) >> 2 );
- if ( !ok_flag )
- for ( i = ( skb->len + 3 ) & ~3; i < HP100_MIN_PACKET_SIZE; i += 4 )
- hp100_outl( 0, DATA32 );
- }
-
- hp100_outb( HP100_TX_CMD | HP100_SET_LB, OPTION_MSW ); /* send packet */
-
- lp->stats.tx_packets++;
+
+ hp100_outb(HP100_TX_CMD | HP100_SET_LB, OPTION_MSW); /* send packet */
+
+ lp->stats.tx_packets++;
#ifdef LINUX_2_1
- lp->stats.tx_bytes += skb->len;
+ lp->stats.tx_bytes += skb->len;
#endif
- dev->trans_start=jiffies;
- hp100_ints_on();
-
- dev_kfree_skb( skb, FREE_WRITE );
-
+ dev->trans_start = jiffies;
+ hp100_ints_on();
+
+ dev_kfree_skb(skb, FREE_WRITE);
+
#ifdef HP100_DEBUG_TX
- printk( "hp100_start_xmit: end\n" );
+ printk("hp100_start_xmit: end\n");
#endif
-
- return 0;
-}
+ return 0;
+}
+
/*
* Receive Function (Non-Busmaster mode)
* Called when an "Receive Packet" interrupt occurs, i.e. the receive
@@ -1732,306 +1618,285 @@ static int hp100_start_xmit( struct sk_buff *skb, struct device *dev )
* and netif_rx.
*/
-static void hp100_rx( struct device *dev )
+static void hp100_rx(struct device *dev)
{
- int packets, pkt_len;
- int ioaddr = dev->base_addr;
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
- u_int header;
- struct sk_buff *skb;
+ int packets, pkt_len;
+ int ioaddr = dev->base_addr;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
+ u_int header;
+ struct sk_buff *skb;
#ifdef DEBUG_B
- hp100_outw( 0x4213, TRACE );
- printk("hp100: rx\n");
+ hp100_outw(0x4213, TRACE);
+ printk("hp100: rx\n");
#endif
- /* First get indication of received lan packet */
- /* RX_PKT_CND indicates the number of packets which have been fully */
- /* received onto the card but have not been fully transfered of the card */
- packets = hp100_inb( RX_PKT_CNT );
+ /* First get indication of received lan packet */
+ /* RX_PKT_CND indicates the number of packets which have been fully */
+ /* received onto the card but have not been fully transfered of the card */
+ packets = hp100_inb(RX_PKT_CNT);
#ifdef HP100_DEBUG_RX
- if ( packets > 1 )
- printk( "hp100_rx: waiting packets = %d\n", packets );
+ if (packets > 1)
+ printk("hp100_rx: waiting packets = %d\n", packets);
#endif
- while ( packets-- > 0 )
- {
- /* If ADV_NXT_PKT is still set, we have to wait until the card has */
- /* really advanced to the next packet. */
- for (pkt_len=0; pkt_len<6000 &&(hp100_inb(OPTION_MSW)&HP100_ADV_NXT_PKT);
- pkt_len++ )
- {
+ while (packets-- > 0) {
+ /* If ADV_NXT_PKT is still set, we have to wait until the card has */
+ /* really advanced to the next packet. */
+ for (pkt_len = 0; pkt_len < 6000 && (hp100_inb(OPTION_MSW) & HP100_ADV_NXT_PKT);
+ pkt_len++) {
#ifdef HP100_DEBUG_RX
- printk( "hp100_rx: busy, remaining packets = %d\n", packets );
-#endif
- }
-
- /* First we get the header, which contains information about the */
- /* actual length of the received packet. */
- if( lp->mode==2 ) /* memory mapped mode */
- {
- if ( lp->mem_ptr_virt ) /* if memory was remapped */
- header = *(__u32 *)lp->mem_ptr_virt;
- else
- header = readl( lp->mem_ptr_phys );
- }
- else /* programmed i/o */
- header = hp100_inl( DATA32 );
-
- pkt_len = header & HP100_PKT_LEN_MASK;
+ printk("hp100_rx: busy, remaining packets = %d\n", packets);
+#endif
+ }
+
+ /* First we get the header, which contains information about the */
+ /* actual length of the received packet. */
+ if (lp->mode == 2) { /* memory mapped mode */
+ if (lp->mem_ptr_virt) /* if memory was remapped */
+ header = *(__u32 *) lp->mem_ptr_virt;
+ else
+ header = readl(lp->mem_ptr_phys);
+ } else /* programmed i/o */
+ header = hp100_inl(DATA32);
+
+ pkt_len = header & HP100_PKT_LEN_MASK;
#ifdef HP100_DEBUG_RX
- printk( "hp100_rx: new packet - length=%d, errors=0x%x, dest=0x%x\n",
- header & HP100_PKT_LEN_MASK, (header>>16)&0xfff8,
- (header>>16)&7);
-#endif
-
- /* Now we allocate the skb and transfer the data into it. */
- /* NOTE! This (and the skb_put() below) depends on the skb-functions
- * allocating more than asked (notably, aligning the request up to
- * the next 16-byte length).
- */
- skb = dev_alloc_skb( pkt_len );
- if ( skb == NULL ) /* Not enough memory->drop packet */
- {
+ printk("hp100_rx: new packet - length=%d, errors=0x%x, dest=0x%x\n",
+ header & HP100_PKT_LEN_MASK, (header >> 16) & 0xfff8,
+ (header >> 16) & 7);
+#endif
+
+ /* Now we allocate the skb and transfer the data into it. */
+ /* NOTE! This (and the skb_put() below) depends on the skb-functions
+ * allocating more than asked (notably, aligning the request up to
+ * the next 16-byte length).
+ */
+ skb = dev_alloc_skb(pkt_len);
+ if (skb == NULL) { /* Not enough memory->drop packet */
#ifdef HP100_DEBUG
- printk( "hp100_rx: couldn't allocate a sk_buff of size %d\n", pkt_len );
+ printk("hp100_rx: couldn't allocate a sk_buff of size %d\n", pkt_len);
#endif
- lp->stats.rx_dropped++;
- }
- else /* skb successfully allocated */
- {
- u_char *ptr;
-
- skb->dev = dev;
-
- /* ptr to start of the sk_buff data area */
- ptr = (u_char *)skb_put( skb, pkt_len );
-
- /* Now transfer the data from the card into that area */
- if ( lp->mode==2 )
- {
- if ( lp->mem_ptr_virt )
- memcpy( ptr, lp->mem_ptr_virt, ( pkt_len + 3 ) & ~3 );
- /* Note alignment to 32bit transfers */
- else
- memcpy_fromio( ptr, lp->mem_ptr_phys, ( pkt_len + 3 ) & ~3 );
- }
- else /* io mapped */
- insl( ioaddr + HP100_REG_DATA32, ptr, ( pkt_len + 3 ) >> 2 );
-
- skb->protocol = eth_type_trans( skb, dev );
-
- netif_rx( skb );
- lp->stats.rx_packets++;
+ lp->stats.rx_dropped++;
+ } else { /* skb successfully allocated */
+ u_char *ptr;
+
+ skb->dev = dev;
+
+ /* ptr to start of the sk_buff data area */
+ ptr = (u_char *) skb_put(skb, pkt_len);
+
+ /* Now transfer the data from the card into that area */
+ if (lp->mode == 2) {
+ if (lp->mem_ptr_virt)
+ memcpy(ptr, lp->mem_ptr_virt, (pkt_len + 3) & ~3);
+ /* Note alignment to 32bit transfers */
+ else
+ memcpy_fromio(ptr, lp->mem_ptr_phys, (pkt_len + 3) & ~3);
+ } else /* io mapped */
+ insl(ioaddr + HP100_REG_DATA32, ptr, (pkt_len + 3) >> 2);
+
+ skb->protocol = eth_type_trans(skb, dev);
+
+ netif_rx(skb);
+ lp->stats.rx_packets++;
#ifdef LINUX_2_1
- lp->stats.rx_bytes += skb->len;
+ lp->stats.rx_bytes += skb->len;
#endif
-
+
#ifdef HP100_DEBUG_RX
- printk( "rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
- ptr[ 0 ], ptr[ 1 ], ptr[ 2 ], ptr[ 3 ], ptr[ 4 ], ptr[ 5 ],
- ptr[ 6 ], ptr[ 7 ], ptr[ 8 ], ptr[ 9 ], ptr[ 10 ], ptr[ 11 ] );
+ printk("rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5],
+ ptr[6], ptr[7], ptr[8], ptr[9], ptr[10], ptr[11]);
#endif
- }
-
- /* Indicate the card that we have got the packet */
- hp100_outb( HP100_ADV_NXT_PKT | HP100_SET_LB, OPTION_MSW );
-
- switch ( header & 0x00070000 ) {
- case (HP100_MULTI_ADDR_HASH<<16):
- case (HP100_MULTI_ADDR_NO_HASH<<16):
- lp->stats.multicast++; break;
- }
- } /* end of while(there are packets) loop */
+ }
+
+ /* Indicate the card that we have got the packet */
+ hp100_outb(HP100_ADV_NXT_PKT | HP100_SET_LB, OPTION_MSW);
+
+ switch (header & 0x00070000) {
+ case (HP100_MULTI_ADDR_HASH << 16):
+ case (HP100_MULTI_ADDR_NO_HASH << 16):
+ lp->stats.multicast++;
+ break;
+ }
+ } /* end of while(there are packets) loop */
#ifdef HP100_DEBUG_RX
- printk( "hp100_rx: end\n" );
+ printk("hp100_rx: end\n");
#endif
}
-
+
/*
* Receive Function for Busmaster Mode
*/
-static void hp100_rx_bm( struct device *dev )
+static void hp100_rx_bm(struct device *dev)
{
- int ioaddr = dev->base_addr;
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
- hp100_ring_t *ptr;
- u_int header;
- int pkt_len;
+ int ioaddr = dev->base_addr;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
+ hp100_ring_t *ptr;
+ u_int header;
+ int pkt_len;
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4214, TRACE );
- printk("hp100: rx_bm\n");
+ hp100_outw(0x4214, TRACE);
+ printk("hp100: rx_bm\n");
#endif
#ifdef HP100_DEBUG
- if(0==lp->rxrcommit)
- {
- printk("hp100: rx_bm called although no PDLs were committed to adapter?\n");
- return;
- }
- else
-
- /* RX_PKT_CNT states how many PDLs are currently formatted and available to
- * the cards BM engine */
- if( (hp100_inw(RX_PKT_CNT)&0x00ff) >= lp->rxrcommit)
- {
- printk("hp100: More packets received than commited? RX_PKT_CNT=0x%x, commit=0x%x\n", hp100_inw(RX_PKT_CNT)&0x00ff, lp->rxrcommit);
- return;
- }
-#endif
-
- while( (lp->rxrcommit > hp100_inb(RX_PDL)) )
- {
- /*
- * The packet was received into the pdl pointed to by lp->rxrhead (
- * the oldest pdl in the ring
- */
-
- /* First we get the header, which contains information about the */
- /* actual length of the received packet. */
-
- ptr=lp->rxrhead;
-
- header = *(ptr->pdl-1);
- pkt_len = (header & HP100_PKT_LEN_MASK);
+ if (0 == lp->rxrcommit) {
+ printk("hp100: rx_bm called although no PDLs were committed to adapter?\n");
+ return;
+ } else
+ /* RX_PKT_CNT states how many PDLs are currently formatted and available to
+ * the cards BM engine */
+ if ((hp100_inw(RX_PKT_CNT) & 0x00ff) >= lp->rxrcommit) {
+ printk("hp100: More packets received than commited? RX_PKT_CNT=0x%x, commit=0x%x\n", hp100_inw(RX_PKT_CNT) & 0x00ff, lp->rxrcommit);
+ return;
+ }
+#endif
+
+ while ((lp->rxrcommit > hp100_inb(RX_PDL))) {
+ /*
+ * The packet was received into the pdl pointed to by lp->rxrhead (
+ * the oldest pdl in the ring
+ */
+
+ /* First we get the header, which contains information about the */
+ /* actual length of the received packet. */
+
+ ptr = lp->rxrhead;
+
+ header = *(ptr->pdl - 1);
+ pkt_len = (header & HP100_PKT_LEN_MASK);
#ifdef HP100_DEBUG_BM
- printk( "hp100: rx_bm: header@0x%x=0x%x length=%d, errors=0x%x, dest=0x%x\n",
- (u_int) (ptr->pdl-1),(u_int) header,
- pkt_len,
- (header>>16)&0xfff8,
- (header>>16)&7);
- printk( "hp100: RX_PDL_COUNT:0x%x TX_PDL_COUNT:0x%x, RX_PKT_CNT=0x%x PDH=0x%x, Data@0x%x len=0x%x\n",
- hp100_inb( RX_PDL ),
- hp100_inb( TX_PDL ),
- hp100_inb( RX_PKT_CNT ),
- (u_int) *(ptr->pdl),
- (u_int) *(ptr->pdl+3),
- (u_int) *(ptr->pdl+4));
-#endif
-
- if( (pkt_len>=MIN_ETHER_SIZE) &&
- (pkt_len<=MAX_ETHER_SIZE) )
- {
- if(ptr->skb==NULL)
- {
- printk("hp100: rx_bm: skb null\n");
- /* can happen if we only allocated room for the pdh due to memory shortage. */
- lp->stats.rx_dropped++;
- }
- else
- {
- skb_trim( ptr->skb, pkt_len ); /* Shorten it */
- ptr->skb->protocol = eth_type_trans( ptr->skb, dev );
-
- netif_rx( ptr->skb ); /* Up and away... */
-
- lp->stats.rx_packets++;
+ printk("hp100: rx_bm: header@0x%x=0x%x length=%d, errors=0x%x, dest=0x%x\n",
+ (u_int) (ptr->pdl - 1), (u_int) header,
+ pkt_len,
+ (header >> 16) & 0xfff8,
+ (header >> 16) & 7);
+ printk("hp100: RX_PDL_COUNT:0x%x TX_PDL_COUNT:0x%x, RX_PKT_CNT=0x%x PDH=0x%x, Data@0x%x len=0x%x\n",
+ hp100_inb(RX_PDL),
+ hp100_inb(TX_PDL),
+ hp100_inb(RX_PKT_CNT),
+ (u_int) * (ptr->pdl),
+ (u_int) * (ptr->pdl + 3),
+ (u_int) * (ptr->pdl + 4));
+#endif
+
+ if ((pkt_len >= MIN_ETHER_SIZE) &&
+ (pkt_len <= MAX_ETHER_SIZE)) {
+ if (ptr->skb == NULL) {
+ printk("hp100: rx_bm: skb null\n");
+ /* can happen if we only allocated room for the pdh due to memory shortage. */
+ lp->stats.rx_dropped++;
+ } else {
+ skb_trim(ptr->skb, pkt_len); /* Shorten it */
+ ptr->skb->protocol = eth_type_trans(ptr->skb, dev);
+
+ netif_rx(ptr->skb); /* Up and away... */
+
+ lp->stats.rx_packets++;
#ifdef LINUX_2_1
- lp->stats.rx_bytes += ptr->skb->len;
-#endif
- }
-
- switch ( header & 0x00070000 ) {
- case (HP100_MULTI_ADDR_HASH<<16):
- case (HP100_MULTI_ADDR_NO_HASH<<16):
- lp->stats.multicast++; break;
- }
- }
- else
- {
+ lp->stats.rx_bytes += ptr->skb->len;
+#endif
+ }
+
+ switch (header & 0x00070000) {
+ case (HP100_MULTI_ADDR_HASH << 16):
+ case (HP100_MULTI_ADDR_NO_HASH << 16):
+ lp->stats.multicast++;
+ break;
+ }
+ } else {
#ifdef HP100_DEBUG
- printk("hp100: rx_bm: Received bad packet (length=%d)\n",pkt_len);
-#endif
- if(ptr->skb!=NULL)
- dev_kfree_skb( ptr->skb, FREE_READ );
- lp->stats.rx_errors++;
- }
-
- lp->rxrhead=lp->rxrhead->next;
-
- /* Allocate a new rx PDL (so lp->rxrcommit stays the same) */
- if (0 == hp100_build_rx_pdl( lp->rxrtail, dev ))
- {
- /* No space for skb, header can still be received. */
+ printk("hp100: rx_bm: Received bad packet (length=%d)\n", pkt_len);
+#endif
+ if (ptr->skb != NULL)
+ dev_kfree_skb(ptr->skb, FREE_READ);
+ lp->stats.rx_errors++;
+ }
+
+ lp->rxrhead = lp->rxrhead->next;
+
+ /* Allocate a new rx PDL (so lp->rxrcommit stays the same) */
+ if (0 == hp100_build_rx_pdl(lp->rxrtail, dev)) {
+ /* No space for skb, header can still be received. */
#ifdef HP100_DEBUG
- printk("hp100: rx_bm: No space for new PDL.\n");
-#endif
- return;
- }
- else
- { /* successfully allocated new PDL - put it in ringlist at tail. */
- hp100_outl((u32)lp->rxrtail->pdl_paddr, RX_PDA);
- lp->rxrtail=lp->rxrtail->next;
+ printk("hp100: rx_bm: No space for new PDL.\n");
+#endif
+ return;
+ } else { /* successfully allocated new PDL - put it in ringlist at tail. */
+ hp100_outl((u32) lp->rxrtail->pdl_paddr, RX_PDA);
+ lp->rxrtail = lp->rxrtail->next;
+ }
+
}
-
- }
}
+
-
/*
* statistics
*/
-static hp100_stats_t *hp100_get_stats( struct device *dev )
+static hp100_stats_t *hp100_get_stats(struct device *dev)
{
- int ioaddr = dev->base_addr;
+ int ioaddr = dev->base_addr;
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4215, TRACE );
+ hp100_outw(0x4215, TRACE);
#endif
- hp100_ints_off();
- hp100_update_stats( dev );
- hp100_ints_on();
- return &((struct hp100_private *)dev->priv)->stats;
+ hp100_ints_off();
+ hp100_update_stats(dev);
+ hp100_ints_on();
+ return &((struct hp100_private *) dev->priv)->stats;
}
-static void hp100_update_stats( struct device *dev )
+static void hp100_update_stats(struct device *dev)
{
- int ioaddr = dev->base_addr;
- u_short val;
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ u_short val;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4216, TRACE );
- printk("hp100: update-stats\n");
-#endif
-
- /* Note: Statistics counters clear when read. */
- hp100_page( MAC_CTRL );
- val = hp100_inw( DROPPED ) & 0x0fff;
- lp->stats.rx_errors += val;
- lp->stats.rx_over_errors += val;
- val = hp100_inb( CRC );
- lp->stats.rx_errors += val;
- lp->stats.rx_crc_errors += val;
- val = hp100_inb( ABORT );
- lp->stats.tx_errors += val;
- lp->stats.tx_aborted_errors += val;
- hp100_page( PERFORMANCE );
+ hp100_outw(0x4216, TRACE);
+ printk("hp100: update-stats\n");
+#endif
+
+ /* Note: Statistics counters clear when read. */
+ hp100_page(MAC_CTRL);
+ val = hp100_inw(DROPPED) & 0x0fff;
+ lp->stats.rx_errors += val;
+ lp->stats.rx_over_errors += val;
+ val = hp100_inb(CRC);
+ lp->stats.rx_errors += val;
+ lp->stats.rx_crc_errors += val;
+ val = hp100_inb(ABORT);
+ lp->stats.tx_errors += val;
+ lp->stats.tx_aborted_errors += val;
+ hp100_page(PERFORMANCE);
}
-static void hp100_clear_stats( int ioaddr )
+static void hp100_clear_stats(int ioaddr)
{
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4217, TRACE );
- printk("hp100: clear_stats\n");
+ hp100_outw(0x4217, TRACE);
+ printk("hp100: clear_stats\n");
#endif
- cli();
- hp100_page( MAC_CTRL ); /* get all statistics bytes */
- hp100_inw( DROPPED );
- hp100_inb( CRC );
- hp100_inb( ABORT );
- hp100_page( PERFORMANCE );
- sti();
+ cli();
+ hp100_page(MAC_CTRL); /* get all statistics bytes */
+ hp100_inw(DROPPED);
+ hp100_inb(CRC);
+ hp100_inb(ABORT);
+ hp100_page(PERFORMANCE);
+ sti();
}
-
+
/*
* multicast setup
*/
@@ -2041,736 +1906,693 @@ static void hp100_clear_stats( int ioaddr )
* TODO: Currently when in multicast mode, card accepts all multicast packets
* for all MC addresses. Should better use the list on the card.
*/
-
-static void hp100_set_multicast_list( struct device *dev)
+
+static void hp100_set_multicast_list(struct device *dev)
{
- int ioaddr = dev->base_addr;
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4218, TRACE );
- printk("hp100: set_mc_list\n");
-#endif
-
- cli();
- hp100_ints_off();
- hp100_page( MAC_CTRL );
- hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); /* stop rx/tx */
-
- if ( dev->flags & IFF_PROMISC )
- {
- lp->mac2_mode = HP100_MAC2MODE6; /* promiscuous mode = get all good */
- lp->mac1_mode = HP100_MAC1MODE6; /* packets on the net */
- }
- else if ( dev->mc_count || (dev->flags&IFF_ALLMULTI) )
- {
- lp->mac2_mode = HP100_MAC2MODE5; /* multicast mode = get packets for */
- lp->mac1_mode = HP100_MAC1MODE5; /* me, broadcasts and all multicasts */
- }
- else
- {
- lp->mac2_mode = HP100_MAC2MODE3; /* normal mode = get packets for me */
- lp->mac1_mode = HP100_MAC1MODE3; /* and broadcasts */
- }
-
- if ( ( (hp100_inb(MAC_CFG_1) & 0x0f)!=lp->mac1_mode ) ||
- ( hp100_inb(MAC_CFG_2)!=lp->mac2_mode ) ) {
- hp100_outb( lp->mac2_mode, MAC_CFG_2 );
- hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 ); /* clear mac1 mode bits */
- hp100_orb( lp->mac1_mode, MAC_CFG_1 ); /* and set the new mode */
-
- if(lp->lan_type==HP100_LAN_100)
- {
+ hp100_outw(0x4218, TRACE);
+ printk("hp100: set_mc_list\n");
+#endif
+
+ cli();
+ hp100_ints_off();
+ hp100_page(MAC_CTRL);
+ hp100_andb(~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1); /* stop rx/tx */
+
+ if (dev->flags & IFF_PROMISC) {
+ lp->mac2_mode = HP100_MAC2MODE6; /* promiscuous mode = get all good */
+ lp->mac1_mode = HP100_MAC1MODE6; /* packets on the net */
+ } else if (dev->mc_count || (dev->flags & IFF_ALLMULTI)) {
+ lp->mac2_mode = HP100_MAC2MODE5; /* multicast mode = get packets for */
+ lp->mac1_mode = HP100_MAC1MODE5; /* me, broadcasts and all multicasts */
+ } else {
+ lp->mac2_mode = HP100_MAC2MODE3; /* normal mode = get packets for me */
+ lp->mac1_mode = HP100_MAC1MODE3; /* and broadcasts */
+ }
+
+ if (((hp100_inb(MAC_CFG_1) & 0x0f) != lp->mac1_mode) ||
+ (hp100_inb(MAC_CFG_2) != lp->mac2_mode)) {
+ hp100_outb(lp->mac2_mode, MAC_CFG_2);
+ hp100_andb(HP100_MAC1MODEMASK, MAC_CFG_1); /* clear mac1 mode bits */
+ hp100_orb(lp->mac1_mode, MAC_CFG_1); /* and set the new mode */
+
+ if (lp->lan_type == HP100_LAN_100) {
#ifdef HP100_DEBUG
- printk("hp100: 100VG MAC settings have changed - relogin.\n");
+ printk("hp100: 100VG MAC settings have changed - relogin.\n");
#endif
- lp->hub_status=hp100_login_to_vg_hub( dev, TRUE ); /* force a relogin to the hub */
- }
- }
-
- hp100_page( MAC_CTRL );
- hp100_orb( HP100_RX_EN | HP100_RX_IDLE | /* enable rx */
- HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 ); /* enable tx */
+ lp->hub_status = hp100_login_to_vg_hub(dev, TRUE); /* force a relogin to the hub */
+ }
+ }
+ hp100_page(MAC_CTRL);
+ hp100_orb(HP100_RX_EN | HP100_RX_IDLE | /* enable rx */
+ HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1); /* enable tx */
- hp100_page( PERFORMANCE );
- hp100_ints_on();
- sti();
+ hp100_page(PERFORMANCE);
+ hp100_ints_on();
+ sti();
}
-
+
/*
* hardware interrupt handling
*/
-static void hp100_interrupt( int irq, void *dev_id, struct pt_regs *regs )
+static void hp100_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- struct device *dev = (struct device *)irq2dev_map[ irq ];
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
+ struct device *dev = dev_id;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
- int ioaddr;
- u_int val;
+ int ioaddr;
+ u_int val;
- if ( dev == NULL ) return;
- ioaddr = dev->base_addr;
+ if (dev == NULL)
+ return;
+ ioaddr = dev->base_addr;
- if ( dev->interrupt )
- printk( "%s: re-entering the interrupt handler\n", dev->name );
- hp100_ints_off();
- dev->interrupt = 1; /* mark that we are inside the handler */
+ if (dev->interrupt)
+ printk("%s: re-entering the interrupt handler\n", dev->name);
+ hp100_ints_off();
+ dev->interrupt = 1; /* mark that we are inside the handler */
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4219, TRACE );
+ hp100_outw(0x4219, TRACE);
#endif
- /* hp100_page( PERFORMANCE ); */
- val = hp100_inw( IRQ_STATUS );
+ /* hp100_page( PERFORMANCE ); */
+ val = hp100_inw(IRQ_STATUS);
#ifdef HP100_DEBUG_IRQ
- printk( "hp100: mode=%x,IRQ_STAT=0x%.4x,RXPKTCNT=0x%.2x RXPDL=0x%.2x TXPKTCNT=0x%.2x TXPDL=0x%.2x\n",
- lp->mode,
- (u_int)val,
- hp100_inb( RX_PKT_CNT ),
- hp100_inb( RX_PDL ),
- hp100_inb( TX_PKT_CNT ),
- hp100_inb( TX_PDL )
- );
-#endif
-
- if(val==0) /* might be a shared interrupt */
- {
- dev->interrupt=0;
- hp100_ints_on();
- return;
- }
- /* We're only interested in those interrupts we really enabled. */
- /* val &= hp100_inw( IRQ_MASK ); */
-
- /*
- * RX_PDL_FILL_COMPL is set whenever a RX_PDL has been executed. A RX_PDL
- * is considered executed whenever the RX_PDL data structure is no longer
- * needed.
- */
- if ( val & HP100_RX_PDL_FILL_COMPL )
- {
- if(lp->mode==1)
- hp100_rx_bm( dev );
- else
- printk("hp100: rx_pdl_fill_compl interrupt although not busmaster?\n");
- }
-
- /*
- * The RX_PACKET interrupt is set, when the receive packet counter is
- * non zero. We use this interrupt for receiving in slave mode. In
- * busmaster mode, we use it to make sure we did not miss any rx_pdl_fill
- * interrupts. If rx_pdl_fill_compl is not set and rx_packet is set, then
- * we somehow have missed a rx_pdl_fill_compl interrupt.
- */
-
- if ( val & HP100_RX_PACKET ) /* Receive Packet Counter is non zero */
- {
- if(lp->mode!=1) /* non busmaster */
- hp100_rx( dev );
- else if ( !(val & HP100_RX_PDL_FILL_COMPL ))
- {
- /* Shouldnt happen - maybe we missed a RX_PDL_FILL Interrupt? */
- hp100_rx_bm( dev );
+ printk("hp100: mode=%x,IRQ_STAT=0x%.4x,RXPKTCNT=0x%.2x RXPDL=0x%.2x TXPKTCNT=0x%.2x TXPDL=0x%.2x\n",
+ lp->mode,
+ (u_int) val,
+ hp100_inb(RX_PKT_CNT),
+ hp100_inb(RX_PDL),
+ hp100_inb(TX_PKT_CNT),
+ hp100_inb(TX_PDL)
+ );
+#endif
+
+ if (val == 0) { /* might be a shared interrupt */
+ dev->interrupt = 0;
+ hp100_ints_on();
+ return;
+ }
+ /* We're only interested in those interrupts we really enabled. */
+ /* val &= hp100_inw( IRQ_MASK ); */
+
+ /*
+ * RX_PDL_FILL_COMPL is set whenever a RX_PDL has been executed. A RX_PDL
+ * is considered executed whenever the RX_PDL data structure is no longer
+ * needed.
+ */
+ if (val & HP100_RX_PDL_FILL_COMPL) {
+ if (lp->mode == 1)
+ hp100_rx_bm(dev);
+ else
+ printk("hp100: rx_pdl_fill_compl interrupt although not busmaster?\n");
}
- }
-
- /*
- * Ack. that we have noticed the interrupt and thereby allow next one.
- * Note that this is now done after the slave rx function, since first
- * acknowledging and then setting ADV_NXT_PKT caused an extra interrupt
- * on the J2573.
- */
- hp100_outw( val, IRQ_STATUS );
-
- /*
- * RX_ERROR is set when a packet is dropped due to no memory resources on
- * the card or when a RCV_ERR occurs.
- * TX_ERROR is set when a TX_ABORT condition occurs in the MAC->exists
- * only in the 802.3 MAC and happens when 16 collisions occur during a TX
- */
- if ( val & ( HP100_TX_ERROR | HP100_RX_ERROR ) )
- {
+ /*
+ * The RX_PACKET interrupt is set, when the receive packet counter is
+ * non zero. We use this interrupt for receiving in slave mode. In
+ * busmaster mode, we use it to make sure we did not miss any rx_pdl_fill
+ * interrupts. If rx_pdl_fill_compl is not set and rx_packet is set, then
+ * we somehow have missed a rx_pdl_fill_compl interrupt.
+ */
+
+ if (val & HP100_RX_PACKET) { /* Receive Packet Counter is non zero */
+ if (lp->mode != 1) /* non busmaster */
+ hp100_rx(dev);
+ else if (!(val & HP100_RX_PDL_FILL_COMPL)) {
+ /* Shouldnt happen - maybe we missed a RX_PDL_FILL Interrupt? */
+ hp100_rx_bm(dev);
+ }
+ }
+ /*
+ * Ack. that we have noticed the interrupt and thereby allow next one.
+ * Note that this is now done after the slave rx function, since first
+ * acknowledging and then setting ADV_NXT_PKT caused an extra interrupt
+ * on the J2573.
+ */
+ hp100_outw(val, IRQ_STATUS);
+
+ /*
+ * RX_ERROR is set when a packet is dropped due to no memory resources on
+ * the card or when a RCV_ERR occurs.
+ * TX_ERROR is set when a TX_ABORT condition occurs in the MAC->exists
+ * only in the 802.3 MAC and happens when 16 collisions occur during a TX
+ */
+ if (val & (HP100_TX_ERROR | HP100_RX_ERROR)) {
#ifdef HP100_DEBUG_IRQ
- printk("hp100: TX/RX Error IRQ\n");
+ printk("hp100: TX/RX Error IRQ\n");
#endif
- hp100_update_stats( dev );
- if(lp->mode==1)
- {
- hp100_rxfill( dev );
- hp100_clean_txring( dev );
+ hp100_update_stats(dev);
+ if (lp->mode == 1) {
+ hp100_rxfill(dev);
+ hp100_clean_txring(dev);
+ }
}
- }
-
- /*
- * RX_PDA_ZERO is set when the PDA count goes from non-zero to zero.
- */
- if ( (lp->mode==1)&&(val &(HP100_RX_PDA_ZERO)) )
- hp100_rxfill( dev );
-
- /*
- * HP100_TX_COMPLETE interrupt occurs when packet transmitted on wire
- * is completed
- */
- if ( (lp->mode==1) && ( val & ( HP100_TX_COMPLETE )) )
- hp100_clean_txring( dev );
-
- /*
- * MISC_ERROR is set when either the LAN link goes down or a detected
- * bus error occurs.
- */
- if ( val & HP100_MISC_ERROR ) /* New for J2585B */
- {
- printk("hp100: Misc. Error Interrupt - Check cabling.\n");
- if(lp->mode==1)
- {
- hp100_clean_txring( dev );
- hp100_rxfill( dev );
+ /*
+ * RX_PDA_ZERO is set when the PDA count goes from non-zero to zero.
+ */
+ if ((lp->mode == 1) && (val & (HP100_RX_PDA_ZERO)))
+ hp100_rxfill(dev);
+
+ /*
+ * HP100_TX_COMPLETE interrupt occurs when packet transmitted on wire
+ * is completed
+ */
+ if ((lp->mode == 1) && (val & (HP100_TX_COMPLETE)))
+ hp100_clean_txring(dev);
+
+ /*
+ * MISC_ERROR is set when either the LAN link goes down or a detected
+ * bus error occurs.
+ */
+ if (val & HP100_MISC_ERROR) { /* New for J2585B */
+ printk("hp100: Misc. Error Interrupt - Check cabling.\n");
+ if (lp->mode == 1) {
+ hp100_clean_txring(dev);
+ hp100_rxfill(dev);
+ }
}
- }
-
- dev->interrupt = 0;
- hp100_ints_on();
+ dev->interrupt = 0;
+ hp100_ints_on();
}
-
+
/*
* some misc functions
*/
-static void hp100_start_interface( struct device *dev )
+static void hp100_start_interface(struct device *dev)
{
- int ioaddr = dev->base_addr;
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4220, TRACE );
- printk("hp100: hp100_start_interface %s\n",dev->name);
-#endif
-
- cli();
-
- /* Ensure the adapter does not want to request an interrupt when */
- /* enabling the IRQ line to be active on the bus (i.e. not tri-stated) */
- hp100_page( PERFORMANCE );
- hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */
- hp100_outw( 0xffff, IRQ_STATUS ); /* ack all IRQs */
- hp100_outw( HP100_FAKE_INT|HP100_INT_EN|HP100_RESET_LB, OPTION_LSW);
- /* Un Tri-state int. TODO: Check if shared interrupts can be realised? */
- hp100_outw( HP100_TRI_INT | HP100_RESET_HB, OPTION_LSW );
-
- if(lp->mode==1)
- {
- /* Make sure BM bit is set... */
- hp100_page(HW_MAP);
- hp100_orb( HP100_BM_MASTER, BM );
- hp100_rxfill( dev );
- }
- else if(lp->mode==2)
- {
- /* Enable memory mapping. Note: Don't do this when busmaster. */
- hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW );
- }
-
- hp100_page(PERFORMANCE);
- hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */
- hp100_outw( 0xffff, IRQ_STATUS ); /* ack IRQ */
-
- /* enable a few interrupts: */
- if(lp->mode==1) /* busmaster mode */
- {
- hp100_outw( HP100_RX_PDL_FILL_COMPL |
- HP100_RX_PDA_ZERO |
- HP100_RX_ERROR |
- /* HP100_RX_PACKET | */
- /* HP100_RX_EARLY_INT | */ HP100_SET_HB |
- /* HP100_TX_PDA_ZERO | */
- HP100_TX_COMPLETE |
- /* HP100_MISC_ERROR | */
- HP100_TX_ERROR | HP100_SET_LB, IRQ_MASK );
- }
- else
- {
- hp100_outw( HP100_RX_PACKET |
- HP100_RX_ERROR | HP100_SET_HB |
- HP100_TX_ERROR | HP100_SET_LB , IRQ_MASK );
- }
-
- /* Enable MAC Tx and RX, set MAC modes, ... */
- /* Note: This function also turns on the interrupts. */
- hp100_set_multicast_list( dev );
-}
+ hp100_outw(0x4220, TRACE);
+ printk("hp100: hp100_start_interface %s\n", dev->name);
+#endif
+
+ cli();
+
+ /* Ensure the adapter does not want to request an interrupt when */
+ /* enabling the IRQ line to be active on the bus (i.e. not tri-stated) */
+ hp100_page(PERFORMANCE);
+ hp100_outw(0xfefe, IRQ_MASK); /* mask off all ints */
+ hp100_outw(0xffff, IRQ_STATUS); /* ack all IRQs */
+ hp100_outw(HP100_FAKE_INT | HP100_INT_EN | HP100_RESET_LB, OPTION_LSW);
+ /* Un Tri-state int. TODO: Check if shared interrupts can be realised? */
+ hp100_outw(HP100_TRI_INT | HP100_RESET_HB, OPTION_LSW);
+
+ if (lp->mode == 1) {
+ /* Make sure BM bit is set... */
+ hp100_page(HW_MAP);
+ hp100_orb(HP100_BM_MASTER, BM);
+ hp100_rxfill(dev);
+ } else if (lp->mode == 2) {
+ /* Enable memory mapping. Note: Don't do this when busmaster. */
+ hp100_outw(HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW);
+ }
+ hp100_page(PERFORMANCE);
+ hp100_outw(0xfefe, IRQ_MASK); /* mask off all ints */
+ hp100_outw(0xffff, IRQ_STATUS); /* ack IRQ */
+
+ /* enable a few interrupts: */
+ if (lp->mode == 1) { /* busmaster mode */
+ hp100_outw(HP100_RX_PDL_FILL_COMPL |
+ HP100_RX_PDA_ZERO |
+ HP100_RX_ERROR |
+ /* HP100_RX_PACKET | */
+ /* HP100_RX_EARLY_INT | */ HP100_SET_HB |
+ /* HP100_TX_PDA_ZERO | */
+ HP100_TX_COMPLETE |
+ /* HP100_MISC_ERROR | */
+ HP100_TX_ERROR | HP100_SET_LB, IRQ_MASK);
+ } else {
+ hp100_outw(HP100_RX_PACKET |
+ HP100_RX_ERROR | HP100_SET_HB |
+ HP100_TX_ERROR | HP100_SET_LB, IRQ_MASK);
+ }
+ /* Enable MAC Tx and RX, set MAC modes, ... */
+ /* Note: This function also turns on the interrupts. */
+ hp100_set_multicast_list(dev);
+}
-static void hp100_stop_interface( struct device *dev )
+
+static void hp100_stop_interface(struct device *dev)
{
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
- int ioaddr = dev->base_addr;
- u_int val;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
+ int ioaddr = dev->base_addr;
+ u_int val;
#ifdef HP100_DEBUG_B
- printk("hp100: hp100_stop_interface %s\n",dev->name);
- hp100_outw( 0x4221, TRACE );
-#endif
-
- if (lp->mode==1)
- hp100_BM_shutdown( dev );
- else
- {
- /* Note: MMAP_DIS will be reenabled by start_interface */
- hp100_outw( HP100_INT_EN | HP100_RESET_LB |
- HP100_TRI_INT | HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW );
- val = hp100_inw( OPTION_LSW );
-
- hp100_page( MAC_CTRL );
- hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 );
-
- if ( !(val & HP100_HW_RST) ) return; /* If reset, imm. return ... */
- /* ... else: busy wait until idle */
- for ( val = 0; val < 6000; val++ )
- if ( ( hp100_inb( MAC_CFG_1 ) & (HP100_TX_IDLE | HP100_RX_IDLE) ) ==
- (HP100_TX_IDLE | HP100_RX_IDLE) )
- {
- hp100_page(PERFORMANCE);
- return;
- }
- printk( "%s: hp100_stop_interface - timeout\n", dev->name );
- hp100_page(PERFORMANCE);
- }
+ printk("hp100: hp100_stop_interface %s\n", dev->name);
+ hp100_outw(0x4221, TRACE);
+#endif
+
+ if (lp->mode == 1)
+ hp100_BM_shutdown(dev);
+ else {
+ /* Note: MMAP_DIS will be reenabled by start_interface */
+ hp100_outw(HP100_INT_EN | HP100_RESET_LB |
+ HP100_TRI_INT | HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW);
+ val = hp100_inw(OPTION_LSW);
+
+ hp100_page(MAC_CTRL);
+ hp100_andb(~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1);
+
+ if (!(val & HP100_HW_RST))
+ return; /* If reset, imm. return ... */
+ /* ... else: busy wait until idle */
+ for (val = 0; val < 6000; val++)
+ if ((hp100_inb(MAC_CFG_1) & (HP100_TX_IDLE | HP100_RX_IDLE)) ==
+ (HP100_TX_IDLE | HP100_RX_IDLE)) {
+ hp100_page(PERFORMANCE);
+ return;
+ }
+ printk("%s: hp100_stop_interface - timeout\n", dev->name);
+ hp100_page(PERFORMANCE);
+ }
}
-
-static void hp100_load_eeprom( struct device *dev )
+
+static void hp100_load_eeprom(struct device *dev)
{
- int i;
- int ioaddr = dev->base_addr;
+ int i;
+ int ioaddr = dev->base_addr;
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4222, TRACE );
+ hp100_outw(0x4222, TRACE);
#endif
- hp100_page( EEPROM_CTRL );
- hp100_andw( ~HP100_EEPROM_LOAD, EEPROM_CTRL );
- hp100_orw( HP100_EEPROM_LOAD, EEPROM_CTRL );
- for ( i = 0; i < 10000; i++ )
- if ( !( hp100_inb( OPTION_MSW ) & HP100_EE_LOAD ) ) return;
- printk( "%s: hp100_load_eeprom - timeout\n", dev->name );
+ hp100_page(EEPROM_CTRL);
+ hp100_andw(~HP100_EEPROM_LOAD, EEPROM_CTRL);
+ hp100_orw(HP100_EEPROM_LOAD, EEPROM_CTRL);
+ for (i = 0; i < 10000; i++)
+ if (!(hp100_inb(OPTION_MSW) & HP100_EE_LOAD))
+ return;
+ printk("%s: hp100_load_eeprom - timeout\n", dev->name);
}
-
+
/* Sense connection status.
* return values: LAN_10 - Connected to 10Mbit/s network
* LAN_100 - Connected to 100Mbit/s network
* LAN_ERR - not connected or 100Mbit/s Hub down
*/
-static int hp100_sense_lan( struct device *dev )
+static int hp100_sense_lan(struct device *dev)
{
- int ioaddr = dev->base_addr;
- u_short val_VG, val_10;
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ u_short val_VG, val_10;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4223, TRACE );
-#endif
-
- hp100_page( MAC_CTRL );
- /* Enable Auto Selection */
- /* hp100_orb( HP100_VG_RESET|HP100_LINK_CMD|HP100_VG_SEL, VG_LAN_CFG_1 ); */
- /* hp100_orb( HP100_DOT3_MAC,10_LAN_CFG_2); */
- /* hp100_orb( HP100_AUTO_MODE,MAC_CFG_3); */
- /* Now we have to wait a while... */
- /* for(i=0; i<5000; i++) */
- /* { */
- val_10 = hp100_inb( 10_LAN_CFG_1 );
- val_VG = hp100_inb( VG_LAN_CFG_1 );
- /* } */
+ hp100_outw(0x4223, TRACE);
+#endif
+
+ hp100_page(MAC_CTRL);
+ /* Enable Auto Selection */
+ /* hp100_orb( HP100_VG_RESET|HP100_LINK_CMD|HP100_VG_SEL, VG_LAN_CFG_1 ); */
+ /* hp100_orb( HP100_DOT3_MAC,10_LAN_CFG_2); */
+ /* hp100_orb( HP100_AUTO_MODE,MAC_CFG_3); */
+ /* Now we have to wait a while... */
+ /* for(i=0; i<5000; i++) */
+ /* { */
+ val_10 = hp100_inb(10_LAN_CFG_1);
+ val_VG = hp100_inb(VG_LAN_CFG_1);
+ /* } */
#ifdef HP100_DEBUG
- printk( "hp100_sense_lan: val_VG = 0x%04x, val_10 = 0x%04x\n", val_VG, val_10 );
-#endif
- if ( val_10 & HP100_LINK_BEAT_ST ) return HP100_LAN_10;
- if ( (lp->id->id == 0x02019F022) ||
- (lp->id->id == 0x01042103c) ||
- (lp->id->id == 0x01040103c) )
- {
- hp100_page(PERFORMANCE);
- return HP100_LAN_ERR; /* Those cards don't have a 100 Mbit connector */
- }
- /* for ( i = 0; i < 2500; i++ ) */
- /* { */
- val_VG = hp100_inb( VG_LAN_CFG_1 );
- hp100_page(PERFORMANCE);
-
- if ( val_VG & HP100_LINK_CABLE_ST ) /* Can hear the HUBs tone. */
- return HP100_LAN_100;
- /* } */
- return HP100_LAN_ERR;
+ printk("hp100_sense_lan: val_VG = 0x%04x, val_10 = 0x%04x\n", val_VG, val_10);
+#endif
+ if (val_10 & HP100_LINK_BEAT_ST)
+ return HP100_LAN_10;
+ if ((lp->id->id == 0x02019F022) ||
+ (lp->id->id == 0x01042103c) ||
+ (lp->id->id == 0x01040103c)) {
+ hp100_page(PERFORMANCE);
+ return HP100_LAN_ERR; /* Those cards don't have a 100 Mbit connector */
+ }
+ /* for ( i = 0; i < 2500; i++ ) */
+ /* { */
+ val_VG = hp100_inb(VG_LAN_CFG_1);
+ hp100_page(PERFORMANCE);
+
+ if (val_VG & HP100_LINK_CABLE_ST) /* Can hear the HUBs tone. */
+ return HP100_LAN_100;
+ /* } */
+ return HP100_LAN_ERR;
}
+
-
-static int hp100_down_vg_link( struct device *dev )
+static int hp100_down_vg_link(struct device *dev)
{
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
- int ioaddr = dev->base_addr;
- unsigned long time;
- long savelan, newlan;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
+ int ioaddr = dev->base_addr;
+ unsigned long time;
+ long savelan, newlan;
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4224, TRACE );
- printk("hp100: down_vg_link\n");
+ hp100_outw(0x4224, TRACE);
+ printk("hp100: down_vg_link\n");
#endif
- hp100_page( MAC_CTRL );
- time=jiffies+(HZ/4);
- do{
- if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break;
- } while (time>jiffies);
+ hp100_page(MAC_CTRL);
+ time = jiffies + (HZ / 4);
+ do {
+ if (hp100_inb(VG_LAN_CFG_1) & HP100_LINK_CABLE_ST)
+ break;
+ } while (time > jiffies);
- if ( jiffies >= time ) /* no signal->no logout */
- return 0;
+ if (jiffies >= time) /* no signal->no logout */
+ return 0;
- /* Drop the VG Link by clearing the link up cmd and load addr.*/
+ /* Drop the VG Link by clearing the link up cmd and load addr. */
- hp100_andb( ~( HP100_LOAD_ADDR| HP100_LINK_CMD), VG_LAN_CFG_1);
- hp100_orb( HP100_VG_SEL, VG_LAN_CFG_1);
+ hp100_andb(~(HP100_LOAD_ADDR | HP100_LINK_CMD), VG_LAN_CFG_1);
+ hp100_orb(HP100_VG_SEL, VG_LAN_CFG_1);
- /* Conditionally stall for >250ms on Link-Up Status (to go down) */
- time=jiffies+(HZ/2);
- do{
- if ( !(hp100_inb( VG_LAN_CFG_1) & HP100_LINK_UP_ST) ) break;
- } while(time>jiffies);
+ /* Conditionally stall for >250ms on Link-Up Status (to go down) */
+ time = jiffies + (HZ / 2);
+ do {
+ if (!(hp100_inb(VG_LAN_CFG_1) & HP100_LINK_UP_ST))
+ break;
+ } while (time > jiffies);
#ifdef HP100_DEBUG
- if (jiffies>=time)
- printk("hp100_down_vg_link: Link does not go down?\n");
-#endif
-
- /* To prevent condition where Rev 1 VG MAC and old hubs do not complete */
- /* logout under traffic (even though all the status bits are cleared), */
- /* do this workaround to get the Rev 1 MAC in its idle state */
- if ( lp->chip==HP100_CHIPID_LASSEN )
- {
- /* Reset VG MAC to insure it leaves the logoff state even if */
- /* the Hub is still emitting tones */
- hp100_andb(~HP100_VG_RESET, VG_LAN_CFG_1);
- udelay(1500); /* wait for >1ms */
- hp100_orb(HP100_VG_RESET, VG_LAN_CFG_1); /* Release Reset */
- udelay(1500);
- }
-
- /* New: For lassen, switch to 10 Mbps mac briefly to clear training ACK */
- /* to get the VG mac to full reset. This is not req.d with later chips */
- /* Note: It will take the between 1 and 2 seconds for the VG mac to be */
- /* selected again! This will be left to the connect hub function to */
- /* perform if desired. */
- if (lp->chip==HP100_CHIPID_LASSEN)
- {
- /* Have to write to 10 and 100VG control registers simultaneously */
- savelan=newlan=hp100_inl(10_LAN_CFG_1); /* read 10+100 LAN_CFG regs */
- newlan &= ~(HP100_VG_SEL<<16);
- newlan |= (HP100_DOT3_MAC)<<8;
- hp100_andb( ~HP100_AUTO_MODE, MAC_CFG_3); /* Autosel off */
- hp100_outl(newlan, 10_LAN_CFG_1);
-
- /* Conditionally stall for 5sec on VG selected. */
- time=jiffies+(HZ*5);
- do{
- if( !(hp100_inb(MAC_CFG_4) & HP100_MAC_SEL_ST) ) break;
- } while(time>jiffies);
-
- hp100_orb( HP100_AUTO_MODE, MAC_CFG_3); /* Autosel back on */
- hp100_outl(savelan, 10_LAN_CFG_1);
- }
-
- time=jiffies+(3*HZ); /* Timeout 3s */
- do {
- if ( (hp100_inb( VG_LAN_CFG_1 )&HP100_LINK_CABLE_ST) == 0) break;
- } while (time>jiffies);
-
- if(time<=jiffies)
- {
+ if (jiffies >= time)
+ printk("hp100_down_vg_link: Link does not go down?\n");
+#endif
+
+ /* To prevent condition where Rev 1 VG MAC and old hubs do not complete */
+ /* logout under traffic (even though all the status bits are cleared), */
+ /* do this workaround to get the Rev 1 MAC in its idle state */
+ if (lp->chip == HP100_CHIPID_LASSEN) {
+ /* Reset VG MAC to insure it leaves the logoff state even if */
+ /* the Hub is still emitting tones */
+ hp100_andb(~HP100_VG_RESET, VG_LAN_CFG_1);
+ udelay(1500); /* wait for >1ms */
+ hp100_orb(HP100_VG_RESET, VG_LAN_CFG_1); /* Release Reset */
+ udelay(1500);
+ }
+ /* New: For lassen, switch to 10 Mbps mac briefly to clear training ACK */
+ /* to get the VG mac to full reset. This is not req.d with later chips */
+ /* Note: It will take the between 1 and 2 seconds for the VG mac to be */
+ /* selected again! This will be left to the connect hub function to */
+ /* perform if desired. */
+ if (lp->chip == HP100_CHIPID_LASSEN) {
+ /* Have to write to 10 and 100VG control registers simultaneously */
+ savelan = newlan = hp100_inl(10_LAN_CFG_1); /* read 10+100 LAN_CFG regs */
+ newlan &= ~(HP100_VG_SEL << 16);
+ newlan |= (HP100_DOT3_MAC) << 8;
+ hp100_andb(~HP100_AUTO_MODE, MAC_CFG_3); /* Autosel off */
+ hp100_outl(newlan, 10_LAN_CFG_1);
+
+ /* Conditionally stall for 5sec on VG selected. */
+ time = jiffies + (HZ * 5);
+ do {
+ if (!(hp100_inb(MAC_CFG_4) & HP100_MAC_SEL_ST))
+ break;
+ } while (time > jiffies);
+
+ hp100_orb(HP100_AUTO_MODE, MAC_CFG_3); /* Autosel back on */
+ hp100_outl(savelan, 10_LAN_CFG_1);
+ }
+ time = jiffies + (3 * HZ); /* Timeout 3s */
+ do {
+ if ((hp100_inb(VG_LAN_CFG_1) & HP100_LINK_CABLE_ST) == 0)
+ break;
+ } while (time > jiffies);
+
+ if (time <= jiffies) {
#ifdef HP100_DEBUG
- printk( "hp100_down_vg_link: timeout\n" );
-#endif
- return -EIO;
- }
-
- time=jiffies+(2*HZ); /* This seems to take a while.... */
- do {} while (time>jiffies);
-
- return 0;
-}
+ printk("hp100_down_vg_link: timeout\n");
+#endif
+ return -EIO;
+ }
+ time = jiffies + (2 * HZ); /* This seems to take a while.... */
+ do {
+ } while (time > jiffies);
+ return 0;
+}
-static int hp100_login_to_vg_hub( struct device *dev, u_short force_relogin )
+
+static int hp100_login_to_vg_hub(struct device *dev, u_short force_relogin)
{
- int ioaddr = dev->base_addr;
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
- u_short val=0;
- unsigned long time;
- int startst;
+ int ioaddr = dev->base_addr;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
+ u_short val = 0;
+ unsigned long time;
+ int startst;
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4225, TRACE );
- printk("hp100: login_to_vg_hub\n");
-#endif
-
- /* Initiate a login sequence iff VG MAC is enabled and either Load Address
- * bit is zero or the force relogin flag is set (e.g. due to MAC address or
- * promiscuous mode change)
- */
- hp100_page( MAC_CTRL );
- startst=hp100_inb( VG_LAN_CFG_1 );
- if((force_relogin==TRUE)||(hp100_inb( MAC_CFG_4 )&HP100_MAC_SEL_ST))
- {
+ hp100_outw(0x4225, TRACE);
+ printk("hp100: login_to_vg_hub\n");
+#endif
+
+ /* Initiate a login sequence iff VG MAC is enabled and either Load Address
+ * bit is zero or the force relogin flag is set (e.g. due to MAC address or
+ * promiscuous mode change)
+ */
+ hp100_page(MAC_CTRL);
+ startst = hp100_inb(VG_LAN_CFG_1);
+ if ((force_relogin == TRUE) || (hp100_inb(MAC_CFG_4) & HP100_MAC_SEL_ST)) {
#ifdef HP100_DEBUG_TRAINING
- printk("hp100: Start training\n");
+ printk("hp100: Start training\n");
#endif
- /* Ensure VG Reset bit is 1 (i.e., do not reset)*/
- hp100_orb( HP100_VG_RESET , VG_LAN_CFG_1 );
+ /* Ensure VG Reset bit is 1 (i.e., do not reset) */
+ hp100_orb(HP100_VG_RESET, VG_LAN_CFG_1);
- /* If Lassen AND auto-select-mode AND VG tones were sensed on */
- /* entry then temporarily put them into force 100Mbit mode */
- if((lp->chip==HP100_CHIPID_LASSEN)&&( startst & HP100_LINK_CABLE_ST ) )
- hp100_andb( ~HP100_DOT3_MAC, 10_LAN_CFG_2 );
-
- /* Drop the VG link by zeroing Link Up Command and Load Address */
- hp100_andb( ~(HP100_LINK_CMD/* |HP100_LOAD_ADDR */), VG_LAN_CFG_1);
+ /* If Lassen AND auto-select-mode AND VG tones were sensed on */
+ /* entry then temporarily put them into force 100Mbit mode */
+ if ((lp->chip == HP100_CHIPID_LASSEN) && (startst & HP100_LINK_CABLE_ST))
+ hp100_andb(~HP100_DOT3_MAC, 10_LAN_CFG_2);
+
+ /* Drop the VG link by zeroing Link Up Command and Load Address */
+ hp100_andb(~(HP100_LINK_CMD /* |HP100_LOAD_ADDR */ ), VG_LAN_CFG_1);
#ifdef HP100_DEBUG_TRAINING
- printk("hp100: Bring down the link\n");
-#endif
+ printk("hp100: Bring down the link\n");
+#endif
+
+ /* Wait for link to drop */
+ time = jiffies + (HZ / 10);
+ do {
+ if (~(hp100_inb(VG_LAN_CFG_1) & HP100_LINK_UP_ST))
+ break;
+ } while (time > jiffies);
+
+ /* Start an addressed training and optionally request promiscuous port */
+ if ((dev->flags) & IFF_PROMISC) {
+ hp100_orb(HP100_PROM_MODE, VG_LAN_CFG_2);
+ if (lp->chip == HP100_CHIPID_LASSEN)
+ hp100_orw(HP100_MACRQ_PROMSC, TRAIN_REQUEST);
+ } else {
+ hp100_andb(~HP100_PROM_MODE, VG_LAN_CFG_2);
+ /* For ETR parts we need to reset the prom. bit in the training
+ * register, otherwise promiscious mode won't be disabled.
+ */
+ if (lp->chip == HP100_CHIPID_LASSEN) {
+ hp100_andw(~HP100_MACRQ_PROMSC, TRAIN_REQUEST);
+ }
+ }
- /* Wait for link to drop */
- time = jiffies + (HZ/10);
- do {
- if (~(hp100_inb( VG_LAN_CFG_1 )& HP100_LINK_UP_ST) ) break;
- } while (time>jiffies);
+ /* With ETR parts, frame format request bits can be set. */
+ if (lp->chip == HP100_CHIPID_LASSEN)
+ hp100_orb(HP100_MACRQ_FRAMEFMT_EITHER, TRAIN_REQUEST);
- /* Start an addressed training and optionally request promiscuous port */
- if ( (dev->flags) & IFF_PROMISC )
- {
- hp100_orb( HP100_PROM_MODE, VG_LAN_CFG_2);
- if(lp->chip==HP100_CHIPID_LASSEN)
- hp100_orw( HP100_MACRQ_PROMSC, TRAIN_REQUEST );
- }
- else
- {
- hp100_andb( ~HP100_PROM_MODE, VG_LAN_CFG_2);
- /* For ETR parts we need to reset the prom. bit in the training
- * register, otherwise promiscious mode won't be disabled.
- */
- if(lp->chip==HP100_CHIPID_LASSEN)
- {
- hp100_andw( ~HP100_MACRQ_PROMSC, TRAIN_REQUEST );
- }
- }
+ hp100_orb(HP100_LINK_CMD | HP100_LOAD_ADDR | HP100_VG_RESET, VG_LAN_CFG_1);
- /* With ETR parts, frame format request bits can be set. */
- if(lp->chip==HP100_CHIPID_LASSEN)
- hp100_orb( HP100_MACRQ_FRAMEFMT_EITHER, TRAIN_REQUEST);
-
- hp100_orb( HP100_LINK_CMD|HP100_LOAD_ADDR|HP100_VG_RESET, VG_LAN_CFG_1);
-
- /* Note: Next wait could be omitted for Hood and earlier chips under */
- /* certain circumstances */
- /* TODO: check if hood/earlier and skip wait. */
-
- /* Wait for either short timeout for VG tones or long for login */
- /* Wait for the card hardware to signalise link cable status ok... */
- hp100_page( MAC_CTRL );
- time = jiffies + ( 1*HZ ); /* 1 sec timeout for cable st */
- do {
- if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break;
- } while ( jiffies < time );
-
- if ( jiffies >= time )
- {
+ /* Note: Next wait could be omitted for Hood and earlier chips under */
+ /* certain circumstances */
+ /* TODO: check if hood/earlier and skip wait. */
+
+ /* Wait for either short timeout for VG tones or long for login */
+ /* Wait for the card hardware to signalise link cable status ok... */
+ hp100_page(MAC_CTRL);
+ time = jiffies + (1 * HZ); /* 1 sec timeout for cable st */
+ do {
+ if (hp100_inb(VG_LAN_CFG_1) & HP100_LINK_CABLE_ST)
+ break;
+ } while (jiffies < time);
+
+ if (jiffies >= time) {
#ifdef HP100_DEBUG_TRAINING
- printk( "hp100: Link cable status not ok? Training aborted.\n" );
-#endif
- }
- else
- {
+ printk("hp100: Link cable status not ok? Training aborted.\n");
+#endif
+ } else {
#ifdef HP100_DEBUG_TRAINING
- printk( "hp100: HUB tones detected. Trying to train.\n");
+ printk("hp100: HUB tones detected. Trying to train.\n");
#endif
- time = jiffies + ( 2*HZ ); /* again a timeout */
- do {
- val = hp100_inb( VG_LAN_CFG_1 );
- if ( (val & ( HP100_LINK_UP_ST )) )
- {
+ time = jiffies + (2 * HZ); /* again a timeout */
+ do {
+ val = hp100_inb(VG_LAN_CFG_1);
+ if ((val & (HP100_LINK_UP_ST))) {
#ifdef HP100_DEBUG_TRAINING
- printk( "hp100: Passed training.\n");
+ printk("hp100: Passed training.\n");
#endif
- break;
- }
- } while ( time > jiffies );
- }
-
- /* If LINK_UP_ST is set, then we are logged into the hub. */
- if ( (jiffies<=time) && (val & HP100_LINK_UP_ST) )
- {
+ break;
+ }
+ } while (time > jiffies);
+ }
+
+ /* If LINK_UP_ST is set, then we are logged into the hub. */
+ if ((jiffies <= time) && (val & HP100_LINK_UP_ST)) {
#ifdef HP100_DEBUG_TRAINING
- printk( "hp100: Successfully logged into the HUB.\n");
- if(lp->chip==HP100_CHIPID_LASSEN)
- {
- val = hp100_inw(TRAIN_ALLOW);
- printk( "hp100: Card supports 100VG MAC Version \"%s\" ",
- (hp100_inw(TRAIN_REQUEST)&HP100_CARD_MACVER) ? "802.12" : "Pre");
- printk( "Driver will use MAC Version \"%s\"\n",
- ( val & HP100_HUB_MACVER) ? "802.12" : "Pre" );
- printk( "hp100: Frame format is %s.\n",(val&HP100_MALLOW_FRAMEFMT)?"802.5":"802.3");
- }
-#endif
- }
- else
- {
- /* If LINK_UP_ST is not set, login was not successful */
- printk("hp100/%s: Problem logging into the HUB.\n",dev->name);
- if(lp->chip==HP100_CHIPID_LASSEN)
- {
- /* Check allowed Register to find out why there is a problem. */
- val = hp100_inw( TRAIN_ALLOW ); /* wont work on non-ETR card */
+ printk("hp100: Successfully logged into the HUB.\n");
+ if (lp->chip == HP100_CHIPID_LASSEN) {
+ val = hp100_inw(TRAIN_ALLOW);
+ printk("hp100: Card supports 100VG MAC Version \"%s\" ",
+ (hp100_inw(TRAIN_REQUEST) & HP100_CARD_MACVER) ? "802.12" : "Pre");
+ printk("Driver will use MAC Version \"%s\"\n",
+ (val & HP100_HUB_MACVER) ? "802.12" : "Pre");
+ printk("hp100: Frame format is %s.\n", (val & HP100_MALLOW_FRAMEFMT) ? "802.5" : "802.3");
+ }
+#endif
+ } else {
+ /* If LINK_UP_ST is not set, login was not successful */
+ printk("hp100/%s: Problem logging into the HUB.\n", dev->name);
+ if (lp->chip == HP100_CHIPID_LASSEN) {
+ /* Check allowed Register to find out why there is a problem. */
+ val = hp100_inw(TRAIN_ALLOW); /* wont work on non-ETR card */
#ifdef HP100_DEBUG_TRAINING
- printk("hp100: MAC Configuration requested: 0x%04x, HUB allowed: 0x%04x\n", hp100_inw(TRAIN_REQUEST), val);
-#endif
- if ( val & HP100_MALLOW_ACCDENIED )
- printk("hp100: HUB access denied.\n");
- if ( val & HP100_MALLOW_CONFIGURE )
- printk("hp100: MAC Configuration is incompatible with the Network.\n");
- if ( val & HP100_MALLOW_DUPADDR )
- printk("hp100: Duplicate MAC Address on the Network.\n");
- }
- }
-
- /* If we have put the chip into forced 100 Mbit mode earlier, go back */
- /* to auto-select mode */
-
- if( (lp->chip==HP100_CHIPID_LASSEN)&&(startst & HP100_LINK_CABLE_ST) )
- {
- hp100_page( MAC_CTRL );
- hp100_orb( HP100_DOT3_MAC, 10_LAN_CFG_2 );
- }
-
- val=hp100_inb(VG_LAN_CFG_1);
-
- /* Clear the MISC_ERROR Interrupt, which might be generated when doing the relogin */
- hp100_page(PERFORMANCE);
- hp100_outw( HP100_MISC_ERROR, IRQ_STATUS);
-
- if (val&HP100_LINK_UP_ST)
- return(0); /* login was ok */
- else
- {
- printk("hp100: Training failed.\n");
- hp100_down_vg_link( dev );
- return -EIO;
- }
- }
- /* no forced relogin & already link there->no training. */
- return -EIO;
-}
+ printk("hp100: MAC Configuration requested: 0x%04x, HUB allowed: 0x%04x\n", hp100_inw(TRAIN_REQUEST), val);
+#endif
+ if (val & HP100_MALLOW_ACCDENIED)
+ printk("hp100: HUB access denied.\n");
+ if (val & HP100_MALLOW_CONFIGURE)
+ printk("hp100: MAC Configuration is incompatible with the Network.\n");
+ if (val & HP100_MALLOW_DUPADDR)
+ printk("hp100: Duplicate MAC Address on the Network.\n");
+ }
+ }
+ /* If we have put the chip into forced 100 Mbit mode earlier, go back */
+ /* to auto-select mode */
+
+ if ((lp->chip == HP100_CHIPID_LASSEN) && (startst & HP100_LINK_CABLE_ST)) {
+ hp100_page(MAC_CTRL);
+ hp100_orb(HP100_DOT3_MAC, 10_LAN_CFG_2);
+ }
+ val = hp100_inb(VG_LAN_CFG_1);
+
+ /* Clear the MISC_ERROR Interrupt, which might be generated when doing the relogin */
+ hp100_page(PERFORMANCE);
+ hp100_outw(HP100_MISC_ERROR, IRQ_STATUS);
+
+ if (val & HP100_LINK_UP_ST)
+ return (0); /* login was ok */
+ else {
+ printk("hp100: Training failed.\n");
+ hp100_down_vg_link(dev);
+ return -EIO;
+ }
+ }
+ /* no forced relogin & already link there->no training. */
+ return -EIO;
+}
-static void hp100_cascade_reset( struct device *dev, u_short enable )
+
+static void hp100_cascade_reset(struct device *dev, u_short enable)
{
- int ioaddr = dev->base_addr;
- struct hp100_private *lp = (struct hp100_private *)dev->priv;
- int i;
+ int ioaddr = dev->base_addr;
+ struct hp100_private *lp = (struct hp100_private *) dev->priv;
+ int i;
#ifdef HP100_DEBUG_B
- hp100_outw( 0x4226, TRACE );
- printk("hp100: cascade_reset\n");
-#endif
-
- if (enable==TRUE)
- {
- hp100_outw( HP100_HW_RST | HP100_RESET_LB, OPTION_LSW );
- if(lp->chip==HP100_CHIPID_LASSEN)
- {
- /* Lassen requires a PCI transmit fifo reset */
- hp100_page( HW_MAP );
- hp100_andb( ~HP100_PCI_RESET, PCICTRL2 );
- hp100_orb( HP100_PCI_RESET, PCICTRL2 );
- /* Wait for min. 300 ns */
- /* we cant use jiffies here, because it may be */
- /* that we have disabled the timer... */
- for (i=0; i<0xffff; i++);
- hp100_andb( ~HP100_PCI_RESET, PCICTRL2 );
- hp100_page( PERFORMANCE );
- }
- }
- else
- { /* bring out of reset */
- hp100_outw(HP100_HW_RST|HP100_SET_LB, OPTION_LSW);
- for (i=0; i<0xffff; i++ );
- hp100_page(PERFORMANCE);
- }
+ hp100_outw(0x4226, TRACE);
+ printk("hp100: cascade_reset\n");
+#endif
+
+ if (enable == TRUE) {
+ hp100_outw(HP100_HW_RST | HP100_RESET_LB, OPTION_LSW);
+ if (lp->chip == HP100_CHIPID_LASSEN) {
+ /* Lassen requires a PCI transmit fifo reset */
+ hp100_page(HW_MAP);
+ hp100_andb(~HP100_PCI_RESET, PCICTRL2);
+ hp100_orb(HP100_PCI_RESET, PCICTRL2);
+ /* Wait for min. 300 ns */
+ /* we cant use jiffies here, because it may be */
+ /* that we have disabled the timer... */
+ for (i = 0; i < 0xffff; i++);
+ hp100_andb(~HP100_PCI_RESET, PCICTRL2);
+ hp100_page(PERFORMANCE);
+ }
+ } else { /* bring out of reset */
+ hp100_outw(HP100_HW_RST | HP100_SET_LB, OPTION_LSW);
+ for (i = 0; i < 0xffff; i++);
+ hp100_page(PERFORMANCE);
+ }
}
-#ifdef HP100_DEBUG
-void hp100_RegisterDump( struct device *dev )
+#ifdef HP100_DEBUG
+void hp100_RegisterDump(struct device *dev)
{
- int ioaddr=dev->base_addr;
- int Page;
- int Register;
-
- /* Dump common registers */
- printk("hp100: Cascade Register Dump\n");
- printk("hardware id #1: 0x%.2x\n",hp100_inb(HW_ID));
- printk("hardware id #2/paging: 0x%.2x\n",hp100_inb(PAGING));
- printk("option #1: 0x%.4x\n",hp100_inw(OPTION_LSW));
- printk("option #2: 0x%.4x\n",hp100_inw(OPTION_MSW));
-
- /* Dump paged registers */
- for (Page = 0; Page < 8; Page++)
- {
- /* Dump registers */
- printk("page: 0x%.2x\n",Page);
- outw( Page, ioaddr+0x02);
- for (Register = 0x8; Register < 0x22; Register += 2)
- {
- /* Display Register contents except data port */
- if (((Register != 0x10) && (Register != 0x12)) || (Page > 0))
- {
- printk("0x%.2x = 0x%.4x\n",Register,inw(ioaddr+Register));
- }
+ int ioaddr = dev->base_addr;
+ int Page;
+ int Register;
+
+ /* Dump common registers */
+ printk("hp100: Cascade Register Dump\n");
+ printk("hardware id #1: 0x%.2x\n", hp100_inb(HW_ID));
+ printk("hardware id #2/paging: 0x%.2x\n", hp100_inb(PAGING));
+ printk("option #1: 0x%.4x\n", hp100_inw(OPTION_LSW));
+ printk("option #2: 0x%.4x\n", hp100_inw(OPTION_MSW));
+
+ /* Dump paged registers */
+ for (Page = 0; Page < 8; Page++) {
+ /* Dump registers */
+ printk("page: 0x%.2x\n", Page);
+ outw(Page, ioaddr + 0x02);
+ for (Register = 0x8; Register < 0x22; Register += 2) {
+ /* Display Register contents except data port */
+ if (((Register != 0x10) && (Register != 0x12)) || (Page > 0)) {
+ printk("0x%.2x = 0x%.4x\n", Register, inw(ioaddr + Register));
+ }
+ }
}
- }
- hp100_page(PERFORMANCE);
+ hp100_page(PERFORMANCE);
}
#endif
+
-
/*
* module section
*/
-
+
#ifdef MODULE
/* Parameters set by insmod */
-int hp100_port[5] = { 0, -1, -1, -1, -1 };
+int hp100_port[5] =
+{0, -1, -1, -1, -1};
#ifdef LINUX_2_1
MODULE_PARM(hp100_port, "1-5i");
#endif
#ifdef LINUX_2_1
-char hp100_name[5][IFNAMSIZ] = { "", "", "", "", "" };
+char hp100_name[5][IFNAMSIZ] =
+{"", "", "", "", ""};
MODULE_PARM(hp100_name, "1-5c" __MODULE_STRING(IFNAMSIZ));
#else
-static char devname[5][IFNAMSIZ] = { "", "", "", "", "" };
-static char *hp100_name[5] = { devname[0], devname[1],
- devname[2], devname[3],
- devname[4] };
+static char devname[5][IFNAMSIZ] =
+{"", "", "", "", ""};
+static char *hp100_name[5] =
+{devname[0], devname[1],
+ devname[2], devname[3],
+ devname[4]};
#endif
/* List of devices */
-static struct device *hp100_devlist[5] = { NULL, NULL, NULL, NULL, NULL };
+static struct device *hp100_devlist[5] =
+{NULL, NULL, NULL, NULL, NULL};
/*
* Note: if you have more than five 100vg cards in your pc, feel free to
@@ -2784,64 +2606,61 @@ static struct device *hp100_devlist[5] = { NULL, NULL, NULL, NULL, NULL };
* option hp100 hp100_port=0x280 hp100_name=eth239
*/
-int init_module( void )
+int init_module(void)
{
- int i;
- int ret = 0;
-
- if (hp100_port == 0 && !EISA_bus && !pcibios_present())
- printk("HP100: You should not use auto-probing with insmod!\n");
-
- /* Loop on all possible base addresses */
- i = -1;
- while((hp100_port[++i] != -1) && (i < 5))
- {
- /* Create device and set basics args */
- hp100_devlist[i] = kmalloc(sizeof(struct device), GFP_KERNEL);
- memset(hp100_devlist[i], 0x00, sizeof(struct device));
- hp100_devlist[i]->name = hp100_name[i];
- hp100_devlist[i]->base_addr = hp100_port[i];
- hp100_devlist[i]->init = &hp100_probe;
-
- /* Try to create the device */
- if(register_netdev(hp100_devlist[i]) != 0)
- {
- /* DeAllocate everything */
- /* Note: if dev->priv is mallocated, there is no way to fail */
- kfree_s(hp100_devlist[i], sizeof(struct device));
- hp100_devlist[i] = (struct device *) NULL;
- ret = -EIO;
- }
- } /* Loop over all devices */
+ int i;
+ int ret = 0;
+
+ if (hp100_port == 0 && !EISA_bus && !pcibios_present())
+ printk("HP100: You should not use auto-probing with insmod!\n");
+
+ /* Loop on all possible base addresses */
+ i = -1;
+ while ((hp100_port[++i] != -1) && (i < 5)) {
+ /* Create device and set basics args */
+ hp100_devlist[i] = kmalloc(sizeof(struct device), GFP_KERNEL);
+ memset(hp100_devlist[i], 0x00, sizeof(struct device));
+ hp100_devlist[i]->name = hp100_name[i];
+ hp100_devlist[i]->base_addr = hp100_port[i];
+ hp100_devlist[i]->init = &hp100_probe;
+
+ /* Try to create the device */
+ if (register_netdev(hp100_devlist[i]) != 0) {
+ /* DeAllocate everything */
+ /* Note: if dev->priv is mallocated, there is no way to fail */
+ kfree_s(hp100_devlist[i], sizeof(struct device));
+ hp100_devlist[i] = (struct device *) NULL;
+ ret = -EIO;
+ }
+ } /* Loop over all devices */
- return ret;
+ return ret;
}
-void cleanup_module( void )
+void cleanup_module(void)
{
- int i;
-
- /* TODO: Check if all skb's are released/freed. */
- for(i = 0; i < 5; i++)
- if(hp100_devlist[i] != (struct device *) NULL)
- {
- unregister_netdev( hp100_devlist[i] );
- release_region( hp100_devlist[i]->base_addr, HP100_REGION_SIZE );
- if( ((struct hp100_private *)hp100_devlist[i]->priv)->mode==1 ) /* busmaster */
- kfree_s( ((struct hp100_private *)hp100_devlist[i]->priv)->page_vaddr, MAX_RINGSIZE+0x0f);
- if ( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt )
- iounmap( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt );
- kfree_s( hp100_devlist[i]->priv, sizeof( struct hp100_private ) );
- hp100_devlist[i]->priv = NULL;
- kfree_s(hp100_devlist[i], sizeof(struct device));
- hp100_devlist[i] = (struct device *) NULL;
- }
+ int i;
+
+ /* TODO: Check if all skb's are released/freed. */
+ for (i = 0; i < 5; i++)
+ if (hp100_devlist[i] != (struct device *) NULL) {
+ unregister_netdev(hp100_devlist[i]);
+ release_region(hp100_devlist[i]->base_addr, HP100_REGION_SIZE);
+ if (((struct hp100_private *) hp100_devlist[i]->priv)->mode == 1) /* busmaster */
+ kfree_s(((struct hp100_private *) hp100_devlist[i]->priv)->page_vaddr, MAX_RINGSIZE + 0x0f);
+ if (((struct hp100_private *) hp100_devlist[i]->priv)->mem_ptr_virt)
+ iounmap(((struct hp100_private *) hp100_devlist[i]->priv)->mem_ptr_virt);
+ kfree_s(hp100_devlist[i]->priv, sizeof(struct hp100_private));
+ hp100_devlist[i]->priv = NULL;
+ kfree_s(hp100_devlist[i], sizeof(struct device));
+ hp100_devlist[i] = (struct device *) NULL;
+ }
}
-#endif /* MODULE */
+#endif /* MODULE */
+
-
/*
* Local variables:
* compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c hp100.c"
diff --git a/drivers/net/hp100.h b/drivers/net/hp100.h
index 436dd3700..c1c62e6bb 100644
--- a/drivers/net/hp100.h
+++ b/drivers/net/hp100.h
@@ -1,7 +1,7 @@
/*
* hp100.h: Hewlett Packard HP10/100VG ANY LAN ethernet driver for Linux.
*
- * $Id: hp100.h,v 1.51 1997/04/08 14:26:42 floeff Exp floeff $
+ * $Id: hp100.h,v 1.4 1997/05/26 21:09:19 davem Exp $
*
* Authors: Jaroslav Kysela, <perex@pf.jcu.cz>
* Siegfried Loeffler <floeff@tunix.mathematik.uni-stuttgart.de>
diff --git a/drivers/net/hydra.c b/drivers/net/hydra.c
index ca0e019ce..b7dbeb815 100644
--- a/drivers/net/hydra.c
+++ b/drivers/net/hydra.c
@@ -87,7 +87,7 @@ struct hydra_private
u16 rx_page_stop;
u16 next_pkt;
struct net_device_stats stats;
- int key;
+ unsigned int key;
};
static int hydra_open(struct device *dev);
@@ -161,15 +161,15 @@ __initfunc(int hydra_probe(struct device *dev))
{
struct hydra_private *priv;
u32 board;
- int key;
- struct ConfigDev *cd;
+ unsigned int key;
+ const struct ConfigDev *cd;
int j;
#ifdef HYDRA_DEBUG
printk("hydra_probe(%x)\n", dev);
#endif
- if ((key = zorro_find(MANUF_HYDRA_SYSTEMS, PROD_AMIGANET, 0, 0)))
+ if ((key = zorro_find(ZORRO_PROD_HYDRA_SYSTEMS_AMIGANET, 0, 0)))
{
cd = zorro_get_board(key);
if((board = (u32) cd->cd_BoardAddr))
@@ -206,7 +206,7 @@ __initfunc(int hydra_probe(struct device *dev))
return(0);
}
}
- return(ENODEV);
+ return(-ENODEV);
}
diff --git a/drivers/net/ibmtr.c b/drivers/net/ibmtr.c
index 2beae46e0..4fe7aefcd 100644
--- a/drivers/net/ibmtr.c
+++ b/drivers/net/ibmtr.c
@@ -267,6 +267,7 @@ __initfunc(static int ibmtr_probe1(struct device *dev, int PIOaddr))
struct tok_info *ti=0;
__u32 cd_chanid;
unsigned char *tchanid, ctemp;
+ unsigned long timeout;
#ifndef MODULE
dev = init_trdev(dev,0);
@@ -406,10 +407,14 @@ __initfunc(static int ibmtr_probe1(struct device *dev, int PIOaddr))
irq=10;
if (intr==3)
irq=11;
- /*
- * FIXME: this wait should have a timeout
- */
- while(!readb(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN));
+
+ timeout = jiffies + TR_SPIN_INTERVAL;
+ while(!readb(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN))
+ if (jiffies > timeout) {
+ DPRINTK("Hardware timeout during initialization.\n");
+ kfree_s(ti, sizeof(struct tok_info));
+ return -ENODEV;
+ }
ti->sram=((__u32)readb(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN)<<12);
ti->global_int_enable=PIOaddr+ADAPTINTREL;
ti->adapter_int_enable=PIOaddr+ADAPTINTREL;
@@ -567,12 +572,11 @@ __initfunc(static int ibmtr_probe1(struct device *dev, int PIOaddr))
DPRINTK("Using %dK shared RAM\n",ti->mapped_ram_size/2);
#endif
- if (request_irq (dev->irq = irq, &tok_interrupt,0,"ibmtr", NULL) != 0) {
+ if (request_irq (dev->irq = irq, &tok_interrupt,0,"ibmtr", dev) != 0) {
DPRINTK("Could not grab irq %d. Halting Token Ring driver.\n",irq);
kfree_s(ti, sizeof(struct tok_info));
return -ENODEV;
}
- irq2dev_map[irq]=dev;
/*?? Now, allocate some of the PIO PORTs for this driver.. */
request_region(PIOaddr,IBMTR_IO_EXTENT,"ibmtr"); /* record PIOaddr range
@@ -701,7 +705,7 @@ void tok_interrupt (int irq, void *dev_id, struct pt_regs *regs)
#if TR_VERBOSE
DPRINTK("Int from tok_driver, dev : %p\n",dev);
#endif
- dev = (struct device *)(irq2dev_map[irq]);
+ dev = dev_id;
ti = (struct tok_info *) dev->priv;
/* Disable interrupts till processing is finished */
@@ -1477,12 +1481,6 @@ static int tok_send_packet(struct sk_buff *skb, struct device *dev)
return 1;
}
- /* Donald does this, so we do too. */
- if (skb==NULL) {
- dev_tint(dev);
- return 0;
- }
-
if (test_and_set_bit(0,(void *)&dev->tbusy)!=0)
DPRINTK("Transmitter access conflict\n");
else {
@@ -1578,8 +1576,7 @@ void cleanup_module(void)
for (i = 0; i < IBMTR_MAX_ADAPTERS; i++)
if (dev_ibmtr[i]) {
unregister_trdev(dev_ibmtr[i]);
- free_irq(dev_ibmtr[i]->irq, NULL);
- irq2dev_map[dev_ibmtr[i]->irq] = NULL;
+ free_irq(dev_ibmtr[i]->irq, dev_ibmtr[i]);
release_region(dev_ibmtr[i]->base_addr, IBMTR_IO_EXTENT);
kfree_s(dev_ibmtr[i]->priv, sizeof(struct tok_info));
kfree_s(dev_ibmtr[i], sizeof(struct device));
diff --git a/drivers/net/ibmtr.h b/drivers/net/ibmtr.h
index bc253ecc8..0c162b586 100644
--- a/drivers/net/ibmtr.h
+++ b/drivers/net/ibmtr.h
@@ -6,6 +6,7 @@
#define TR_RETRY_INTERVAL (5*HZ) /* 500 on PC = 5 s */
#define TR_RESET_INTERVAL (HZ/20) /* 5 on PC = 50 ms */
#define TR_BUSY_INTERVAL (HZ/5) /* 5 on PC = 200 ms */
+#define TR_SPIN_INTERVAL (3*HZ) /* 3 seconds before init timeout */
#define TR_ISA 1
#define TR_MCA 2
@@ -227,7 +228,7 @@ struct tok_info {
/* DIR_OPEN_ADAPTER options */
#define OPEN_PASS_BCON_MAC 0x0100
-#define NUM_RCV_BUF 3
+#define NUM_RCV_BUF 2
#define RCV_BUF_LEN 1024
#define DHB_LENGTH 2048
#define NUM_DHB 2
diff --git a/drivers/net/iow.h b/drivers/net/iow.h
deleted file mode 100644
index 6e15688fb..000000000
--- a/drivers/net/iow.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _ASM_IOW_H
-#define _ASM_IOW_H
-
-/* no longer used */
-
-#endif
diff --git a/drivers/net/ipddp.c b/drivers/net/ipddp.c
index 8106ffc2d..fdca59467 100644
--- a/drivers/net/ipddp.c
+++ b/drivers/net/ipddp.c
@@ -1,3 +1,5 @@
+#warning "Needs new networking merges before it will work"
+#if 0
/*
* ipddp.c: IP-over-DDP driver for Linux
*
@@ -46,6 +48,7 @@ static const char *version =
#include <asm/dma.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
@@ -118,7 +121,6 @@ int ipddp_init(struct device *dev)
dev->rebuild_header = ipddp_rebuild_header;
dev->type = ARPHRD_IPDDP; /* IP over DDP tunnel */
- dev->family = AF_INET;
dev->mtu = 585;
dev->flags |= IFF_NOARP;
@@ -307,3 +309,4 @@ void cleanup_module(void)
}
#endif /* MODULE */
+#endif
diff --git a/drivers/net/lance.c b/drivers/net/lance.c
index c827a9518..97d0f27d6 100644
--- a/drivers/net/lance.c
+++ b/drivers/net/lance.c
@@ -643,15 +643,13 @@ lance_open(struct device *dev)
int i;
if (dev->irq == 0 ||
- request_irq(dev->irq, &lance_interrupt, 0, lp->name, NULL)) {
+ request_irq(dev->irq, &lance_interrupt, 0, lp->name, dev)) {
return -EAGAIN;
}
/* We used to allocate DMA here, but that was silly.
DMA lines can't be shared! We now permanently allocate them. */
- irq2dev_map[dev->irq] = dev;
-
/* Reset the LANCE */
inw(ioaddr+LANCE_RESET);
@@ -902,7 +900,7 @@ static int lance_start_xmit(struct sk_buff *skb, struct device *dev)
static void
lance_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
- struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct device *dev = dev_id;
struct lance_private *lp;
int csr0, ioaddr, boguscnt=10;
int must_restart;
@@ -1131,9 +1129,7 @@ lance_close(struct device *dev)
if (dev->dma != 4)
disable_dma(dev->dma);
- free_irq(dev->irq, NULL);
-
- irq2dev_map[dev->irq] = 0;
+ free_irq(dev->irq, dev);
return 0;
}
diff --git a/drivers/net/lapbether.c b/drivers/net/lapbether.c
index 5f8cb5cca..bee5d50bf 100644
--- a/drivers/net/lapbether.c
+++ b/drivers/net/lapbether.c
@@ -41,7 +41,6 @@
#include <linux/stat.h>
#include <linux/firewall.h>
#include <linux/module.h>
-#include <linux/net_alias.h>
#include <linux/lapb.h>
#include <linux/init.h>
@@ -109,9 +108,6 @@ static __inline__ int dev_is_ethdev(struct device *dev)
return (
dev->type == ARPHRD_ETHER
&& strncmp(dev->name, "dummy", 5)
-#ifdef CONFIG_NET_ALIAS
- && !net_alias_is(dev)
-#endif
);
}
@@ -468,17 +464,7 @@ static int lapbeth_new_device(struct device *dev)
dev->get_stats = lapbeth_get_stats;
dev->do_ioctl = lapbeth_ioctl;
- /* preset with reasonable values */
-
dev->flags = 0;
- dev->family = AF_INET;
-
-#ifdef CONFIG_INET
- dev->pa_addr = in_aton("192.168.0.1");
- dev->pa_brdaddr = in_aton("192.168.0.255");
- dev->pa_mask = in_aton("255.255.255.0");
- dev->pa_alen = 4;
-#endif
dev->type = ARPHRD_X25;
dev->hard_header_len = 3;
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index 629f6a0ca..075304def 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -28,7 +28,6 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
@@ -129,14 +128,7 @@ __initfunc(int loopback_init(struct device *dev))
dev->type = ARPHRD_LOOPBACK; /* 0x0001 */
dev->rebuild_header = eth_rebuild_header;
dev->open = loopback_open;
- dev->flags = IFF_LOOPBACK|IFF_BROADCAST;
- dev->family = AF_INET;
-#ifdef CONFIG_INET
- dev->pa_addr = in_aton("127.0.0.1");
- dev->pa_brdaddr = in_aton("127.255.255.255");
- dev->pa_mask = in_aton("255.0.0.0");
- dev->pa_alen = 4;
-#endif
+ dev->flags = IFF_LOOPBACK;
dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
if (dev->priv == NULL)
return -ENOMEM;
diff --git a/drivers/net/ltpc.c b/drivers/net/ltpc.c
index 1da2ee64f..8626b8406 100644
--- a/drivers/net/ltpc.c
+++ b/drivers/net/ltpc.c
@@ -249,6 +249,12 @@ static unsigned long dma_mem_alloc(int size)
static unsigned char *ltdmabuf;
static unsigned char *ltdmacbuf;
+struct ltpc_private
+{
+ struct net_device_stats stats;
+ struct at_addr my_addr;
+};
+
struct xmitQel {
struct xmitQel *next;
unsigned char *cbuf;
@@ -650,7 +656,7 @@ static int do_read(struct device *dev, void *cbuf, int cbuflen,
static struct timer_list ltpc_timer;
static int ltpc_xmit(struct sk_buff *skb, struct device *dev);
-static struct enet_statistics *ltpc_get_stats(struct device *dev);
+static struct net_device_stats *ltpc_get_stats(struct device *dev);
static int ltpc_open(struct device *dev)
{
@@ -691,7 +697,7 @@ static int sendup_buffer (struct device *dev)
int dnode, snode, llaptype, len;
int sklen;
struct sk_buff *skb;
- struct net_device_stats *stats = (struct enet_statistics *)dev->priv;
+ struct net_device_stats *stats = &((struct ltpc_private *)dev->priv)->stats;
struct lt_rcvlap *ltc = (struct lt_rcvlap *) ltdmacbuf;
if (ltc->command != LT_RCVLAP) {
@@ -755,7 +761,7 @@ static int sendup_buffer (struct device *dev)
static void ltpc_interrupt(int irq, void *dev_id, struct pt_regs *reg_ptr)
{
- struct device *dev = (struct device *) irq2dev_map[irq];
+ struct device *dev = dev_id;
if (dev==NULL) {
printk("ltpc_interrupt: unknown device.\n");
@@ -786,7 +792,7 @@ static int ltpc_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
{
struct sockaddr_at *sa = (struct sockaddr_at *) &ifr->ifr_addr;
/* we'll keep the localtalk node address in dev->pa_addr */
- struct at_addr *aa = (struct at_addr *) &dev->pa_addr;
+ struct at_addr *aa = &((struct ltpc_private *)dev->priv)->my_addr;
struct lt_init c;
int ltflags;
@@ -851,14 +857,14 @@ static int ltpc_init(struct device *dev)
dev->hard_start_xmit = ltpc_xmit;
dev->hard_header = ltpc_hard_header;
- dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
+ dev->priv = kmalloc(sizeof(struct ltpc_private), GFP_KERNEL);
if(!dev->priv)
{
printk(KERN_INFO "%s: could not allocate statistics buffer\n", dev->name);
return -ENOMEM;
}
- memset(dev->priv, 0, sizeof(struct net_device_stats));
+ memset(dev->priv, 0, sizeof(struct ltpc_private));
dev->get_stats = ltpc_get_stats;
dev->open = ltpc_open;
@@ -915,7 +921,7 @@ static int ltpc_xmit(struct sk_buff *skb, struct device *dev)
* and skb->len is the length of the ddp data + ddp header
*/
- struct net_device_stats *stats = (struct enet_statistics *)dev->priv;
+ struct net_device_stats *stats = &((struct ltpc_private *)dev->priv)->stats;
int i;
struct lt_sendlap cbuf;
@@ -951,7 +957,7 @@ static int ltpc_xmit(struct sk_buff *skb, struct device *dev)
static struct net_device_stats *ltpc_get_stats(struct device *dev)
{
- struct net_device_stats *stats = (struct net_device_stats *) dev->priv;
+ struct net_device_stats *stats = &((struct ltpc_private *) dev->priv)->stats;
return stats;
}
@@ -984,9 +990,9 @@ __initfunc(int ltpc_probe(struct device *dev))
save_flags(flags);
cli();
- probe3 = request_irq( 3, &lt_probe_handler, 0, "ltpc_probe",NULL);
- probe4 = request_irq( 4, &lt_probe_handler, 0, "ltpc_probe",NULL);
- probe9 = request_irq( 9, &lt_probe_handler, 0, "ltpc_probe",NULL);
+ probe3 = request_irq( 3, &lt_probe_handler, 0, "ltpc_probe",dev);
+ probe4 = request_irq( 4, &lt_probe_handler, 0, "ltpc_probe",dev);
+ probe9 = request_irq( 9, &lt_probe_handler, 0, "ltpc_probe",dev);
irqhitmask = 0;
@@ -1023,9 +1029,9 @@ __initfunc(int ltpc_probe(struct device *dev))
cli();
- if (!probe3) free_irq(3,NULL);
- if (!probe4) free_irq(4,NULL);
- if (!probe9) free_irq(9,NULL);
+ if (!probe3) free_irq(3,dev);
+ if (!probe4) free_irq(4,dev);
+ if (!probe9) free_irq(9,dev);
sti();
@@ -1193,8 +1199,7 @@ __initfunc(int ltpc_probe(struct device *dev))
ltpc_timer.data = (unsigned long) dev;
if (irq) {
- irq2dev_map[irq] = dev;
- (void) request_irq( irq, &ltpc_interrupt, 0, "ltpc",NULL);
+ (void) request_irq( irq, &ltpc_interrupt, 0, "ltpc", dev);
(void) inb_p(base+7); /* enable interrupts from board */
(void) inb_p(base+7); /* and reset irq line */
ltpc_timer.expires = 100;
@@ -1249,7 +1254,7 @@ void cleanup_module(void)
if(debug&DEBUG_VERBOSE) printk("freeing irq\n");
if(dev_ltpc.irq) {
- free_irq(dev_ltpc.irq,NULL);
+ free_irq(dev_ltpc.irq,&dev_ltpc);
dev_ltpc.irq = 0;
}
diff --git a/drivers/net/mace.c b/drivers/net/mace.c
index bcc630f4c..74bc0a5e4 100644
--- a/drivers/net/mace.c
+++ b/drivers/net/mace.c
@@ -45,6 +45,7 @@ struct mace_data {
unsigned char tx_bad_runt;
struct net_device_stats stats;
struct timer_list tx_timeout;
+ int timeout_active;
};
/*
@@ -165,6 +166,8 @@ mace_probe(struct device *dev)
memset(&mp->stats, 0, sizeof(mp->stats));
memset((char *) mp->tx_cmds, 0,
(NCMDS_TX*N_TX_RING + N_RX_RING + 2) * sizeof(struct dbdma_cmd));
+ init_timer(&mp->tx_timeout);
+ mp->timeout_active = 0;
mace_reset(dev);
@@ -346,11 +349,18 @@ static int mace_close(struct device *dev)
static inline void mace_set_timeout(struct device *dev)
{
struct mace_data *mp = (struct mace_data *) dev->priv;
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ if (mp->timeout_active)
+ del_timer(&mp->tx_timeout);
mp->tx_timeout.expires = jiffies + TX_TIMEOUT;
mp->tx_timeout.function = mace_tx_timeout;
mp->tx_timeout.data = (unsigned long) dev;
add_timer(&mp->tx_timeout);
+ mp->timeout_active = 1;
+ restore_flags(flags);
}
static int mace_xmit_start(struct sk_buff *skb, struct device *dev)
@@ -524,6 +534,7 @@ static void mace_interrupt(int irq, void *dev_id, struct pt_regs *regs)
mp->tx_bad_runt = 0;
mb->xmtfc = AUTO_PAD_XMIT;
del_timer(&mp->tx_timeout);
+ mp->timeout_active = 0;
continue;
}
dstat = ld_le32(&td->status);
@@ -597,17 +608,18 @@ static void mace_interrupt(int irq, void *dev_id, struct pt_regs *regs)
mace_last_fs = fs;
mace_last_xcount = xcount;
del_timer(&mp->tx_timeout);
+ mp->timeout_active = 0;
}
- mp->tx_empty = i;
- i += mp->tx_active;
- if (i >= N_TX_RING)
- i -= N_TX_RING;
- if (i != mp->tx_fill && mp->tx_fullup) {
+ if (i != mp->tx_empty && mp->tx_fullup) {
mp->tx_fullup = 0;
dev->tbusy = 0;
mark_bh(NET_BH);
}
+ mp->tx_empty = i;
+ i += mp->tx_active;
+ if (i >= N_TX_RING)
+ i -= N_TX_RING;
if (!mp->tx_bad_runt && i != mp->tx_fill && mp->tx_active < MAX_TX_ACTIVE) {
do {
/* set up the next one */
@@ -636,6 +648,7 @@ static void mace_tx_timeout(unsigned long data)
save_flags(flags);
cli();
+ mp->timeout_active = 0;
if (mp->tx_active == 0 && !mp->tx_bad_runt)
goto out;
diff --git a/drivers/net/mkiss.c b/drivers/net/mkiss.c
index 34980a7aa..562a27681 100644
--- a/drivers/net/mkiss.c
+++ b/drivers/net/mkiss.c
@@ -489,9 +489,6 @@ static int ax_open(struct device *dev)
ax->xleft = 0;
ax->flags &= (1 << AXF_INUSE); /* Clear ESCAPE & ERROR flags */
- /* Needed because address '0' is special */
- if (dev->pa_addr == 0)
- dev->pa_addr = ntohl(0xC0A80001);
dev->tbusy = 0;
dev->start = 1;
@@ -632,7 +629,12 @@ static void ax25_close(struct tty_struct *tty)
return;
mkiss = ax->mode;
- dev_close(ax->dev);
+ if (ax->dev->flags & IFF_UP)
+ {
+ dev_lock_wait();
+ dev_close(ax->dev);
+ dev_unlock_list();
+ }
tty->disc_data = 0;
ax->tty = NULL;
@@ -910,14 +912,6 @@ static int ax25_init(struct device *dev)
/* New-style flags. */
dev->flags = 0;
- dev->family = AF_INET;
-
-#ifdef CONFIG_INET
- dev->pa_addr = in_aton("192.168.0.1");
- dev->pa_brdaddr = in_aton("192.168.0.255");
- dev->pa_mask = in_aton("255.255.255.0");
- dev->pa_alen = 4;
-#endif
return 0;
}
diff --git a/drivers/net/myri_sbus.c b/drivers/net/myri_sbus.c
index e37b3c783..73ea41f26 100644
--- a/drivers/net/myri_sbus.c
+++ b/drivers/net/myri_sbus.c
@@ -594,12 +594,6 @@ static int myri_start_xmit(struct sk_buff *skb, struct device *dev)
}
}
- if(skb == NULL || skb->len <= 0) {
- DTX(("skb is null, aieee... returning 0\n"));
- dev_tint(dev);
- return 0;
- }
-
if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) {
DTX(("tbusy, maybe a race? returning 1\n"));
printk("%s: Transmitter access conflict.\n", dev->name);
@@ -1089,7 +1083,7 @@ static inline int myri_ether_init(struct device *dev, struct linux_sbus_device *
dev->hard_start_xmit = &myri_start_xmit;
dev->get_stats = &myri_get_stats;
dev->set_multicast_list = &myri_set_multicast;
- dev->irq = (unsigned char) sdev->irqs[0].pri;
+ dev->irq = sdev->irqs[0].pri;
dev->dma = 0;
/* Register interrupt handler now. */
diff --git a/drivers/net/ne.c b/drivers/net/ne.c
index c989d1841..04cc02961 100644
--- a/drivers/net/ne.c
+++ b/drivers/net/ne.c
@@ -42,6 +42,7 @@ static const char *version =
#include <linux/pci.h>
#include <linux/bios32.h>
#include <linux/init.h>
+#include <linux/delay.h>
#include <asm/system.h>
#include <asm/io.h>
@@ -77,6 +78,8 @@ pci_clone_list[] __initdata = {
{PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_89C940},
{PCI_VENDOR_ID_COMPEX, PCI_DEVICE_ID_COMPEX_RL2000},
{PCI_VENDOR_ID_KTI, PCI_DEVICE_ID_KTI_ET32P2},
+ {PCI_VENDOR_ID_NETVIN, PCI_DEVICE_ID_NETVIN_NV5000SC},
+ {PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C926},
{0,}
};
#endif
@@ -408,6 +411,7 @@ __initfunc(static int ne_probe1(struct device *dev, int ioaddr))
outb_p(0x00, ioaddr + EN0_RCNTLO);
outb_p(0x00, ioaddr + EN0_RCNTHI);
outb_p(E8390_RREAD+E8390_START, ioaddr); /* Trigger it... */
+ udelay(10000); /* wait 10ms for interrupt to propagate */
outb_p(0x00, ioaddr + EN0_IMR); /* Mask it again. */
dev->irq = autoirq_report(0);
if (ei_debug > 2)
@@ -425,7 +429,8 @@ __initfunc(static int ne_probe1(struct device *dev, int ioaddr))
/* Snarf the interrupt now. There's no point in waiting since we cannot
share and the board will usually be enabled. */
{
- int irqval = request_irq(dev->irq, ei_interrupt, SA_INTERRUPT, name, NULL);
+ int irqval = request_irq(dev->irq, ei_interrupt,
+ pci_irq_line ? SA_SHIRQ : 0, name, dev);
if (irqval) {
printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
return EAGAIN;
@@ -437,7 +442,7 @@ __initfunc(static int ne_probe1(struct device *dev, int ioaddr))
/* Allocate dev->priv and fill in 8390 specific dev fields. */
if (ethdev_init(dev)) {
printk (" unable to get memory for dev->priv.\n");
- free_irq(dev->irq, NULL);
+ free_irq(dev->irq, dev);
return -ENOMEM;
}
@@ -782,8 +787,7 @@ cleanup_module(void)
if (dev->priv != NULL) {
kfree(dev->priv);
dev->priv = NULL;
- free_irq(dev->irq, NULL);
- irq2dev_map[dev->irq] = NULL;
+ free_irq(dev->irq, dev);
release_region(dev->base_addr, NE_IO_EXTENT);
unregister_netdev(dev);
}
diff --git a/drivers/net/net_init.c b/drivers/net/net_init.c
index ef15a14da..ae57553a3 100644
--- a/drivers/net/net_init.c
+++ b/drivers/net/net_init.c
@@ -59,7 +59,7 @@
*/
/* The list of used and available "eth" slots (for "eth0", "eth1", etc.) */
-#define MAX_ETH_CARDS 16 /* same as the number if irq's in irq2dev[] */
+#define MAX_ETH_CARDS 16
static struct device *ethdev_index[MAX_ETH_CARDS];
@@ -439,7 +439,7 @@ void unregister_netdev(struct device *dev)
#ifdef CONFIG_TR
/* The list of used and available "tr" slots */
-#define MAX_TR_CARDS 16 /* same as the number of irq's in irq2dev[] */
+#define MAX_TR_CARDS 16
static struct device *trdev_index[MAX_TR_CARDS];
struct device *init_trdev(struct device *dev, int sizeof_priv)
diff --git a/drivers/net/ni5010.c b/drivers/net/ni5010.c
new file mode 100644
index 000000000..9a853e9c9
--- /dev/null
+++ b/drivers/net/ni5010.c
@@ -0,0 +1,835 @@
+/* ni5010.c: A network driver for the MiCom-Interlan NI5010 ethercard.
+ *
+ * Copyright 1996,1997 Jan-Pascal van Best and Andreas Mohr.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * The authors may be reached as:
+ * jvbest@wi.leidenuniv.nl a.mohr@mailto.de
+ * or by snail mail as
+ * Jan-Pascal van Best Andreas Mohr
+ * Klikspaanweg 58-4 Stauferstr. 6
+ * 2324 LZ Leiden D-71272 Renningen
+ * The Netherlands Germany
+ *
+ * Sources:
+ * Donald Becker's "skeleton.c"
+ * Crynwr ni5010 packet driver
+ *
+ * Changes:
+ * v0.0: First test version
+ * v0.1: First working version
+ * v0.2:
+ * v0.3->v0.90: Now demand setting io and irq when loading as module
+ * 970430 v0.91: modified for Linux 2.1.14
+ * v0.92: Implemented Andreas' (better) NI5010 probe
+ * 970503 v0.93: Fixed auto-irq failure on warm reboot (JB)
+ * 970623 v1.00: First kernel version (AM)
+ * 970814 v1.01: Added detection of onboard receive buffer size (AM)
+ * Bugs:
+ * - None known...
+ * - Note that you have to patch ifconfig for the new /proc/net/dev
+ * format. It gives incorrect stats otherwise.
+ *
+ * To do:
+ * Fix all bugs :-)
+ * Move some stuff to chipset_init()
+ * Handle xmt errors other than collisions
+ * Complete merge with Andreas' driver
+ * Implement ring buffers (Is this useful? You can't squeeze
+ * too many packet in a 2k buffer!)
+ * Implement DMA (Again, is this useful? Some docs says DMA is
+ * slower than programmed I/O)
+ *
+ * Compile with:
+ * gcc -O2 -fomit-frame-pointer -m486 -D__KERNEL__ \
+ * -DMODULE -c ni5010.c
+ *
+ * Insert with e.g.:
+ * insmod ni5010.o io=0x300 irq=5
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "ni5010.h"
+
+static const char *boardname = "NI5010";
+static char *version =
+ "ni5010.c: v1.00 06/23/97 Jan-Pascal van Best and Andreas Mohr\n";
+
+/* bufsize_rcv == 0 means autoprobing */
+unsigned int bufsize_rcv = 0;
+
+#define jumpered_interrupts /* IRQ line jumpered on board */
+#undef jumpered_dma /* No DMA used */
+#undef FULL_IODETECT /* Only detect in portlist */
+
+#ifndef FULL_IODETECT
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int ni5010_portlist[] __initdata =
+ { 0x300, 0x320, 0x340, 0x360, 0x380, 0x3a0, 0 };
+#endif
+
+/* Use 0 for production, 1 for verification, >2 for debug */
+#ifndef NI5010_DEBUG
+#define NI5010_DEBUG 0
+#endif
+
+/* Information that needs to be kept for each board. */
+struct ni5010_local {
+ struct net_device_stats stats;
+ int o_pkt_size;
+ int i_pkt_size;
+};
+
+/* Index to functions, as function prototypes. */
+
+extern int ni5010_probe(struct device *dev);
+static int ni5010_probe1(struct device *dev, int ioaddr);
+static int ni5010_open(struct device *dev);
+static int ni5010_send_packet(struct sk_buff *skb, struct device *dev);
+static void ni5010_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void ni5010_rx(struct device *dev);
+static int ni5010_close(struct device *dev);
+static struct net_device_stats *ni5010_get_stats(struct device *dev);
+static void ni5010_set_multicast_list(struct device *dev);
+static void reset_receiver(struct device *dev);
+
+static int process_xmt_interrupt(struct device *dev);
+#define tx_done(dev) 1
+extern void hardware_send_packet(struct device *dev, char *buf, int length);
+extern void chipset_init(struct device *dev, int startp);
+static void dump_packet(void *buf, int len);
+static void show_registers(struct device *dev);
+
+
+__initfunc(int ni5010_probe(struct device *dev))
+{
+ int *port;
+
+ int base_addr = dev ? dev->base_addr : 0;
+
+ PRINTK2((KERN_DEBUG "%s: Entering ni5010_probe\n", dev->name));
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return ni5010_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return -ENXIO;
+
+#ifdef FULL_IODETECT
+ for (int ioaddr=0x200; ioaddr<0x400; ioaddr+=0x20) {
+ if (check_region(ioaddr, NI5010_IO_EXTENT))
+ continue;
+ if (ni5010_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+#else
+ for (port = ni5010_portlist; *port; port++) {
+ int ioaddr = *port;
+ if (check_region(ioaddr, NI5010_IO_EXTENT))
+ continue;
+ if (ni5010_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+#endif /* FULL_IODETECT */
+ return -ENODEV;
+}
+
+static inline int rd_port(int ioaddr)
+{
+ inb(IE_RBUF);
+ return inb(IE_SAPROM);
+}
+
+__initfunc(void trigger_irq(int ioaddr))
+{
+ outb(0x00, EDLC_RESET); /* Clear EDLC hold RESET state */
+ outb(0x00, IE_RESET); /* Board reset */
+ outb(0x00, EDLC_XMASK); /* Disable all Xmt interrupts */
+ outb(0x00, EDLC_RMASK); /* Disable all Rcv interrupt */
+ outb(0xff, EDLC_XCLR); /* Clear all pending Xmt interrupts */
+ outb(0xff, EDLC_RCLR); /* Clear all pending Rcv interrupts */
+ /*
+ * Transmit packet mode: Ignore parity, Power xcvr,
+ * Enable loopback
+ */
+ outb(XMD_IG_PAR | XMD_T_MODE | XMD_LBC, EDLC_XMODE);
+ outb(RMD_BROADCAST, EDLC_RMODE); /* Receive normal&broadcast */
+ outb(XM_ALL, EDLC_XMASK); /* Enable all Xmt interrupts */
+ udelay(50); /* FIXME: Necessary? */
+ outb(MM_EN_XMT|MM_MUX, IE_MMODE); /* Start transmission */
+}
+
+/*
+ * This is the real probe routine. Linux has a history of friendly device
+ * probes on the ISA bus. A good device probes avoids doing writes, and
+ * verifies that the correct device exists and functions.
+ */
+
+__initfunc(static int ni5010_probe1(struct device *dev, int ioaddr))
+{
+ static unsigned version_printed = 0;
+ int i;
+ unsigned int data;
+ int boguscount = 40;
+
+ /*
+ * This is no "official" probe method, I've rather tested which
+ * probe works best with my seven NI5010 cards
+ * (they have very different serial numbers)
+ * Suggestions or failure reports are very, very welcome !
+ * But I think it is a relatively good probe method
+ * since it doesn't use any "outb"
+ * It should be nearly 100% reliable !
+ * well-known WARNING: this probe method (like many others)
+ * will hang the system if a NE2000 card region is probed !
+ *
+ * - Andreas
+ */
+
+ PRINTK2((KERN_DEBUG "%s: entering ni5010_probe1(%#3x)\n",
+ dev->name, ioaddr));
+
+ if (inb(ioaddr+0) == 0xff) return -ENODEV;
+
+ while ( (rd_port(ioaddr) & rd_port(ioaddr) & rd_port(ioaddr) &
+ rd_port(ioaddr) & rd_port(ioaddr) & rd_port(ioaddr)) != 0xff)
+ {
+ if (boguscount-- == 0) return -ENODEV;
+ }
+
+ PRINTK2((KERN_DEBUG "%s: I/O #1 passed!\n", dev->name));
+
+ for (i=0; i<32; i++)
+ if ( (data = rd_port(ioaddr)) != 0xff) break;
+ if (data==0xff) return -ENODEV;
+
+ PRINTK2((KERN_DEBUG "%s: I/O #2 passed!\n", dev->name));
+
+ if ( (data == SA_ADDR0) &&
+ (rd_port(ioaddr) == SA_ADDR1) &&
+ (rd_port(ioaddr) == SA_ADDR2) ) {
+ for (i=0; i<4; i++) rd_port(ioaddr);
+ if ( (rd_port(ioaddr) != NI5010_MAGICVAL1) ||
+ (rd_port(ioaddr) != NI5010_MAGICVAL2) ) {
+ return -ENODEV;
+ }
+ } else return -ENODEV;
+
+ PRINTK2((KERN_DEBUG "%s: I/O #3 passed!\n", dev->name));
+
+ if (dev == NULL) {
+ dev = init_etherdev(0,0);
+ if (dev == NULL) {
+ printk(KERN_WARNING "%s: Failed to allocate device memory\n", boardname);
+ return -ENOMEM;
+ }
+ }
+
+ if (NI5010_DEBUG && version_printed++ == 0)
+ printk(KERN_INFO "%s", version);
+
+ printk("NI5010 ethercard probe at 0x%x: ", ioaddr);
+
+ dev->base_addr = ioaddr;
+
+ for (i=0; i<6; i++) {
+ outw(i, IE_GP);
+ printk("%2.2x ", dev->dev_addr[i] = inb(IE_SAPROM));
+ }
+
+ PRINTK2((KERN_DEBUG "%s: I/O #4 passed!\n", dev->name));
+
+#ifdef jumpered_interrupts
+ if (dev->irq == 0xff)
+ ;
+ else if (dev->irq < 2) {
+ PRINTK2((KERN_DEBUG "%s: I/O #5 passed!\n", dev->name));
+
+ autoirq_setup(0);
+ trigger_irq(ioaddr);
+ dev->irq = autoirq_report(2);
+
+ PRINTK2((KERN_DEBUG "%s: I/O #6 passed!\n", dev->name));
+
+ if (dev->irq == 0) {
+ printk(KERN_WARNING "%s: no IRQ found!\n", dev->name);
+ return -EAGAIN;
+ }
+ PRINTK2((KERN_DEBUG "%s: I/O #7 passed!\n", dev->name));
+ } else if (dev->irq == 2) {
+ dev->irq = 9;
+ }
+#endif /* jumpered_irq */
+ PRINTK2((KERN_DEBUG "%s: I/O #9 passed!\n", dev->name));
+
+ /* DMA is not supported (yet?), so no use detecting it */
+
+ if (dev->priv == NULL) {
+ dev->priv = kmalloc(sizeof(struct ni5010_local), GFP_KERNEL|GFP_DMA);
+ if (dev->priv == NULL) {
+ printk(KERN_WARNING "%s: Failed to allocate private memory\n", dev->name);
+ return -ENOMEM;
+ }
+ }
+
+ PRINTK2((KERN_DEBUG "%s: I/O #10 passed!\n", dev->name));
+
+/* get the size of the onboard receive buffer
+ * higher addresses than bufsize are wrapped into real buffer
+ * i.e. data for offs. 0x801 is written to 0x1 with a 2K onboard buffer
+ */
+ if (!bufsize_rcv) {
+ outb(1, IE_MMODE); /* Put Rcv buffer on system bus */
+ outw(0, IE_GP); /* Point GP at start of packet */
+ outb(0, IE_RBUF); /* set buffer byte 0 to 0 */
+ for (i = 1; i < 0xff; i++) {
+ outw(i << 8, IE_GP); /* Point GP at packet size to be tested */
+ outb(i, IE_RBUF);
+ outw(0x0, IE_GP); /* Point GP at start of packet */
+ data = inb(IE_RBUF);
+ if (data == i) break;
+ }
+ bufsize_rcv = i << 8;
+ outw(0, IE_GP); /* Point GP at start of packet */
+ outb(0, IE_RBUF); /* set buffer byte 0 to 0 again */
+ }
+ printk("// bufsize rcv/xmt=%d/%d\n", bufsize_rcv, NI5010_BUFSIZE);
+ memset(dev->priv, 0, sizeof(struct ni5010_local));
+
+ /* Grab the region so we can find another board if autoIRQ fails. */
+ request_region(ioaddr, NI5010_IO_EXTENT, boardname);
+
+ dev->open = ni5010_open;
+ dev->stop = ni5010_close;
+ dev->hard_start_xmit = ni5010_send_packet;
+ dev->get_stats = ni5010_get_stats;
+ dev->set_multicast_list = &ni5010_set_multicast_list;
+
+ /* Fill in the fields of the device structure with ethernet values. */
+ ether_setup(dev);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 0;
+
+ dev->flags &= ~IFF_MULTICAST; /* Multicast doesn't work */
+
+ /* Shut up the ni5010 */
+ outb(0, EDLC_RMASK); /* Mask all receive interrupts */
+ outb(0, EDLC_XMASK); /* Mask all xmit interrupts */
+ outb(0xff, EDLC_RCLR); /* Kill all pending rcv interrupts */
+ outb(0xff, EDLC_XCLR); /* Kill all pending xmt interrupts */
+
+ printk(KERN_INFO "%s: NI5010 found at 0x%x, using IRQ %d", dev->name, ioaddr, dev->irq);
+ if (dev->dma) printk(" & DMA %d", dev->dma);
+ printk(".\n");
+
+ printk(KERN_INFO "Join the NI5010 driver development team!\n");
+ printk(KERN_INFO "Mail to a.mohr@mailto.de or jvbest@wi.leidenuniv.nl\n");
+ return 0;
+}
+
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+
+static int ni5010_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+ int i;
+
+ PRINTK2((KERN_DEBUG "%s: entering ni5010_open()\n", dev->name));
+
+ if (request_irq(dev->irq, &ni5010_interrupt, 0, boardname, dev)) {
+ printk(KERN_WARNING "%s: Cannot get irq %#2x\n", dev->name, dev->irq);
+ return -EAGAIN;
+ }
+ PRINTK3((KERN_DEBUG "%s: passed open() #1\n", dev->name));
+ /*
+ * Always allocate the DMA channel after the IRQ,
+ * and clean up on failure.
+ */
+#ifdef jumpered_dma
+ if (request_dma(dev->dma, cardname)) {
+ printk(KERN_WARNING "%s: Cannot get dma %#2x\n", dev->name, dev->dma);
+ free_irq(dev->irq, NULL);
+ return -EAGAIN;
+ }
+#endif /* jumpered_dma */
+
+ PRINTK3((KERN_DEBUG "%s: passed open() #2\n", dev->name));
+ /* Reset the hardware here. Don't forget to set the station address. */
+
+ outb(RS_RESET, EDLC_RESET); /* Hold up EDLC_RESET while configing board */
+ outb(0, IE_RESET); /* Hardware reset of ni5010 board */
+ outb(XMD_LBC, EDLC_XMODE); /* Only loopback xmits */
+
+ PRINTK3((KERN_DEBUG "%s: passed open() #3\n", dev->name));
+ /* Set the station address */
+ for(i = 0;i < 6; i++) {
+ outb(dev->dev_addr[i], EDLC_ADDR + i);
+ }
+
+ PRINTK3((KERN_DEBUG "%s: Initialising ni5010\n", dev->name));
+ outb(0, EDLC_XMASK); /* No xmit interrupts for now */
+ outb(XMD_IG_PAR | XMD_T_MODE | XMD_LBC, EDLC_XMODE);
+ /* Normal packet xmit mode */
+ outb(0xff, EDLC_XCLR); /* Clear all pending xmit interrupts */
+ outb(RMD_BROADCAST, EDLC_RMODE);
+ /* Receive broadcast and normal packets */
+ reset_receiver(dev); /* Ready ni5010 for receiving packets */
+
+ outb(0, EDLC_RESET); /* Un-reset the ni5010 */
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ if (NI5010_DEBUG) show_registers(dev);
+
+ MOD_INC_USE_COUNT;
+ PRINTK((KERN_DEBUG "%s: open successful\n", dev->name));
+ return 0;
+}
+
+static void reset_receiver(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ PRINTK3((KERN_DEBUG "%s: resetting receiver\n", dev->name));
+ outw(0, IE_GP); /* Receive packet at start of buffer */
+ outb(0xff, EDLC_RCLR); /* Clear all pending rcv interrupts */
+ outb(0, IE_MMODE); /* Put EDLC to rcv buffer */
+ outb(MM_EN_RCV, IE_MMODE); /* Enable rcv */
+ outb(0xff, EDLC_RMASK); /* Enable all rcv interrupts */
+}
+
+static int ni5010_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ PRINTK2((KERN_DEBUG "%s: entering ni5010_send_packet\n", dev->name));
+ if (dev->tbusy) {
+ /*
+ * If we get here, some higher level has decided we are broken.
+ * There should really be a "kick me" function call instead.
+ */
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+ printk("tbusy\n");
+ printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name,
+ tx_done(dev) ? "IRQ conflict" : "network cable problem");
+ /* Try to restart the adaptor. */
+ /* FIXME: Give it a real kick here */
+ chipset_init(dev, 1);
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ }
+
+ /*
+ * Block a timer-based transmit from overlapping. This could better be
+ * done with atomic_swap(1, dev->tbusy), but test_and_set_bit() works as well.
+ */
+ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+ printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name);
+ return 1;
+ } else {
+ int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+
+ hardware_send_packet(dev, (unsigned char *)skb->data, length);
+ dev->trans_start = jiffies;
+ }
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ return 0;
+}
+
+/*
+ * The typical workload of the driver:
+ * Handle the network interface interrupts.
+ */
+static void
+ni5010_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct device *dev = dev_id;
+ struct ni5010_local *lp;
+ int ioaddr, status;
+ int xmit_was_error = 0;
+
+ if (dev == NULL || dev->irq != irq) {
+ printk(KERN_WARNING "%s: irq %d for unknown device.\n",
+ boardname, irq);
+ return;
+ }
+
+ if (dev->interrupt) printk(KERN_WARNING "%s: Reentering IRQ-handler!\n", dev->name);
+ dev->interrupt = 1;
+
+ PRINTK2((KERN_DEBUG "%s: entering ni5010_interrupt\n", dev->name));
+
+ ioaddr = dev->base_addr;
+ lp = (struct ni5010_local *)dev->priv;
+
+ status = inb(IE_ISTAT);
+ PRINTK3((KERN_DEBUG "%s: IE_ISTAT = %#02x\n", dev->name, status));
+
+ if ((status & IS_R_INT) == 0) ni5010_rx(dev);
+
+ if ((status & IS_X_INT) == 0) {
+ xmit_was_error = process_xmt_interrupt(dev);
+ }
+
+ if ((status & IS_DMA_INT) == 0) {
+ PRINTK((KERN_DEBUG "%s: DMA complete (???)\n", dev->name));
+ outb(0, IE_DMA_RST); /* Reset DMA int */
+ }
+
+ if (!xmit_was_error)
+ reset_receiver(dev);
+
+ dev->interrupt = 0;
+ return;
+}
+
+
+static void dump_packet(void *buf, int len)
+{
+ int i;
+
+ printk(KERN_DEBUG "Packet length = %#4x\n", len);
+ for (i = 0; i < len; i++){
+ if (i % 16 == 0) printk(KERN_DEBUG "%#4.4x", i);
+ if (i % 2 == 0) printk(" ");
+ printk("%2.2x", ((unsigned char *)buf)[i]);
+ if (i % 16 == 15) printk("\n");
+ }
+ printk("\n");
+
+ return;
+}
+
+/* We have a good packet, get it out of the buffer. */
+static void
+ni5010_rx(struct device *dev)
+{
+ struct ni5010_local *lp = (struct ni5010_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ unsigned char rcv_stat;
+ struct sk_buff *skb;
+
+ PRINTK2((KERN_DEBUG "%s: entering ni5010_rx()\n", dev->name));
+
+ rcv_stat = inb(EDLC_RSTAT);
+ PRINTK3((KERN_DEBUG "%s: EDLC_RSTAT = %#2x\n", dev->name, rcv_stat));
+
+ if ( (rcv_stat & RS_VALID_BITS) != RS_PKT_OK) {
+ PRINTK((KERN_INFO "%s: receive error.\n", dev->name));
+ lp->stats.rx_errors++;
+ if (rcv_stat & RS_RUNT) lp->stats.rx_length_errors++;
+ if (rcv_stat & RS_ALIGN) lp->stats.rx_frame_errors++;
+ if (rcv_stat & RS_CRC_ERR) lp->stats.rx_crc_errors++;
+ if (rcv_stat & RS_OFLW) lp->stats.rx_fifo_errors++;
+ outb(0xff, EDLC_RCLR); /* Clear the interrupt */
+ return;
+ }
+
+ outb(0xff, EDLC_RCLR); /* Clear the interrupt */
+
+ lp->i_pkt_size = inw(IE_RCNT);
+ if (lp->i_pkt_size > ETH_FRAME_LEN || lp->i_pkt_size < 10 ) {
+ PRINTK((KERN_DEBUG "%s: Packet size error, packet size = %#4.4x\n",
+ dev->name, lp->i_pkt_size));
+ lp->stats.rx_errors++;
+ lp->stats.rx_length_errors++;
+ return;
+ }
+
+ /* Malloc up new buffer. */
+ skb = dev_alloc_skb(lp->i_pkt_size + 3);
+ if (skb == NULL) {
+ printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ return;
+ }
+
+ skb->dev = dev;
+ skb_reserve(skb, 2);
+
+ /* Read packet into buffer */
+ outb(MM_MUX, IE_MMODE); /* Rcv buffer to system bus */
+ outw(0, IE_GP); /* Seek to beginning of packet */
+ insb(IE_RBUF, skb_put(skb, lp->i_pkt_size), lp->i_pkt_size);
+
+ if (NI5010_DEBUG >= 4)
+ dump_packet(skb->data, skb->len);
+
+ skb->protocol = eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes += lp->i_pkt_size;
+
+ PRINTK2((KERN_DEBUG "%s: Received packet, size=%#4.4x\n",
+ dev->name, lp->i_pkt_size));
+
+}
+
+static int process_xmt_interrupt(struct device *dev)
+{
+ struct ni5010_local *lp = (struct ni5010_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int xmit_stat;
+
+ PRINTK2((KERN_DEBUG "%s: entering process_xmt_interrupt\n", dev->name));
+
+ xmit_stat = inb(EDLC_XSTAT);
+ PRINTK3((KERN_DEBUG "%s: EDLC_XSTAT = %2.2x\n", dev->name, xmit_stat));
+
+ outb(0, EDLC_XMASK); /* Disable xmit IRQ's */
+ outb(0xff, EDLC_XCLR); /* Clear all pending xmit IRQ's */
+
+ if (xmit_stat & XS_COLL){
+ printk("ether collision\n"); /* FIXME: remove */
+ PRINTK((KERN_DEBUG "%s: collision detected, retransmitting\n",
+ dev->name));
+ outw(NI5010_BUFSIZE - lp->o_pkt_size, IE_GP);
+ /* outb(0, IE_MMODE); */ /* xmt buf on sysbus FIXME: needed ? */
+ outb(MM_EN_XMT | MM_MUX, IE_MMODE);
+ outb(XM_ALL, EDLC_XMASK); /* Enable xmt IRQ's */
+ lp->stats.collisions++;
+ return 1;
+ }
+
+ /* FIXME: handle other xmt error conditions */
+
+ lp->stats.tx_packets++;
+ lp->stats.tx_bytes += lp->o_pkt_size;
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+
+ PRINTK2((KERN_DEBUG "%s: sent packet, size=%#4.4x\n",
+ dev->name, lp->o_pkt_size));
+
+ return 0;
+}
+
+/* The inverse routine to ni5010_open(). */
+static int
+ni5010_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ PRINTK2((KERN_DEBUG "%s: entering ni5010_close\n", dev->name));
+#ifdef jumpered_interrupts
+ free_irq(dev->irq, NULL);
+#endif
+ /* Put card in held-RESET state */
+ outb(0, IE_MMODE);
+ outb(RS_RESET, EDLC_RESET);
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ MOD_DEC_USE_COUNT;
+ PRINTK((KERN_DEBUG "%s: %s closed down\n", dev->name, boardname));
+ return 0;
+
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct net_device_stats *
+ni5010_get_stats(struct device *dev)
+{
+ struct ni5010_local *lp = (struct ni5010_local *)dev->priv;
+
+ PRINTK2((KERN_DEBUG "%s: entering ni5010_get_stats\n", dev->name));
+
+ if (NI5010_DEBUG) show_registers(dev);
+
+ /* cli(); */
+ /* Update the statistics from the device registers. */
+ /* We do this in the interrupt handler */
+ /* sti(); */
+
+ return &lp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ num_addrs == -1 Promiscuous mode, receive all packets
+ num_addrs == 0 Normal mode, clear multicast list
+ num_addrs > 0 Multicast mode, receive normal and MC packets, and do
+ best-effort filtering.
+*/
+static void
+ni5010_set_multicast_list(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ PRINTK2((KERN_DEBUG "%s: entering set_multicast_list\n", dev->name));
+
+ if (dev->flags&IFF_PROMISC || dev->flags&IFF_ALLMULTI) {
+ dev->flags |= IFF_PROMISC;
+ outb(RMD_PROMISC, EDLC_RMODE); /* Enable promiscuous mode */
+ PRINTK((KERN_DEBUG "%s: Entering promiscuous mode\n", dev->name));
+ } else if (dev->mc_list) {
+ /* Sorry, multicast not supported */
+ PRINTK((KERN_DEBUG "%s: No multicast, entering broadcast mode\n", dev->name));
+ outb(RMD_BROADCAST, EDLC_RMODE);
+ } else {
+ PRINTK((KERN_DEBUG "%s: Entering broadcast mode\n", dev->name));
+ outb(RMD_BROADCAST, EDLC_RMODE); /* Disable promiscuous mode, use normal mode */
+ }
+}
+
+extern void hardware_send_packet(struct device *dev, char *buf, int length)
+{
+ struct ni5010_local *lp = (struct ni5010_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ unsigned long flags;
+ unsigned int buf_offs;
+
+ PRINTK2((KERN_DEBUG "%s: entering hardware_send_packet\n", dev->name));
+
+ if (length > ETH_FRAME_LEN) {
+ PRINTK((KERN_WARNING "%s: packet too large, not possible\n",
+ dev->name));
+ return;
+ }
+
+ if (NI5010_DEBUG) show_registers(dev);
+
+ if (inb(IE_ISTAT) & IS_EN_XMT) {
+ PRINTK((KERN_WARNING "%s: sending packet while already transmitting, not possible\n",
+ dev->name));
+ return;
+ }
+
+ if (NI5010_DEBUG > 3) dump_packet(buf, length);
+
+ buf_offs = NI5010_BUFSIZE - length;
+ lp->o_pkt_size = length;
+
+ save_flags(flags);
+ cli();
+
+ outb(0, EDLC_RMASK); /* Mask all receive interrupts */
+ outb(0, IE_MMODE); /* Put Xmit buffer on system bus */
+ outb(0xff, EDLC_RCLR); /* Clear out pending rcv interrupts */
+
+ outw(buf_offs, IE_GP); /* Point GP at start of packet */
+ outsb(IE_XBUF, buf, length); /* Put data in buffer */
+ outw(buf_offs, IE_GP); /* Rewrite where packet starts */
+
+ /* should work without that outb() (Crynwr used it) */
+ /*outb(MM_MUX, IE_MMODE);*/ /* Xmt buffer to EDLC bus */
+ outb(MM_EN_XMT | MM_MUX, IE_MMODE); /* Begin transmission */
+ outb(XM_ALL, EDLC_XMASK); /* Cause interrupt after completion or fail */
+
+ restore_flags(flags);
+
+ if (NI5010_DEBUG) show_registers(dev);
+}
+
+extern void chipset_init(struct device *dev, int startp)
+{
+ /* FIXME: Move some stuff here */
+ PRINTK3((KERN_DEBUG "%s: doing NOTHING in chipset_init\n", dev->name));
+}
+
+static void show_registers(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ PRINTK3((KERN_DEBUG "%s: XSTAT %#2.2x\n", dev->name, inb(EDLC_XSTAT)));
+ PRINTK3((KERN_DEBUG "%s: XMASK %#2.2x\n", dev->name, inb(EDLC_XMASK)));
+ PRINTK3((KERN_DEBUG "%s: RSTAT %#2.2x\n", dev->name, inb(EDLC_RSTAT)));
+ PRINTK3((KERN_DEBUG "%s: RMASK %#2.2x\n", dev->name, inb(EDLC_RMASK)));
+ PRINTK3((KERN_DEBUG "%s: RMODE %#2.2x\n", dev->name, inb(EDLC_RMODE)));
+ PRINTK3((KERN_DEBUG "%s: XMODE %#2.2x\n", dev->name, inb(EDLC_XMODE)));
+ PRINTK3((KERN_DEBUG "%s: ISTAT %#2.2x\n", dev->name, inb(IE_ISTAT)));
+}
+
+#ifdef MODULE
+static struct device dev_ni5010 = {
+ " ",
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, ni5010_probe };
+
+int io = 0;
+int irq = 0;
+
+MODULE_PARM(io, "i");
+MODULE_PARM(irq, "i");
+
+int init_module(void)
+{
+ int result;
+
+ PRINTK2((KERN_DEBUG "%s: entering init_module\n", boardname));
+ /*
+ if(io <= 0 || irq == 0){
+ printk(KERN_WARNING "%s: Autoprobing not allowed for modules.\n", boardname);
+ printk(KERN_WARNING "%s: Set symbols 'io' and 'irq'\n", boardname);
+ return -EINVAL;
+ }
+ */
+ if (io <= 0){
+ printk(KERN_WARNING "%s: Autoprobing for modules is hazardous, trying anyway..\n", boardname);
+ }
+
+ PRINTK2((KERN_DEBUG "%s: init_module irq=%#2x, io=%#3x\n", boardname, irq, io));
+ dev_ni5010.irq=irq;
+ dev_ni5010.base_addr=io;
+ if ((result = register_netdev(&dev_ni5010)) != 0) {
+ PRINTK((KERN_WARNING "%s: register_netdev returned %d.\n",
+ boardname, result));
+ return -EIO;
+ }
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ PRINTK2((KERN_DEBUG "%s: entering cleanup_module\n", boardname));
+
+ unregister_netdev(&dev_ni5010);
+
+ release_region(dev_ni5010.base_addr, NI5010_IO_EXTENT);
+ if (dev_ni5010.priv != NULL){
+ kfree(dev_ni5010.priv);
+ dev_ni5010.priv = NULL;
+ }
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c ni5010.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * End:
+ */
diff --git a/drivers/net/ni5010.h b/drivers/net/ni5010.h
new file mode 100644
index 000000000..00c47822b
--- /dev/null
+++ b/drivers/net/ni5010.h
@@ -0,0 +1,144 @@
+/*
+ * Racal-Interlan ni5010 Ethernet definitions
+ *
+ * This is an extension to the Linux operating system, and is covered by the
+ * same Gnu Public License that covers that work.
+ *
+ * copyrights (c) 1996 by Jan-Pascal van Best (jvbest@wi.leidenuniv.nl)
+ *
+ * I have done a look in the following sources:
+ * crynwr-packet-driver by Russ Nelson
+ */
+
+#define NI5010_BUFSIZE 2048 /* number of bytes in a buffer */
+
+#define NI5010_MAGICVAL0 0x00 /* magic-values for ni5010 card */
+#define NI5010_MAGICVAL1 0x55
+#define NI5010_MAGICVAL2 0xAA
+
+#define SA_ADDR0 0x02
+#define SA_ADDR1 0x07
+#define SA_ADDR2 0x01
+
+/* The number of low I/O ports used by the ni5010 ethercard. */
+#define NI5010_IO_EXTENT 32
+
+#define PRINTK(x) if (NI5010_DEBUG) printk x
+#define PRINTK2(x) if (NI5010_DEBUG>=2) printk x
+#define PRINTK3(x) if (NI5010_DEBUG>=3) printk x
+
+/* The various IE command registers */
+#define EDLC_XSTAT (ioaddr + 0x00) /* EDLC transmit csr */
+#define EDLC_XCLR (ioaddr + 0x00) /* EDLC transmit "Clear IRQ" */
+#define EDLC_XMASK (ioaddr + 0x01) /* EDLC transmit "IRQ Masks" */
+#define EDLC_RSTAT (ioaddr + 0x02) /* EDLC receive csr */
+#define EDLC_RCLR (ioaddr + 0x02) /* EDLC receive "Clear IRQ" */
+#define EDLC_RMASK (ioaddr + 0x03) /* EDLC receive "IRQ Masks" */
+#define EDLC_XMODE (ioaddr + 0x04) /* EDLC transmit Mode */
+#define EDLC_RMODE (ioaddr + 0x05) /* EDLC receive Mode */
+#define EDLC_RESET (ioaddr + 0x06) /* EDLC RESET register */
+#define EDLC_TDR1 (ioaddr + 0x07) /* "Time Domain Reflectometry" reg1 */
+#define EDLC_ADDR (ioaddr + 0x08) /* EDLC station address, 6 bytes */
+ /* 0x0E doesn't exist for r/w */
+#define EDLC_TDR2 (ioaddr + 0x0f) /* "Time Domain Reflectometry" reg2 */
+#define IE_GP (ioaddr + 0x10) /* GP pointer (word register) */
+ /* 0x11 is 2nd byte of GP Pointer */
+#define IE_RCNT (ioaddr + 0x10) /* Count of bytes in rcv'd packet */
+ /* 0x11 is 2nd byte of "Byte Count" */
+#define IE_MMODE (ioaddr + 0x12) /* Memory Mode register */
+#define IE_DMA_RST (ioaddr + 0x13) /* IE DMA Reset. write only */
+#define IE_ISTAT (ioaddr + 0x13) /* IE Interrupt Status. read only */
+#define IE_RBUF (ioaddr + 0x14) /* IE Receive Buffer port */
+#define IE_XBUF (ioaddr + 0x15) /* IE Transmit Buffer port */
+#define IE_SAPROM (ioaddr + 0x16) /* window on station addr prom */
+#define IE_RESET (ioaddr + 0x17) /* any write causes Board Reset */
+
+/* bits in EDLC_XSTAT, interrupt clear on write, status when read */
+#define XS_TPOK 0x80 /* transmit packet successful */
+#define XS_CS 0x40 /* carrier sense */
+#define XS_RCVD 0x20 /* transmitted packet received */
+#define XS_SHORT 0x10 /* transmission media is shorted */
+#define XS_UFLW 0x08 /* underflow. iff failed board */
+#define XS_COLL 0x04 /* collision occurred */
+#define XS_16COLL 0x02 /* 16th collision occurred */
+#define XS_PERR 0x01 /* parity error */
+
+#define XS_CLR_UFLW 0x08 /* clear underflow */
+#define XS_CLR_COLL 0x04 /* clear collision */
+#define XS_CLR_16COLL 0x02 /* clear 16th collision */
+#define XS_CLR_PERR 0x01 /* clear parity error */
+
+/* bits in EDLC_XMASK, mask/enable transmit interrupts. register is r/w */
+#define XM_TPOK 0x80 /* =1 to enable Xmt Pkt OK interrupts */
+#define XM_RCVD 0x20 /* =1 to enable Xmt Pkt Rcvd ints */
+#define XM_UFLW 0x08 /* =1 to enable Xmt Underflow ints */
+#define XM_COLL 0x04 /* =1 to enable Xmt Collision ints */
+#define XM_COLL16 0x02 /* =1 to enable Xmt 16th Coll ints */
+#define XM_PERR 0x01 /* =1 to enable Xmt Parity Error ints */
+ /* note: always clear this bit */
+#define XM_ALL (XM_TPOK | XM_RCVD | XM_UFLW | XM_COLL | XM_COLL16)
+
+/* bits in EDLC_RSTAT, interrupt clear on write, status when read */
+#define RS_PKT_OK 0x80 /* received good packet */
+#define RS_RST_PKT 0x10 /* RESET packet received */
+#define RS_RUNT 0x08 /* Runt Pkt rcvd. Len < 64 Bytes */
+#define RS_ALIGN 0x04 /* Alignment error. not 8 bit aligned */
+#define RS_CRC_ERR 0x02 /* Bad CRC on rcvd pkt */
+#define RS_OFLW 0x01 /* overflow for rcv FIFO */
+#define RS_VALID_BITS ( RS_PKT_OK | RS_RST_PKT | RS_RUNT | RS_ALIGN | RS_CRC_ERR | RS_OFLW )
+ /* all valid RSTAT bits */
+
+#define RS_CLR_PKT_OK 0x80 /* clear rcvd packet interrupt */
+#define RS_CLR_RST_PKT 0x10 /* clear RESET packet received */
+#define RS_CLR_RUNT 0x08 /* clear Runt Pckt received */
+#define RS_CLR_ALIGN 0x04 /* clear Alignment error */
+#define RS_CLR_CRC_ERR 0x02 /* clear CRC error */
+#define RS_CLR_OFLW 0x01 /* clear rcv FIFO Overflow */
+
+/* bits in EDLC_RMASK, mask/enable receive interrupts. register is r/w */
+#define RM_PKT_OK 0x80 /* =1 to enable rcvd good packet ints */
+#define RM_RST_PKT 0x10 /* =1 to enable RESET packet ints */
+#define RM_RUNT 0x08 /* =1 to enable Runt Pkt rcvd ints */
+#define RM_ALIGN 0x04 /* =1 to enable Alignment error ints */
+#define RM_CRC_ERR 0x02 /* =1 to enable Bad CRC error ints */
+#define RM_OFLW 0x01 /* =1 to enable overflow error ints */
+
+/* bits in EDLC_RMODE, set Receive Packet mode. register is r/w */
+#define RMD_TEST 0x80 /* =1 for Chip testing. normally 0 */
+#define RMD_ADD_SIZ 0x10 /* =1 5-byte addr match. normally 0 */
+#define RMD_EN_RUNT 0x08 /* =1 enable runt rcv. normally 0 */
+#define RMD_EN_RST 0x04 /* =1 to rcv RESET pkt. normally 0 */
+
+#define RMD_PROMISC 0x03 /* receive *all* packets. unusual */
+#define RMD_MULTICAST 0x02 /* receive multicasts too. unusual */
+#define RMD_BROADCAST 0x01 /* receive broadcasts & normal. usual */
+#define RMD_NO_PACKETS 0x00 /* don't receive any packets. unusual */
+
+/* bits in EDLC_XMODE, set Transmit Packet mode. register is r/w */
+#define XMD_COLL_CNT 0xf0 /* coll's since success. read-only */
+#define XMD_IG_PAR 0x08 /* =1 to ignore parity. ALWAYS set */
+#define XMD_T_MODE 0x04 /* =1 to power xcvr. ALWAYS set this */
+#define XMD_LBC 0x02 /* =1 for loopbakc. normally set */
+#define XMD_DIS_C 0x01 /* =1 disables contention. normally 0 */
+
+/* bits in EDLC_RESET, write only */
+#define RS_RESET 0x80 /* =1 to hold EDLC in reset state */
+
+/* bits in IE_MMODE, write only */
+#define MM_EN_DMA 0x80 /* =1 begin DMA xfer, Cplt clrs it */
+#define MM_EN_RCV 0x40 /* =1 allows Pkt rcv. clr'd by rcv */
+#define MM_EN_XMT 0x20 /* =1 begin Xmt pkt. Cplt clrs it */
+#define MM_BUS_PAGE 0x18 /* =00 ALWAYS. Used when MUX=1 */
+#define MM_NET_PAGE 0x06 /* =00 ALWAYS. Used when MUX=0 */
+#define MM_MUX 0x01 /* =1 means Rcv Buff on system bus */
+ /* =0 means Xmt Buff on system bus */
+
+/* bits in IE_ISTAT, read only */
+#define IS_TDIAG 0x80 /* =1 if Diagnostic problem */
+#define IS_EN_RCV 0x20 /* =1 until frame is rcv'd cplt */
+#define IS_EN_XMT 0x10 /* =1 until frame is xmt'd cplt */
+#define IS_EN_DMA 0x08 /* =1 until DMA is cplt or aborted */
+#define IS_DMA_INT 0x04 /* =0 iff DMA done interrupt. */
+#define IS_R_INT 0x02 /* =0 iff unmasked Rcv interrupt */
+#define IS_X_INT 0x01 /* =0 iff unmasked Xmt interrupt */
+
diff --git a/drivers/net/ni52.c b/drivers/net/ni52.c
index 0935f4f0b..332addbe3 100644
--- a/drivers/net/ni52.c
+++ b/drivers/net/ni52.c
@@ -240,8 +240,7 @@ struct priv
*/
static int ni52_close(struct device *dev)
{
- free_irq(dev->irq, NULL);
- irq2dev_map[dev->irq] = NULL;
+ free_irq(dev->irq, dev);
ni_reset586(); /* the hard way to stop the receiver */
@@ -264,12 +263,11 @@ static int ni52_open(struct device *dev)
startrecv586(dev);
ni_enaint();
- if(request_irq(dev->irq, &ni52_interrupt,0,"ni5210",NULL))
+ if(request_irq(dev->irq, &ni52_interrupt,0,"ni5210",dev))
{
ni_reset586();
return -EAGAIN;
}
- irq2dev_map[dev->irq] = dev;
dev->interrupt = 0;
dev->tbusy = 0;
@@ -819,7 +817,7 @@ static void *alloc_rfa(struct device *dev,void *ptr)
static void ni52_interrupt(int irq,void *dev_id,struct pt_regs *reg_ptr)
{
- struct device *dev = (struct device *) irq2dev_map[irq];
+ struct device *dev = dev_id;
unsigned short stat;
int cnt=0;
struct priv *p;
@@ -1157,14 +1155,6 @@ static int ni52_send_packet(struct sk_buff *skb, struct device *dev)
return 0;
}
- if(skb == NULL)
- {
- dev_tint(dev);
- return 0;
- }
-
- if (skb->len <= 0)
- return 0;
if(skb->len > XMIT_BUFF_SIZE)
{
printk("%s: Sorry, max. framelength is %d bytes. The length of your frame is %d bytes.\n",dev->name,XMIT_BUFF_SIZE,skb->len);
diff --git a/drivers/net/ni65.c b/drivers/net/ni65.c
index a008a3ea7..e0812c6d6 100644
--- a/drivers/net/ni65.c
+++ b/drivers/net/ni65.c
@@ -268,13 +268,12 @@ static int ni65_open(struct device *dev)
{
struct priv *p = (struct priv *) dev->priv;
int irqval = request_irq(dev->irq, &ni65_interrupt,0,
- cards[p->cardno].cardname,NULL);
+ cards[p->cardno].cardname,dev);
if (irqval) {
printk ("%s: unable to get IRQ %d (irqval=%d).\n",
dev->name,dev->irq, irqval);
return -EAGAIN;
}
- irq2dev_map[dev->irq] = dev;
if(ni65_lance_reinit(dev))
{
@@ -286,8 +285,7 @@ static int ni65_open(struct device *dev)
}
else
{
- irq2dev_map[dev->irq] = NULL;
- free_irq(dev->irq,NULL);
+ free_irq(dev->irq,dev);
dev->start = 0;
return -EAGAIN;
}
@@ -314,8 +312,7 @@ static int ni65_close(struct device *dev)
}
}
#endif
- irq2dev_map[dev->irq] = NULL;
- free_irq(dev->irq,NULL);
+ free_irq(dev->irq,dev);
dev->tbusy = 1;
dev->start = 0;
MOD_DEC_USE_COUNT;
@@ -795,7 +792,7 @@ static int ni65_lance_reinit(struct device *dev)
static void ni65_interrupt(int irq, void * dev_id, struct pt_regs * regs)
{
int csr0;
- struct device *dev = (struct device *) irq2dev_map[irq];
+ struct device *dev = dev_id;
struct priv *p;
int bcnt = 32;
diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c
index b4c973e5f..d9c8ce39b 100644
--- a/drivers/net/pcnet32.c
+++ b/drivers/net/pcnet32.c
@@ -597,14 +597,6 @@ pcnet32_start_xmit(struct sk_buff *skb, struct device *dev)
return 0;
}
- if (skb == NULL) {
- dev_tint(dev);
- return 0;
- }
-
- if (skb->len <= 0)
- return 0;
-
if (pcnet32_debug > 3) {
outw(0x0000, ioaddr+PCNET32_ADDR);
printk("%s: pcnet32_start_xmit() called, csr0 %4.4x.\n", dev->name,
diff --git a/drivers/net/pi2.c b/drivers/net/pi2.c
index 6a2b4f112..3eb3a9223 100644
--- a/drivers/net/pi2.c
+++ b/drivers/net/pi2.c
@@ -1374,7 +1374,7 @@ static int pi_probe(struct device *dev, int card_type)
now. There is no point in waiting since no other device can use
the interrupt, and this marks the 'irqaction' as busy. */
{
- int irqval = request_irq(dev->irq, &pi_interrupt,0, "pi2", NULL);
+ int irqval = request_irq(dev->irq, &pi_interrupt,0, "pi2", dev);
if (irqval) {
printk(KERN_ERR "PI: unable to get IRQ %d (irqval=%d).\n",
dev->irq, irqval);
@@ -1413,12 +1413,6 @@ static int pi_probe(struct device *dev, int card_type)
/* New-style flags. */
dev->flags = 0;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = 4;
-
return 0;
}
@@ -1439,10 +1433,9 @@ static int pi_open(struct device *dev)
if (dev->base_addr & 2) { /* if A channel */
if (first_time) {
if (request_dma(dev->dma,"pi2")) {
- free_irq(dev->irq, NULL);
+ free_irq(dev->irq, dev);
return -EAGAIN;
}
- irq2dev_map[dev->irq] = dev;
}
/* Reset the hardware here. */
chipset_init(dev);
@@ -1670,9 +1663,8 @@ int init_module(void)
void cleanup_module(void)
{
- free_irq(pi0a.irq, NULL); /* IRQs and IO Ports are shared */
+ free_irq(pi0a.irq, &pi0a); /* IRQs and IO Ports are shared */
release_region(pi0a.base_addr & 0x3f0, PI_TOTAL_SIZE);
- irq2dev_map[pi0a.irq] = NULL;
kfree(pi0a.priv);
pi0a.priv = NULL;
diff --git a/drivers/net/plip.c b/drivers/net/plip.c
index 223210e91..17c85a9ba 100644
--- a/drivers/net/plip.c
+++ b/drivers/net/plip.c
@@ -1,3 +1,5 @@
+#warning This wont work until we merge the networking changes
+#if 0
/* $Id: plip.c,v 1.3.6.2 1997/04/16 15:07:56 phil Exp $ */
/* PLIP: A parallel port "network" driver for Linux. */
/* This driver is for parallel port with 5-bit cable (LapLink (R) cable). */
@@ -99,6 +101,7 @@ static const char *version = "NET3 PLIP version 2.2-parport gniibe@mri.co.jp\n";
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/inetdevice.h>
#include <linux/skbuff.h>
#include <linux/if_plip.h>
@@ -258,6 +261,7 @@ plip_init_dev(struct device *dev, struct parport *pb))
dev->do_ioctl = plip_ioctl;
dev->tx_queue_len = 10;
dev->flags = IFF_POINTOPOINT|IFF_NOARP;
+ memset(dev->dev_addr, 0xfc, ETH_ALEN);
/* Set the private structure */
dev->priv = kmalloc(sizeof (struct net_local), GFP_KERNEL);
@@ -797,7 +801,7 @@ plip_error(struct device *dev, struct net_local *nl,
static void
plip_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
- struct device *dev = (struct device *) irq2dev_map[irq];
+ struct device *dev = dev_id;
struct net_local *nl;
struct plip_local *rcv;
unsigned char c0;
@@ -857,32 +861,12 @@ plip_rebuild_header(struct sk_buff *skb)
struct device *dev = skb->dev;
struct net_local *nl = (struct net_local *)dev->priv;
struct ethhdr *eth = (struct ethhdr *)skb->data;
- int i;
if ((dev->flags & IFF_NOARP)==0)
return nl->orig_rebuild_header(skb);
- if (eth->h_proto != __constant_htons(ETH_P_IP)
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- && eth->h_proto != __constant_htons(ETH_P_IPV6)
-#endif
- ) {
- printk("plip_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto);
- memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
- return 0;
- }
-
- for (i=0; i < ETH_ALEN - sizeof(u32); i++)
- eth->h_dest[i] = 0xfc;
-#if 0
- *(u32 *)(eth->h_dest+i) = dst;
-#else
- /* Do not want to include net/route.h here.
- * In any case, it is TOP of silliness to emulate
- * hardware addresses on PtP link. --ANK
- */
- *(u32 *)(eth->h_dest+i) = 0;
-#endif
+ memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+ memcpy(eth->h_dest, dev->broadcast, dev->addr_len);
return 0;
}
@@ -902,14 +886,6 @@ plip_tx_packet(struct sk_buff *skb, struct device *dev)
nl->port_owner = 1;
}
- /* If some higher layer thinks we've missed an tx-done interrupt
- we are passed NULL. Caution: dev_tint() handles the cli()/sti()
- itself. */
- if (skb == NULL) {
- dev_tint(dev);
- return 0;
- }
-
if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
printk("%s: Transmitter access conflict.\n", dev->name);
return 1;
@@ -950,7 +926,7 @@ static int
plip_open(struct device *dev)
{
struct net_local *nl = (struct net_local *)dev->priv;
- int i;
+ struct in_device *in_dev;
/* Grab the port */
if (!nl->port_owner) {
@@ -973,15 +949,26 @@ plip_open(struct device *dev)
nl->is_deferred = 0;
/* Fill in the MAC-level header. */
- for (i=0; i < ETH_ALEN - sizeof(u32); i++)
- dev->dev_addr[i] = 0xfc;
- /* Ugh, this is like death. */
- *(u32 *)(dev->dev_addr+i) = dev->pa_addr;
+ memset(dev->dev_addr, 0xfc, ETH_ALEN);
+
+ /* Now PLIP doesnt have a real mac addr which is a pain..
+ we need to create one, and to be DOS compatible its a good
+ idea to use the same rules. Layering purists please look away */
+
+ if((in_dev=dev->ip_ptr)!=NULL)
+ {
+ /*
+ * Any address wil do - we take the first
+ */
+ struct in_ifaddr *ifa=in_dev->ifa_list;
+ if(ifa!=NULL)
+ memcpy(dev->dev_addr+2,&ifa->ifa_local,4);
+ }
+
dev->interrupt = 0;
dev->start = 1;
dev->tbusy = 0;
- irq2dev_map[dev->irq] = dev;
MOD_INC_USE_COUNT;
return 0;
@@ -998,7 +985,6 @@ plip_close(struct device *dev)
dev->tbusy = 1;
dev->start = 0;
cli();
- irq2dev_map[dev->irq] = NULL;
sti();
#ifdef NOTDEF
outb(0x00, PAR_DATA(dev));
@@ -1067,7 +1053,6 @@ plip_wakeup(void *handle)
if (!parport_claim(nl->pardev)) {
nl->port_owner = 1;
- irq2dev_map[dev->irq] = dev;
/* Clear the data port. */
outb (0x00, PAR_DATA(dev));
}
@@ -1120,7 +1105,7 @@ plip_ioctl(struct device *dev, struct ifreq *rq, int cmd)
return 0;
}
-static int parport[PLIP_MAX] = { -1, };
+static int parport[PLIP_MAX] = { [0 ... PLIP_MAX-1] = -1 };
static int timid = 0;
MODULE_PARM(parport, "1-" __MODULE_STRING(PLIP_MAX) "i");
@@ -1253,3 +1238,4 @@ plip_init(void))
* compile-command: "gcc -DMODULE -DMODVERSIONS -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -g -fomit-frame-pointer -pipe -m486 -c plip.c"
* End:
*/
+#endif
diff --git a/drivers/net/ppp.c b/drivers/net/ppp.c
index 740bfc1ff..3c86a779f 100644
--- a/drivers/net/ppp.c
+++ b/drivers/net/ppp.c
@@ -190,10 +190,10 @@ static struct net_device_stats *ppp_dev_stats (struct device *);
* TTY callbacks
*/
-static int ppp_tty_read (struct tty_struct *, struct file *, __u8 *,
- unsigned int);
-static int ppp_tty_write (struct tty_struct *, struct file *, const __u8 *,
- unsigned int);
+static ssize_t ppp_tty_read (struct tty_struct *, struct file *, __u8 *,
+ size_t);
+static ssize_t ppp_tty_write (struct tty_struct *, struct file *,
+ const __u8 *, size_t);
static int ppp_tty_ioctl (struct tty_struct *, struct file *, unsigned int,
unsigned long);
static unsigned int ppp_tty_poll (struct tty_struct *tty, struct file *filp, poll_table * wait);
@@ -405,11 +405,6 @@ ppp_init_dev (struct device *dev)
/* New-style flags */
dev->flags = IFF_POINTOPOINT;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = 4; /* sizeof (__u32) */
return 0;
}
@@ -687,9 +682,9 @@ ppp_release (struct ppp *ppp)
if (tty != NULL && tty->disc_data == ppp)
tty->disc_data = NULL; /* Break the tty->ppp link */
+ /* Strong layering violation. */
if (dev && dev->flags & IFF_UP) {
dev_close (dev); /* close the device properly */
- dev->flags = 0; /* prevent recursion */
}
ppp_free_buf (ppp->rbuf);
@@ -1665,13 +1660,12 @@ ppp_doframe (struct ppp *ppp)
waiting if necessary
*/
-static int
-ppp_tty_read (struct tty_struct *tty, struct file *file, __u8 * buf,
- unsigned int nr)
+static ssize_t
+ppp_tty_read (struct tty_struct *tty, struct file *file, __u8 * buf, size_t nr)
{
struct ppp *ppp = tty2ppp (tty);
__u8 c;
- int len, indx;
+ ssize_t len, indx;
#define GETC(c) \
{ \
@@ -1692,8 +1686,8 @@ ppp_tty_read (struct tty_struct *tty, struct file *file, __u8 * buf,
if (ppp->flags & SC_DEBUG)
printk (KERN_DEBUG
- "ppp_tty_read: called buf=%p nr=%u\n",
- buf, nr);
+ "ppp_tty_read: called buf=%p nr=%lu\n",
+ buf, (unsigned long)nr);
/*
* Acquire the read lock.
*/
@@ -1711,7 +1705,7 @@ ppp_tty_read (struct tty_struct *tty, struct file *file, __u8 * buf,
current->state = TASK_INTERRUPTIBLE;
schedule ();
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -EINTR;
continue;
}
@@ -1753,7 +1747,7 @@ ppp_tty_read (struct tty_struct *tty, struct file *file, __u8 * buf,
"ppp_tty_read: sleeping(read_wait)\n");
interruptible_sleep_on (&ppp->read_wait);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -EINTR;
continue;
}
@@ -1761,7 +1755,8 @@ ppp_tty_read (struct tty_struct *tty, struct file *file, __u8 * buf,
* Reset the time of the last read operation.
*/
if (ppp->flags & SC_DEBUG)
- printk (KERN_DEBUG "ppp_tty_read: len = %d\n", len);
+ printk (KERN_DEBUG "ppp_tty_read: len = %ld\n",
+ (long)len);
/*
* Ensure that the frame will fit within the caller's buffer. If not, then
* discard the frame from the input buffer.
@@ -1771,8 +1766,8 @@ ppp_tty_read (struct tty_struct *tty, struct file *file, __u8 * buf,
if (ppp->flags & SC_DEBUG)
printk (KERN_DEBUG
- "ppp: read of %u bytes too small for %d "
- "frame\n", nr, len + 2);
+ "ppp: read of %lu bytes too small for %ld "
+ "frame\n", (unsigned long)nr, (long)len + 2);
ppp->ubuf->tail += len;
ppp->ubuf->tail &= ppp->ubuf->size;
clear_bit (0, &ppp->ubuf->locked);
@@ -1811,7 +1806,8 @@ ppp_tty_read (struct tty_struct *tty, struct file *file, __u8 * buf,
len += 2; /* Account for ADDRESS and CONTROL bytes */
if (ppp->flags & SC_DEBUG)
printk (KERN_DEBUG
- "ppp_tty_read: passing %d bytes up\n", len);
+ "ppp_tty_read: passing %ld bytes up\n",
+ (long) len);
return len;
}
#undef GETC
@@ -2051,13 +2047,13 @@ send_revise_frame (register struct ppp *ppp, __u8 *data, int len)
* we have to put the FCS field on ourselves
*/
-static int
+static ssize_t
ppp_tty_write (struct tty_struct *tty, struct file *file, const __u8 * data,
- unsigned int count)
+ size_t count)
{
struct ppp *ppp = tty2ppp (tty);
__u8 *new_data;
- int status;
+ ssize_t status;
/*
* Verify the pointers.
*/
@@ -2075,7 +2071,8 @@ ppp_tty_write (struct tty_struct *tty, struct file *file, const __u8 * data,
if (ppp->flags & SC_DEBUG)
printk (KERN_WARNING
"ppp_tty_write: truncating user packet "
- "from %u to mtu %d\n", count, PPP_MTU);
+ "from %lu to mtu %d\n",
+ (unsigned long) count, PPP_MTU);
count = PPP_MTU;
}
/*
@@ -2104,7 +2101,7 @@ ppp_tty_write (struct tty_struct *tty, struct file *file, const __u8 * data,
return 0;
}
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
kfree (new_data);
return -EINTR;
}
@@ -2129,7 +2126,7 @@ ppp_tty_write (struct tty_struct *tty, struct file *file, const __u8 * data,
*/
ppp_dev_xmit_frame (ppp, ppp->tbuf, new_data, count);
kfree (new_data);
- return (int) count;
+ return count;
}
/*
@@ -2399,7 +2396,7 @@ ppp_tty_ioctl (struct tty_struct *tty, struct file * file,
sizeof (struct ppp_idle));
if (error == 0) {
struct ppp_idle cur_ddinfo;
- __u32 cur_jiffies = jiffies;
+ unsigned long cur_jiffies = jiffies;
/* change absolute times to relative times. */
cur_ddinfo.xmit_idle = (cur_jiffies - ppp->ddinfo.xmit_idle) / HZ;
@@ -2620,9 +2617,6 @@ ppp_dev_open (struct device *dev)
{
struct ppp *ppp = dev2ppp (dev);
- /* reset POINTOPOINT every time, since dev_close zaps it! */
- dev->flags |= IFF_POINTOPOINT;
-
if (ppp2tty (ppp) == NULL) {
if (ppp->flags & SC_DEBUG)
printk (KERN_ERR
@@ -3017,12 +3011,16 @@ ppp_dev_xmit (sk_buff *skb, struct device *dev)
printk (KERN_WARNING "ppp_dev_xmit: null packet!\n");
return 0;
}
+
/*
* Avoid timing problem should tty hangup while data is queued to be sent
*/
if (!ppp->inuse) {
dev_kfree_skb (skb, FREE_WRITE);
+ printk("I am dying to know, are you still alive?\n");
+#ifdef main_got_it_is_something
dev_close (dev);
+#endif
return 0;
}
/*
diff --git a/drivers/net/ppp_deflate.c b/drivers/net/ppp_deflate.c
new file mode 100644
index 000000000..754ec7c21
--- /dev/null
+++ b/drivers/net/ppp_deflate.c
@@ -0,0 +1,660 @@
+/*
+ * ==FILEVERSION 971001==
+ *
+ * ppp_deflate.c - interface the zlib procedures for Deflate compression
+ * and decompression (as used by gzip) to the PPP code.
+ * This version is for use with Linux kernel 1.3.X.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * From: deflate.c,v 1.1 1996/01/18 03:17:48 paulus Exp
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+#include <linux/errno.h>
+#include <linux/sched.h> /* to get the struct task_struct */
+#include <linux/string.h> /* used in new tty drivers */
+#include <linux/signal.h> /* used in new tty drivers */
+
+#include <asm/system.h>
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/inet.h>
+#include <linux/ioctl.h>
+
+#include <linux/ppp_defs.h>
+
+#undef PACKETPTR
+#define PACKETPTR 1
+#include <linux/ppp-comp.h>
+#undef PACKETPTR
+
+#include "zlib.c"
+
+/*
+ * State for a Deflate (de)compressor.
+ */
+struct ppp_deflate_state {
+ int seqno;
+ int w_size;
+ int unit;
+ int mru;
+ int debug;
+ z_stream strm;
+ struct compstat stats;
+};
+
+#define DEFLATE_OVHD 2 /* Deflate overhead/packet */
+
+static void *zalloc __P((void *, unsigned int items, unsigned int size));
+static void *zalloc_init __P((void *, unsigned int items,
+ unsigned int size));
+static void zfree __P((void *, void *ptr));
+static void *z_comp_alloc __P((unsigned char *options, int opt_len));
+static void *z_decomp_alloc __P((unsigned char *options, int opt_len));
+static void z_comp_free __P((void *state));
+static void z_decomp_free __P((void *state));
+static int z_comp_init __P((void *state, unsigned char *options,
+ int opt_len,
+ int unit, int hdrlen, int debug));
+static int z_decomp_init __P((void *state, unsigned char *options,
+ int opt_len,
+ int unit, int hdrlen, int mru, int debug));
+static int z_compress __P((void *state, unsigned char *rptr,
+ unsigned char *obuf,
+ int isize, int osize));
+static void z_incomp __P((void *state, unsigned char *ibuf, int icnt));
+static int z_decompress __P((void *state, unsigned char *ibuf,
+ int isize, unsigned char *obuf, int osize));
+static void z_comp_reset __P((void *state));
+static void z_decomp_reset __P((void *state));
+static void z_comp_stats __P((void *state, struct compstat *stats));
+
+struct chunk_header {
+ int valloced; /* allocated with valloc, not kmalloc */
+ int guard; /* check for overwritten header */
+};
+
+#define GUARD_MAGIC 0x77a8011a
+#define MIN_VMALLOC 2048 /* use kmalloc for blocks < this */
+
+/*
+ * Space allocation and freeing routines for use by zlib routines.
+ */
+void
+zfree(arg, ptr)
+ void *arg;
+ void *ptr;
+{
+ struct chunk_header *hdr = ((struct chunk_header *)ptr) - 1;
+
+ if (hdr->guard != GUARD_MAGIC) {
+ printk(KERN_WARNING "zfree: header corrupted (%x %x) at %p\n",
+ hdr->valloced, hdr->guard, hdr);
+ return;
+ }
+ if (hdr->valloced)
+ vfree(hdr);
+ else
+ kfree(hdr);
+}
+
+void *
+zalloc(arg, items, size)
+ void *arg;
+ unsigned int items, size;
+{
+ struct chunk_header *hdr;
+ unsigned nbytes;
+
+ nbytes = items * size + sizeof(*hdr);
+ hdr = kmalloc(nbytes, GFP_ATOMIC);
+ if (hdr == 0)
+ return 0;
+ hdr->valloced = 0;
+ hdr->guard = GUARD_MAGIC;
+ return (void *) (hdr + 1);
+}
+
+void *
+zalloc_init(arg, items, size)
+ void *arg;
+ unsigned int items, size;
+{
+ struct chunk_header *hdr;
+ unsigned nbytes;
+
+ nbytes = items * size + sizeof(*hdr);
+ if (nbytes >= MIN_VMALLOC)
+ hdr = vmalloc(nbytes);
+ else
+ hdr = kmalloc(nbytes, GFP_KERNEL);
+ if (hdr == 0)
+ return 0;
+ hdr->valloced = nbytes >= MIN_VMALLOC;
+ hdr->guard = GUARD_MAGIC;
+ return (void *) (hdr + 1);
+}
+
+static void
+z_comp_free(arg)
+ void *arg;
+{
+ struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+ if (state) {
+ deflateEnd(&state->strm);
+ kfree(state);
+ MOD_DEC_USE_COUNT;
+ }
+}
+
+/*
+ * Allocate space for a compressor.
+ */
+static void *
+z_comp_alloc(options, opt_len)
+ unsigned char *options;
+ int opt_len;
+{
+ struct ppp_deflate_state *state;
+ int w_size;
+
+ if (opt_len != CILEN_DEFLATE || options[0] != CI_DEFLATE
+ || options[1] != CILEN_DEFLATE
+ || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
+ || options[3] != DEFLATE_CHK_SEQUENCE)
+ return NULL;
+ w_size = DEFLATE_SIZE(options[2]);
+ if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
+ return NULL;
+
+ state = (struct ppp_deflate_state *) kmalloc(sizeof(*state), GFP_KERNEL);
+ if (state == NULL)
+ return NULL;
+
+ MOD_INC_USE_COUNT;
+ memset (state, 0, sizeof (struct ppp_deflate_state));
+ state->strm.next_in = NULL;
+ state->strm.zalloc = zalloc_init;
+ state->strm.zfree = zfree;
+ state->w_size = w_size;
+
+ if (deflateInit2(&state->strm, Z_DEFAULT_COMPRESSION,
+ DEFLATE_METHOD_VAL, -w_size, 8, Z_DEFAULT_STRATEGY)
+ != Z_OK) {
+ z_comp_free(state);
+ return NULL;
+ }
+
+ state->strm.zalloc = zalloc;
+ return (void *) state;
+}
+
+static int
+z_comp_init(arg, options, opt_len, unit, hdrlen, debug)
+ void *arg;
+ unsigned char *options;
+ int opt_len, unit, hdrlen, debug;
+{
+ struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+ if (opt_len < CILEN_DEFLATE || options[0] != CI_DEFLATE
+ || options[1] != CILEN_DEFLATE
+ || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
+ || DEFLATE_SIZE(options[2]) != state->w_size
+ || options[3] != DEFLATE_CHK_SEQUENCE)
+ return 0;
+
+ state->seqno = 0;
+ state->unit = unit;
+ state->debug = debug;
+
+ deflateReset(&state->strm);
+
+ return 1;
+}
+
+static void
+z_comp_reset(arg)
+ void *arg;
+{
+ struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+ state->seqno = 0;
+ deflateReset(&state->strm);
+}
+
+int
+z_compress(arg, rptr, obuf, isize, osize)
+ void *arg;
+ unsigned char *rptr; /* uncompressed packet (in) */
+ unsigned char *obuf; /* compressed packet (out) */
+ int isize, osize;
+{
+ struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+ int r, proto, off, olen;
+ unsigned char *wptr;
+
+ /*
+ * Check that the protocol is in the range we handle.
+ */
+ proto = PPP_PROTOCOL(rptr);
+ if (proto > 0x3fff || proto == 0xfd || proto == 0xfb)
+ return 0;
+
+ /* Don't generate compressed packets which are larger than
+ the uncompressed packet. */
+ if (osize > isize)
+ osize = isize;
+
+ wptr = obuf;
+
+ /*
+ * Copy over the PPP header and store the 2-byte sequence number.
+ */
+ wptr[0] = PPP_ADDRESS(rptr);
+ wptr[1] = PPP_CONTROL(rptr);
+ wptr[2] = PPP_COMP >> 8;
+ wptr[3] = PPP_COMP;
+ wptr += PPP_HDRLEN;
+ wptr[0] = state->seqno >> 8;
+ wptr[1] = state->seqno;
+ wptr += 2;
+ state->strm.next_out = wptr;
+ state->strm.avail_out = osize - (PPP_HDRLEN + 2);
+ ++state->seqno;
+
+ off = (proto > 0xff) ? 2 : 3; /* skip 1st proto byte if 0 */
+ rptr += off;
+ state->strm.next_in = rptr;
+ state->strm.avail_in = (isize - off);
+
+ olen = 0;
+ for (;;) {
+ r = deflate(&state->strm, Z_PACKET_FLUSH);
+ if (r != Z_OK) {
+ if (state->debug)
+ printk(KERN_DEBUG "z_compress: deflate returned %d (%s)\n",
+ r, (state->strm.msg? state->strm.msg: ""));
+ break;
+ }
+ if (state->strm.avail_out == 0) {
+ olen += osize;
+ state->strm.next_out = NULL;
+ state->strm.avail_out = 1000000;
+ } else {
+ break; /* all done */
+ }
+ }
+ if (olen < osize)
+ olen += osize - state->strm.avail_out;
+
+ /*
+ * See if we managed to reduce the size of the packet.
+ */
+ if (olen < isize) {
+ state->stats.comp_bytes += olen;
+ state->stats.comp_packets++;
+ } else {
+ state->stats.inc_bytes += isize;
+ state->stats.inc_packets++;
+ olen = 0;
+ }
+ state->stats.unc_bytes += isize;
+ state->stats.unc_packets++;
+
+ return olen;
+}
+
+static void
+z_comp_stats(arg, stats)
+ void *arg;
+ struct compstat *stats;
+{
+ struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+ *stats = state->stats;
+}
+
+static void
+z_decomp_free(arg)
+ void *arg;
+{
+ struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+ if (state) {
+ inflateEnd(&state->strm);
+ kfree(state);
+ MOD_DEC_USE_COUNT;
+ }
+}
+
+/*
+ * Allocate space for a decompressor.
+ */
+static void *
+z_decomp_alloc(options, opt_len)
+ unsigned char *options;
+ int opt_len;
+{
+ struct ppp_deflate_state *state;
+ int w_size;
+
+ if (opt_len != CILEN_DEFLATE || options[0] != CI_DEFLATE
+ || options[1] != CILEN_DEFLATE
+ || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
+ || options[3] != DEFLATE_CHK_SEQUENCE)
+ return NULL;
+ w_size = DEFLATE_SIZE(options[2]);
+ if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
+ return NULL;
+
+ state = (struct ppp_deflate_state *) kmalloc(sizeof(*state), GFP_KERNEL);
+ if (state == NULL)
+ return NULL;
+
+ MOD_INC_USE_COUNT;
+ memset (state, 0, sizeof (struct ppp_deflate_state));
+ state->w_size = w_size;
+ state->strm.next_out = NULL;
+ state->strm.zalloc = zalloc_init;
+ state->strm.zfree = zfree;
+
+ if (inflateInit2(&state->strm, -w_size) != Z_OK) {
+ z_decomp_free(state);
+ return NULL;
+ }
+
+ state->strm.zalloc = zalloc;
+ return (void *) state;
+}
+
+static int
+z_decomp_init(arg, options, opt_len, unit, hdrlen, mru, debug)
+ void *arg;
+ unsigned char *options;
+ int opt_len, unit, hdrlen, mru, debug;
+{
+ struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+ if (opt_len < CILEN_DEFLATE || options[0] != CI_DEFLATE
+ || options[1] != CILEN_DEFLATE
+ || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
+ || DEFLATE_SIZE(options[2]) != state->w_size
+ || options[3] != DEFLATE_CHK_SEQUENCE)
+ return 0;
+
+ state->seqno = 0;
+ state->unit = unit;
+ state->debug = debug;
+ state->mru = mru;
+
+ inflateReset(&state->strm);
+
+ return 1;
+}
+
+static void
+z_decomp_reset(arg)
+ void *arg;
+{
+ struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+ state->seqno = 0;
+ inflateReset(&state->strm);
+}
+
+/*
+ * Decompress a Deflate-compressed packet.
+ *
+ * Because of patent problems, we return DECOMP_ERROR for errors
+ * found by inspecting the input data and for system problems, but
+ * DECOMP_FATALERROR for any errors which could possibly be said to
+ * be being detected "after" decompression. For DECOMP_ERROR,
+ * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be
+ * infringing a patent of Motorola's if we do, so we take CCP down
+ * instead.
+ *
+ * Given that the frame has the correct sequence number and a good FCS,
+ * errors such as invalid codes in the input most likely indicate a
+ * bug, so we return DECOMP_FATALERROR for them in order to turn off
+ * compression, even though they are detected by inspecting the input.
+ */
+int
+z_decompress(arg, ibuf, isize, obuf, osize)
+ void *arg;
+ unsigned char *ibuf;
+ int isize;
+ unsigned char *obuf;
+ int osize;
+{
+ struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+ int olen, seq, r;
+ int decode_proto, overflow;
+ unsigned char overflow_buf[1];
+
+ if (isize <= PPP_HDRLEN + DEFLATE_OVHD) {
+ if (state->debug)
+ printk(KERN_DEBUG "z_decompress%d: short pkt (%d)\n",
+ state->unit, isize);
+ return DECOMP_ERROR;
+ }
+
+ /* Check the sequence number. */
+ seq = (ibuf[PPP_HDRLEN] << 8) + ibuf[PPP_HDRLEN+1];
+ if (seq != state->seqno) {
+ if (state->debug)
+ printk(KERN_DEBUG "z_decompress%d: bad seq # %d, expected %d\n",
+ state->unit, seq, state->seqno);
+ return DECOMP_ERROR;
+ }
+ ++state->seqno;
+
+ /*
+ * Fill in the first part of the PPP header. The protocol field
+ * comes from the decompressed data.
+ */
+ obuf[0] = PPP_ADDRESS(ibuf);
+ obuf[1] = PPP_CONTROL(ibuf);
+ obuf[2] = 0;
+
+ /*
+ * Set up to call inflate. We set avail_out to 1 initially so we can
+ * look at the first byte of the output and decide whether we have
+ * a 1-byte or 2-byte protocol field.
+ */
+ state->strm.next_in = ibuf + PPP_HDRLEN + DEFLATE_OVHD;
+ state->strm.avail_in = isize - (PPP_HDRLEN + DEFLATE_OVHD);
+ state->strm.next_out = obuf + 3;
+ state->strm.avail_out = 1;
+ decode_proto = 1;
+ overflow = 0;
+
+ /*
+ * Call inflate, supplying more input or output as needed.
+ */
+ for (;;) {
+ r = inflate(&state->strm, Z_PACKET_FLUSH);
+ if (r != Z_OK) {
+ if (state->debug)
+ printk(KERN_DEBUG "z_decompress%d: inflate returned %d (%s)\n",
+ state->unit, r, (state->strm.msg? state->strm.msg: ""));
+ return DECOMP_FATALERROR;
+ }
+ if (state->strm.avail_out != 0)
+ break; /* all done */
+ if (decode_proto) {
+ state->strm.avail_out = osize - PPP_HDRLEN;
+ if ((obuf[3] & 1) == 0) {
+ /* 2-byte protocol field */
+ obuf[2] = obuf[3];
+ --state->strm.next_out;
+ ++state->strm.avail_out;
+ }
+ decode_proto = 0;
+ } else if (!overflow) {
+ /*
+ * We've filled up the output buffer; the only way to
+ * find out whether inflate has any more characters
+ * left is to give it another byte of output space.
+ */
+ state->strm.next_out = overflow_buf;
+ state->strm.avail_out = 1;
+ overflow = 1;
+ } else {
+ if (state->debug)
+ printk(KERN_DEBUG "z_decompress%d: ran out of mru\n",
+ state->unit);
+ return DECOMP_FATALERROR;
+ }
+ }
+
+ if (decode_proto)
+ return DECOMP_ERROR;
+
+ olen = osize + overflow - state->strm.avail_out;
+ state->stats.unc_bytes += olen;
+ state->stats.unc_packets++;
+ state->stats.comp_bytes += isize;
+ state->stats.comp_packets++;
+
+ return olen;
+}
+
+/*
+ * Incompressible data has arrived - add it to the history.
+ */
+static void
+z_incomp(arg, ibuf, icnt)
+ void *arg;
+ unsigned char *ibuf;
+ int icnt;
+{
+ struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+ int proto, r;
+
+ /*
+ * Check that the protocol is one we handle.
+ */
+ proto = PPP_PROTOCOL(ibuf);
+ if (proto > 0x3fff || proto == 0xfd || proto == 0xfb)
+ return;
+
+ ++state->seqno;
+
+ /*
+ * We start at the either the 1st or 2nd byte of the protocol field,
+ * depending on whether the protocol value is compressible.
+ */
+ state->strm.next_in = ibuf + 3;
+ state->strm.avail_in = icnt - 3;
+ if (proto > 0xff) {
+ --state->strm.next_in;
+ ++state->strm.avail_in;
+ }
+
+ r = inflateIncomp(&state->strm);
+ if (r != Z_OK) {
+ /* gak! */
+ if (state->debug) {
+ printk(KERN_DEBUG "z_incomp%d: inflateIncomp returned %d (%s)\n",
+ state->unit, r, (state->strm.msg? state->strm.msg: ""));
+ }
+ return;
+ }
+
+ /*
+ * Update stats.
+ */
+ state->stats.inc_bytes += icnt;
+ state->stats.inc_packets++;
+ state->stats.unc_bytes += icnt;
+ state->stats.unc_packets++;
+}
+
+/*************************************************************
+ * Module interface table
+ *************************************************************/
+
+/* These are in ppp.c */
+extern int ppp_register_compressor (struct compressor *cp);
+extern void ppp_unregister_compressor (struct compressor *cp);
+
+/*
+ * Procedures exported to if_ppp.c.
+ */
+struct compressor ppp_deflate = {
+ CI_DEFLATE, /* compress_proto */
+ z_comp_alloc, /* comp_alloc */
+ z_comp_free, /* comp_free */
+ z_comp_init, /* comp_init */
+ z_comp_reset, /* comp_reset */
+ z_compress, /* compress */
+ z_comp_stats, /* comp_stat */
+ z_decomp_alloc, /* decomp_alloc */
+ z_decomp_free, /* decomp_free */
+ z_decomp_init, /* decomp_init */
+ z_decomp_reset, /* decomp_reset */
+ z_decompress, /* decompress */
+ z_incomp, /* incomp */
+ z_comp_stats, /* decomp_stat */
+};
+
+#ifdef MODULE
+/*************************************************************
+ * Module support routines
+ *************************************************************/
+
+int
+init_module(void)
+{
+ int answer = ppp_register_compressor (&ppp_deflate);
+ if (answer == 0)
+ printk (KERN_INFO
+ "PPP Deflate Compression module registered\n");
+ return answer;
+}
+
+void
+cleanup_module(void)
+{
+ if (MOD_IN_USE)
+ printk (KERN_INFO
+ "Deflate Compression module busy, remove delayed\n");
+ else
+ ppp_unregister_compressor (&ppp_deflate);
+}
+#endif
diff --git a/drivers/net/pt.c b/drivers/net/pt.c
index bdef58992..11bef1d6f 100644
--- a/drivers/net/pt.c
+++ b/drivers/net/pt.c
@@ -484,7 +484,7 @@ __initfunc(int pt_init(void))
{ 0x230, 0x240, 0x250, 0x260, 0x270, 0x280, 0x290, 0x2a0,
0x2b0, 0x300, 0x330, 0x3f0, 0};
- printk(KERN_INFO "PT: 0.41 ALPHA 07 October 1995 Craig Small (vk2xlz@vk2xlz.ampr.org)\n");
+ printk(KERN_INFO "PT: 0.41 ALPHA 07 October 1995 Craig Small (csmall@small.dropbear.id.au)\n");
for (port = &ports[0]; *port && !card_type; port++) {
ioaddr = *port;
@@ -830,7 +830,7 @@ static int pt_probe(struct device *dev)
* the interrupt, and this marks the 'irqaction' as busy.
*/
{
- int irqval = request_irq(dev->irq, &pt_interrupt,0, "pt", NULL);
+ int irqval = request_irq(dev->irq, &pt_interrupt,0, "pt", dev);
if (irqval) {
printk(KERN_ERR "PT: ERROR: Unable to get IRQ %d (irqval = %d).\n",
dev->irq, irqval);
@@ -866,11 +866,6 @@ static int pt_probe(struct device *dev)
/* New style flags */
dev->flags = 0;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = sizeof(unsigned long);
return 0;
} /* pt_probe() */
@@ -896,11 +891,10 @@ static int pt_open(struct device *dev)
{
if (request_dma(dev->dma, "pt"))
{
- free_irq(dev->irq, NULL);
+ free_irq(dev->irq, dev);
return -EAGAIN;
}
}
- irq2dev_map[dev->irq] = dev;
/* Reset hardware */
chipset_init(dev);
@@ -1770,9 +1764,8 @@ int init_module(void)
void cleanup_module(void)
{
- free_irq(pt0a.irq, NULL); /* IRQs and IO Ports are shared */
+ free_irq(pt0a.irq, &pt0a); /* IRQs and IO Ports are shared */
release_region(pt0a.base_addr & 0x3f0, PT_TOTAL_SIZE);
- irq2dev_map[pt0a.irq] = NULL;
kfree(pt0a.priv);
pt0a.priv = NULL;
diff --git a/drivers/net/ptifddi.c b/drivers/net/ptifddi.c
new file mode 100644
index 000000000..efd6f3eaf
--- /dev/null
+++ b/drivers/net/ptifddi.c
@@ -0,0 +1,268 @@
+/* $Id: ptifddi.c,v 1.5 1997/04/16 10:27:27 jj Exp $
+ * ptifddi.c: Network driver for Performance Technologies single-attach
+ * and dual-attach FDDI sbus cards.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+static char *version =
+ "ptifddi.c:v1.0 10/Dec/96 David S. Miller (davem@caipfs.rutgers.edu)\n";
+
+#include <linux/init.h>
+
+#include "ptifddi.h"
+
+#include "ptifddi_asm.h"
+
+#ifdef MODULE
+static struct ptifddi *root_pti_dev = NULL;
+#endif
+
+static inline void pti_reset(struct ptifddi *pp)
+{
+ pp->reset = 1;
+}
+
+static inline void pti_unreset(struct ptifddi *pp)
+{
+ pp->unreset = 1;
+}
+
+static inline void pti_load_code_base(struct dfddi_ram *rp, unsigned short addr)
+{
+ rp->loader_addr = ((addr << 8) & 0xff00) | ((addr >> 8) & 0x00ff);
+}
+
+static inline void pti_clear_dpram(struct ptifddi *pp)
+{
+ memset(pp->dpram, 0, DPRAM_SIZE);
+}
+
+#define CARD_TEST_TIMEOUT 100000
+
+static inline int pti_card_test(struct ptifddi *pp)
+{
+ struct dfddi_ram *rp = pp->dpram;
+ unsigned char *code = &rp->loader;
+ unsigned char *status = (unsigned char *) rp;
+ int clicks = CARD_TEST_TIMEOUT;
+
+ /* Clear it out. */
+ pti_clear_dpram(pp);
+
+ /* Load test data. */
+ for(i = 0; i < test_firmware_size; i++)
+ code[i] = test_firmware[i];
+
+ /* Tell card where to execute the code. */
+ pti_load_code_base(pp, test_firmware_dev_addr);
+
+ /* Clear test run status in dpram. */
+ *status = 0;
+
+ /* Reset single attach state machine before the test. */
+ rp->reset = 1;
+
+ /* Unreset, to get the test code running. */
+ pti_unreset(pp);
+
+ /* Wait for dpram status to become 5, else fail if we time out. */
+ while(--clicks) {
+ if(*status == 5) {
+ pti_reset(pp);
+ return 0;
+ }
+ udelay(20);
+ }
+ return 1;
+}
+
+static inline void pti_init_firmware_loader(struct ptifddi *pp)
+{
+ struct dfddi_ram *rp = pp->dpram;
+ int i;
+
+ for(i = 0; i < firmware_loader_size; i++)
+ rp->loader.loader_firmware[i] = firmware_loader[i];
+}
+
+static inline void pti_load_main_firmware(struct ptifddi *pp)
+{
+ struct dfddi_ram *rp = pp->dpram;
+ struct dpram_loader *lp = &rp.loader;
+ int i;
+
+
+}
+
+static void pti_init_rings(struct ptifddi *pp, int from_irq)
+{
+}
+
+static int pti_init(struct ptifddi *pp, int from_irq)
+{
+}
+
+static void pti_is_not_so_happy(struct ptifddi *pp)
+{
+}
+
+static inline void pti_tx(struct ptifddi *pp, struct device *dev)
+{
+}
+
+static inline void myri_rx(struct ptifddi *pp, struct device *dev)
+{
+}
+
+static void pti_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *) dev_id;
+ struct ptifddi *pp = (struct ptifddi *) dev->priv;
+
+}
+
+static int pti_open(struct device *dev)
+{
+ struct ptifddi *pp = (struct ptifddi *) dev->priv;
+
+ return pti_init(pp, in_interrupt());
+}
+
+static int pti_close(struct device *dev)
+{
+ struct ptifddi *pp = (struct ptifddi *) dev->priv;
+
+ return 0;
+}
+
+static int pti_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct ptifddi *pp = (struct ptifddi *) dev->priv;
+}
+
+static struct enet_statistics *pti_get_stats(struct device *dev)
+{ return &(((struct ptifddi *)dev->priv)->enet_stats); }
+
+static void pti_set_multicast(struct device *dev)
+{
+}
+
+static inline int pti_fddi_init(struct device *dev, struct linux_sbus_device *sdev, int num)
+{
+ static unsigned version_printed = 0;
+ struct ptifddi *pp;
+ int i;
+
+ dev = init_fddidev(0, sizeof(struct ptifddi));
+
+ if(version_printed++ == 0)
+ printk(version);
+
+ prom_apply_sbus_ranges(sdev->my_bus, &sdev->reg_addrs[0],
+ sdev->num_registers, sdev);
+
+ /* Register 0 mapping contains DPRAM. */
+ pp->dpram = sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0,
+ sdev->reg_addrs[0].reg_size,
+ "PTI FDDI DPRAM",
+ sdev->reg_addrs[0].which_io, 0);
+ if(!pp->dpram) {
+ printk("ptiFDDI: Cannot map DPRAM I/O area.\n");
+ return ENODEV;
+ }
+
+ /* Next, register 1 contains reset byte. */
+ pp->reset = sparc_alloc_io(sdev->reg_addrs[1].phys_addr, 0,
+ sdev->reg_addrs[1].reg_size,
+ "PTI FDDI RESET Byte",
+ sdev->reg_addrs[1].which_io, 0);
+ if(!pp->reset) {
+ printk("ptiFDDI: Cannot map RESET byte.\n");
+ return ENODEV;
+ }
+
+ /* Register 2 contains unreset byte. */
+ pp->unreset = sparc_alloc_io(sdev->reg_addrs[2].phys_addr, 0,
+ sdev->reg_addrs[2].reg_size,
+ "PTI FDDI UNRESET Byte",
+ sdev->reg_addrs[2].which_io, 0);
+ if(!pp->unreset) {
+ printk("ptiFDDI: Cannot map UNRESET byte.\n");
+ return ENODEV;
+ }
+
+ /* Reset the card. */
+ pti_reset(pp);
+
+ /* Run boot-up card tests. */
+ i = pti_card_test(pp);
+ if(i) {
+ printk("ptiFDDI: Bootup card test fails.\n");
+ return ENODEV;
+ }
+
+ /* Clear DPRAM, start afresh. */
+ pti_clear_dpram(pp);
+
+ /* Init the firmware loader. */
+ pti_init_firmware_loader(pp);
+
+ /* Now load main card FDDI firmware, using the loader. */
+ pti_load_main_firmware(pp);
+}
+
+__initfunc(int ptifddi_sbus_probe(struct device *dev))
+{
+ struct linux_sbus *bus;
+ struct linux_sbus_device *sdev = 0;
+ static int called = 0;
+ int cards = 0, v;
+
+ if(called)
+ return ENODEV;
+ called++;
+
+ for_each_sbus(bus) {
+ for_each_sbusdev(sdev, bus) {
+ if(cards) dev = NULL;
+ if(!strcmp(sdev->prom_name, "PTI,sbs600") ||
+ !strcmp(sdev->prom_name, "DPV,fddid")) {
+ cards++;
+ DET(("Found PTI FDDI as %s\n", sdev->prom_name));
+ if((v = pti_fddi_init(dev, sdev, (cards - 1))))
+ return v;
+ }
+ }
+ }
+ if(!cards)
+ return ENODEV;
+ return 0;
+}
+
+
+#ifdef MODULE
+
+int
+init_module(void)
+{
+ root_pti_dev = NULL;
+ return ptifddi_sbus_probe(NULL);
+}
+
+void
+cleanup_module(void)
+{
+ struct ptifddi *pp;
+
+ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+ while (root_pti_dev) {
+ pp = root_pti_dev->next_module;
+
+ unregister_netdev(root_pti_dev->dev);
+ kfree(root_pti_dev->dev);
+ root_pti_dev = mp;
+ }
+}
+
+#endif /* MODULE */
diff --git a/drivers/net/ptifddi.h b/drivers/net/ptifddi.h
new file mode 100644
index 000000000..dc59ca358
--- /dev/null
+++ b/drivers/net/ptifddi.h
@@ -0,0 +1,77 @@
+/* $Id: ptifddi.h,v 1.2 1996/12/16 06:15:15 davem Exp $
+ * ptifddi.c: Defines for Performance Technologies FDDI sbus cards.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _PTIFDDI_H
+#define _PTIFDDI_H
+
+struct dpram_loader {
+ volatile unsigned char dpram_stat;
+ volatile unsigned char _unused;
+ volatile unsigned char addr_low;
+ volatile unsigned char addr_hi;
+ volatile unsigned char num_bytes;
+ volatile unsigned char data[0x3b];
+
+ volatile unsigned char loader_firmware[0xc0];
+};
+
+struct dfddi_ram {
+/*0x000*/ unsigned char _unused0[0x100];
+/*0x100*/ struct dpram_loader loader;
+/*0x200*/ unsigned char instructions[0x400];
+/*0x600*/ unsigned char msg_in[0x20];
+/*0x620*/ unsigned char msg_out[0x20];
+/*0x640*/ unsigned char _unused2[0x50];
+/*0x690*/ unsigned char smsg_in[0x20];
+/*0x6b0*/ unsigned char _unused3[0x30];
+/*0x6e0*/ unsigned char beacom_frame[0x20];
+/*0x700*/ unsigned char re_sync;
+/*0x701*/ unsigned char _unused4;
+/*0x702*/ unsigned short tswitch;
+/*0x704*/ unsigned char evq_lost;
+/*0x705*/ unsigned char _unused6;
+/*0x706*/ unsigned char signal_lost;
+/*0x707*/ unsigned char _unused7;
+/*0x708*/ unsigned char lerror;
+/*0x709*/ unsigned char _unused8;
+/*0x70a*/ unsigned char rstate;
+/*0x70b*/ unsigned char _unused9[0x13];
+/*0x716*/ unsigned short dswitch;
+/*0x718*/ unsigned char _unused10[0x48];
+/*0x750*/ unsigned char cbusy;
+/*0x751*/ unsigned char hbusy;
+/*0x752*/ unsigned short istat;
+/*0x754*/ unsigned char _unused11[];
+/*0x756*/ unsigned char disable;
+/*0x757*/ unsigned char _unused12[];
+/*0x78e*/ unsigned char ucvalid;
+/*0x78f*/ unsigned char _unused13;
+/*0x790*/ unsigned int u0addr;
+/*0x794*/ unsigned char _unused14[];
+/*0x7a8*/ unsigned int P_player;
+/*0x7ac*/ unsigned int Q_player;
+/*0x7b0*/ unsigned int macsi;
+/*0x7b4*/ unsigned char _unused15[];
+/*0x7be*/ unsigned short reset;
+/*0x7c0*/ unsigned char _unused16[];
+/*0x7fc*/ unsigned short iack;
+/*0x7fe*/ unsigned short loader_addr;
+};
+
+#define DPRAM_SIZE 0x800
+
+#define DPRAM_STAT_VALID 0x80
+#define DPRAM_STAT_EMPTY 0x00
+
+struct ptifddi {
+ struct dfddi_ram *dpram;
+ unsigned char *reset;
+ unsigned char *unreset;
+ struct device *dev;
+ struct ptifddi *next_module;
+};
+
+#endif /* !(_PTIFDDI_H) */
diff --git a/drivers/net/ptifddi_asm.h b/drivers/net/ptifddi_asm.h
new file mode 100644
index 000000000..467c69b83
--- /dev/null
+++ b/drivers/net/ptifddi_asm.h
@@ -0,0 +1,2543 @@
+/* $Id: ptifddi_asm.h,v 1.3 1997/04/16 10:27:28 jj Exp $ */
+#ifndef _PTIFDDI_ASM_H
+#define _PTIFDDI_ASM_H
+
+/* This is the code for the DPRAM firmware loader. */
+const unsigned short firmware_loader_code_addr = 0x140; /* Offset from dpram base. */
+const unsigned short firmware_loader_dev_addr = 0xf940; /* Offset as seen by device. */
+const unsigned short firmware_loader_size = 0x6f; /* Size of fware loader code.*/
+
+static unsigned char firmware_loader[] __initdata = {
+ 0xb7, 0x01, 0xc0, 0xc4, 0x97, 0x14, 0xc0, 0xb7, 0xf9, 0x02, 0x12, 0x03, 0x30,
+ 0x2c, 0x8b, 0x0d, 0x30, 0x28, 0x8b, 0x0c, 0x30, 0x24, 0x8b, 0x11, 0x82, 0x00,
+ 0x11, 0xdc, 0x56, 0x30, 0x1b, 0x86, 0xff, 0xfc, 0x0c, 0xfc, 0x49, 0x86, 0xff,
+ 0xfd, 0x0c, 0xfc, 0x43, 0xad, 0x0c, 0x8b, 0xa9, 0x0c, 0x8a, 0x11, 0x75, 0x83,
+ 0x00, 0xf9, 0x00, 0xab, 0x95, 0x31, 0x03, 0x40, 0xb6, 0xf9, 0x00, 0xa8, 0x96,
+ 0xc8, 0x17, 0x41, 0x68, 0xad, 0x12, 0x88, 0xa9, 0x12, 0x86, 0xf9, 0x16, 0x12,
+ 0xfc, 0x47, 0x86, 0xf9, 0x16, 0x12, 0xfd, 0x41, 0x3c, 0x83, 0x00, 0xf9, 0x00,
+ 0xab, 0xb7, 0xf9, 0x02, 0x12, 0x3c, 0x83, 0x00, 0xf9, 0x00, 0xab, 0xb7, 0xf9,
+ 0x02, 0x12, 0x03, 0x95, 0x2f
+};
+
+/* This is test code which we run at boot time to go and verify the proper
+ * functioning of the PTI fddi card.
+ */
+const unsigned short test_firmware_code_addr = 0x100; /* Offset from dpram base. */
+const unsigned short test_firmware_dev_addr = 0xfa00; /* Offset as seen by device. */
+const unsigned short test_firmware_size = 0x414; /* Size of card test code. */
+
+static unsigned char test_firmware[] __initdata = {
+ 0x97, 0x00, 0xd0, 0x90, 0x01, 0x8b, 0xd4, 0x8b, 0xf4, 0x97, 0x1f, 0xe2, 0x97,
+ 0x1f, 0xf2, 0x97, 0x14, 0xc0, 0xb7, 0x01, 0xc0, 0xc4, 0x00, 0x8b, 0x0b, 0x00,
+ 0x8b, 0x03, 0x8b, 0x0a, 0xab, 0x08, 0x90, 0x00, 0x8b, 0x04, 0x90, 0x40, 0x8b,
+ 0x05, 0x90, 0xff, 0x8b, 0x06, 0x90, 0xf7, 0x8b, 0x07, 0x90, 0x00, 0x8b, 0x0c,
+ 0x33, 0x8d, 0x82, 0x00, 0x0a, 0xdd, 0x94, 0x57, 0x89, 0x0c, 0x89, 0x0b, 0x33,
+ 0x81, 0x82, 0x00, 0x0a, 0xdd, 0x94, 0x4b, 0x89, 0x0c, 0x33, 0x77, 0x82, 0x00,
+ 0x0a, 0xdd, 0x94, 0x41, 0x91, 0x01, 0x30, 0xc4, 0x00, 0x30, 0xd2, 0x9c, 0x00,
+ 0x91, 0x02, 0x9d, 0x00, 0x91, 0x42, 0x30, 0xb7, 0xb6, 0x01, 0x04, 0x88, 0x96,
+ 0xc8, 0x10, 0x41, 0x4e, 0x90, 0x01, 0x30, 0xbb, 0x9c, 0x00, 0x91, 0x03, 0x9d,
+ 0x00, 0x91, 0x43, 0x30, 0xa0, 0x31, 0x97, 0x9c, 0x00, 0x91, 0x04, 0x9d, 0x00,
+ 0x91, 0x44, 0x30, 0x94, 0x32, 0x6b, 0x9c, 0x00, 0x91, 0x05, 0x9d, 0x00, 0x91,
+ 0x45, 0x30, 0x88, 0x60, 0x91, 0x41, 0x95, 0x41, 0xab, 0xce, 0xe1, 0x41, 0x3c,
+ 0x82, 0x02, 0x0c, 0xdc, 0x49, 0xa8, 0xce, 0x03, 0xf7, 0x07, 0xb8, 0x00, 0x01,
+ 0x72, 0xa8, 0xcc, 0x75, 0xab, 0xce, 0xc1, 0x41, 0x3c, 0x82, 0x02, 0x0c, 0xdc,
+ 0x4b, 0xa8, 0xce, 0x03, 0xf7, 0x96, 0xc9, 0x10, 0xb8, 0x00, 0x01, 0x74, 0xa8,
+ 0xcc, 0x77, 0xab, 0xce, 0xe0, 0x41, 0x3c, 0x96, 0xce, 0xfc, 0x42, 0x30, 0x46,
+ 0x82, 0x02, 0x0c, 0xdc, 0x4b, 0xae, 0xce, 0x03, 0xf7, 0x07, 0xb8, 0x00, 0x01,
+ 0xae, 0xce, 0x78, 0xac, 0xcc, 0xce, 0x7c, 0xab, 0xce, 0xc0, 0x41, 0x3c, 0xaf,
+ 0xc8, 0x96, 0xce, 0xfb, 0x99, 0xff, 0x9c, 0x00, 0x45, 0x3f, 0xc8, 0x30, 0x1b,
+ 0x42, 0x3f, 0xc8, 0x82, 0x02, 0x0c, 0xdc, 0x4e, 0xae, 0xce, 0x03, 0xf7, 0x96,
+ 0xc9, 0x10, 0xb8, 0x00, 0x01, 0xae, 0xce, 0x95, 0x25, 0xac, 0xcc, 0xce, 0x95,
+ 0x2a, 0x97, 0x01, 0x0a, 0x3c, 0x97, 0x01, 0x0a, 0x3c, 0x82, 0x05, 0xca, 0xfd,
+ 0x46, 0xa1, 0xca, 0xf8, 0x00, 0x8b, 0x3c, 0xa1, 0xca, 0xf8, 0x00, 0x8b, 0x60,
+ 0xb7, 0x20, 0x00, 0x10, 0x9d, 0x00, 0xb7, 0x28, 0x00, 0x10, 0xac, 0x10, 0xcc,
+ 0x97, 0x37, 0x12, 0x30, 0x64, 0xac, 0x10, 0xcc, 0x97, 0x37, 0x12, 0x30, 0x87,
+ 0x30, 0x0a, 0x82, 0x00, 0x0a, 0xdd, 0x42, 0x00, 0x3c, 0x90, 0x01, 0x3c, 0xac,
+ 0x10, 0xcc, 0x97, 0x00, 0x12, 0x97, 0x01, 0x03, 0x44, 0xeb, 0x58, 0x24, 0x3c,
+ 0xb7, 0xfb, 0x5b, 0x14, 0x91, 0x01, 0xad, 0x14, 0x88, 0x96, 0xca, 0xf9, 0xae,
+ 0xca, 0x03, 0xf7, 0xae, 0xca, 0x9d, 0x00, 0x30, 0x15, 0xa9, 0xcc, 0xa9, 0xcc,
+ 0x86, 0x01, 0x00, 0xca, 0xfc, 0x41, 0x7a, 0xa9, 0x14, 0x86, 0xfb, 0x5f, 0x14,
+ 0xfc, 0x3c, 0x95, 0x25, 0xc4, 0x88, 0x12, 0xc6, 0xc4, 0x96, 0x12, 0xdc, 0x3c,
+ 0x80, 0x12, 0xce, 0xab, 0xa9, 0xcc, 0x34, 0x87, 0xae, 0xcc, 0x05, 0xae, 0xcc,
+ 0x3c, 0xb7, 0xfb, 0x5b, 0x14, 0x91, 0x01, 0xad, 0x14, 0x88, 0x96, 0xca, 0xf9,
+ 0xae, 0xca, 0x03, 0xf7, 0xae, 0xca, 0x9d, 0x00, 0x30, 0x40, 0xa9, 0xcc, 0xa9,
+ 0xcc, 0x86, 0x01, 0x00, 0xca, 0xfc, 0x41, 0x7a, 0xa9, 0x14, 0x86, 0xfb, 0x5f,
+ 0x14, 0xfc, 0x3c, 0x95, 0x25, 0xb7, 0xfb, 0x5b, 0x14, 0x91, 0x01, 0xad, 0x14,
+ 0x88, 0x96, 0xca, 0xf9, 0xae, 0xca, 0x03, 0xf7, 0xae, 0xca, 0x9d, 0x00, 0x30,
+ 0x1c, 0xa9, 0xcc, 0xa9, 0xcc, 0x86, 0x01, 0x00, 0xca, 0xfc, 0x41, 0x7a, 0xa9,
+ 0x14, 0x86, 0xfb, 0x5f, 0x14, 0xfc, 0x3c, 0x95, 0x25, 0xc4, 0x88, 0x12, 0xc6,
+ 0x89, 0x12, 0x3c, 0xc4, 0x96, 0x12, 0xdc, 0x4d, 0x80, 0x12, 0xce, 0xab, 0xa9,
+ 0xcc, 0x34, 0xf7, 0xae, 0xcc, 0x05, 0xae, 0xcc, 0x89, 0x12, 0x3c, 0xb2, 0x10,
+ 0x00, 0x00, 0xc6, 0xb2, 0x10, 0x00, 0x97, 0x55, 0x12, 0x30, 0x61, 0xb2, 0x10,
+ 0x00, 0x97, 0x55, 0x12, 0x30, 0x84, 0x82, 0x00, 0x0a, 0xdd, 0x44, 0x30, 0x05,
+ 0x00, 0x3c, 0x90, 0x01, 0x3c, 0xb2, 0x10, 0x00, 0x97, 0x00, 0x12, 0x97, 0x01,
+ 0x03, 0x58, 0x02, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x5e, 0xef,
+ 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb7, 0xfc, 0x3e, 0x14, 0x91, 0x01, 0xad, 0x14, 0x88, 0x96, 0xca, 0xf9, 0xae,
+ 0xca, 0x03, 0xf7, 0xae, 0xca, 0x9d, 0x00, 0x34, 0xe0, 0xa9, 0xcc, 0xa9, 0xcc,
+ 0x86, 0x01, 0x00, 0xca, 0xfc, 0x41, 0x7a, 0xa9, 0x14, 0x86, 0xfc, 0x56, 0x14,
+ 0xfc, 0x3c, 0x95, 0x25, 0xb7, 0xfc, 0x3e, 0x14, 0x91, 0x01, 0xad, 0x14, 0x88,
+ 0x96, 0xca, 0xf9, 0xae, 0xca, 0x03, 0xf7, 0xae, 0xca, 0x9d, 0x00, 0x30, 0x40,
+ 0xa9, 0xcc, 0xa9, 0xcc, 0x86, 0x01, 0x00, 0xca, 0xfc, 0x41, 0x7a, 0xa9, 0x14,
+ 0x86, 0xfc, 0x56, 0x14, 0xfc, 0x3c, 0x95, 0x25, 0xb7, 0xfc, 0x3e, 0x14, 0x91,
+ 0x01, 0xad, 0x14, 0x88, 0x96, 0xca, 0xf9, 0xae, 0xca, 0x03, 0xf7, 0xae, 0xca,
+ 0x9d, 0x00, 0x30, 0x1c, 0xa9, 0xcc, 0xa9, 0xcc, 0x86, 0x01, 0x00, 0xca, 0xfc,
+ 0x41, 0x7a, 0xa9, 0x14, 0x86, 0xfc, 0x56, 0x14, 0xfc, 0x3c, 0x95, 0x25, 0xc4,
+ 0x88, 0x12, 0xc6, 0x89, 0x12, 0x3c, 0xc4, 0x96, 0x12, 0xdc, 0x4d, 0x80, 0x12,
+ 0xce, 0xab, 0xa9, 0xcc, 0x35, 0xd7, 0xae, 0xcc, 0x05, 0xae, 0xcc, 0x89, 0x12,
+ 0x3c, 0xb2, 0x12, 0x00, 0x00, 0xc6, 0xb2, 0x12, 0x00, 0x97, 0x55, 0x12, 0x30,
+ 0x51, 0xb2, 0x12, 0x00, 0x97, 0x55, 0x12, 0x30, 0x74, 0x82, 0x00, 0x0a, 0xdd,
+ 0x48, 0x30, 0x09, 0x82, 0x00, 0x0a, 0xdc, 0x00, 0x3c, 0x90, 0x01, 0x3c, 0xb2,
+ 0x12, 0x00, 0x97, 0x00, 0x12, 0x97, 0x01, 0x03, 0x44, 0x02, 0xa8, 0x0f, 0x80,
+ 0xb7, 0xfd, 0x22, 0x14, 0x91, 0x01, 0xad, 0x14, 0x88, 0x96, 0xca, 0xf9, 0xae,
+ 0xca, 0x03, 0xf7, 0xae, 0xca, 0x9d, 0x00, 0x35, 0xb0, 0xa9, 0xcc, 0xa9, 0xcc,
+ 0x86, 0x01, 0x00, 0xca, 0xfc, 0x41, 0x7a, 0xa9, 0x14, 0x86, 0xfd, 0x26, 0x14,
+ 0xfc, 0x3c, 0x95, 0x25, 0xb7, 0xfd, 0x22, 0x14, 0x91, 0x01, 0xad, 0x14, 0x88,
+ 0x96, 0xca, 0xf9, 0xae, 0xca, 0x03, 0xf7, 0xae, 0xca, 0x9d, 0x00, 0x30, 0x40,
+ 0xa9, 0xcc, 0xa9, 0xcc, 0x86, 0x01, 0x00, 0xca, 0xfc, 0x41, 0x7a, 0xa9, 0x14,
+ 0x86, 0xfd, 0x26, 0x14, 0xfc, 0x3c, 0x95, 0x25, 0xb7, 0xfd, 0x22, 0x14, 0x91,
+ 0x01, 0xad, 0x14, 0x88, 0x96, 0xca, 0xf9, 0xae, 0xca, 0x03, 0xf7, 0xae, 0xca,
+ 0x9d, 0x00, 0x30, 0x1c, 0xa9, 0xcc, 0xa9, 0xcc, 0x86, 0x01, 0x00, 0xca, 0xfc,
+ 0x41, 0x7a, 0xa9, 0x14, 0x86, 0xfd, 0x26, 0x14, 0xfc, 0x3c, 0x95, 0x25, 0xc4,
+ 0x88, 0x12, 0xc6, 0x89, 0x12, 0x3c, 0xc4, 0x96, 0x12, 0xdc, 0x4d, 0x80, 0x12,
+ 0xce, 0xab, 0xa9, 0xcc, 0x36, 0xa7, 0xae, 0xcc, 0x05, 0xae, 0xcc, 0x89, 0x12,
+ 0x3c, 0xac, 0x04, 0xcc, 0xac, 0x06, 0xca, 0xb0, 0xff, 0x7f, 0x82, 0x00, 0x0c,
+ 0xdc, 0xb0, 0x04, 0x01, 0x82, 0x02, 0x0c, 0xdc, 0x90, 0x05, 0x82, 0x00, 0x0b,
+ 0xdc, 0x37, 0x30, 0x82, 0x00, 0x0b, 0xdd, 0x37, 0x4c, 0x00, 0xaa, 0xc8, 0x62,
+ 0xaa, 0xc8, 0x62, 0xaa, 0xc8, 0x62, 0xac, 0x04, 0xcc, 0xac, 0x06, 0xca, 0xb0,
+ 0xff, 0x7f, 0x82, 0x00, 0x0c, 0xdc, 0xb0, 0x04, 0x01, 0x82, 0x02, 0x0c, 0xdc,
+ 0x90, 0x05, 0x82, 0x00, 0x0b, 0xdc, 0x37, 0x25, 0x82, 0x00, 0x0b, 0xdd, 0x37,
+ 0x4a,0x3c
+};
+
+/* This is the main firmware which implements the FDDI PHY transactions and talks
+ * to the MAC chipsets.
+ */
+const unsigned short main_firmware_dev_addr = 0xf000; /* Offset as seen by device. */
+const unsigned short main_firmware_size = 0x1000; /* Size of main firmware. */
+
+static unsigned char main_firmware[] __initdata = {
+ 0x00, 0xaa, 0xc8, 0x62, 0xab, 0x00, 0xab, 0xbe, 0x8b, 0xba, 0x97, 0x3c, 0xd0,
+ 0x90, 0x01, 0x8b, 0xd4, 0x8b, 0xf4, 0xb7, 0x02, 0x03, 0xe2, 0xb7, 0x02, 0x03,
+ 0xf2, 0x97, 0x14, 0xc0, 0xb2, 0x20, 0x3c, 0xb3, 0x01, 0x52, 0x00, 0xf3, 0xa7,
+ 0x01, 0x92, 0x01, 0x82, 0x90, 0x0c, 0xe3, 0xb0, 0xcc, 0xcc, 0xf6, 0xe6, 0xb6,
+ 0x01, 0x5c, 0x08, 0xf1, 0xb0, 0x88, 0xcc, 0xe3, 0xb0, 0x21, 0x52, 0xe3, 0x00,
+ 0x05, 0xe3, 0x63, 0x8f, 0x08, 0x8d, 0x00, 0x07, 0xe4, 0x04, 0xe6, 0xb3, 0xff,
+ 0x80, 0xf1, 0x00, 0xaa, 0xca, 0x64, 0xb7, 0x3e, 0x80, 0xb6, 0x91, 0x16, 0xf1,
+ 0xa8, 0xb6, 0x82, 0x80, 0xb7, 0xdb, 0xaa, 0xca, 0x69, 0xb0, 0x3f, 0xff, 0xf1,
+ 0xf0, 0xb2, 0x20, 0x3c, 0xb7, 0x01, 0xc0, 0xc4, 0xb6, 0xff, 0xfe, 0xa8, 0x27,
+ 0x90, 0xf7, 0x92, 0xba, 0x17, 0x20, 0x1f, 0x00, 0x8b, 0xd2, 0xab, 0xaa, 0x05,
+ 0xf3, 0x9c, 0x00, 0x55, 0xb6, 0x01, 0x04, 0x10, 0x9a, 0x04, 0xab, 0xbe, 0x83,
+ 0x01, 0xff, 0x16, 0xab, 0x96, 0xc8, 0x12, 0x83, 0x02, 0xff, 0x16, 0xab, 0x90,
+ 0x01, 0xb6, 0x01, 0x04, 0x11, 0x04, 0xb6, 0xff, 0x02, 0xab, 0x92, 0xce, 0xb3,
+ 0x20, 0x30, 0x30, 0x26, 0xb3, 0x28, 0x30, 0x96, 0xbe, 0x12, 0x30, 0x1e, 0xb2,
+ 0x20, 0x2e, 0x30, 0x06, 0xb6, 0xff, 0x00, 0x89, 0x94, 0x2b, 0x00, 0xc6, 0xc4,
+ 0x99, 0x01, 0x9d, 0x00, 0x4a, 0x90, 0x40, 0xc6, 0xa8, 0xe2, 0xb9, 0xfd, 0xff,
+ 0xab, 0xe2, 0x3c, 0xd4, 0x9d, 0x11, 0x3c, 0xc7, 0x0e, 0x90, 0x01, 0x06, 0xd6,
+ 0x0b, 0x09, 0x8f, 0x0e, 0x3c, 0xb6, 0xff, 0x8e, 0xae, 0xb6, 0xff, 0x8c, 0xab,
+ 0x0e, 0x96, 0xd0, 0x18, 0xb6, 0x01, 0x20, 0x11, 0x26, 0x92, 0xaa, 0x18, 0x13,
+ 0x25, 0xb6, 0xff, 0x8e, 0xa8, 0xda, 0x8b, 0xab, 0xdb, 0xe7, 0xe7, 0xb8, 0xff,
+ 0x90, 0xab, 0xb4, 0xab, 0xce, 0xf0, 0xab, 0xac, 0x8f, 0xfb, 0x05, 0xbd, 0x7f,
+ 0xfe, 0x94, 0x40, 0xa8, 0xac, 0xbd, 0x3f, 0xff, 0xb4, 0x01, 0x8a, 0x04, 0xab,
+ 0xb2, 0x05, 0xe7, 0xe7, 0xab, 0xce, 0xf0, 0xab, 0xae, 0xf4, 0xab, 0xb0, 0xb3,
+ 0xff, 0x82, 0xf0, 0xe7, 0x07, 0x28, 0x92, 0xba, 0x16, 0x24, 0x92, 0xaa, 0xa8,
+ 0xb0, 0xbd, 0x7f, 0xff, 0xb4, 0x01, 0x9d, 0xbd, 0x07, 0xff, 0xb4, 0x01, 0xf0,
+ 0x9d, 0x08, 0xb4, 0x02, 0xbf, 0xb4, 0x01, 0x06, 0xa8, 0xb2, 0xad, 0xb4, 0xab,
+ 0x92, 0xaa, 0x90, 0x08, 0xdd, 0x96, 0xd0, 0x08, 0x95, 0x6f, 0x90, 0x08, 0x2d,
+ 0x97, 0xfb, 0xd2, 0xb3, 0x10, 0x5e, 0xb2, 0x12, 0x0a, 0x00, 0xd5, 0x8b, 0xb6,
+ 0x00, 0xc5, 0x96, 0xbe, 0x10, 0x5a, 0x8b, 0xb7, 0xb3, 0x18, 0x5e, 0xb2, 0x1a,
+ 0x0a, 0x00, 0xd5, 0x8b, 0xb8, 0x00, 0xc5, 0xc5, 0x88, 0xb8, 0xd5, 0xb2, 0x12,
+ 0x0a, 0xb3, 0x10, 0x5e, 0x88, 0xb7, 0xc5, 0x88, 0xb6, 0xd5, 0x92, 0xaa, 0x57,
+ 0x90, 0x06, 0x2d, 0xb3, 0x20, 0x06, 0x00, 0xd5, 0xd5, 0x4d, 0x90, 0x07, 0x2d,
+ 0xb3, 0x28, 0x06, 0x6a, 0x90, 0x05, 0x42, 0x90, 0x09, 0x2d, 0x1e, 0xa5, 0xff,
+ 0x8c, 0xff, 0x8e, 0xab, 0x3c, 0xab, 0xca, 0xb9, 0x07, 0xff, 0xbd, 0x03, 0xff,
+ 0x41, 0x5a, 0x92, 0xcb, 0x97, 0x7c, 0xc9, 0xe7, 0x11, 0x43, 0x10, 0x46, 0x3c,
+ 0x10, 0x47, 0xad, 0xc8, 0xa9, 0xad, 0xc8, 0xa8, 0x50, 0xab, 0xcc, 0xe4, 0x05,
+ 0xe6, 0x04, 0x49, 0x1e, 0x17, 0x42, 0x94, 0x59, 0x1f, 0x16, 0x67, 0x57, 0x92,
+ 0xc9, 0xbd, 0x03, 0xff, 0x6d, 0xbd, 0x02, 0xdf, 0x94, 0x29, 0xe7, 0x12, 0x51,
+ 0x11, 0x48, 0xe7, 0x11, 0x0b, 0x10, 0x0c, 0x0e, 0xc7, 0x53, 0xbd, 0x03, 0xbe,
+ 0x53, 0xb8, 0xfa, 0x00, 0xb8, 0x14, 0x00, 0x10, 0x41, 0x46, 0x96, 0xc8, 0x17,
+ 0xb8, 0x00, 0x80, 0x96, 0xaa, 0x08, 0x3c, 0xc7, 0xb8, 0x2e, 0x40, 0x68, 0x10,
+ 0x55, 0x96, 0xc8, 0x14, 0x4d, 0xbd, 0x02, 0xe0, 0xb0, 0x34, 0x00, 0xbc, 0x02,
+ 0xe0, 0xb0, 0x30, 0x00, 0x7b, 0xb8, 0xfd, 0xc9, 0x4a, 0x96, 0xc8, 0x17, 0x43,
+ 0x99, 0x7f, 0x6b, 0xb8, 0x1c, 0x80, 0xe7, 0x3c, 0xa8, 0xae, 0x96, 0xc9, 0x17,
+ 0x41, 0x3c, 0x2b, 0xab, 0xcc, 0xc4, 0x96, 0xaa, 0x10, 0x41, 0xe4, 0xae, 0xca,
+ 0x88, 0xc9, 0xe7, 0x3b, 0x99, 0x0f, 0x96, 0xca, 0xf8, 0x3c, 0xcc, 0x09, 0x1c,
+ 0x0d, 0x16, 0x38, 0x37, 0x4d, 0x63, 0x6e, 0xa1, 0xae, 0xff, 0xbe, 0xab, 0x2f,
+ 0x17, 0x4a, 0x0f, 0xa5, 0xff, 0x8e, 0xff, 0x8a, 0xab, 0x44, 0x17, 0x41, 0x5d,
+ 0x1f, 0x2a, 0xab, 0xcc, 0x99, 0x0f, 0x9d, 0x0b, 0x4a, 0x9c, 0x0b, 0xb6, 0xff,
+ 0x8a, 0xa8, 0xb6, 0xff, 0x8e, 0xab, 0xa8, 0xcc, 0x82, 0x02, 0xb0, 0xdc, 0xb6,
+ 0xff, 0xfc, 0xab, 0xb4, 0xfe, 0xbd, 0x2a, 0xae, 0xb2, 0x92, 0xaa, 0x15, 0x96,
+ 0xc9, 0x0e, 0x96, 0xb0, 0x10, 0x0d, 0xaf, 0xc8, 0x96, 0xb3, 0x16, 0x41, 0x76,
+ 0xa8, 0xb2, 0x2c, 0x3f, 0xc8, 0x92, 0xc9, 0x17, 0x4c, 0x16, 0x43, 0x96, 0xaa,
+ 0x1d, 0x1e, 0xab, 0xb2, 0x95, 0x28, 0xe7, 0xe7, 0x92, 0xaa, 0xaf, 0xc8, 0x3c,
+ 0x2a, 0x92, 0xc9, 0x1f, 0x16, 0x0f, 0xa0, 0xc8, 0xb2, 0xf8, 0x95, 0x3b, 0x88,
+ 0xaf, 0x97, 0x7c, 0xc9, 0xe7, 0xad, 0xc8, 0xaa, 0xad, 0xc8, 0xa8, 0x9c, 0x00,
+ 0x95, 0x4b, 0x94, 0x4a, 0x96, 0xb1, 0x16, 0x08, 0x2b, 0x92, 0xaa, 0xab, 0xce,
+ 0x10, 0x41, 0xf4, 0xd4, 0x93, 0xb6, 0xf6, 0x88, 0xaf, 0x96, 0xb1, 0x16, 0x4a,
+ 0x97, 0x7c, 0xc9, 0xe7, 0xad, 0xc8, 0xa8, 0x10, 0x99, 0xff, 0x8d, 0xb8, 0x00,
+ 0xe6, 0x88, 0xb1, 0x3b, 0x99, 0x03, 0xcc, 0x04, 0x07, 0x0a, 0x0d, 0xf4, 0xfc,
+ 0x4e, 0x50, 0xf4, 0xfd, 0x4a, 0x4c, 0xf4, 0xfd, 0x46, 0x6a, 0xf4, 0xf9, 0x9d,
+ 0x00, 0x43, 0x96, 0xca, 0x0b, 0x88, 0xb1, 0x96, 0xca, 0xf9, 0x9c, 0x00, 0x4c,
+ 0x88, 0xae, 0x96, 0xc8, 0x17, 0xba, 0xff, 0x00, 0xa0, 0xc8, 0xb2, 0xf8, 0xb4,
+ 0xfe, 0x18, 0x2b, 0xab, 0xce, 0xab, 0xb6, 0x88, 0xb1, 0x9d, 0x77, 0x46, 0x92,
+ 0xb8, 0xac, 0xae, 0xb8, 0x4f, 0x96, 0xaf, 0x17, 0x76, 0xa8, 0xae, 0xbd, 0x77,
+ 0xff, 0x6f, 0x2b, 0xab, 0xcc, 0x88, 0xaf, 0xe7, 0x3b, 0x99, 0x0f, 0x96, 0xaa,
+ 0x10, 0x96, 0xc8, 0x0c, 0x02, 0xcc, 0x4f, 0x1f, 0x20, 0x22, 0x24, 0x28, 0x2a,
+ 0x2d, 0x2f, 0x31, 0x33, 0x36, 0x3a, 0x3c, 0x3e, 0x40, 0x77, 0x47, 0x48, 0x4a,
+ 0x4c, 0x4e, 0x50, 0x53, 0x55, 0x59, 0x5b, 0x5e, 0x62, 0x64, 0x66, 0x68, 0xe4,
+ 0x4d, 0xf4, 0xe5, 0x4a, 0xf4, 0xf8, 0x47, 0xf4, 0xf8, 0x00, 0xf7, 0x42, 0xf4,
+ 0xeb, 0x58, 0xe4, 0x8f, 0xeb, 0x54, 0xf4, 0xfe, 0x5b, 0xf4, 0xfe, 0x49, 0xf4,
+ 0xff, 0x55, 0xe4, 0x8f, 0xff, 0x51, 0xf4, 0xff, 0xa8, 0xce, 0x4c, 0xf4, 0xf9,
+ 0x4c, 0xf4, 0xfa, 0x49, 0xf4, 0xfb, 0x46, 0xf4, 0x23, 0x43, 0xac, 0xb6, 0xce,
+ 0xf6, 0x94, 0x36, 0xc4, 0x4b, 0xd4, 0xc5, 0x48, 0xd4, 0xd8, 0x45, 0xd4, 0xd8,
+ 0x4c, 0xd4, 0xcb, 0x5a, 0xc4, 0x8f, 0xcb, 0x56, 0xd4, 0xde, 0x5d, 0xd4, 0xde,
+ 0x88, 0xc9, 0x58, 0xd4, 0xdf, 0x55, 0xc4, 0x8f, 0xdf, 0x51, 0xd4, 0xdf, 0xa8,
+ 0xce, 0x4c, 0xd4, 0xd9, 0x4c, 0xd4, 0xda, 0x49, 0xd4, 0xdb, 0x46, 0xd4, 0x23,
+ 0x43, 0xac, 0xb6, 0xce, 0xd6, 0xb4, 0xfd, 0x5c, 0xae, 0xca, 0xc4, 0x99, 0x0f,
+ 0xae, 0xca, 0x82, 0x00, 0xca, 0xfc, 0x3c, 0x14, 0x45, 0xe7, 0xaa, 0xca, 0x63,
+ 0x3c, 0xc7, 0xaa, 0xca, 0x63, 0x3c, 0xbd, 0x03, 0xff, 0x5e, 0x92, 0xc9, 0xb3,
+ 0x12, 0x10, 0x10, 0xb3, 0x1a, 0x10, 0x96, 0xc8, 0x17, 0x94, 0x72, 0xa8, 0xae,
+ 0x17, 0x4c, 0x16, 0x94, 0x5e, 0x15, 0x94, 0x3f, 0x9c, 0x80, 0x94, 0x1f, 0x9d,
+ 0x00, 0xb4, 0xfd, 0x1f, 0xa8, 0xce, 0xb8, 0xff, 0x2e, 0xab, 0xcc, 0x9a, 0x28,
+ 0xab, 0xca, 0xb8, 0xfe, 0xf2, 0xab, 0xce, 0x00, 0xe1, 0x62, 0x00, 0xd6, 0x97,
+ 0x38, 0xce, 0xd6, 0x7c, 0xa8, 0xce, 0xb8, 0xfe, 0x80, 0xab, 0xcc, 0x9a, 0x0c,
+ 0xab, 0xca, 0x22, 0x97, 0xb0, 0xcc, 0x97, 0xfe, 0xca, 0x22, 0x74, 0x00, 0x96,
+ 0xcc, 0x14, 0x05, 0xe1, 0x66, 0x3c, 0x04, 0x97, 0x7c, 0xc9, 0xe7, 0xab, 0xcc,
+ 0x91, 0x04, 0xe2, 0xab, 0xb8, 0xe4, 0xab, 0xb6, 0x97, 0x04, 0xce, 0x00, 0xf1,
+ 0x92, 0xb9, 0xc2, 0xd6, 0xaa, 0xca, 0x64, 0x4b, 0xac, 0xce, 0xcc, 0x97, 0x04,
+ 0xce, 0x99, 0x1f, 0xd6, 0x18, 0x10, 0x5c, 0x62, 0x3b, 0x99, 0xf0, 0x8b, 0xb6,
+ 0x2a, 0x96, 0xc9, 0x10, 0x96, 0xb6, 0x08, 0xac, 0xce, 0xcc, 0x97, 0x1a, 0xce,
+ 0x11, 0x41, 0x62, 0xd6, 0xf3, 0x88, 0xb6, 0xd6, 0x19, 0xb4, 0xfc, 0xa2, 0x8d,
+ 0xab, 0xb2, 0xc0, 0x3b, 0x3c, 0xe7, 0x07, 0x45, 0x82, 0x06, 0xb0, 0xfd, 0x3c,
+ 0xe7, 0xf4, 0x07, 0x2c, 0xb6, 0xff, 0x86, 0xa8, 0xb9, 0x03, 0xff, 0xe7, 0xe7,
+ 0xe7, 0xba, 0x60, 0x00, 0xab, 0xce, 0x29, 0x3b, 0x3b, 0x97, 0x80, 0xc8, 0xf1,
+ 0xe0, 0x62, 0xb6, 0xff, 0x86, 0xa9, 0x3c, 0x20, 0x21, 0x27, 0x9c, 0xf6, 0x65,
+ 0x92, 0xba, 0x9d, 0xef, 0x4f, 0x93, 0xbd, 0x9d, 0x2f, 0x9d, 0x3f, 0x42, 0x9a,
+ 0xf0, 0xd3, 0x20, 0x21, 0xd5, 0x94, 0x69, 0xac, 0xbc, 0xce, 0x99, 0x0f, 0xcc,
+ 0x44, 0x2a, 0x38, 0x33, 0x2e, 0x1c, 0x3e, 0x09, 0x12, 0x0f, 0x10, 0x0d, 0x30,
+ 0x37, 0x36, 0x35, 0x0f, 0x1c, 0xa1, 0xce, 0xff, 0xbe, 0xab, 0x2f, 0x0d, 0x41,
+ 0x1d, 0x0e, 0x96, 0xc8, 0x11, 0x41, 0x3c, 0x90, 0xf0, 0x96, 0xaa, 0x14, 0x4a,
+ 0x96, 0xaa, 0x0b, 0x3c, 0x90, 0xf1, 0x20, 0x21, 0x8e, 0xbb, 0x55, 0x88, 0xbb,
+ 0xd5, 0x51, 0x88, 0xbb, 0xd6, 0x4d, 0xd4, 0x4b, 0x1e, 0x14, 0x3f, 0xc8, 0x1c,
+ 0x90, 0xfc, 0x43, 0x29, 0x92, 0xba, 0x5b, 0x1b, 0x0c, 0x90, 0x0a, 0xb6, 0xff,
+ 0x8e, 0xae, 0xb6, 0xff, 0x88, 0xab, 0xa1, 0xbc, 0xff, 0xb8, 0xab, 0x2e, 0x1c,
+ 0xa5, 0xff, 0x88, 0xff, 0x8e, 0xab, 0x90, 0xf5, 0xb6, 0x01, 0x20, 0x10, 0x41,
+ 0x65, 0xb6, 0x01, 0x26, 0x8b, 0x3c, 0xb6, 0x01, 0x20, 0x11, 0x41, 0x65, 0xb6,
+ 0x01, 0x24, 0x88, 0x3c, 0x15, 0x45, 0x82, 0x06, 0xb0, 0xfd, 0x3c, 0x0c, 0x90,
+ 0x80, 0x20, 0x21, 0x9c, 0xff, 0x42, 0x26, 0x69, 0x1c, 0x29, 0x20, 0x21, 0xc0,
+ 0x63, 0x3c, 0x40, 0x40, 0x40, 0x40, 0x3c, 0x40, 0x40, 0x40, 0x40, 0x3c, 0x3c,
+ 0x3c, 0x91, 0x00, 0xb6, 0x12, 0x08, 0x88, 0x96, 0xc8, 0x13, 0x30, 0x55, 0x96,
+ 0xc8, 0x14, 0x30, 0x41, 0x96, 0xc8, 0x16, 0x94, 0x2b, 0x96, 0xc8, 0x17, 0x5c,
+ 0xa8, 0xca, 0x96, 0xc8, 0x13, 0x30, 0x2b, 0x00, 0xb6, 0x12, 0x08, 0x8b, 0xb6,
+ 0x10, 0x5c, 0x88, 0x96, 0xc8, 0x10, 0x30, 0x5d, 0x82, 0x00, 0xca, 0xfc, 0x3c,
+ 0x94, 0x77, 0x00, 0xb6, 0x12, 0x0e, 0x8b, 0x82, 0x08, 0xca, 0xfa, 0x95, 0x25,
+ 0x00, 0xb6, 0x12, 0x16, 0x8b, 0x82, 0x08, 0xca, 0xfa, 0x95, 0x30, 0x00, 0xb6,
+ 0x12, 0x0a, 0x8b, 0x3c, 0xae, 0xce, 0xb6, 0x12, 0x1c, 0x88, 0x99, 0x55, 0xb6,
+ 0x12, 0x1c, 0x8b, 0xae, 0xce, 0x3c, 0xae, 0xce, 0xb6, 0x12, 0x28, 0x88, 0x96,
+ 0xc8, 0x10, 0x54, 0x96, 0xc8, 0x14, 0x50, 0x96, 0xc8, 0x12, 0x4c, 0x83, 0x00,
+ 0x12, 0x2a, 0x8b, 0x82, 0x08, 0xca, 0xfa, 0xae, 0xce, 0x3c, 0x99, 0xea, 0xb6,
+ 0x12, 0x28, 0x8b, 0x82, 0x02, 0xca, 0xfa, 0xae, 0xce, 0x3c, 0xb6, 0x10, 0x1c,
+ 0x88, 0xb6, 0xff, 0x0a, 0x8b, 0xb6, 0x10, 0x20, 0x88, 0x99, 0xfc, 0xb6, 0x10,
+ 0x20, 0x8b, 0xb6, 0x10, 0x5c, 0x88, 0x99, 0xfe, 0xb6, 0x10, 0x5c, 0x8b, 0x82,
+ 0x04, 0xca, 0xfa, 0x3c, 0xb3, 0xff, 0x50, 0xa2, 0x01, 0xce, 0x88, 0x9d, 0x00,
+ 0x66, 0x90, 0x01, 0xd6, 0xa2, 0x01, 0xce, 0x88, 0x9d, 0x00, 0x5b, 0xb6, 0xff,
+ 0x52, 0xa8, 0x96, 0xca, 0xfa, 0xb6, 0xff, 0x52, 0xab, 0xb6, 0xff, 0xfc, 0xa8,
+ 0x83, 0x00, 0xff, 0x56, 0xfd, 0xb6, 0xff, 0xfc, 0xab, 0x00, 0xd6, 0x3c, 0x00,
+ 0xd6, 0x95, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0xfe,
+ 0x93, 0x40, 0xb4, 0xfe, 0x8e, 0x40, 0xb4, 0xfe, 0x8c, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xf9, 0x01,
+ 0x12, 0x30, 0x1f, 0x8b, 0x11, 0x30, 0x1b, 0x8b, 0x0d, 0x30, 0x17, 0x8b, 0x0c,
+ 0x82, 0x00, 0x11, 0xdc, 0x4a, 0x30, 0x0e, 0xad, 0x0c, 0x8b, 0xa9, 0x0c, 0x8a,
+ 0x11, 0x69, 0x83, 0x00, 0xf9, 0x00, 0xab, 0x3c, 0xb6, 0xf9, 0x00, 0xa8, 0x96,
+ 0xc8, 0x17, 0x41, 0x68, 0xad, 0x12, 0x88, 0xa9, 0x12, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xf5, 0x68, 0xf5, 0x5c, 0xf4, 0xf1, 0xf3,
+ 0x73, 0xf5, 0x42, 0xf5, 0xdc, 0xf4, 0x6e, 0xf5, 0xb1, 0xf4, 0xab, 0xf4, 0x37,
+ 0xf2, 0xb2, 0xf1, 0xb7, 0xf2, 0xe2, 0xf0, 0xeb, 0xf0, 0x0a, 0xf0, 0x0a, 0xf0,
+ 0xa7, 0xf1, 0xa4, 0xf1, 0x9d, 0xf1, 0x93, 0xf1, 0x5c, 0xf1, 0x00, 0x00, 0x00,
+ 0xf0
+};
+
+/* This is the FDDI station management firmware. */
+const unsigned short smt_firmware_dev_addr = 0x4000; /* Offset as seen by device. */
+const unsigned short smt_firmware_size = 0x72b0; /* Size of SMT firmware. */
+
+static unsigned char smt_firmware[] __initdata = {
+ 0x94, 0xaa, 0x00, 0x00, 0x94, 0x5d, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x94, 0x2c,
+ 0x00, 0x00, 0xb4, 0x5d, 0xfa, 0x00, 0x94, 0xc4, 0x00, 0x00, 0x94, 0xc8, 0x00, 0x00,
+ 0x94, 0xcc, 0x00, 0x00, 0xb4, 0x01, 0xbc, 0x00, 0x00, 0x00, 0xb2, 0xfe, 0x20, 0xc4,
+ 0x96, 0xc8, 0x17, 0x41, 0x3c, 0xb5, 0x56, 0xa7, 0xb2, 0xfe, 0x20, 0x00, 0xe6, 0xb4,
+ 0x04, 0x1a, 0x83, 0x00, 0xfe, 0x94, 0xab, 0xb2, 0xfe, 0x20, 0xe4, 0x96, 0xc8, 0x17,
+ 0x41, 0x45, 0x99, 0x3f, 0x9c, 0x03, 0x46, 0x83, 0x00, 0xfe, 0x20, 0xab, 0x3c, 0x82,
+ 0x02, 0xcc, 0xf8, 0xb3, 0xfe, 0x90, 0xb5, 0x3b, 0x12, 0x83, 0xe0, 0xfe, 0x94, 0xab,
+ 0x75, 0xac, 0x74, 0xce, 0x92, 0x00, 0xb5, 0x35, 0xdf, 0x92, 0x0e, 0xb6, 0x10, 0x00,
+ 0x88, 0x99, 0xfe, 0xb6, 0x10, 0x00, 0x8b, 0xb5, 0x35, 0xc3, 0x90, 0x01, 0xb5, 0x35,
+ 0xd8, 0xb5, 0x35, 0x1e, 0xb6, 0x10, 0x00, 0x88, 0x9a, 0x01, 0xb6, 0x10, 0x00, 0x8b,
+ 0x89, 0x7d, 0xac, 0x6e, 0x02, 0xac, 0x70, 0x04, 0xb7, 0x04, 0x00, 0x06, 0xac, 0x74,
+ 0xcc, 0x82, 0x34, 0xcc, 0xf8, 0x00, 0xb5, 0x2c, 0xf0, 0x90, 0x01, 0xb5, 0x34, 0x80,
+ 0xb5, 0x34, 0xf5, 0x3c, 0xa7, 0xb8, 0x00, 0xda, 0xda, 0x00, 0xe1, 0x62, 0x8d, 0x02,
+ 0x80, 0x00, 0xe1, 0x62, 0xa8, 0xbe, 0x99, 0x0c, 0xc7, 0xc7, 0x04, 0x8b, 0x7e, 0xb7,
+ 0xb8, 0x0c, 0x72, 0xb7, 0xb8, 0x44, 0x74, 0xb7, 0xb8, 0xa4, 0x76, 0xb7, 0xba, 0x8a,
+ 0x78, 0xb5, 0x02, 0xf4, 0xb4, 0x03, 0x1b, 0x3c, 0xb5, 0x2e, 0xf4, 0xb4, 0x03, 0x74,
+ 0x40, 0x40, 0xb7, 0x00, 0x00, 0x08, 0xb2, 0x20, 0x00, 0x4c, 0x82, 0x01, 0x7e, 0xdc,
+ 0x3c, 0xb7, 0x00, 0x01, 0x08, 0xb2, 0x28, 0x00, 0xa2, 0x04, 0xcc, 0x88, 0xa2, 0x06,
+ 0xcc, 0xd9, 0x8b, 0x0a, 0x99, 0x60, 0x9c, 0x00, 0x94, 0x56, 0xa8, 0x08, 0xb5, 0x33,
+ 0x33, 0xab, 0xca, 0x99, 0x20, 0x9c, 0x00, 0x57, 0xb7, 0x02, 0x0a, 0x02, 0xac, 0xca,
+ 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x08, 0xb5, 0x04, 0x98, 0x90, 0x20, 0x01, 0xa0,
+ 0xc8, 0xca, 0xf9, 0xa8, 0xca, 0x99, 0x02, 0x9c, 0x00, 0x57, 0xa8, 0x08, 0xb5, 0x02,
+ 0xed, 0xa2, 0x4e, 0x76, 0xa8, 0x9c, 0x03, 0x4b, 0xaf, 0xca, 0xa8, 0x08, 0x91, 0x02,
+ 0xb5, 0x33, 0x86, 0x3f, 0xca, 0x82, 0x00, 0xca, 0xfc, 0x50, 0xb7, 0x02, 0x09, 0x02,
+ 0xac, 0xca, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x08, 0xb5, 0x04, 0x5e, 0x88, 0x0a,
+ 0x99, 0x10, 0x9c, 0x00, 0x55, 0xa8, 0x08, 0x32, 0x11, 0xb7, 0x05, 0x05, 0x02, 0xb7,
+ 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x08, 0xb5, 0x04, 0x42, 0x90, 0x80,
+ 0xa2, 0x04, 0xcc, 0x8e, 0xb4, 0x02, 0xd3, 0x00, 0xb6, 0xff, 0x44, 0xa9, 0xb6, 0x12,
+ 0x0e, 0x8b, 0x82, 0x08, 0xca, 0xfa, 0x94, 0x6d, 0x00, 0xb6, 0xff, 0x42, 0xa9, 0xb6,
+ 0x12, 0x16, 0x8b, 0x82, 0x08, 0xca, 0xfa, 0x94, 0x5e, 0x00, 0xb6, 0x12, 0x0a, 0x8b,
+ 0x3c, 0xae, 0xce, 0xb6, 0x12, 0x1c, 0x88, 0x99, 0x55, 0xb6, 0x12, 0x1c, 0x8b, 0xae,
+ 0xce, 0x3c, 0xae, 0xce, 0xb6, 0x12, 0x28, 0x88, 0x96, 0xc8, 0x10, 0x54, 0x96, 0xc8,
+ 0x14, 0x50, 0x96, 0xc8, 0x12, 0x4c, 0x83, 0x00, 0x12, 0x2a, 0x8b, 0x82, 0x08, 0xca,
+ 0xfa, 0xae, 0xce, 0x3c, 0x99, 0xea, 0xb6, 0x12, 0x28, 0x8b, 0x82, 0x02, 0xca, 0xfa,
+ 0xae, 0xce, 0x3c, 0xb7, 0x00, 0x00, 0x0c, 0x91, 0x00, 0xb6, 0x12, 0x08, 0x88, 0x96,
+ 0xc8, 0x13, 0x34, 0x38, 0x96, 0xc8, 0x14, 0x34, 0x4c, 0x96, 0xc8, 0x16, 0x95, 0x66,
+ 0x96, 0xc8, 0x17, 0x95, 0x7a, 0xa8, 0xca, 0x96, 0xc8, 0x13, 0x34, 0x63, 0x00, 0xb6,
+ 0x12, 0x08, 0x8b, 0xac, 0xca, 0x0c, 0x97, 0x00, 0x09, 0xb6, 0x10, 0x5c, 0x88, 0xb6,
+ 0x10, 0x5e, 0xd9, 0x8b, 0x0a, 0x99, 0x01, 0x9c, 0x00, 0x94, 0x7f, 0xb6, 0x10, 0x24,
+ 0x88, 0x99, 0x23, 0x9c, 0x00, 0x94, 0x3f, 0xab, 0xca, 0x99, 0x20, 0x9c, 0x00, 0x46,
+ 0x97, 0x01, 0x09, 0x97, 0x20, 0x08, 0xa8, 0xca, 0x99, 0x02, 0x9c, 0x00, 0x47, 0x82,
+ 0x02, 0x09, 0xda, 0x97, 0x02, 0x08, 0xa8, 0xca, 0x99, 0x01, 0x9c, 0x00, 0x48, 0x82,
+ 0x04, 0x09, 0xda, 0x82, 0x01, 0x08, 0xda, 0xa8, 0xca, 0x01, 0xab, 0xca, 0xb6, 0x10,
+ 0x26, 0x88, 0x96, 0xca, 0xf9, 0xb6, 0x10, 0x26, 0x8b, 0x00, 0xb6, 0x10, 0x24, 0x8b,
+ 0xb6, 0x10, 0x20, 0x88, 0x99, 0x03, 0x9c, 0x00, 0x94, 0x2c, 0xb6, 0x10, 0x1c, 0x88,
+ 0x99, 0x80, 0x9c, 0x00, 0x48, 0x90, 0x40, 0xb5, 0x5b, 0x18, 0x90, 0x80, 0x41, 0x00,
+ 0xb6, 0xff, 0x0a, 0x8b, 0x82, 0x04, 0x0c, 0xfa, 0xb7, 0x04, 0x03, 0x02, 0xab, 0x04,
+ 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb6, 0x10, 0x20, 0x8b, 0xb5, 0x03, 0x1c, 0x88, 0x0a,
+ 0x99, 0x02, 0x9c, 0x00, 0x94, 0x3f, 0xb6, 0x10, 0x28, 0x88, 0x99, 0x10, 0x9c, 0x00,
+ 0x5a, 0x82, 0x08, 0x09, 0xda, 0x90, 0x10, 0x01, 0xab, 0xca, 0xb6, 0x10, 0x2a, 0x88,
+ 0x96, 0xca, 0xf9, 0xb6, 0x10, 0x2a, 0x8b, 0x00, 0xb6, 0x10, 0x28, 0x8b, 0x40, 0xb6,
+ 0x10, 0x28, 0x88, 0x99, 0x40, 0x9c, 0x00, 0x53, 0xb5, 0x33, 0x96, 0x90, 0x40, 0x01,
+ 0xab, 0xca, 0xb6, 0x10, 0x2a, 0x88, 0x96, 0xca, 0xf9, 0xb6, 0x10, 0x2a, 0x8b, 0x88,
+ 0x0a, 0x99, 0x08, 0x9c, 0x00, 0x47, 0x30, 0x40, 0x00, 0xb6, 0x10, 0x38, 0x8b, 0x00,
+ 0xb6, 0x10, 0x5c, 0x8e, 0x82, 0x00, 0x09, 0xdc, 0x50, 0xb7, 0x04, 0x04, 0x02, 0x80,
+ 0x09, 0x04, 0xab, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb5, 0x02, 0xad, 0x82, 0x00, 0x0c,
+ 0xfc, 0xb4, 0x01, 0x40, 0xac, 0x0c, 0xca, 0xb5, 0xb3, 0x29, 0xb4, 0x01, 0x37, 0x88,
+ 0x09, 0x9a, 0x20, 0x8b, 0x09, 0xb6, 0x10, 0x20, 0x88, 0x99, 0xbf, 0xb6, 0x10, 0x20,
+ 0x8b, 0x3c, 0xb2, 0x10, 0x38, 0x00, 0xc5, 0xab, 0xca, 0x93, 0x5a, 0x96, 0xca, 0x10,
+ 0x30, 0x36, 0x82, 0x02, 0xce, 0xf8, 0x96, 0xca, 0x11, 0x30, 0x2d, 0x82, 0x02, 0xce,
+ 0xf8, 0x96, 0xca, 0x12, 0x30, 0x24, 0x82, 0x02, 0xce, 0xf8, 0x96, 0xca, 0x13, 0x30,
+ 0x1b, 0x82, 0x02, 0xce, 0xf8, 0x96, 0xca, 0x14, 0x30, 0x12, 0x82, 0x02, 0xce, 0xf8,
+ 0x96, 0xca, 0x15, 0x30, 0x09, 0x82, 0x02, 0xce, 0xf8, 0x96, 0xca, 0x16, 0x30, 0x00,
+ 0xf4, 0xb8, 0x00, 0x10, 0xf6, 0x3c, 0xb2, 0x28, 0x00, 0x9c, 0x00, 0xb2, 0x20, 0x00,
+ 0xe7, 0x99, 0x02, 0x93, 0x54, 0xa0, 0xc8, 0xce, 0xf8, 0xf4, 0x9c, 0x00, 0x4f, 0x9d,
+ 0xff, 0x90, 0xff, 0x02, 0xf5, 0x8f, 0xeb, 0xf5, 0xa2, 0x2a, 0xcc, 0x8e, 0x00, 0x3c,
+ 0x90, 0x64, 0xa2, 0x2a, 0xcc, 0x8e, 0x90, 0x01, 0x3c, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x80,
+ 0x01, 0x43, 0x00, 0x80, 0x00, 0x80, 0x01, 0x43, 0x00, 0x80, 0x08, 0x80, 0x01, 0x43,
+ 0x00, 0x80, 0x0c, 0x00, 0xb5, 0x00, 0x7d, 0xb5, 0x00, 0x80, 0xb7, 0x00, 0x01, 0x02,
+ 0x82, 0x00, 0x02, 0xfc, 0x4b, 0xa8, 0x02, 0xb5, 0x28, 0x06, 0xae, 0x02, 0x05, 0xae,
+ 0x02, 0x6f, 0xb5, 0x5a, 0x0d, 0xb5, 0x01, 0x16, 0xb5, 0x29, 0x45, 0x3c, 0x90, 0x00,
+ 0xb6, 0xb8, 0x00, 0xab, 0xb5, 0x01, 0x33, 0xb5, 0x59, 0xfa, 0xb5, 0x00, 0x53, 0xb5,
+ 0x29, 0x51, 0xb5, 0x27, 0xe1, 0xb5, 0x59, 0x84, 0x30, 0x04, 0xb5, 0xfc, 0xce, 0x3c,
+ 0x3c, 0x40, 0x96, 0x7a, 0xdc, 0x3c, 0x8b, 0x7a, 0xb7, 0xb8, 0x44, 0x74, 0x04, 0xaa,
+ 0xc8, 0x41, 0x3c, 0x82, 0x60, 0x74, 0xf8, 0x68, 0x96, 0x7b, 0xdc, 0x3c, 0x8b, 0x7b,
+ 0xb7, 0xb8, 0xa4, 0x76, 0x04, 0xaa, 0xc8, 0x41, 0x3c, 0x82, 0xa2, 0x76, 0xf8, 0x68,
+ 0x96, 0x7c, 0xdc, 0x3c, 0x8b, 0x7c, 0xb7, 0xba, 0x8a, 0x78, 0x04, 0xaa, 0xc8, 0x41,
+ 0x3c, 0x82, 0x16, 0x78, 0xf8, 0x68, 0xb7, 0xb8, 0x0c, 0x72, 0x3c, 0x00, 0x97, 0x00,
+ 0x43, 0x3c, 0x82, 0x00, 0x43, 0xdd, 0x3c, 0xa2, 0x0e, 0x72, 0x88, 0x9c, 0x00, 0x94,
+ 0x4f, 0x97, 0x01, 0x43, 0x56, 0x88, 0xd2, 0x96, 0xc8, 0x12, 0x94, 0x4c, 0x96, 0xc8,
+ 0x15, 0x94, 0x65, 0x96, 0xc8, 0x13, 0x94, 0x68, 0x96, 0xc8, 0x14, 0x94, 0x74, 0xb5,
+ 0x00, 0xe4, 0xbc, 0x03, 0x01, 0x94, 0x29, 0xb0, 0x44, 0x65, 0xaf, 0xc8, 0xa8, 0x44,
+ 0xb9, 0xff, 0x00, 0xbc, 0x01, 0x00, 0xb4, 0x0d, 0x5a, 0xbc, 0x02, 0x00, 0xb4, 0x60,
+ 0xbb, 0xbc, 0x04, 0x00, 0xb4, 0x3d, 0x1a, 0xbc, 0x03, 0x00, 0xb4, 0x23, 0xc1, 0xbc,
+ 0x05, 0x00, 0xb4, 0x06, 0x02, 0x3c, 0x97, 0x00, 0x43, 0x3c, 0xb5, 0x00, 0xaf, 0xbc,
+ 0x03, 0x01, 0x6a, 0x67, 0xb5, 0xfb, 0x65, 0x97, 0xfb, 0xd2, 0x00, 0xb6, 0x12, 0x0a,
+ 0x8e, 0xab, 0xca, 0x00, 0xb6, 0x10, 0x5e, 0x8e, 0xb6, 0x10, 0x5e, 0x8e, 0xae, 0xca,
+ 0xb6, 0x12, 0x0a, 0x8e, 0x95, 0x6f, 0x97, 0xdf, 0xd2, 0xb5, 0xfb, 0x38, 0x95, 0x77,
+ 0xb5, 0xfb, 0x37, 0x97, 0xf7, 0xd2, 0x00, 0xb6, 0x20, 0x06, 0x8e, 0xb6, 0x20, 0x06,
+ 0x8e, 0x95, 0x72, 0xb5, 0xfb, 0x2a, 0x97, 0xef, 0xd2, 0x00, 0xb6, 0x28, 0x06, 0x8e,
+ 0xb6, 0x28, 0x06, 0x8e, 0x95, 0x83, 0x00, 0xab, 0x4e, 0xab, 0x50, 0xb1, 0x01, 0xf4,
+ 0xb2, 0xba, 0xb6, 0x00, 0xe6, 0xa2, 0x02, 0xcc, 0xab, 0xa2, 0x04, 0xcc, 0xab, 0xa2,
+ 0x06, 0xcc, 0xab, 0xa2, 0x08, 0xcc, 0xab, 0xae, 0xca, 0xaa, 0xc8, 0x41, 0x3c, 0xae,
+ 0xca, 0x82, 0x0a, 0xcc, 0xf8, 0x7d, 0xb1, 0x01, 0xf4, 0xb2, 0xba, 0xb6, 0x00, 0xa1,
+ 0x08, 0xb8, 0x00, 0xfc, 0x41, 0x51, 0xe6, 0xa2, 0x02, 0xcc, 0xab, 0xa2, 0x04, 0xcc,
+ 0xab, 0xa2, 0x06, 0xcc, 0xab, 0xa2, 0x08, 0xcc, 0xab, 0xae, 0xca, 0xaa, 0xc8, 0x41,
+ 0x3c, 0xae, 0xca, 0x82, 0x0a, 0xcc, 0xf8, 0x95, 0x24, 0xa8, 0x4e, 0x96, 0x50, 0xfc,
+ 0x42, 0x00, 0x3c, 0x90, 0x01, 0x63, 0x34, 0x0b, 0x9c, 0x00, 0x44, 0xb0, 0x03, 0x01,
+ 0x3c, 0xa8, 0xc4, 0xb6, 0xff, 0x38, 0xab, 0xb2, 0xba, 0xb6, 0xa0, 0x4e, 0xcc, 0xf8,
+ 0xe4, 0xab, 0x44, 0xb6, 0xff, 0x32, 0xab, 0xa2, 0x02, 0xcc, 0xa8, 0xab, 0x46, 0xb6,
+ 0xff, 0x34, 0xab, 0xa2, 0x04, 0xcc, 0xa8, 0xab, 0x48, 0xb6, 0xff, 0x36, 0xab, 0xa2,
+ 0x06, 0xcc, 0xa8, 0xab, 0x4a, 0xa2, 0x08, 0xcc, 0xa8, 0xab, 0x4c, 0xa8, 0x4e, 0xb6,
+ 0xff, 0x30, 0xab, 0xbc, 0x13, 0x7e, 0x47, 0xb8, 0x00, 0x0a, 0xab, 0x4e, 0x00, 0x3c,
+ 0xb7, 0x00, 0x00, 0x4e, 0x66, 0x83, 0x00, 0xb8, 0x00, 0xab, 0xac, 0xcc, 0x28, 0xac,
+ 0xca, 0x2a, 0xab, 0xca, 0xa8, 0x50, 0xb8, 0x00, 0x0a, 0x96, 0x4e, 0xfc, 0x94, 0x2e,
+ 0xb2, 0xba, 0xb6, 0xa0, 0x50, 0xcc, 0xf8, 0xa8, 0x02, 0xe6, 0xa8, 0xca, 0xa2, 0x02,
+ 0xcc, 0xab, 0xa8, 0x04, 0xa2, 0x04, 0xcc, 0xab, 0xa8, 0x06, 0xa2, 0x06, 0xcc, 0xab,
+ 0xa8, 0x50, 0xb8, 0x00, 0x0a, 0xbc, 0x13, 0x7e, 0x00, 0xab, 0x50, 0xac, 0x28, 0xcc,
+ 0xac, 0x2a, 0xca, 0x3c, 0xb6, 0xff, 0x06, 0xa9, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x5c, 0xc5, 0x2f, 0x14, 0x2b, 0x02, 0x45, 0x00, 0x0e, 0x00,
+ 0x06, 0x00, 0x06, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x50, 0x00,
+ 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x40, 0x1f, 0x00, 0x00, 0x80, 0x38, 0x01, 0x00,
+ 0x00, 0x35, 0x0c, 0x00, 0x00, 0x12, 0x7a, 0x00, 0x00, 0xb4, 0xc4, 0x04, 0x00, 0x08,
+ 0xaf, 0x2f, 0x0a, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00,
+ 0x10, 0x27, 0x00, 0x00, 0xa0, 0x86, 0x01, 0x00, 0x40, 0x42, 0x0f, 0x00, 0x80, 0x96,
+ 0x98, 0x00, 0x00, 0xe1, 0xf5, 0x05, 0x00, 0xca, 0x9a, 0x3b, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xaf, 0x02, 0xb7, 0x00, 0x00, 0x02, 0x82, 0x0b, 0x02, 0xfc,
+ 0x94, 0x20, 0xae, 0xca, 0xa2, 0x02, 0xcc, 0xfc, 0x53, 0xa2, 0x02, 0xcc, 0xfd, 0x41,
+ 0x53, 0xae, 0xca, 0xa9, 0x02, 0xa9, 0xcc, 0xa9, 0xcc, 0xa9, 0xcc, 0xa9, 0xcc, 0x7f,
+ 0xae, 0xca, 0xfc, 0x6e, 0xfd, 0x70, 0xa8, 0x02, 0x3f, 0x02, 0x3c, 0x90, 0x00, 0xa2,
+ 0x76, 0xce, 0xab, 0x90, 0x00, 0xb5, 0x32, 0x51, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00,
+ 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xac, 0xce, 0xcc, 0x82, 0x70, 0xcc, 0xf8,
+ 0xb4, 0x26, 0xc8, 0xb7, 0x00, 0x00, 0x02, 0xac, 0xce, 0xcc, 0x82, 0x7a, 0xcc, 0xf8,
+ 0x82, 0x05, 0x02, 0xfc, 0x55, 0x00, 0xe6, 0xa2, 0x02, 0xcc, 0xab, 0xa2, 0x04, 0xcc,
+ 0xab, 0xa2, 0x06, 0xcc, 0xab, 0xa9, 0x02, 0x82, 0x08, 0xcc, 0xf8, 0x79, 0x00, 0xa2,
+ 0x78, 0xce, 0x8b, 0x90, 0x01, 0xa2, 0x76, 0xce, 0xab, 0xa2, 0x10, 0xce, 0x88, 0xe7,
+ 0xb8, 0x46, 0x00, 0xad, 0xc8, 0xa8, 0xa2, 0x72, 0xce, 0xab, 0xb5, 0x31, 0x9c, 0x90,
+ 0x01, 0xb5, 0x31, 0xf3, 0xb7, 0x5e, 0x10, 0x02, 0xb7, 0x00, 0x5f, 0x04, 0xb7, 0x05,
+ 0x00, 0x06, 0xac, 0xce, 0xcc, 0x82, 0x70, 0xcc, 0xf8, 0xa2, 0x22, 0xce, 0x88, 0xb4,
+ 0x26, 0x67, 0xb5, 0x31, 0xb1, 0xab, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xa2, 0x72, 0xce,
+ 0xa8, 0x02, 0x96, 0x02, 0xeb, 0xab, 0x02, 0x90, 0x00, 0x96, 0x04, 0xeb, 0xab, 0x04,
+ 0xa2, 0x10, 0xce, 0x88, 0xe7, 0xb8, 0x46, 0x00, 0xad, 0xc8, 0xa8, 0xa2, 0x72, 0xce,
+ 0xab, 0xb5, 0x31, 0x51, 0xaf, 0x02, 0xaf, 0x04, 0xb7, 0x5e, 0x10, 0x02, 0xb7, 0x00,
+ 0x5f, 0x04, 0xb7, 0x05, 0x00, 0x06, 0xac, 0xce, 0xcc, 0x82, 0x70, 0xcc, 0xf8, 0xa2,
+ 0x22, 0xce, 0x88, 0xb5, 0x26, 0x1d, 0x3f, 0x04, 0x3f, 0x02, 0xac, 0x76, 0xce, 0xb7,
+ 0x00, 0x00, 0x06, 0xb7, 0x00, 0x00, 0x08, 0xb7, 0x00, 0x00, 0x0a, 0xb7, 0x00, 0x00,
+ 0x0c, 0xb7, 0x00, 0x00, 0x0e, 0xac, 0xce, 0xcc, 0x82, 0x7a, 0xcc, 0xf8, 0x82, 0x05,
+ 0x0e, 0xfc, 0x94, 0x2b, 0xe4, 0x03, 0x96, 0x06, 0xf8, 0xab, 0x06, 0xa2, 0x02, 0xcc,
+ 0xa8, 0x96, 0x08, 0xe8, 0xab, 0x08, 0xa2, 0x04, 0xcc, 0xa8, 0x03, 0x96, 0x0a, 0xf8,
+ 0xab, 0x0a, 0xa2, 0x06, 0xcc, 0xa8, 0x96, 0x0c, 0xe8, 0xab, 0x0c, 0xa9, 0x0e, 0x82,
+ 0x08, 0xcc, 0xf8, 0x95, 0x2f, 0xa2, 0x78, 0xce, 0x88, 0xac, 0xce, 0xcc, 0x9e, 0x08,
+ 0xac, 0xcc, 0xce, 0xa0, 0xc8, 0xcc, 0xf8, 0x82, 0x7a, 0xcc, 0xf8, 0xe4, 0xa2, 0x02,
+ 0xcc, 0xf8, 0x9d, 0x00, 0x94, 0x88, 0xa2, 0x06, 0xcc, 0xa8, 0x9d, 0x1e, 0x94, 0x53,
+ 0x9c, 0x1e, 0x94, 0x41, 0xe4, 0xa2, 0x02, 0xcc, 0xfa, 0x9d, 0x00, 0x94, 0x73, 0xa8,
+ 0x02, 0x96, 0x04, 0xfa, 0x9c, 0x00, 0x94, 0x6a, 0x96, 0xc9, 0x17, 0x94, 0x65, 0xa2,
+ 0x06, 0xcc, 0xa8, 0x9d, 0x00, 0x94, 0x30, 0x9c, 0x00, 0x41, 0x4c, 0xa2, 0x04, 0xcc,
+ 0xa8, 0x9c, 0x40, 0x94, 0x24, 0x9d, 0x40, 0x94, 0x20, 0xa8, 0x0c, 0x9d, 0x00, 0x5b,
+ 0x9c, 0x00, 0x42, 0x94, 0x43, 0xa8, 0x0a, 0xbd, 0x02, 0x00, 0x50, 0x94, 0x3b, 0xa2,
+ 0x04, 0xcc, 0xa8, 0xbc, 0x84, 0x80, 0x46, 0xbd, 0x84, 0x80, 0x42, 0x95, 0x4d, 0xa2,
+ 0x78, 0xce, 0x88, 0x04, 0x9c, 0x05, 0x00, 0x9d, 0x05, 0x00, 0xa2, 0x78, 0xce, 0x8b,
+ 0xac, 0xce, 0xcc, 0x9e, 0x08, 0xac, 0xcc, 0xce, 0xa0, 0xc8, 0xcc, 0xf8, 0x82, 0x7a,
+ 0xcc, 0xf8, 0x00, 0xa2, 0x04, 0xcc, 0xab, 0xa2, 0x06, 0xcc, 0xab, 0xe6, 0xa2, 0x02,
+ 0xcc, 0xab, 0xe4, 0x03, 0x96, 0x02, 0xf8, 0xe6, 0xa2, 0x02, 0xcc, 0xa8, 0x96, 0x04,
+ 0xe8, 0xa2, 0x02, 0xcc, 0xab, 0xa2, 0x04, 0xcc, 0xa8, 0x03, 0xb8, 0x00, 0x04, 0xa2,
+ 0x04, 0xcc, 0xab, 0xa2, 0x06, 0xcc, 0xa8, 0x82, 0x00, 0xc8, 0xe8, 0xa2, 0x06, 0xcc,
+ 0xab, 0xa8, 0x06, 0x03, 0x96, 0x02, 0xf8, 0xab, 0x06, 0xa8, 0x08, 0x96, 0x04, 0xe8,
+ 0xab, 0x08, 0xa8, 0x0a, 0x03, 0xb8, 0x00, 0x04, 0xab, 0x0a, 0xa8, 0x0c, 0x82, 0x00,
+ 0xc8, 0xe8, 0xab, 0x0c, 0xa2, 0x74, 0xce, 0x88, 0xab, 0x0e, 0xa8, 0x06, 0xac, 0x08,
+ 0xca, 0xb2, 0x46, 0x4c, 0x36, 0x4c, 0xab, 0x10, 0xa8, 0x0a, 0xac, 0x0c, 0xca, 0xb2,
+ 0x46, 0x20, 0x36, 0x58, 0xab, 0x12, 0x02, 0x96, 0x10, 0xeb, 0xb8, 0x00, 0x06, 0xa2,
+ 0x74, 0xce, 0x8b, 0xa2, 0x11, 0xce, 0x88, 0xa2, 0x74, 0xce, 0xdd, 0x94, 0xe9, 0xa2,
+ 0x74, 0xce, 0xdc, 0x94, 0xe3, 0xa8, 0x02, 0x96, 0x04, 0xfa, 0x9c, 0x00, 0x94, 0x23,
+ 0xa2, 0x6c, 0xce, 0xa8, 0x03, 0x96, 0x02, 0xf8, 0xa2, 0x6c, 0xce, 0xab, 0xa2, 0x6e,
+ 0xce, 0xa8, 0x96, 0x04, 0xe8, 0xa2, 0x6e, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab,
+ 0x18, 0xb0, 0x40, 0x35, 0xb5, 0x40, 0x59, 0xa8, 0x0e, 0xa2, 0x74, 0xce, 0xdc, 0x4c,
+ 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x33, 0xb5, 0x40, 0x46, 0xa2, 0x10,
+ 0xce, 0x88, 0xa2, 0x74, 0xce, 0xdd, 0x94, 0x46, 0xa2, 0x74, 0xce, 0xdc, 0x94, 0x40,
+ 0xa2, 0x11, 0xce, 0x88, 0xa2, 0x74, 0xce, 0xdd, 0x5e, 0xa2, 0x74, 0xce, 0xdc, 0x59,
+ 0xa2, 0x12, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0x90, 0x00, 0xa2, 0x12, 0xce, 0x8b, 0xa2,
+ 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x40, 0xb4, 0x40, 0x0f, 0xa2, 0x12, 0xce,
+ 0x88, 0x9d, 0x00, 0x7f, 0x90, 0x01, 0xa2, 0x12, 0xce, 0x8b, 0xa2, 0x22, 0xce, 0x88,
+ 0xab, 0x18, 0xb0, 0x40, 0x40, 0xb4, 0x3f, 0xf6, 0x90, 0x01, 0xa2, 0x40, 0xce, 0x8b,
+ 0xb6, 0xff, 0x08, 0xa9, 0xa2, 0x68, 0xce, 0xa8, 0x03, 0xb8, 0x00, 0x01, 0xa2, 0x68,
+ 0xce, 0xab, 0xa2, 0x6a, 0xce, 0xa8, 0x82, 0x00, 0xc8, 0xe8, 0xa2, 0x6a, 0xce, 0xab,
+ 0xb7, 0x05, 0x02, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22,
+ 0xce, 0x88, 0xb5, 0xfc, 0x07, 0xb7, 0x02, 0x11, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7,
+ 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xfb, 0xf4, 0xa2, 0x22, 0xce, 0x88,
+ 0xab, 0x18, 0xb0, 0x40, 0x34, 0xb4, 0x3f, 0xa2, 0x90, 0x00, 0x96, 0x08, 0xfd, 0x4e,
+ 0x96, 0x08, 0xfc, 0x42, 0x95, 0xed, 0x90, 0x05, 0x96, 0x06, 0xfd, 0x42, 0x95, 0xf5,
+ 0x90, 0x0f, 0xa2, 0x11, 0xce, 0xdd, 0x47, 0xa2, 0x74, 0xce, 0x8b, 0xb4, 0xfe, 0xfb,
+ 0xa2, 0x11, 0xce, 0x88, 0x04, 0x6c, 0xa2, 0x6c, 0x76, 0xa8, 0x03, 0xa2, 0x72, 0x76,
+ 0xf8, 0xa2, 0x6c, 0x76, 0xab, 0xa2, 0x6e, 0x76, 0xa8, 0x82, 0x00, 0xc8, 0xe8, 0xa2,
+ 0x6e, 0x76, 0xab, 0xa2, 0x10, 0x76, 0x88, 0xa2, 0x74, 0x76, 0x8b, 0xa2, 0x22, 0x76,
+ 0x88, 0xab, 0x18, 0xb0, 0x40, 0x35, 0xb5, 0x3f, 0x4d, 0xa2, 0x22, 0x76, 0x88, 0xab,
+ 0x18, 0xb0, 0x40, 0x33, 0xb5, 0x3f, 0x41, 0x90, 0x01, 0xa2, 0x40, 0x76, 0x8b, 0xa2,
+ 0x68, 0x76, 0xa8, 0x03, 0xb8, 0x00, 0x01, 0xa2, 0x68, 0x76, 0xab, 0xa2, 0x6a, 0x76,
+ 0xa8, 0x82, 0x00, 0xc8, 0xe8, 0xa2, 0x6a, 0x76, 0xab, 0xb7, 0x05, 0x02, 0x02, 0xb7,
+ 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0x76, 0x88, 0xb5, 0xfb, 0x56,
+ 0xb7, 0x02, 0x11, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22,
+ 0x76, 0x88, 0xb5, 0xfb, 0x43, 0xa2, 0x22, 0x76, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x34,
+ 0xb4, 0x3e, 0xf1, 0x90, 0x03, 0xb5, 0x2e, 0x21, 0x90, 0x01, 0xb5, 0x2e, 0x78, 0xa2,
+ 0x0c, 0x76, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x4a, 0xa2, 0x0f, 0x72, 0x88, 0x9c, 0x00,
+ 0x43, 0xb5, 0x2e, 0xab, 0x90, 0x02, 0xa2, 0x76, 0x76, 0xab, 0x90, 0x01, 0xa2, 0x42,
+ 0x76, 0x8b, 0x3c, 0xa8, 0x46, 0xb5, 0xf9, 0x70, 0xa8, 0x44, 0xbc, 0x05, 0xff, 0x53,
+ 0xac, 0x76, 0xce, 0xa2, 0x76, 0x76, 0xa8, 0x9c, 0x00, 0x59, 0x9c, 0x01, 0x94, 0x23,
+ 0x9c, 0x02, 0x94, 0x33, 0x3c, 0xa2, 0x70, 0x76, 0xa8, 0x96, 0x48, 0xfc, 0x41, 0x3c,
+ 0x00, 0xa2, 0x70, 0x76, 0xab, 0x95, 0x21, 0xa8, 0x44, 0xbc, 0x05, 0x01, 0xb4, 0xfb,
+ 0xe8, 0xbc, 0x05, 0x03, 0x95, 0x63, 0x3c, 0xa8, 0x44, 0xbc, 0x05, 0x02, 0xb4, 0xfb,
+ 0xb8, 0xbc, 0x05, 0x05, 0x95, 0xfa, 0xbc, 0x05, 0xff, 0xb4, 0xfc, 0x30, 0x3c, 0xa8,
+ 0x44, 0xbc, 0x05, 0x05, 0x49, 0xbc, 0x05, 0x02, 0x50, 0xbc, 0x05, 0x04, 0x4c, 0x3c,
+ 0x90, 0x00, 0xa2, 0x42, 0x76, 0x8b, 0x90, 0x00, 0xb4, 0x2d, 0xee, 0xb5, 0xfb, 0x8f,
+ 0xa2, 0x0c, 0x76, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x4a, 0xa2, 0x0f, 0x72, 0x88, 0x9c,
+ 0x00, 0x43, 0xb5, 0x2e, 0x1f, 0xa8, 0x44, 0xbc, 0x05, 0x04, 0x41, 0x3c, 0xa2, 0x42,
+ 0x76, 0x88, 0x9c, 0x00, 0x4e, 0x00, 0xa2, 0x40, 0x76, 0x8b, 0xa2, 0x30, 0x76, 0xab,
+ 0xa2, 0x32, 0x76, 0xab, 0x3c, 0xa2, 0x30, 0x76, 0xa8, 0x03, 0xb8, 0x00, 0x01, 0xa2,
+ 0x30, 0x76, 0xab, 0xa2, 0x32, 0x76, 0xa8, 0x82, 0x00, 0xc8, 0xe8, 0xa2, 0x32, 0x76,
+ 0xab, 0xa2, 0x22, 0x76, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x2a, 0xb4, 0x3e, 0x07, 0x00,
+ 0xab, 0xca, 0xa2, 0x15, 0x72, 0x88, 0x9c, 0x00, 0x3c, 0xa8, 0xca, 0xa2, 0x14, 0x72,
+ 0x8b, 0xb7, 0x00, 0x00, 0x18, 0xb0, 0x10, 0x2b, 0xb5, 0x3d, 0xed, 0xb7, 0x02, 0x12,
+ 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x10, 0x72, 0x88, 0xb5,
+ 0xfa, 0x20, 0xb7, 0x02, 0x12, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06,
+ 0xa2, 0x11, 0x72, 0x88, 0xb5, 0xfa, 0x0d, 0xb7, 0x00, 0x00, 0x08, 0x82, 0x01, 0x08,
+ 0xfc, 0x3c, 0xa8, 0x08, 0xb5, 0xf8, 0x53, 0xa2, 0x00, 0x74, 0xa8, 0x9c, 0x00, 0x51,
+ 0xb7, 0x04, 0x0c, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x08,
+ 0xb5, 0xf9, 0xe7, 0xa9, 0x08, 0x95, 0x24, 0xac, 0x02, 0x0a, 0xac, 0x04, 0x0c, 0xb7,
+ 0x00, 0x00, 0x10, 0xa8, 0x02, 0xb5, 0xf8, 0x28, 0xa8, 0x04, 0x9c, 0x02, 0x94, 0x40,
+ 0xa8, 0x0a, 0xb5, 0xf8, 0x31, 0xa2, 0x66, 0x76, 0xa8, 0x9c, 0x00, 0x52, 0x9c, 0x04,
+ 0x55, 0x9c, 0x02, 0x5a, 0x9c, 0x01, 0x5d, 0x9c, 0x05, 0x94, 0x1f, 0x9c, 0x03, 0x5c,
+ 0x94, 0x49, 0xb7, 0x00, 0x01, 0x10, 0x94, 0x43, 0xa2, 0x0a, 0x76, 0xa8, 0xab, 0x0e,
+ 0x94, 0x3b, 0xb7, 0x00, 0x02, 0x0e, 0x94, 0x35, 0xb7, 0x00, 0x01, 0x0e, 0x94, 0x2f,
+ 0xa2, 0x06, 0x76, 0xa8, 0x9c, 0x00, 0x72, 0x6d, 0xa2, 0x56, 0x74, 0xa8, 0x9c, 0x00,
+ 0x5b, 0x9c, 0x04, 0x51, 0x9c, 0x02, 0x49, 0x9c, 0x01, 0x41, 0x56, 0xb7, 0x00, 0x01,
+ 0x0e, 0x51, 0xb7, 0x00, 0x02, 0x0e, 0x4c, 0xa2, 0x06, 0x74, 0xa8, 0xab, 0x0e, 0x45,
+ 0xb7, 0x00, 0x01, 0x10, 0x40, 0x82, 0x00, 0x10, 0xfd, 0x94, 0xf1, 0xa8, 0x0e, 0x05,
+ 0xab, 0x12, 0x82, 0x02, 0x0c, 0xfc, 0x5c, 0xa8, 0x12, 0x9e, 0x04, 0xac, 0x76, 0xcc,
+ 0x82, 0x1a, 0xcc, 0xf8, 0xa0, 0xc8, 0xcc, 0xf8, 0xa2, 0x02, 0xcc, 0x88, 0xab, 0x0a,
+ 0xa2, 0x03, 0xcc, 0x88, 0xab, 0x0c, 0x5b, 0xa8, 0x12, 0x9e, 0x04, 0xac, 0x74, 0xcc,
+ 0x82, 0x58, 0xcc, 0xf8, 0xa0, 0xc8, 0xcc, 0xf8, 0xa2, 0x02, 0xcc, 0x88, 0xab, 0x0a,
+ 0xa2, 0x03, 0xcc, 0x88, 0xab, 0x0c, 0x82, 0x02, 0x0c, 0xfc, 0x94, 0x71, 0xa8, 0x0a,
+ 0xb5, 0xf7, 0x7d, 0xa2, 0x66, 0x76, 0xa8, 0x9c, 0x00, 0x5a, 0x9c, 0x04, 0x51, 0x9c,
+ 0x02, 0x5a, 0x9c, 0x01, 0x94, 0x20, 0x9c, 0x05, 0x94, 0x26, 0x9c, 0x03, 0x94, 0x45,
+ 0x95, 0x6b, 0xa8, 0x0e, 0xa2, 0x0a, 0x76, 0xfc, 0xb7, 0x00, 0x01, 0x10, 0x95, 0x77,
+ 0x82, 0x02, 0x0e, 0xfc, 0xb7, 0x00, 0x01, 0x10, 0x95, 0x81, 0x82, 0x01, 0x0e, 0xfc,
+ 0xb7, 0x00, 0x01, 0x10, 0x95, 0x8b, 0xa2, 0x06, 0x76, 0xa8, 0x9c, 0x00, 0x4b, 0x82,
+ 0x02, 0x0e, 0xfc, 0x51, 0xb7, 0x00, 0x02, 0x0e, 0x95, 0x9d, 0x82, 0x01, 0x0e, 0xfc,
+ 0x46, 0xb7, 0x00, 0x01, 0x0e, 0x95, 0xa8, 0xb7, 0x00, 0x01, 0x10, 0x95, 0xae, 0x90,
+ 0x03, 0x02, 0x96, 0x0e, 0xeb, 0x9d, 0x00, 0xb7, 0x00, 0x01, 0x10, 0x95, 0xbc, 0xa8,
+ 0x0a, 0xb5, 0xf6, 0xf8, 0xa2, 0x56, 0x74, 0xa8, 0x9c, 0x00, 0x95, 0xc9, 0x9c, 0x04,
+ 0x48, 0x9c, 0x02, 0x52, 0x9c, 0x01, 0x59, 0x95, 0xd4, 0xa2, 0x06, 0x74, 0xa8, 0x96,
+ 0x0e, 0xfc, 0xb7, 0x00, 0x01, 0x10, 0x95, 0xe1, 0x82, 0x02, 0x0e, 0xfc, 0xb7, 0x00,
+ 0x01, 0x10, 0x95, 0xeb, 0x82, 0x01, 0x0e, 0xfc, 0xb7, 0x00, 0x01, 0x10, 0x95, 0xf5,
+ 0xac, 0x0a, 0x06, 0xac, 0x0c, 0x08, 0x3c, 0xab, 0x22, 0xb5, 0xf6, 0xcc, 0xa2, 0x54,
+ 0x76, 0x88, 0x9c, 0x00, 0x5e, 0xa2, 0x32, 0x72, 0xa8, 0x05, 0xa2, 0x32, 0x72, 0xab,
+ 0x90, 0x00, 0xa2, 0x54, 0x76, 0x8b, 0xb7, 0x00, 0x00, 0x06, 0xac, 0x76, 0xcc, 0x82,
+ 0x52, 0xcc, 0xf8, 0x00, 0xb5, 0x20, 0x10, 0xa2, 0x30, 0x72, 0xa8, 0x02, 0xa2, 0x32,
+ 0x72, 0xeb, 0x9c, 0x00, 0x3c, 0x96, 0xc9, 0x17, 0x3c, 0xab, 0x0a, 0xa8, 0x22, 0x96,
+ 0x7e, 0xdd, 0xb0, 0xff, 0xff, 0x04, 0xab, 0x0c, 0xb7, 0x00, 0x00, 0x0e, 0x80, 0x7e,
+ 0x0e, 0xfc, 0x3c, 0x82, 0x00, 0x0a, 0xfc, 0x3c, 0xa8, 0x0c, 0xb5, 0xf6, 0x77, 0xa2,
+ 0x0e, 0x76, 0xa8, 0x9c, 0x00, 0x94, 0x2c, 0xa2, 0x55, 0x76, 0x88, 0x9c, 0x00, 0x94,
+ 0x24, 0xa2, 0x06, 0x76, 0xa8, 0x9c, 0x03, 0x41, 0x5c, 0x90, 0x00, 0xa2, 0x55, 0x76,
+ 0x8b, 0xb7, 0x02, 0x14, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8,
+ 0x0c, 0xb5, 0xf7, 0xe0, 0xa8, 0x0a, 0x05, 0xab, 0x0a, 0xa8, 0x0c, 0x96, 0x7e, 0xdd,
+ 0xb0, 0xff, 0xff, 0x04, 0xab, 0x0c, 0x95, 0x4e, 0xa2, 0x1e, 0x72, 0x88, 0x9c, 0x02,
+ 0x94, 0x44, 0xa2, 0x20, 0x72, 0xa8, 0x9c, 0x04, 0x94, 0x3c, 0xb5, 0x23, 0x0b, 0x9c,
+ 0x00, 0x94, 0x2f, 0x90, 0x02, 0xa2, 0x1e, 0x72, 0x8b, 0x00, 0x96, 0x7e, 0xdc, 0x94,
+ 0x29, 0xab, 0x0a, 0xb5, 0xf6, 0x22, 0xa2, 0x12, 0x78, 0xa8, 0x9c, 0x00, 0x52, 0x90,
+ 0x00, 0xa2, 0x12, 0x78, 0xab, 0xb0, 0x32, 0x0e, 0xa2, 0x10, 0x78, 0x88, 0xab, 0x18,
+ 0xb5, 0x3b, 0x47, 0xa8, 0x0a, 0x04, 0x95, 0x26, 0x90, 0x03, 0xa2, 0x1e, 0x72, 0x8b,
+ 0x90, 0x00, 0xa2, 0x20, 0x72, 0xab, 0xb0, 0x10, 0x29, 0xb7, 0x00, 0x00, 0x18, 0xb4,
+ 0x3b, 0x2c, 0x90, 0x01, 0xa2, 0x20, 0x72, 0xab, 0xb0, 0x10, 0x29, 0xb7, 0x00, 0x00,
+ 0x18, 0xb5, 0x3b, 0x1c, 0x90, 0x01, 0x36, 0xec, 0xb7, 0x00, 0x00, 0x0a, 0x82, 0x01,
+ 0x0a, 0xfc, 0x94, 0x2b, 0xa8, 0x0a, 0xb5, 0xf5, 0xa3, 0xa2, 0x00, 0x74, 0xa8, 0x9c,
+ 0x00, 0x5b, 0xb7, 0x04, 0x0e, 0x02, 0xb7, 0x00, 0x01, 0x04, 0xb7, 0x00, 0x00, 0x06,
+ 0xa8, 0x0a, 0xb5, 0xf7, 0x37, 0xaf, 0x0a, 0xac, 0x74, 0xce, 0xb5, 0x18, 0xa3, 0x3f,
+ 0x0a, 0xa9, 0x0a, 0x95, 0x2f, 0xb7, 0x00, 0x00, 0x0a, 0x80, 0x7e, 0x0a, 0xfc, 0x3c,
+ 0xa8, 0x0a, 0xb5, 0xf5, 0x83, 0xa2, 0x0e, 0x76, 0xa8, 0x9c, 0x00, 0x51, 0xb7, 0x02,
+ 0x01, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x0a, 0xb5, 0xf7,
+ 0x03, 0xa9, 0x0a, 0x95, 0x24, 0x90, 0x02, 0xa2, 0x20, 0x72, 0xab, 0xb0, 0x10, 0x29,
+ 0xb7, 0x00, 0x00, 0x18, 0xb5, 0x3a, 0xa9, 0xa2, 0x02, 0x72, 0xa8, 0xab, 0x02, 0xa2,
+ 0x04, 0x72, 0xa8, 0xab, 0x04, 0xb7, 0x01, 0x00, 0x06, 0xac, 0x72, 0xcc, 0x82, 0x18,
+ 0xcc, 0xf8, 0x00, 0xb4, 0x1e, 0xad, 0x90, 0x03, 0xa2, 0x20, 0x72, 0xab, 0xb0, 0x10,
+ 0x29, 0xb7, 0x00, 0x00, 0x18, 0xb5, 0x3a, 0x7e, 0xb7, 0x00, 0x00, 0x0a, 0x80, 0x7e,
+ 0x0a, 0xfc, 0x94, 0x21, 0xa8, 0x0a, 0xb5, 0xf5, 0x1d, 0xa2, 0x0e, 0x76, 0xa8, 0x9c,
+ 0x00, 0x51, 0xb7, 0x02, 0x04, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06,
+ 0xa8, 0x0a, 0xb5, 0xf6, 0x9d, 0xa9, 0x0a, 0x95, 0x25, 0xb7, 0x00, 0x00, 0x0a, 0x82,
+ 0x01, 0x0a, 0xfc, 0x94, 0x21, 0xa8, 0x0a, 0xb5, 0xf4, 0xde, 0xa2, 0x00, 0x74, 0xa8,
+ 0x9c, 0x00, 0x51, 0xb7, 0x04, 0x0e, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00,
+ 0x06, 0xa8, 0x0a, 0xb5, 0xf6, 0x72, 0xa9, 0x0a, 0x95, 0x25, 0xb7, 0x1e, 0x84, 0x02,
+ 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x01, 0x00, 0x06, 0xac, 0x72, 0xcc, 0x82, 0x18, 0xcc,
+ 0xf8, 0x00, 0xb4, 0x1e, 0x30, 0x90, 0x04, 0xa2, 0x20, 0x72, 0xab, 0xb0, 0x10, 0x29,
+ 0xb7, 0x00, 0x00, 0x18, 0xb5, 0x3a, 0x01, 0x90, 0x01, 0xa2, 0x1e, 0x72, 0x8b, 0xb5,
+ 0x21, 0x8e, 0x9c, 0x00, 0x94, 0x3b, 0xb7, 0x01, 0x04, 0x02, 0xb7, 0x00, 0x02, 0x04,
+ 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb5, 0xf6, 0x2a, 0xb7, 0x00, 0x00, 0x0a, 0x80, 0x7e,
+ 0x0a, 0xfc, 0x3c, 0xa8, 0x0a, 0xb5, 0xf4, 0x98, 0xa2, 0x12, 0x78, 0xa8, 0x9c, 0x00,
+ 0x52, 0x90, 0x00, 0xa2, 0x12, 0x78, 0xab, 0xb0, 0x32, 0x0e, 0xa2, 0x10, 0x78, 0x88,
+ 0xab, 0x18, 0xb5, 0x39, 0xbd, 0xa9, 0x0a, 0x95, 0x25, 0xb7, 0x01, 0x04, 0x02, 0xb7,
+ 0x00, 0x03, 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4, 0xf5, 0xef, 0x90, 0x05, 0xa2,
+ 0x20, 0x72, 0xab, 0xb0, 0x10, 0x29, 0xb7, 0x00, 0x00, 0x18, 0xb5, 0x39, 0x99, 0x90,
+ 0x00, 0xb5, 0x22, 0xc4, 0xb7, 0xf4, 0x24, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x01,
+ 0x00, 0x06, 0xac, 0x72, 0xcc, 0x82, 0x18, 0xcc, 0xf8, 0x00, 0xb4, 0x1d, 0x9c, 0xa2,
+ 0x1e, 0x72, 0x88, 0x9c, 0x02, 0x42, 0x95, 0x9b, 0xa2, 0x0a, 0x72, 0x88, 0x9c, 0x02,
+ 0x49, 0x90, 0x00, 0xa2, 0x16, 0x72, 0x8b, 0xb4, 0xfe, 0x38, 0x90, 0x06, 0xa2, 0x2e,
+ 0xce, 0xf9, 0x9d, 0x00, 0x94, 0x33, 0xa2, 0x16, 0x72, 0x88, 0x9c, 0x00, 0x3c, 0x90,
+ 0x41, 0xa2, 0x2e, 0xce, 0xf9, 0x9c, 0x00, 0x50, 0x90, 0x02, 0xa2, 0x2e, 0xcc, 0xf9,
+ 0x9c, 0x00, 0x47, 0x90, 0x01, 0xa2, 0x16, 0x72, 0x8b, 0x3c, 0x90, 0x02, 0xa2, 0x2e,
+ 0xce, 0xf9, 0x9c, 0x00, 0x3c, 0x90, 0x41, 0xa2, 0x2e, 0xcc, 0xf9, 0x9c, 0x00, 0x3c,
+ 0x79, 0x90, 0x06, 0xa2, 0x2e, 0xcc, 0xf9, 0x9c, 0x00, 0x95, 0x3b, 0x90, 0x00, 0xa2,
+ 0x16, 0x72, 0x8b, 0xb4, 0xfd, 0xe8, 0xa2, 0x20, 0x72, 0xa8, 0x9c, 0x06, 0x50, 0x90,
+ 0x06, 0xa2, 0x20, 0x72, 0xab, 0xb0, 0x10, 0x29, 0xb7, 0x00, 0x00, 0x18, 0xb5, 0x38,
+ 0xfd, 0xa2, 0x10, 0x72, 0x88, 0xb5, 0xf3, 0xa4, 0xac, 0x76, 0x24, 0xa2, 0x11, 0x72,
+ 0x88, 0xb5, 0xf3, 0x9a, 0xac, 0x76, 0x26, 0xa2, 0x0e, 0x24, 0xa8, 0x9c, 0x00, 0x57,
+ 0xa2, 0x22, 0x24, 0x88, 0xb5, 0x23, 0xa5, 0xa2, 0x2e, 0x24, 0xab, 0xa2, 0x22, 0x24,
+ 0x88, 0xb5, 0x23, 0x9a, 0xa2, 0x2e, 0x24, 0xab, 0x46, 0x90, 0x02, 0xa2, 0x2e, 0x24,
+ 0xab, 0xa2, 0x0e, 0x26, 0xa8, 0x9c, 0x00, 0x56, 0xa2, 0x22, 0x26, 0x88, 0xb5, 0x23,
+ 0x81, 0xa2, 0x2e, 0x26, 0xab, 0xa2, 0x22, 0x26, 0x88, 0xb5, 0x23, 0x76, 0xa2, 0x2e,
+ 0x26, 0xab, 0x90, 0x02, 0xa2, 0x2e, 0x26, 0xab, 0xac, 0x24, 0xce, 0xac, 0x26, 0xcc,
+ 0x34, 0xe1, 0xa2, 0x16, 0x72, 0x88, 0x9c, 0x00, 0x3c, 0xb7, 0xc4, 0xb4, 0x02, 0xb7,
+ 0x00, 0x04, 0x04, 0xb7, 0x01, 0x00, 0x06, 0xac, 0x72, 0xcc, 0x82, 0x18, 0xcc, 0xf8,
+ 0x00, 0xb4, 0x1c, 0x9b, 0x90, 0x07, 0xa2, 0x20, 0x72, 0xab, 0xb0, 0x10, 0x29, 0xb7,
+ 0x00, 0x00, 0x18, 0xb5, 0x38, 0x6c, 0x90, 0x01, 0xb5, 0x21, 0x97, 0xb7, 0x98, 0x96,
+ 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x01, 0x00, 0x06, 0xac, 0x72, 0xcc, 0x82, 0x18,
+ 0xcc, 0xf8, 0x00, 0xb4, 0x1c, 0x6f, 0x82, 0x02, 0x04, 0xfc, 0x42, 0x94, 0x22, 0xa8,
+ 0x02, 0xb5, 0xf2, 0xde, 0xa2, 0x54, 0x74, 0xa8, 0xaf, 0xc8, 0xb5, 0xf2, 0xfd, 0xa2,
+ 0x12, 0x78, 0xa8, 0x9a, 0x01, 0xa2, 0x12, 0x78, 0xab, 0x3f, 0xc8, 0xab, 0x18, 0xb0,
+ 0x32, 0x0e, 0xb5, 0x38, 0x27, 0xb5, 0xfa, 0x87, 0x82, 0x02, 0x08, 0xfc, 0x94, 0x32,
+ 0xa8, 0x06, 0xb5, 0xf2, 0xc7, 0xa2, 0x64, 0x76, 0xa8, 0xaf, 0xc8, 0xb5, 0xf2, 0xd2,
+ 0xa2, 0x12, 0x78, 0xa8, 0x9a, 0x02, 0xa2, 0x12, 0x78, 0xab, 0x3f, 0xc8, 0xab, 0x18,
+ 0xb0, 0x32, 0x0e, 0xb5, 0x37, 0xfc, 0xb7, 0x02, 0x03, 0x02, 0xac, 0x08, 0x04, 0xa8,
+ 0x06, 0xb7, 0x00, 0x00, 0x06, 0xb4, 0xf4, 0x32, 0xa8, 0x06, 0xb5, 0xf2, 0x81, 0xa2,
+ 0x54, 0x74, 0xa8, 0xaf, 0xc8, 0xb5, 0xf2, 0xa0, 0xa2, 0x12, 0x78, 0xa8, 0x9a, 0x04,
+ 0xa2, 0x12, 0x78, 0xab, 0x3f, 0xc8, 0xab, 0x18, 0xb0, 0x32, 0x0e, 0xb5, 0x37, 0xca,
+ 0xb7, 0x01, 0x04, 0x02, 0xb7, 0x00, 0x04, 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4,
+ 0xf4, 0x00, 0x36, 0xd4, 0xb7, 0x00, 0x00, 0x0a, 0x80, 0x7e, 0x0a, 0xfc, 0x3c, 0xa8,
+ 0x0a, 0xb5, 0xf2, 0x6c, 0x90, 0x03, 0xa2, 0x12, 0x78, 0xf9, 0x9d, 0x00, 0x43, 0xa9,
+ 0x0a, 0x75, 0xa2, 0x12, 0x78, 0xa8, 0x9a, 0x08, 0xa2, 0x12, 0x78, 0xab, 0xa2, 0x10,
+ 0x78, 0x88, 0xab, 0x18, 0xb0, 0x32, 0x0e, 0xb5, 0x37, 0x88, 0x79, 0xa8, 0x44, 0xbc,
+ 0x01, 0x04, 0xb4, 0x01, 0xd8, 0xa8, 0x44, 0xbc, 0x01, 0xff, 0xb4, 0x01, 0xc0, 0xa2,
+ 0x20, 0x72, 0xa8, 0x9c, 0x00, 0xb4, 0x01, 0x8e, 0x9c, 0x01, 0xb4, 0x01, 0x0a, 0x9c,
+ 0x02, 0x94, 0xc3, 0x9c, 0x03, 0x51, 0x9c, 0x04, 0x94, 0x98, 0x9c, 0x05, 0x94, 0x6e,
+ 0x9c, 0x06, 0x94, 0x43, 0x9c, 0x07, 0x94, 0x75, 0x3c, 0xa8, 0x44, 0xbc, 0x01, 0xff,
+ 0x4a, 0xbc, 0x01, 0x01, 0x5b, 0xbc, 0x01, 0x02, 0x94, 0x20, 0x3c, 0xa2, 0x1e, 0x72,
+ 0x88, 0x9c, 0x04, 0xb4, 0xfd, 0x27, 0xa2, 0x00, 0x72, 0x88, 0x9c, 0x00, 0xb4, 0xfb,
+ 0xa7, 0xb4, 0xfe, 0xb0, 0xa2, 0x1e, 0x72, 0x88, 0x9c, 0x02, 0xb4, 0xfb, 0xf7, 0x3c,
+ 0xa2, 0x1e, 0x72, 0x88, 0x9c, 0x04, 0x41, 0x3c, 0x90, 0x05, 0xa2, 0x1e, 0x72, 0x8b,
+ 0x3c, 0xa8, 0x44, 0xbc, 0x01, 0xff, 0xb4, 0xfd, 0xf7, 0xbc, 0x01, 0x02, 0xb4, 0xfe,
+ 0x89, 0x3c, 0xa2, 0x10, 0x72, 0x88, 0xb5, 0xf1, 0xab, 0xaf, 0x76, 0xa2, 0x11, 0x72,
+ 0x88, 0xb5, 0xf1, 0xa2, 0xac, 0x76, 0xcc, 0x3f, 0xce, 0xb4, 0xfd, 0x6f, 0xa8, 0x44,
+ 0xbc, 0x01, 0xff, 0xb4, 0xfd, 0xd0, 0xbc, 0x01, 0x02, 0xb4, 0xfe, 0x62, 0x3c, 0xa8,
+ 0x44, 0xbc, 0x01, 0xff, 0xb4, 0xfb, 0x4d, 0xbc, 0x01, 0x01, 0x41, 0x3c, 0xa2, 0x1e,
+ 0x72, 0x88, 0x9c, 0x02, 0xb4, 0xfd, 0x1e, 0x3c, 0xa8, 0x44, 0xbc, 0x01, 0x04, 0x45,
+ 0xbc, 0x01, 0x02, 0x4e, 0x3c, 0xa2, 0x1e, 0x72, 0x88, 0x9c, 0x03, 0x46, 0x9c, 0x02,
+ 0xb4, 0xfb, 0x83, 0x3c, 0xa2, 0x00, 0x72, 0x88, 0x9d, 0x00, 0xb4, 0xfe, 0x29, 0xb4,
+ 0xfb, 0x1a, 0xa8, 0x44, 0xbc, 0x01, 0x03, 0x53, 0xbc, 0x01, 0x02, 0x58, 0xbc, 0x01,
+ 0x04, 0x5d, 0xbc, 0x01, 0xff, 0x94, 0x22, 0xbc, 0x01, 0x07, 0x94, 0x26, 0x3c, 0xac,
+ 0x46, 0x02, 0xac, 0x48, 0x04, 0xb4, 0xfe, 0x30, 0x90, 0x05, 0xa2, 0x1e, 0x72, 0x8b,
+ 0xb4, 0xfb, 0xe9, 0xa2, 0x1e, 0x72, 0x88, 0x9c, 0x04, 0xb4, 0xfb, 0xe0, 0x3c, 0x90,
+ 0x04, 0xa2, 0x1e, 0x72, 0x8b, 0xb4, 0xfe, 0xaa, 0xa8, 0x46, 0xb4, 0xfa, 0x3c, 0xa8,
+ 0x44, 0xbc, 0x01, 0x06, 0x94, 0x26, 0xbc, 0x01, 0x03, 0x57, 0xbc, 0x01, 0x02, 0x50,
+ 0xbc, 0x01, 0x07, 0x47, 0xbc, 0x01, 0x04, 0xb4, 0xfb, 0xb8, 0x3c, 0xa8, 0x46, 0xb4,
+ 0xfa, 0x1d, 0xb4, 0xfb, 0xaf, 0xac, 0x46, 0x02, 0xac, 0x48, 0x04, 0x36, 0x19, 0xb4,
+ 0xfb, 0x79, 0xa2, 0x14, 0x72, 0x88, 0x9c, 0x00, 0x5d, 0xa2, 0x15, 0x72, 0x88, 0x9c,
+ 0x00, 0x56, 0xa2, 0x1a, 0x72, 0x88, 0x9d, 0x00, 0x4f, 0xa2, 0x1c, 0x72, 0x88, 0x9d,
+ 0x00, 0x48, 0xa2, 0x22, 0x72, 0xa8, 0x9c, 0x0c, 0x94, 0x29, 0xa2, 0x14, 0x72, 0x88,
+ 0x9d, 0x00, 0x3c, 0xa2, 0x15, 0x72, 0x88, 0x9c, 0x00, 0x4e, 0xa2, 0x1a, 0x72, 0x88,
+ 0x9c, 0x00, 0x4c, 0xa2, 0x1c, 0x72, 0x88, 0x9c, 0x00, 0x45, 0x90, 0x01, 0xb4, 0xf7,
+ 0xe7, 0xa2, 0x22, 0x72, 0xa8, 0x9c, 0x0c, 0x3c, 0x6c, 0x90, 0x00, 0xb4, 0xf7, 0xda,
+ 0xa8, 0x44, 0xbc, 0x01, 0x01, 0x4f, 0xbc, 0x01, 0x04, 0x41, 0x3c, 0xa2, 0x1e, 0x72,
+ 0x88, 0x9c, 0x04, 0xb4, 0xfa, 0x44, 0x3c, 0xa2, 0x1e, 0x72, 0x88, 0x9c, 0x02, 0x41,
+ 0x3c, 0xa2, 0x00, 0x72, 0x88, 0x9c, 0x00, 0xb4, 0xfa, 0x8e, 0xb4, 0xfc, 0x0e, 0xa8,
+ 0x48, 0xa2, 0x18, 0x72, 0xfc, 0x41, 0x3c, 0x00, 0xa2, 0x18, 0x72, 0xab, 0xb4, 0xfe,
+ 0x30, 0xa8, 0x48, 0xa2, 0x1e, 0x72, 0x8b, 0xb4, 0xfe, 0x1f, 0xaf, 0xce, 0xac, 0x74,
+ 0xce, 0xb6, 0xb8, 0x02, 0x88, 0x99, 0x05, 0x9c, 0x00, 0x5a, 0xa2, 0x52, 0xce, 0xa8,
+ 0x9c, 0x03, 0x41, 0x52, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0x21, 0xbd, 0x90, 0x05, 0x01,
+ 0xb6, 0xb8, 0x02, 0xd9, 0xb6, 0xb8, 0x02, 0x8b, 0xb6, 0xb8, 0x02, 0x88, 0x99, 0x06,
+ 0x9c, 0x00, 0x5a, 0xa2, 0x52, 0xce, 0xa8, 0x9c, 0x02, 0x41, 0x52, 0xa2, 0x22, 0xce,
+ 0x88, 0xb5, 0x21, 0x9a, 0x90, 0x06, 0x01, 0xb6, 0xb8, 0x02, 0xd9, 0xb6, 0xb8, 0x02,
+ 0x8b, 0xb6, 0xb8, 0x02, 0x88, 0x99, 0x08, 0x9c, 0x00, 0x94, 0x1f, 0xa2, 0x52, 0xce,
+ 0xa8, 0x9c, 0x01, 0x41, 0x57, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0x21, 0x76, 0x90, 0x08,
+ 0x01, 0xb6, 0xb8, 0x02, 0xd9, 0xb6, 0xb8, 0x02, 0x8b, 0x00, 0xb6, 0xb8, 0x04, 0xab,
+ 0x3f, 0xce, 0x3c, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x0b, 0x00, 0x00, 0x05, 0x00,
+ 0x01, 0x09, 0x06, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x03, 0x09, 0x0a, 0x00,
+ 0x00, 0x00, 0x0a, 0x00, 0xb7, 0x00, 0x00, 0x0a, 0x80, 0x7e, 0x0a, 0xfc, 0x3c, 0xa8,
+ 0x0a, 0xb5, 0xef, 0xb0, 0xa2, 0x14, 0x78, 0x88, 0x9c, 0x00, 0x4b, 0x90, 0x00, 0xa2,
+ 0x14, 0x78, 0x8b, 0xa8, 0x0a, 0xb5, 0x24, 0xb5, 0xa9, 0x0a, 0x7e, 0x90, 0x00, 0xa2,
+ 0x66, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x62, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab,
+ 0x18, 0xb0, 0x40, 0x10, 0xb5, 0x34, 0xc1, 0x90, 0x04, 0xa2, 0x28, 0xce, 0x8b, 0xa2,
+ 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x0d, 0xb4, 0x34, 0xaf, 0x90, 0x01, 0xa2,
+ 0x66, 0xce, 0xab, 0x90, 0x03, 0xa2, 0x62, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x64, 0xce,
+ 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x10, 0xb5, 0x34, 0x91, 0xa2,
+ 0x06, 0xce, 0xa8, 0x9c, 0x03, 0x41, 0x3c, 0xa2, 0x28, 0x72, 0xa8, 0xa2, 0x28, 0x72,
+ 0xa9, 0x9c, 0x01, 0xb4, 0x13, 0x33, 0x3c, 0x90, 0x02, 0xa2, 0x66, 0xce, 0xab, 0x90,
+ 0x02, 0xa2, 0x62, 0xce, 0xab, 0x90, 0x01, 0xa2, 0x64, 0xce, 0xab, 0xa2, 0x22, 0xce,
+ 0x88, 0xab, 0x18, 0xb0, 0x40, 0x10, 0xb5, 0x34, 0x5d, 0xa2, 0x06, 0xce, 0xa8, 0x9c,
+ 0x03, 0x41, 0x3c, 0xa2, 0x2a, 0x72, 0xa8, 0xa2, 0x2a, 0x72, 0xa9, 0x9c, 0x01, 0xb4,
+ 0x12, 0xff, 0x3c, 0x90, 0x03, 0xa2, 0x66, 0xce, 0xab, 0x90, 0x05, 0xa2, 0x62, 0xce,
+ 0xab, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x00, 0x52, 0x90, 0x00, 0xa2, 0x64, 0xce, 0xab,
+ 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x10, 0xb4, 0x34, 0x22, 0x90, 0x01,
+ 0x72, 0x90, 0x04, 0xa2, 0x66, 0xce, 0xab, 0x90, 0x01, 0xa2, 0x62, 0xce, 0xab, 0xa2,
+ 0x0a, 0xce, 0xa8, 0x05, 0xa2, 0x64, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18,
+ 0xb0, 0x40, 0x10, 0xb4, 0x33, 0xfe, 0x90, 0x05, 0xa2, 0x66, 0xce, 0xab, 0x90, 0x04,
+ 0xa2, 0x62, 0xce, 0xab, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x00, 0x52, 0x90, 0x00, 0xa2,
+ 0x64, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x10, 0xb4, 0x33,
+ 0xd9, 0x90, 0x01, 0x72, 0xa2, 0x54, 0xce, 0xa8, 0xb5, 0xee, 0x91, 0xa2, 0x0c, 0x78,
+ 0xa8, 0xa2, 0x42, 0x74, 0xab, 0xa2, 0x0e, 0x78, 0xa8, 0xa2, 0x44, 0x74, 0xab, 0xa2,
+ 0x08, 0x78, 0xa8, 0xa2, 0x3e, 0x74, 0xab, 0xa2, 0x0a, 0x78, 0xa8, 0xa2, 0x40, 0x74,
+ 0xab, 0xa2, 0x04, 0x78, 0xa8, 0xa2, 0x46, 0x74, 0xab, 0xa2, 0x06, 0x78, 0xa8, 0xa2,
+ 0x48, 0x74, 0xab, 0xa2, 0x10, 0xce, 0xa8, 0xa2, 0x44, 0xce, 0xfd, 0x94, 0x52, 0xa2,
+ 0x44, 0xce, 0xfc, 0x94, 0x2f, 0xa2, 0x0c, 0xce, 0xa8, 0xa2, 0x40, 0xce, 0xfd, 0x94,
+ 0x3b, 0xa2, 0x40, 0xce, 0xfc, 0x94, 0x2a, 0xa2, 0x24, 0xce, 0xa8, 0xab, 0xca, 0x90,
+ 0x00, 0xa2, 0x24, 0xce, 0xab, 0x96, 0xca, 0xfc, 0x94, 0x31, 0xa2, 0x22, 0xce, 0x88,
+ 0xab, 0x18, 0xb0, 0x2f, 0x07, 0xb5, 0x33, 0x62, 0x94, 0x23, 0xa2, 0x0e, 0xce, 0xa8,
+ 0xa2, 0x42, 0xce, 0xfd, 0x54, 0x95, 0x38, 0xa2, 0x0a, 0xce, 0xa8, 0xa2, 0x3e, 0xce,
+ 0xfd, 0x42, 0x95, 0x33, 0x90, 0x02, 0xa2, 0x24, 0xce, 0xab, 0x46, 0x90, 0x01, 0xa2,
+ 0x24, 0xce, 0xab, 0xa2, 0x24, 0xce, 0xa8, 0x9c, 0x00, 0x3c, 0xb7, 0x04, 0x02, 0x02,
+ 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xef,
+ 0x69, 0xb7, 0x04, 0x01, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2,
+ 0x22, 0xce, 0x88, 0xb5, 0xef, 0x56, 0x90, 0x00, 0xa2, 0x56, 0xce, 0xab, 0x90, 0x00,
+ 0xa2, 0x52, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x2f, 0x07, 0xb4,
+ 0x32, 0xf8, 0xb7, 0x04, 0x02, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06,
+ 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xef, 0x2b, 0xb7, 0x04, 0x01, 0x02, 0xb7, 0x00, 0x00,
+ 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xef, 0x18, 0x90, 0x00,
+ 0xa2, 0x56, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x52, 0xce, 0xab, 0xa2, 0x24, 0xce, 0xa8,
+ 0x9c, 0x00, 0x52, 0x90, 0x00, 0xa2, 0x24, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab,
+ 0x18, 0xb0, 0x2f, 0x07, 0xb5, 0x32, 0xad, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0,
+ 0x20, 0x17, 0xb4, 0x32, 0xa1, 0x90, 0x01, 0xa2, 0x56, 0xce, 0xab, 0x90, 0x03, 0xa2,
+ 0x52, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x54, 0xce, 0xab, 0x35, 0x47, 0xa2, 0x22, 0xce,
+ 0x88, 0xab, 0x18, 0xb0, 0x20, 0x17, 0xb5, 0x32, 0x81, 0xa2, 0x52, 0xce, 0xa8, 0x9c,
+ 0x03, 0x41, 0x3c, 0xb7, 0x04, 0x02, 0x02, 0xb7, 0x00, 0x01, 0x04, 0xb7, 0x00, 0x00,
+ 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xee, 0xac, 0xa2, 0x2c, 0x72, 0xa8, 0xa2, 0x2c,
+ 0x72, 0xa9, 0x9c, 0x01, 0xb4, 0x11, 0x10, 0x3c, 0x90, 0x02, 0xa2, 0x56, 0xce, 0xab,
+ 0x90, 0x02, 0xa2, 0x52, 0xce, 0xab, 0x90, 0x01, 0xa2, 0x54, 0xce, 0xab, 0x35, 0x90,
+ 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x17, 0xb5, 0x32, 0x38, 0xa2, 0x52,
+ 0xce, 0xa8, 0x9c, 0x02, 0x41, 0x3c, 0xb7, 0x04, 0x02, 0x02, 0xb7, 0x00, 0x01, 0x04,
+ 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xee, 0x63, 0xa2, 0x2e, 0x72,
+ 0xa8, 0xa2, 0x2e, 0x72, 0xa9, 0x9c, 0x01, 0xb4, 0x10, 0xc7, 0x3c, 0x90, 0x04, 0xa2,
+ 0x56, 0xce, 0xab, 0x90, 0x01, 0xa2, 0x52, 0xce, 0xab, 0xa2, 0x06, 0xce, 0xa8, 0x05,
+ 0xa2, 0x54, 0xce, 0xab, 0x35, 0xdc, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20,
+ 0x17, 0xb5, 0x31, 0xec, 0xa2, 0x52, 0xce, 0xa8, 0x9c, 0x01, 0x41, 0x3c, 0xb7, 0x04,
+ 0x01, 0x02, 0xb7, 0x00, 0x01, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88,
+ 0xb4, 0xee, 0x17, 0xa2, 0x66, 0xcc, 0xa8, 0xaf, 0xce, 0x9e, 0x06, 0x3f, 0xce, 0xab,
+ 0xca, 0xa2, 0x66, 0xce, 0xa8, 0x96, 0xca, 0xf8, 0xb8, 0x54, 0x54, 0xad, 0xc8, 0x88,
+ 0xab, 0x0c, 0xa2, 0x22, 0x72, 0xab, 0xb7, 0x00, 0x00, 0x18, 0xb0, 0x10, 0x2a, 0xb5,
+ 0x31, 0xa8, 0xb7, 0x00, 0x00, 0x0a, 0xa2, 0x29, 0xce, 0x88, 0x9c, 0x02, 0x42, 0x94,
+ 0x25, 0x82, 0x05, 0x0c, 0xfc, 0x4b, 0x82, 0x09, 0x0c, 0xfc, 0x46, 0x82, 0x07, 0x0c,
+ 0xfc, 0x41, 0x55, 0xb7, 0x00, 0x01, 0x0a, 0x57, 0x82, 0x06, 0x0c, 0xfc, 0x69, 0x82,
+ 0x0a, 0x0c, 0xfc, 0x6e, 0x82, 0x07, 0x0c, 0xfc, 0x73, 0x47, 0xa2, 0x29, 0xcc, 0x88,
+ 0x9c, 0x02, 0x76, 0xa2, 0x27, 0x72, 0x88, 0xa0, 0xc8, 0x0a, 0xfc, 0x50, 0xa8, 0x0a,
+ 0xa2, 0x27, 0x72, 0x8b, 0xb7, 0x00, 0x00, 0x18, 0xb0, 0x10, 0x2e, 0xb5, 0x31, 0x56,
+ 0xa2, 0x15, 0x72, 0x88, 0x9c, 0x00, 0x3c, 0xb7, 0x01, 0x06, 0x02, 0xb7, 0x00, 0x00,
+ 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4, 0xed, 0x85, 0xa2, 0x66, 0xce, 0xa8, 0xb8,
+ 0x54, 0x4e, 0xad, 0xc8, 0x88, 0xa2, 0x22, 0x72, 0xab, 0xab, 0x0a, 0xb7, 0x00, 0x00,
+ 0x18, 0xb0, 0x10, 0x2a, 0xb5, 0x31, 0x25, 0xb7, 0x00, 0x00, 0x0c, 0xa2, 0x29, 0xce,
+ 0x88, 0x9c, 0x02, 0x57, 0xa2, 0x27, 0x72, 0x88, 0x9c, 0x0c, 0x3c, 0xa8, 0x0c, 0xa2,
+ 0x27, 0x72, 0x8b, 0xb7, 0x00, 0x00, 0x18, 0xb0, 0x10, 0x2e, 0xb4, 0x31, 0x03, 0xa2,
+ 0x28, 0xce, 0x88, 0x9c, 0x02, 0x7d, 0x82, 0x08, 0x0a, 0xfc, 0x47, 0x82, 0x0b, 0x0a,
+ 0xfc, 0x42, 0x95, 0x28, 0xb7, 0x00, 0x01, 0x0c, 0x95, 0x2e, 0xa2, 0x66, 0xce, 0xa8,
+ 0x9c, 0x00, 0x56, 0x9c, 0x02, 0x94, 0xaa, 0x9c, 0x03, 0xb4, 0x01, 0x0c, 0x9c, 0x04,
+ 0xb4, 0x01, 0x66, 0x9c, 0x05, 0xb4, 0x01, 0x8d, 0xb4, 0x01, 0xf4, 0xa2, 0x3a, 0xce,
+ 0x88, 0x9c, 0x00, 0x56, 0xa2, 0x57, 0xce, 0x88, 0x9c, 0x00, 0x4f, 0x37, 0xbc, 0xb6,
+ 0xb8, 0x02, 0x88, 0x9a, 0x02, 0xb6, 0xb8, 0x02, 0x8b, 0xb4, 0x01, 0xd7, 0xa2, 0x3a,
+ 0xce, 0x88, 0x9c, 0x00, 0x5d, 0xa2, 0x56, 0xce, 0x88, 0x9c, 0x00, 0x56, 0xa2, 0x3a,
+ 0xcc, 0x88, 0x9c, 0x00, 0x4f, 0x37, 0xac, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x02, 0xb6,
+ 0xb8, 0x02, 0x8b, 0xb4, 0x01, 0xb3, 0xa2, 0x39, 0xce, 0x88, 0x9c, 0x00, 0x5e, 0xa2,
+ 0x5e, 0xce, 0x88, 0x9c, 0x00, 0x57, 0x37, 0xa1, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x08,
+ 0xb6, 0xb8, 0x02, 0x8b, 0xa2, 0x0a, 0xce, 0xa8, 0xb6, 0xb8, 0x04, 0xab, 0xb4, 0x01,
+ 0x8e, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0xb4, 0x01, 0x85, 0xa2, 0x58, 0xce, 0x88,
+ 0x9c, 0x00, 0xb4, 0x01, 0x7c, 0x37, 0xa9, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x04, 0xb6,
+ 0xb8, 0x02, 0x8b, 0xb4, 0x01, 0x6d, 0x90, 0x01, 0xb5, 0xeb, 0x03, 0x90, 0x01, 0xa2,
+ 0x14, 0x78, 0x8b, 0xb5, 0xfb, 0x5f, 0xb4, 0x01, 0x5c, 0xa2, 0x3a, 0xce, 0x88, 0x9c,
+ 0x00, 0x77, 0xa2, 0x3c, 0xce, 0x88, 0x9d, 0x00, 0x7e, 0xa2, 0x3a, 0xce, 0x88, 0x9c,
+ 0x00, 0x94, 0x1f, 0xa2, 0x56, 0xce, 0x88, 0x9c, 0x00, 0x58, 0xa2, 0x3a, 0xcc, 0x88,
+ 0x9c, 0x00, 0x51, 0x90, 0x01, 0xb5, 0xea, 0xce, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b,
+ 0xb5, 0xfb, 0xbc, 0xb4, 0x01, 0x27, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0xb4, 0x01,
+ 0x1e, 0xa2, 0x58, 0xce, 0x88, 0x9c, 0x00, 0xb4, 0x01, 0x15, 0xb5, 0xfb, 0xed, 0xb6,
+ 0xb8, 0x02, 0x88, 0x9a, 0x04, 0xb6, 0xb8, 0x02, 0x8b, 0xb4, 0x01, 0x05, 0x90, 0x01,
+ 0xb5, 0xea, 0x9b, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xfa, 0xf7, 0x94, 0xf5,
+ 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x76, 0xa2, 0x3c, 0xce, 0x88, 0x9d, 0x00, 0x7d,
+ 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x57, 0xa2, 0x57, 0xce, 0x88, 0x9c, 0x00, 0x50,
+ 0x90, 0x01, 0xb5, 0xea, 0x6f, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xfb, 0x29,
+ 0x94, 0xc9, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x94, 0xc1, 0xa2, 0x58, 0xce, 0x88,
+ 0x9c, 0x00, 0x94, 0xb9, 0x90, 0x01, 0xb5, 0xea, 0x4f, 0x90, 0x01, 0xa2, 0x14, 0x78,
+ 0x8b, 0xb5, 0xfb, 0x86, 0x94, 0xa9, 0xa2, 0x0a, 0xce, 0xa8, 0x05, 0xb5, 0xea, 0x3c,
+ 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xfa, 0x98, 0x94, 0x96, 0xa2, 0x39, 0xce,
+ 0x88, 0x9c, 0x00, 0x79, 0xa2, 0x5e, 0xce, 0x88, 0x9c, 0x00, 0x95, 0x20, 0x94, 0x85,
+ 0x90, 0x00, 0xb5, 0xea, 0x1b, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0x90, 0x01, 0xb5,
+ 0xea, 0x10, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xfa, 0x6c, 0x94, 0x6a, 0xa2,
+ 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x95, 0x21, 0xa2, 0x3c, 0xce, 0x88, 0x9d, 0x00, 0x95,
+ 0x29, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x22, 0xa2, 0x57, 0xce, 0x88, 0x9c,
+ 0x00, 0x5b, 0x90, 0x00, 0xb5, 0xe9, 0xe1, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0x90,
+ 0x01, 0xb5, 0xe9, 0xd6, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xfa, 0x90, 0x94,
+ 0x30, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x28, 0xa2, 0x56, 0xce, 0x88, 0x9c,
+ 0x00, 0x94, 0x20, 0xa2, 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x59, 0x90, 0x00, 0xb5, 0xe9,
+ 0xaf, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0x90, 0x01, 0xb5, 0xe9, 0xa4, 0x90, 0x01,
+ 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xfa, 0x92, 0xa2, 0x66, 0xcc, 0xa8, 0x9c, 0x00, 0x54,
+ 0x9c, 0x01, 0x94, 0xae, 0x9c, 0x03, 0xb4, 0x01, 0x0e, 0x9c, 0x04, 0xb4, 0x01, 0x69,
+ 0x9c, 0x05, 0xb4, 0x01, 0x90, 0x3c, 0xa2, 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x58, 0xa2,
+ 0x57, 0xcc, 0x88, 0x9c, 0x00, 0x51, 0xac, 0xcc, 0xce, 0xb5, 0xf9, 0xfb, 0xb6, 0xb8,
+ 0x02, 0x88, 0x9a, 0x01, 0xb6, 0xb8, 0x02, 0x8b, 0x3c, 0xa2, 0x3a, 0xcc, 0x88, 0x9c,
+ 0x00, 0x94, 0x1f, 0xa2, 0x56, 0xcc, 0x88, 0x9c, 0x00, 0x58, 0xa2, 0x3a, 0xce, 0x88,
+ 0x9c, 0x00, 0x51, 0xac, 0xcc, 0xce, 0xb5, 0xfa, 0x3c, 0xb6, 0xb8, 0x02, 0x88, 0x9a,
+ 0x01, 0xb6, 0xb8, 0x02, 0x8b, 0x3c, 0xa2, 0x39, 0xcc, 0x88, 0x9c, 0x00, 0x94, 0x20,
+ 0xa2, 0x5e, 0xcc, 0x88, 0x9c, 0x00, 0x59, 0xac, 0xcc, 0xce, 0xb5, 0xfa, 0x44, 0xb6,
+ 0xb8, 0x02, 0x88, 0x9a, 0x08, 0xb6, 0xb8, 0x02, 0x8b, 0xa2, 0x0a, 0xcc, 0xa8, 0xb6,
+ 0xb8, 0x04, 0xab, 0x3c, 0xa2, 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x58, 0xcc,
+ 0x88, 0x9c, 0x00, 0x3c, 0xac, 0xcc, 0xce, 0xb5, 0xfa, 0x3e, 0xb6, 0xb8, 0x02, 0x88,
+ 0x9a, 0x04, 0xb6, 0xb8, 0x02, 0x8b, 0x3c, 0x90, 0x00, 0xb5, 0xe8, 0xee, 0x90, 0x01,
+ 0xa2, 0x14, 0x78, 0x8b, 0xac, 0xcc, 0xce, 0xb4, 0xf9, 0x47, 0xa2, 0x3a, 0xcc, 0x88,
+ 0x9c, 0x00, 0x77, 0xa2, 0x3c, 0xcc, 0x88, 0x9d, 0x00, 0x7e, 0xa2, 0x3a, 0xcc, 0x88,
+ 0x9c, 0x00, 0x94, 0x1f, 0xa2, 0x56, 0xcc, 0x88, 0x9c, 0x00, 0x58, 0xa2, 0x3a, 0xce,
+ 0x88, 0x9c, 0x00, 0x51, 0x90, 0x00, 0xb5, 0xe8, 0xb9, 0x90, 0x01, 0xa2, 0x14, 0x78,
+ 0x8b, 0xac, 0xcc, 0xce, 0xb4, 0xf9, 0xa4, 0xa2, 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x3c,
+ 0xa2, 0x58, 0xcc, 0x88, 0x9c, 0x00, 0x3c, 0xac, 0xcc, 0xce, 0xb5, 0xf9, 0xd9, 0xb6,
+ 0xb8, 0x02, 0x88, 0x9a, 0x04, 0xb6, 0xb8, 0x02, 0x8b, 0x3c, 0x90, 0x00, 0xb5, 0xe8,
+ 0x89, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xac, 0xcc, 0xce, 0xb4, 0xf8, 0xe2, 0xa2,
+ 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x77, 0xa2, 0x3c, 0xcc, 0x88, 0x9d, 0x00, 0x7e, 0xa2,
+ 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x58, 0xa2, 0x57, 0xcc, 0x88, 0x9c, 0x00, 0x51, 0x90,
+ 0x00, 0xb5, 0xe8, 0x5c, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xac, 0xcc, 0xce, 0xb4,
+ 0xf8, 0xdf, 0xa2, 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x58, 0xcc, 0x88, 0x9c,
+ 0x00, 0x3c, 0x90, 0x00, 0xb5, 0xe8, 0x3d, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xac,
+ 0xcc, 0xce, 0xb4, 0xf9, 0x71, 0xa2, 0x0a, 0xcc, 0xa8, 0x05, 0xb5, 0xe8, 0x29, 0x90,
+ 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xac, 0xcc, 0xce, 0xb4, 0xf8, 0x82, 0xa2, 0x39, 0xcc,
+ 0x88, 0x9c, 0x00, 0x7a, 0xa2, 0x5e, 0xcc, 0x88, 0x9c, 0x00, 0x95, 0x21, 0x3c, 0x90,
+ 0x00, 0xb5, 0xe8, 0x08, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0x90, 0x01, 0xb5, 0xe7,
+ 0xfd, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xac, 0xcc, 0xce, 0xb4, 0xf8, 0x56, 0xa2,
+ 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x95, 0x22, 0xa2, 0x3c, 0xcc, 0x88, 0x9d, 0x00, 0x95,
+ 0x2a, 0xa2, 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x94, 0x23, 0xa2, 0x57, 0xcc, 0x88, 0x9c,
+ 0x00, 0x5c, 0x90, 0x00, 0xb5, 0xe7, 0xcd, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0x90,
+ 0x01, 0xb5, 0xe7, 0xc2, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xac, 0xcc, 0xce, 0xb4,
+ 0xf8, 0x45, 0xa2, 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x56, 0xcc, 0x88, 0x9c,
+ 0x00, 0x3c, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0x90, 0x00, 0xb5, 0xe7, 0x9c,
+ 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0x90, 0x01, 0xb5, 0xe7, 0x91, 0x90, 0x01, 0xa2,
+ 0x14, 0x78, 0x8b, 0xac, 0xcc, 0xce, 0xb4, 0xf8, 0x7c, 0xa2, 0x66, 0xce, 0xa8, 0x9c,
+ 0x00, 0x4e, 0x9c, 0x01, 0x94, 0x84, 0x9c, 0x02, 0x94, 0xd9, 0x9c, 0x04, 0xb4, 0x01,
+ 0x1a, 0x3c, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x55, 0xa2, 0x59, 0xce, 0x88, 0x9c,
+ 0x00, 0x4e, 0xb5, 0xf7, 0xee, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x01, 0xb6, 0xb8, 0x02,
+ 0x8b, 0x3c, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x55, 0xa2, 0x5a, 0xce, 0x88, 0x9c,
+ 0x00, 0x4e, 0xb5, 0xf8, 0x06, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x02, 0xb6, 0xb8, 0x02,
+ 0x8b, 0x3c, 0xa2, 0x39, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x5e, 0xce, 0x88, 0x9c,
+ 0x00, 0x3c, 0xb5, 0xf8, 0x46, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x08, 0xb6, 0xb8, 0x02,
+ 0x8b, 0xa2, 0x0a, 0xce, 0xa8, 0xb6, 0xb8, 0x04, 0xab, 0x3c, 0x90, 0x00, 0xb5, 0xe7,
+ 0x0f, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xf7, 0x6b, 0xa2, 0x28, 0x72, 0xa8,
+ 0x05, 0xa2, 0x28, 0x72, 0xab, 0x04, 0x9c, 0x00, 0xb4, 0x0a, 0xec, 0x3c, 0xa2, 0x3a,
+ 0xce, 0x88, 0x9c, 0x00, 0x95, 0x24, 0xa2, 0x3c, 0xce, 0x88, 0x9d, 0x00, 0x95, 0x2c,
+ 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x5a, 0xce, 0x88, 0x9c, 0x00, 0x3c,
+ 0xb5, 0xf7, 0x98, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x02, 0xb6, 0xb8, 0x02, 0x8b, 0xa2,
+ 0x28, 0x72, 0xa8, 0x05, 0xa2, 0x28, 0x72, 0xab, 0x04, 0x9c, 0x00, 0xb4, 0x0a, 0xb1,
+ 0x3c, 0x90, 0x01, 0xb5, 0xe6, 0xb6, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xf7,
+ 0x12, 0xa2, 0x2a, 0x72, 0xa8, 0x05, 0xa2, 0x2a, 0x72, 0xab, 0x04, 0x9c, 0x00, 0xb4,
+ 0x0a, 0x93, 0x3c, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x95, 0x24, 0xa2, 0x3c, 0xce,
+ 0x88, 0x9d, 0x00, 0x95, 0x2c, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x59,
+ 0xce, 0x88, 0x9c, 0x00, 0x3c, 0x90, 0x01, 0xb5, 0xe6, 0x7a, 0x90, 0x01, 0xa2, 0x14,
+ 0x78, 0x8b, 0xb5, 0xf7, 0x00, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x01, 0xb6, 0xb8, 0x02,
+ 0x8b, 0xa2, 0x2a, 0x72, 0xa8, 0x05, 0xa2, 0x2a, 0x72, 0xab, 0x04, 0x9c, 0x00, 0xb4,
+ 0x0a, 0x4d, 0x3c, 0xa2, 0x39, 0xce, 0x88, 0x9c, 0x00, 0x48, 0xa2, 0x5e, 0xce, 0x88,
+ 0x9c, 0x00, 0x41, 0x3c, 0xa2, 0x0a, 0xce, 0xa8, 0x05, 0xb5, 0xe6, 0x40, 0x90, 0x01,
+ 0xa2, 0x14, 0x78, 0x8b, 0xb4, 0xf6, 0x9c, 0xa2, 0x66, 0xce, 0xa8, 0x9c, 0x00, 0x4d,
+ 0x9c, 0x01, 0x94, 0x73, 0x9c, 0x04, 0x94, 0x99, 0x9c, 0x05, 0x94, 0xce, 0x3c, 0xa2,
+ 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x55, 0xa2, 0x57, 0xce, 0x88, 0x9c, 0x00, 0x4e, 0xb5,
+ 0xf6, 0xa1, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x01, 0xb6, 0xb8, 0x02, 0x8b, 0x3c, 0xa2,
+ 0x39, 0xce, 0x88, 0x9c, 0x00, 0x5d, 0xa2, 0x5e, 0xce, 0x88, 0x9c, 0x00, 0x56, 0xb5,
+ 0xf7, 0x15, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x08, 0xb6, 0xb8, 0x02, 0x8b, 0xa2, 0x0a,
+ 0xce, 0xa8, 0xb6, 0xb8, 0x04, 0xab, 0x3c, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x3c,
+ 0xa2, 0x58, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xb5, 0xf7, 0x12, 0xb6, 0xb8, 0x02, 0x88,
+ 0x9a, 0x04, 0xb6, 0xb8, 0x02, 0x8b, 0x3c, 0x90, 0x00, 0xb5, 0xe5, 0xc2, 0x90, 0x01,
+ 0xa2, 0x14, 0x78, 0x8b, 0xb4, 0xf6, 0x1e, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x74,
+ 0xa2, 0x3c, 0xce, 0x88, 0x9d, 0x00, 0x7b, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x3c,
+ 0xa2, 0x58, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xb5, 0xf6, 0xda, 0xb6, 0xb8, 0x02, 0x88,
+ 0x9a, 0x01, 0xb6, 0xb8, 0x02, 0x8b, 0x3c, 0xa2, 0x39, 0xce, 0x88, 0x9c, 0x00, 0x48,
+ 0xa2, 0x5e, 0xce, 0x88, 0x9c, 0x00, 0x41, 0x3c, 0xa2, 0x0a, 0xce, 0xa8, 0x05, 0xb5,
+ 0xe5, 0x78, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb4, 0xf5, 0xd4, 0x90, 0x00, 0xb5,
+ 0xe5, 0x6a, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0x90, 0x01, 0xb5, 0xe5, 0x5f, 0x90,
+ 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb4, 0xf5, 0xbb, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00,
+ 0x7f, 0xa2, 0x3c, 0xce, 0x88, 0x9d, 0x00, 0x95, 0x26, 0xa2, 0x3a, 0xce, 0x88, 0x9c,
+ 0x00, 0x3c, 0xa2, 0x57, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0x90, 0x00, 0xb5, 0xe5, 0x34,
+ 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0x90, 0x01, 0xb5, 0xe5, 0x29, 0x90, 0x01, 0xa2,
+ 0x14, 0x78, 0x8b, 0xb4, 0xf5, 0xaf, 0xa2, 0x56, 0xce, 0xa8, 0x9c, 0x00, 0x4e, 0x9c,
+ 0x01, 0x94, 0x28, 0x9c, 0x02, 0xb4, 0x01, 0x19, 0x9c, 0x04, 0x94, 0x9a, 0x3c, 0xa2,
+ 0x4a, 0xce, 0x88, 0x9c, 0x00, 0x43, 0xb4, 0xf7, 0x9e, 0xa2, 0x4b, 0xce, 0x88, 0x9c,
+ 0x00, 0x43, 0xb4, 0xf7, 0xdd, 0xa2, 0x4c, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xb4, 0xf8,
+ 0x1c, 0xa2, 0x4a, 0xce, 0x88, 0xa2, 0x4b, 0xce, 0xda, 0x9c, 0x00, 0x94, 0x51, 0xa2,
+ 0x4b, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xb7, 0x04, 0x02, 0x02, 0xb7, 0x00, 0x00, 0x04,
+ 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xe6, 0x4b, 0x90, 0x00, 0xb5,
+ 0xe4, 0xc2, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xf7, 0x9f, 0xa2, 0x2c, 0x72,
+ 0xa8, 0x05, 0xa2, 0x2c, 0x72, 0xab, 0x04, 0x9c, 0x00, 0xb5, 0x08, 0x9f, 0xa2, 0x2f,
+ 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x1a, 0x72, 0xa8, 0x05, 0xa2, 0x1a, 0x72, 0xab,
+ 0xa2, 0x1c, 0x72, 0xa8, 0x04, 0xa2, 0x1c, 0x72, 0xab, 0x3c, 0x90, 0x00, 0xb5, 0xe4,
+ 0x8b, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xf6, 0xc8, 0xa2, 0x2c, 0x72, 0xa8,
+ 0x05, 0xa2, 0x2c, 0x72, 0xab, 0x04, 0x9c, 0x00, 0xb4, 0x08, 0x68, 0x3c, 0xa2, 0x4a,
+ 0xce, 0x88, 0xa2, 0x4b, 0xce, 0xda, 0x9c, 0x00, 0x94, 0x51, 0xa2, 0x4a, 0xce, 0x88,
+ 0x9c, 0x00, 0x3c, 0xb7, 0x04, 0x02, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00,
+ 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xe5, 0xd0, 0x90, 0x01, 0xb5, 0xe4, 0x47, 0x90,
+ 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xf6, 0xdb, 0xa2, 0x2e, 0x72, 0xa8, 0x05, 0xa2,
+ 0x2e, 0x72, 0xab, 0x04, 0x9c, 0x00, 0xb5, 0x08, 0x24, 0xa2, 0x2f, 0xce, 0x88, 0x9c,
+ 0x00, 0x3c, 0xa2, 0x1a, 0x72, 0xa8, 0x04, 0xa2, 0x1a, 0x72, 0xab, 0xa2, 0x1c, 0x72,
+ 0xa8, 0x05, 0xa2, 0x1c, 0x72, 0xab, 0x3c, 0x90, 0x01, 0xb5, 0xe4, 0x10, 0x90, 0x01,
+ 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xf6, 0xed, 0xa2, 0x2e, 0x72, 0xa8, 0x05, 0xa2, 0x2e,
+ 0x72, 0xab, 0x04, 0x9c, 0x00, 0xb4, 0x07, 0xed, 0x3c, 0xa2, 0x4c, 0xce, 0x88, 0x9d,
+ 0x00, 0x3c, 0xa2, 0x06, 0xce, 0xa8, 0x05, 0xb5, 0xe3, 0xe8, 0x90, 0x01, 0xa2, 0x14,
+ 0x78, 0x8b, 0xb4, 0xf6, 0x25, 0x00, 0xb6, 0xb8, 0x02, 0x8b, 0xb6, 0xb8, 0x04, 0xab,
+ 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x00, 0xb4, 0x05, 0x1e, 0x9c, 0x01, 0xb4, 0x05, 0x19,
+ 0x9c, 0x02, 0xb4, 0x03, 0x4d, 0x9c, 0x03, 0xb4, 0x06, 0x18, 0xa2, 0x06, 0xce, 0xa8,
+ 0x9c, 0x00, 0x4e, 0x9c, 0x01, 0x4b, 0x9c, 0x02, 0xb4, 0x02, 0x1a, 0x9c, 0x03, 0xb4,
+ 0x02, 0xa1, 0x3c, 0xa2, 0x26, 0x72, 0x88, 0x9c, 0x00, 0x59, 0xa2, 0x3a, 0x0c, 0x88,
+ 0x9c, 0x00, 0x52, 0x90, 0x01, 0xa2, 0x56, 0x0a, 0x8b, 0x90, 0x00, 0xa2, 0x58, 0x0a,
+ 0x8b, 0xa2, 0x57, 0x0a, 0x8b, 0x94, 0xa2, 0xa2, 0x5b, 0x0c, 0x88, 0x9c, 0x00, 0x5b,
+ 0xa2, 0x3a, 0x0c, 0x88, 0x9c, 0x00, 0x54, 0x94, 0x2f, 0x90, 0x00, 0xa2, 0x56, 0x0a,
+ 0x8b, 0xa2, 0x57, 0x0a, 0x8b, 0x90, 0x01, 0xa2, 0x58, 0x0a, 0x8b, 0x94, 0x80, 0xa2,
+ 0x5b, 0x0a, 0x88, 0x9d, 0x00, 0x78, 0xa2, 0x5f, 0x0a, 0x88, 0x9c, 0x00, 0x4f, 0xa2,
+ 0x5f, 0x0c, 0x88, 0x9c, 0x00, 0x94, 0x20, 0xa2, 0x3a, 0x0c, 0x88, 0x9c, 0x00, 0x59,
+ 0xa2, 0x5b, 0x0c, 0x88, 0x9c, 0x00, 0x48, 0xa2, 0x3a, 0x0c, 0x88, 0x9c, 0x00, 0x41,
+ 0x5c, 0xa2, 0x5d, 0x0a, 0x88, 0x9d, 0x00, 0x94, 0x3b, 0x94, 0x22, 0xa2, 0x5c, 0x0c,
+ 0x88, 0x9c, 0x00, 0x95, 0x4e, 0xa2, 0x3a, 0x0c, 0x88, 0x9c, 0x00, 0x95, 0x56, 0x95,
+ 0x29, 0x90, 0x00, 0xa2, 0x56, 0x0a, 0x8b, 0xa2, 0x58, 0x0a, 0x8b, 0xa2, 0x57, 0x0a,
+ 0x8b, 0x94, 0x28, 0xa2, 0x61, 0x0a, 0x88, 0x9c, 0x00, 0x76, 0xa2, 0x5f, 0x0c, 0x88,
+ 0x9c, 0x00, 0x49, 0xa2, 0x3a, 0x0c, 0x88, 0x9c, 0x00, 0x42, 0x95, 0x25, 0x90, 0x00,
+ 0xa2, 0x56, 0x0a, 0x8b, 0xa2, 0x58, 0x0a, 0x8b, 0x90, 0x01, 0xa2, 0x57, 0x0a, 0x8b,
+ 0x40, 0xa2, 0x3c, 0x0a, 0x88, 0xab, 0x0e, 0xa2, 0x56, 0x0a, 0x88, 0xa2, 0x57, 0x0a,
+ 0xda, 0xa2, 0x58, 0x0a, 0xda, 0x9c, 0x00, 0x94, 0x3b, 0x90, 0x00, 0xa2, 0x3c, 0x0a,
+ 0x8b, 0x96, 0x0e, 0xfc, 0x51, 0xb7, 0x02, 0x13, 0x02, 0xab, 0x04, 0xb7, 0x00, 0x00,
+ 0x06, 0xa2, 0x22, 0x0a, 0x88, 0xb5, 0xe4, 0x3a, 0xa2, 0x26, 0x72, 0x88, 0x9c, 0x00,
+ 0x5d, 0xa2, 0x3a, 0x0a, 0x88, 0x9c, 0x00, 0x56, 0x90, 0x01, 0xa2, 0x56, 0x0c, 0x8b,
+ 0x90, 0x00, 0xa2, 0x58, 0x0c, 0x8b, 0xa2, 0x57, 0x0c, 0x8b, 0x94, 0x90, 0x90, 0x01,
+ 0x95, 0x3b, 0xa2, 0x5b, 0x0c, 0x88, 0x9d, 0x00, 0x58, 0xa2, 0x5f, 0x0c, 0x88, 0x9c,
+ 0x00, 0x94, 0x30, 0xa2, 0x5b, 0x0a, 0x88, 0x9c, 0x00, 0x5b, 0xa2, 0x3a, 0x0a, 0x88,
+ 0x9c, 0x00, 0x54, 0x94, 0x20, 0x90, 0x00, 0xa2, 0x56, 0x0c, 0x8b, 0xa2, 0x57, 0x0c,
+ 0x8b, 0x90, 0x01, 0xa2, 0x58, 0x0c, 0x8b, 0x94, 0x5b, 0xa2, 0x5d, 0x0a, 0x88, 0x9c,
+ 0x00, 0x78, 0xa2, 0x3a, 0x0a, 0x88, 0x9c, 0x00, 0x7f, 0xa2, 0x3a, 0x0a, 0x88, 0x9c,
+ 0x00, 0x48, 0xa2, 0x5b, 0x0a, 0x88, 0x9c, 0x00, 0x41, 0x5e, 0xa2, 0x5c, 0x0c, 0x88,
+ 0x9d, 0x00, 0x94, 0x25, 0xa2, 0x60, 0x0c, 0x88, 0x9c, 0x00, 0x4f, 0xa2, 0x5f, 0x0a,
+ 0x88, 0x9c, 0x00, 0x57, 0xa2, 0x3a, 0x0a, 0x88, 0x9c, 0x00, 0x50, 0x40, 0x90, 0x00,
+ 0xa2, 0x56, 0x0c, 0x8b, 0xa2, 0x58, 0x0c, 0x8b, 0xa2, 0x57, 0x0c, 0x8b, 0x51, 0x90,
+ 0x00, 0xa2, 0x56, 0x0c, 0x8b, 0xa2, 0x58, 0x0c, 0x8b, 0x90, 0x01, 0xa2, 0x57, 0x0c,
+ 0x8b, 0x40, 0xa2, 0x3c, 0x0c, 0x88, 0xab, 0x0e, 0xa2, 0x56, 0x0c, 0x88, 0xa2, 0x57,
+ 0x0c, 0xda, 0xa2, 0x58, 0x0c, 0xda, 0x9c, 0x00, 0x94, 0x57, 0x90, 0x00, 0xa2, 0x3c,
+ 0x0c, 0x8b, 0x96, 0x0e, 0xfc, 0x51, 0xb7, 0x02, 0x13, 0x02, 0xab, 0x04, 0xb7, 0x00,
+ 0x00, 0x06, 0xa2, 0x22, 0x0c, 0x88, 0xb5, 0xe3, 0x59, 0xa2, 0x66, 0x0a, 0xa8, 0xaf,
+ 0xc8, 0xa2, 0x66, 0x0c, 0xa8, 0xaf, 0xc8, 0xac, 0x0a, 0xce, 0xac, 0x0c, 0xcc, 0xb5,
+ 0xf6, 0x14, 0x3f, 0xc8, 0xa2, 0x66, 0x0c, 0xfc, 0x58, 0x3f, 0xc8, 0xac, 0x0a, 0xce,
+ 0xac, 0x0c, 0xcc, 0xb5, 0xf5, 0x1b, 0xb5, 0xf1, 0xed, 0xa2, 0x22, 0x72, 0xa8, 0xb5,
+ 0x10, 0x93, 0xb4, 0xf1, 0x41, 0x3f, 0xc8, 0xa2, 0x66, 0x0a, 0xfc, 0x3c, 0x7d, 0x90,
+ 0x01, 0x95, 0x57, 0xa2, 0x5b, 0xce, 0x88, 0xa2, 0x5f, 0xce, 0xda, 0x9c, 0x00, 0x4e,
+ 0x90, 0x01, 0xa2, 0x58, 0xce, 0x8b, 0x90, 0x00, 0xa2, 0x57, 0xce, 0x8b, 0x94, 0x22,
+ 0xa2, 0x5c, 0xce, 0x88, 0xa2, 0x60, 0xce, 0xda, 0x9c, 0x00, 0x4d, 0x90, 0x00, 0xa2,
+ 0x58, 0xce, 0x8b, 0x90, 0x01, 0xa2, 0x57, 0xce, 0x8b, 0x4a, 0x90, 0x00, 0xa2, 0x58,
+ 0xce, 0x8b, 0xa2, 0x57, 0xce, 0x8b, 0xa2, 0x3c, 0xce, 0x88, 0xab, 0x0e, 0xa2, 0x57,
+ 0xce, 0x88, 0xa2, 0x58, 0xce, 0xda, 0x9c, 0x00, 0x94, 0x3b, 0x90, 0x00, 0xa2, 0x3c,
+ 0xce, 0x8b, 0x96, 0x0e, 0xfc, 0x51, 0xb7, 0x02, 0x13, 0x02, 0xab, 0x04, 0xb7, 0x00,
+ 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xe2, 0xb1, 0xa2, 0x66, 0xce, 0xa8, 0xaf,
+ 0xc8, 0xb5, 0xfa, 0xed, 0x3f, 0xc8, 0xa2, 0x66, 0xce, 0xfc, 0x3c, 0xb5, 0xf5, 0x19,
+ 0xb5, 0xf1, 0x59, 0xa2, 0x22, 0x72, 0xa8, 0xb5, 0x0f, 0xff, 0xb4, 0xf0, 0xad, 0x90,
+ 0x01, 0x95, 0x3b, 0xa2, 0x5c, 0xce, 0x88, 0x9d, 0x00, 0x4e, 0xa2, 0x60, 0xce, 0x88,
+ 0x9c, 0x00, 0x55, 0xa2, 0x5d, 0xce, 0x88, 0x9d, 0x00, 0x4e, 0x90, 0x01, 0xa2, 0x59,
+ 0xce, 0x8b, 0x90, 0x00, 0xa2, 0x5a, 0xce, 0x8b, 0x94, 0x22, 0xa2, 0x5d, 0xce, 0x88,
+ 0xa2, 0x61, 0xce, 0xda, 0x9c, 0x00, 0x4d, 0x90, 0x00, 0xa2, 0x59, 0xce, 0x8b, 0x90,
+ 0x01, 0xa2, 0x5a, 0xce, 0x8b, 0x4a, 0x90, 0x00, 0xa2, 0x59, 0xce, 0x8b, 0xa2, 0x5a,
+ 0xce, 0x8b, 0xa2, 0x3c, 0xce, 0x88, 0xab, 0x0e, 0xa2, 0x59, 0xce, 0x88, 0xa2, 0x5a,
+ 0xce, 0xda, 0x9c, 0x00, 0x94, 0x38, 0x90, 0x00, 0xa2, 0x3c, 0xce, 0x8b, 0x96, 0x0e,
+ 0xfc, 0x51, 0xb7, 0x02, 0x13, 0x02, 0xab, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22,
+ 0xce, 0x88, 0xb5, 0xe2, 0x1b, 0xa2, 0x66, 0xce, 0xa8, 0xaf, 0xc8, 0xb5, 0xf9, 0x09,
+ 0x3f, 0xc8, 0xa2, 0x66, 0xce, 0xfc, 0x3c, 0xb5, 0xf0, 0xc6, 0xa2, 0x22, 0xce, 0x88,
+ 0xb5, 0x05, 0x95, 0xb4, 0xf0, 0x1a, 0x90, 0x01, 0x95, 0x38, 0x90, 0x00, 0xa2, 0x26,
+ 0x72, 0x8b, 0xa2, 0x2c, 0x72, 0xa8, 0x9c, 0x00, 0xb4, 0x01, 0x9c, 0x90, 0x00, 0xa2,
+ 0x24, 0x72, 0x8b, 0xa2, 0x2e, 0x72, 0xa8, 0x9c, 0x00, 0xb4, 0x01, 0x9b, 0x90, 0x00,
+ 0xa2, 0x25, 0x72, 0x8b, 0xa1, 0xce, 0xb8, 0x0a, 0xab, 0x30, 0x0b, 0x30, 0x4f, 0x30,
+ 0x93, 0x30, 0xc5, 0x31, 0x5f, 0xb4, 0xfc, 0x82, 0xaf, 0xce, 0xa4, 0xb8, 0x0a, 0xce,
+ 0xab, 0xa2, 0x17, 0xce, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x94, 0x32, 0xa2, 0x17, 0xce,
+ 0x88, 0x99, 0x02, 0x9c, 0x00, 0x94, 0x28, 0xa8, 0xce, 0xb8, 0x00, 0x13, 0xa2, 0x29,
+ 0xce, 0xd8, 0xad, 0xc8, 0x88, 0x99, 0x40, 0x9c, 0x00, 0x57, 0xa2, 0x24, 0x72, 0x88,
+ 0x9c, 0x00, 0x47, 0xa2, 0x25, 0x72, 0x88, 0x9d, 0x00, 0x49, 0x90, 0x01, 0xa2, 0x5b,
+ 0xce, 0x8b, 0x3f, 0xce, 0x3c, 0x90, 0x00, 0x69, 0xaf, 0xce, 0xa4, 0xb8, 0x0a, 0xce,
+ 0xab, 0xa2, 0x17, 0xce, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x94, 0x32, 0xa2, 0x17, 0xce,
+ 0x88, 0x99, 0x02, 0x9c, 0x00, 0x94, 0x28, 0xa8, 0xce, 0xb8, 0x00, 0x13, 0xa2, 0x29,
+ 0xce, 0xd8, 0xad, 0xc8, 0x88, 0x99, 0x08, 0x9c, 0x00, 0x57, 0xa2, 0x24, 0x72, 0x88,
+ 0x9c, 0x00, 0x47, 0xa2, 0x25, 0x72, 0x88, 0x9d, 0x00, 0x49, 0x90, 0x01, 0xa2, 0x5f,
+ 0xce, 0x8b, 0x3f, 0xce, 0x3c, 0x90, 0x00, 0x69, 0xaf, 0xce, 0xa4, 0xb8, 0x0a, 0xce,
+ 0xab, 0xa2, 0x17, 0xce, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x5b, 0xa8, 0xce, 0xb8, 0x00,
+ 0x13, 0xa2, 0x29, 0xce, 0xd8, 0xad, 0xc8, 0x88, 0x99, 0x20, 0x9c, 0x00, 0x4a, 0xa2,
+ 0x24, 0x72, 0x88, 0x9d, 0x00, 0x43, 0x90, 0x01, 0x42, 0x90, 0x00, 0xa2, 0x5c, 0xce,
+ 0x8b, 0x3f, 0xce, 0x3c, 0xaf, 0xce, 0xa4, 0xb8, 0x0a, 0xce, 0xab, 0xa2, 0x17, 0xce,
+ 0x88, 0x99, 0x01, 0x9c, 0x00, 0x5b, 0xa8, 0xce, 0xb8, 0x00, 0x13, 0xa2, 0x29, 0xce,
+ 0xd8, 0xad, 0xc8, 0x88, 0x99, 0x04, 0x9c, 0x00, 0x4a, 0xa2, 0x24, 0x72, 0x88, 0x9d,
+ 0x00, 0x43, 0x90, 0x01, 0x42, 0x90, 0x00, 0xa2, 0x60, 0xce, 0x8b, 0x3f, 0xce, 0x3c,
+ 0xaf, 0xce, 0xa4, 0xb8, 0x0a, 0xce, 0xab, 0xa2, 0x17, 0xce, 0x88, 0x99, 0x02, 0x9c,
+ 0x00, 0x5b, 0xa8, 0xce, 0xb8, 0x00, 0x13, 0xa2, 0x29, 0xce, 0xd8, 0xad, 0xc8, 0x88,
+ 0x99, 0x10, 0x9c, 0x00, 0x4a, 0xa2, 0x25, 0x72, 0x88, 0x9d, 0x00, 0x43, 0x90, 0x01,
+ 0x42, 0x90, 0x00, 0xa2, 0x5d, 0xce, 0x8b, 0x3f, 0xce, 0x3c, 0xaf, 0xce, 0xa4, 0xb8,
+ 0x0a, 0xce, 0xab, 0xa2, 0x17, 0xce, 0x88, 0x99, 0x02, 0x9c, 0x00, 0x5b, 0xa8, 0xce,
+ 0xb8, 0x00, 0x13, 0xa2, 0x29, 0xce, 0xd8, 0xad, 0xc8, 0x88, 0x99, 0x02, 0x9c, 0x00,
+ 0x4a, 0xa2, 0x25, 0x72, 0x88, 0x9d, 0x00, 0x43, 0x90, 0x01, 0x42, 0x90, 0x00, 0xa2,
+ 0x61, 0xce, 0x8b, 0x3f, 0xce, 0x3c, 0xaf, 0xce, 0xa4, 0xb8, 0x0a, 0xce, 0xab, 0xa2,
+ 0x17, 0xce, 0x88, 0x99, 0x04, 0x9c, 0x00, 0x42, 0x90, 0x01, 0xa2, 0x5e, 0xce, 0x8b,
+ 0x3f, 0xce, 0x3c, 0xa2, 0x28, 0x72, 0xa8, 0x9d, 0x00, 0xb4, 0xfe, 0x5b, 0x90, 0x01,
+ 0xb4, 0xfe, 0x58, 0xa2, 0x2a, 0x72, 0xa8, 0x9d, 0x00, 0xb4, 0xfe, 0x5c, 0x90, 0x01,
+ 0xb4, 0xfe, 0x59, 0x9c, 0x00, 0x4e, 0xac, 0xce, 0x0c, 0xa2, 0x10, 0x72, 0x88, 0xb5,
+ 0xde, 0x8e, 0xac, 0x76, 0x0a, 0x4d, 0xac, 0xce, 0x0a, 0xa2, 0x11, 0x72, 0x88, 0xb5,
+ 0xde, 0x80, 0xac, 0x76, 0x0c, 0xa2, 0x10, 0x72, 0x88, 0xb5, 0xde, 0x76, 0xa8, 0x76,
+ 0xb6, 0xb8, 0x06, 0xab, 0xa2, 0x11, 0x72, 0x88, 0xb5, 0xde, 0x69, 0xa8, 0x76, 0xb6,
+ 0xb8, 0x08, 0xab, 0xa2, 0x17, 0x0a, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x94, 0x53, 0xa2,
+ 0x17, 0x0c, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x94, 0x49, 0xa2, 0x17, 0x0a, 0x88, 0x99,
+ 0x02, 0x9c, 0x00, 0x94, 0x3f, 0xa2, 0x17, 0x0c, 0x88, 0x99, 0x02, 0x9c, 0x00, 0x94,
+ 0x35, 0xa2, 0x29, 0x0a, 0x88, 0x9c, 0x02, 0x42, 0x94, 0x2c, 0xa2, 0x29, 0x0c, 0x88,
+ 0x9c, 0x02, 0x42, 0x94, 0x23, 0xa8, 0x0a, 0xb8, 0x00, 0x13, 0xb8, 0x00, 0x02, 0xad,
+ 0xc8, 0x88, 0x99, 0x80, 0x9c, 0x00, 0x53, 0xa8, 0x0c, 0xb8, 0x00, 0x13, 0xb8, 0x00,
+ 0x02, 0xad, 0xc8, 0x88, 0x99, 0x80, 0x9c, 0x00, 0x43, 0x90, 0x01, 0x42, 0x90, 0x00,
+ 0xa2, 0x26, 0x72, 0x8b, 0x40, 0xa2, 0x2c, 0x72, 0xa8, 0x9c, 0x00, 0x43, 0x90, 0x00,
+ 0x51, 0xa2, 0x28, 0x72, 0xa8, 0x9c, 0x00, 0x41, 0x6a, 0xa2, 0x26, 0x72, 0x88, 0x9d,
+ 0x00, 0x71, 0x90, 0x01, 0xa2, 0x24, 0x72, 0x8b, 0xa2, 0x2e, 0x72, 0xa8, 0x9c, 0x00,
+ 0x43, 0x90, 0x00, 0x51, 0xa2, 0x2a, 0x72, 0xa8, 0x9c, 0x00, 0x41, 0x6a, 0xa2, 0x26,
+ 0x72, 0x88, 0x9d, 0x00, 0x71, 0x90, 0x01, 0xa2, 0x25, 0x72, 0x8b, 0xb6, 0xb8, 0x06,
+ 0xa8, 0xb6, 0xb8, 0x0a, 0xab, 0x36, 0x73, 0x36, 0x2f, 0x35, 0xeb, 0x35, 0xb9, 0x35,
+ 0x87, 0x35, 0x55, 0x35, 0x23, 0xb6, 0xb8, 0x08, 0xa8, 0xb6, 0xb8, 0x0a, 0xab, 0x36,
+ 0x89, 0x36, 0x45, 0x36, 0x01, 0x35, 0xcf, 0x35, 0x9d, 0x35, 0x6b, 0x35, 0x39, 0xb4,
+ 0xf9, 0xe8, 0xa2, 0x17, 0xce, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x54, 0xa8, 0xce, 0xb8,
+ 0x00, 0x13, 0xa2, 0x29, 0xce, 0xd8, 0xad, 0xc8, 0x88, 0x99, 0x20, 0x9c, 0x00, 0x43,
+ 0x90, 0x01, 0x42, 0x90, 0x00, 0xa2, 0x5c, 0xce, 0x8b, 0xa2, 0x17, 0xce, 0x88, 0x99,
+ 0x01, 0x9c, 0x00, 0x54, 0xa8, 0xce, 0xb8, 0x00, 0x13, 0xa2, 0x29, 0xce, 0xd8, 0xad,
+ 0xc8, 0x88, 0x99, 0x04, 0x9c, 0x00, 0x43, 0x90, 0x01, 0x42, 0x90, 0x00, 0xa2, 0x60,
+ 0xce, 0x8b, 0xa2, 0x17, 0xce, 0x88, 0x99, 0x02, 0x9c, 0x00, 0x54, 0xa8, 0xce, 0xb8,
+ 0x00, 0x13, 0xa2, 0x29, 0xce, 0xd8, 0xad, 0xc8, 0x88, 0x99, 0x10, 0x9c, 0x00, 0x43,
+ 0x90, 0x01, 0x42, 0x90, 0x00, 0xa2, 0x5d, 0xce, 0x8b, 0xa2, 0x17, 0xce, 0x88, 0x99,
+ 0x02, 0x9c, 0x00, 0x54, 0xa8, 0xce, 0xb8, 0x00, 0x13, 0xa2, 0x29, 0xce, 0xd8, 0xad,
+ 0xc8, 0x88, 0x99, 0x02, 0x9c, 0x00, 0x43, 0x90, 0x01, 0x42, 0x90, 0x00, 0xa2, 0x61,
+ 0xce, 0x8b, 0xa2, 0x17, 0xce, 0x88, 0x99, 0x04, 0x9c, 0x00, 0x42, 0x90, 0x01, 0xa2,
+ 0x5e, 0xce, 0x8b, 0xb4, 0xf9, 0x4a, 0xa2, 0x1e, 0xce, 0x88, 0x99, 0x01, 0x9c, 0x00,
+ 0x4b, 0xa2, 0x08, 0xce, 0x88, 0x99, 0x20, 0x9c, 0x00, 0x42, 0x90, 0x01, 0xa2, 0x4d,
+ 0xce, 0x8b, 0xa2, 0x1e, 0xce, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x4b, 0xa2, 0x08, 0xce,
+ 0x88, 0x99, 0x04, 0x9c, 0x00, 0x42, 0x90, 0x01, 0xa2, 0x50, 0xce, 0x8b, 0xa2, 0x1e,
+ 0xce, 0x88, 0x99, 0x02, 0x9c, 0x00, 0x4b, 0xa2, 0x08, 0xce, 0x88, 0x99, 0x10, 0x9c,
+ 0x00, 0x42, 0x90, 0x01, 0xa2, 0x4e, 0xce, 0x8b, 0xa2, 0x1e, 0xce, 0x88, 0x99, 0x02,
+ 0x9c, 0x00, 0x4b, 0xa2, 0x08, 0xce, 0x88, 0x99, 0x02, 0x9c, 0x00, 0x42, 0x90, 0x01,
+ 0xa2, 0x51, 0xce, 0x8b, 0xa2, 0x1e, 0xce, 0x88, 0x99, 0x04, 0x9c, 0x00, 0x4b, 0xa2,
+ 0x08, 0xce, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x42, 0x90, 0x01, 0xa2, 0x4f, 0xce, 0x8b,
+ 0xa2, 0x28, 0xce, 0x88, 0x9c, 0x00, 0x57, 0xa2, 0x4d, 0xce, 0x88, 0x9d, 0x00, 0x94,
+ 0x62, 0xa2, 0x50, 0xce, 0x88, 0x9c, 0x00, 0x48, 0xa2, 0x4e, 0xce, 0x88, 0x9c, 0x00,
+ 0x94, 0x53, 0xa2, 0x28, 0xce, 0x88, 0x9c, 0x00, 0x5d, 0xa2, 0x4e, 0xce, 0x88, 0xa2,
+ 0x51, 0xce, 0xda, 0x9c, 0x00, 0x52, 0x90, 0x01, 0xa2, 0x4b, 0xce, 0x8b, 0x90, 0x00,
+ 0xa2, 0x4a, 0xce, 0x8b, 0xa2, 0x4c, 0xce, 0x8b, 0x94, 0x40, 0xa2, 0x28, 0xce, 0x88,
+ 0x9c, 0x00, 0x59, 0xa2, 0x4f, 0xce, 0x88, 0x9c, 0x00, 0x52, 0x90, 0x00, 0xa2, 0x4a,
+ 0xce, 0x8b, 0xa2, 0x4b, 0xce, 0x8b, 0x90, 0x01, 0xa2, 0x4c, 0xce, 0x8b, 0x94, 0x20,
+ 0x90, 0x00, 0xa2, 0x4a, 0xce, 0x8b, 0xa2, 0x4b, 0xce, 0x8b, 0xa2, 0x4c, 0xce, 0x8b,
+ 0x51, 0x90, 0x01, 0xa2, 0x4a, 0xce, 0x8b, 0x90, 0x00, 0xa2, 0x4b, 0xce, 0x8b, 0xa2,
+ 0x4c, 0xce, 0x8b, 0x40, 0xb4, 0xf6, 0xeb, 0xa2, 0x0a, 0x72, 0x88, 0x9c, 0x01, 0x44,
+ 0x9c, 0x02, 0x51, 0x3c, 0xa2, 0x12, 0x72, 0x88, 0x9c, 0x02, 0x3c, 0xb5, 0xdb, 0xe2,
+ 0xac, 0x76, 0xce, 0xb4, 0xf8, 0x11, 0xa2, 0x10, 0x72, 0x88, 0x9c, 0x02, 0x49, 0xb5,
+ 0xdb, 0xd2, 0xac, 0x76, 0xce, 0xb4, 0xf8, 0x01, 0xa2, 0x11, 0x72, 0x88, 0x9c, 0x02,
+ 0x3c, 0xb5, 0xdb, 0xc2, 0xac, 0x76, 0xce, 0xb4, 0xf7, 0xf1, 0x00, 0xb6, 0xb8, 0x02,
+ 0x8b, 0xb6, 0xb8, 0x04, 0xab, 0xa8, 0x46, 0xb5, 0xdb, 0xae, 0xa8, 0x44, 0xbc, 0x03,
+ 0x02, 0x58, 0xbc, 0x03, 0x01, 0x5b, 0xa2, 0x06, 0x76, 0xa8, 0x9c, 0x03, 0x5c, 0x9c,
+ 0x02, 0x94, 0x38, 0x9c, 0x00, 0x94, 0x57, 0x9c, 0x01, 0x94, 0x53, 0x3c, 0xa8, 0x48,
+ 0xa2, 0x3a, 0x76, 0x8b, 0x7a, 0xa8, 0x48, 0xa2, 0x39, 0x76, 0x8b, 0x95, 0x21, 0xac,
+ 0x76, 0xce, 0xa2, 0x66, 0xce, 0xa8, 0xaf, 0xc8, 0xb5, 0xf4, 0x04, 0x3f, 0xc8, 0xa2,
+ 0x66, 0xce, 0xfc, 0x3c, 0xb5, 0xeb, 0xc1, 0xac, 0x76, 0xce, 0xa8, 0x46, 0x30, 0x90,
+ 0xb4, 0xeb, 0x15, 0xac, 0x76, 0xce, 0xa2, 0x66, 0xce, 0xa8, 0xaf, 0xc8, 0xb5, 0xf5,
+ 0x32, 0x3f, 0xc8, 0xa2, 0x66, 0xce, 0xfc, 0x3c, 0xb5, 0xef, 0x5e, 0xb5, 0xeb, 0x9e,
+ 0xa2, 0x22, 0x72, 0xa8, 0xb5, 0x0a, 0x44, 0xb4, 0xea, 0xf2, 0xa8, 0x44, 0xbc, 0x03,
+ 0x02, 0x94, 0x51, 0xa2, 0x06, 0x76, 0xa8, 0x9c, 0x00, 0x94, 0x4f, 0xac, 0x76, 0xcc,
+ 0xa2, 0x10, 0x72, 0x88, 0xb5, 0xdb, 0x25, 0xac, 0x76, 0xce, 0x43, 0xac, 0x76, 0xcc,
+ 0xa2, 0x66, 0xce, 0xa8, 0xaf, 0xc8, 0xa2, 0x66, 0xcc, 0xa8, 0xaf, 0xc8, 0xaf, 0xce,
+ 0xaf, 0xcc, 0xb5, 0xef, 0x73, 0x3f, 0xcc, 0x3f, 0xce, 0x3f, 0xc8, 0xa2, 0x66, 0xce,
+ 0xfc, 0x52, 0x3f, 0xc8, 0xb5, 0xee, 0x7c, 0xb5, 0xeb, 0x4e, 0xa2, 0x22, 0x72, 0xa8,
+ 0xb5, 0x09, 0xf4, 0xb4, 0xea, 0xa2, 0x3f, 0xc8, 0xa2, 0x66, 0xcc, 0xfc, 0x3c, 0x77,
+ 0xac, 0x76, 0xce, 0xb4, 0xf7, 0x15, 0xac, 0x76, 0xce, 0xa2, 0x11, 0x72, 0x88, 0xb5,
+ 0xda, 0xd6, 0x95, 0x4b, 0xaf, 0xc8, 0xa2, 0x66, 0xce, 0xa8, 0xb5, 0x09, 0x9c, 0xa2,
+ 0x30, 0x72, 0x88, 0x3f, 0xca, 0x9c, 0x00, 0x3c, 0xb7, 0x01, 0x07, 0x02, 0xb7, 0x00,
+ 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0xca, 0xb4, 0xdc, 0x49, 0x00, 0x90, 0x00,
+ 0xf6, 0x90, 0x00, 0xa2, 0x02, 0xce, 0x8b, 0xa2, 0x04, 0xce, 0xab, 0x90, 0x03, 0xa2,
+ 0x06, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x08, 0xce, 0x8b, 0x90, 0x04, 0xa2, 0x20, 0xce,
+ 0xab, 0xb7, 0x00, 0x00, 0x0a, 0xac, 0xce, 0xcc, 0x82, 0x58, 0xcc, 0xf8, 0xa8, 0x0a,
+ 0x96, 0x7e, 0xdc, 0x5a, 0xa2, 0x22, 0xce, 0x88, 0xc6, 0xa2, 0x02, 0xcc, 0x8b, 0x90,
+ 0x02, 0xa2, 0x01, 0xcc, 0x8b, 0xa2, 0x03, 0xcc, 0x8b, 0x82, 0x04, 0xcc, 0xf8, 0xa9,
+ 0x0a, 0x7f, 0x00, 0xa2, 0x26, 0xce, 0x8b, 0xa2, 0x27, 0xce, 0x8b, 0xa2, 0x28, 0xce,
+ 0x8b, 0x90, 0x00, 0xa2, 0x3a, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x24, 0xce, 0xab, 0x3c,
+ 0x00, 0xa2, 0x0a, 0xce, 0xab, 0xa2, 0x0c, 0xce, 0xab, 0xa2, 0x0e, 0xce, 0xab, 0xa2,
+ 0x10, 0xce, 0xab, 0xac, 0xce, 0xcc, 0x82, 0x12, 0xcc, 0xf8, 0xb1, 0x43, 0xb1, 0x33,
+ 0x11, 0xac, 0xce, 0xcc, 0x82, 0x18, 0xcc, 0xf8, 0xb1, 0x43, 0xb1, 0x33, 0x05, 0x90,
+ 0x00, 0xa2, 0x1e, 0xce, 0x8b, 0x00, 0xa2, 0x29, 0xce, 0x8b, 0xa2, 0x2a, 0xce, 0x8b,
+ 0xa2, 0x2b, 0xce, 0x8b, 0xa2, 0x2c, 0xce, 0x8b, 0xa2, 0x2d, 0xce, 0x8b, 0xa2, 0x2e,
+ 0xce, 0x8b, 0xa2, 0x30, 0xce, 0x8b, 0xa2, 0x31, 0xce, 0x8b, 0xa2, 0x32, 0xce, 0x8b,
+ 0xa2, 0x33, 0xce, 0x8b, 0xa2, 0x34, 0xce, 0xab, 0xa2, 0x36, 0xce, 0xab, 0xa2, 0x38,
+ 0xce, 0xab, 0x90, 0x01, 0xa2, 0x2f, 0xce, 0x8b, 0x00, 0xa2, 0x4a, 0xce, 0x8b, 0xa2,
+ 0x4b, 0xce, 0x8b, 0xa2, 0x4c, 0xce, 0x8b, 0xa2, 0x4d, 0xce, 0x8b, 0xa2, 0x4e, 0xce,
+ 0x8b, 0xa2, 0x4f, 0xce, 0x8b, 0xa2, 0x50, 0xce, 0x8b, 0xa2, 0x51, 0xce, 0x8b, 0xa2,
+ 0x54, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x52, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x56, 0xce,
+ 0xab, 0x3c, 0x00, 0xf6, 0xa2, 0x02, 0xce, 0xab, 0x90, 0x02, 0xa2, 0x04, 0xce, 0xab,
+ 0x90, 0x04, 0xa2, 0x06, 0xce, 0xab, 0x00, 0xa2, 0x08, 0xce, 0xab, 0x90, 0x03, 0xa2,
+ 0x0a, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x0c, 0xce, 0x8b, 0x90, 0x00, 0xa2, 0x0e, 0xce,
+ 0xab, 0x90, 0x07, 0xa2, 0x10, 0xce, 0x8b, 0x90, 0x08, 0xa2, 0x11, 0xce, 0x8b, 0xac,
+ 0xce, 0xcc, 0x82, 0x13, 0xcc, 0xf8, 0x90, 0x00, 0xc6, 0xa2, 0x01, 0xcc, 0x8b, 0xa2,
+ 0x02, 0xcc, 0x8b, 0xb7, 0x00, 0x00, 0x0a, 0xac, 0xce, 0xcc, 0x82, 0x1a, 0xcc, 0xf8,
+ 0xa8, 0x0a, 0x96, 0x7e, 0xdc, 0x3c, 0xa2, 0x22, 0xce, 0x88, 0xc6, 0xa2, 0x02, 0xcc,
+ 0x8b, 0x90, 0x04, 0xa2, 0x01, 0xcc, 0x8b, 0xa2, 0x03, 0xcc, 0x8b, 0x82, 0x04, 0xcc,
+ 0xf8, 0xa9, 0x0a, 0x7f, 0x90, 0x00, 0xa2, 0x17, 0xce, 0x8b, 0x90, 0x00, 0xa2, 0x18,
+ 0xce, 0x8b, 0x00, 0xa2, 0x24, 0xce, 0xab, 0xa2, 0x26, 0xce, 0xab, 0x90, 0x04, 0xa2,
+ 0x28, 0xce, 0x8b, 0x90, 0x00, 0xa2, 0x29, 0xce, 0x8b, 0x90, 0x00, 0xa2, 0x2a, 0xce,
+ 0x8b, 0x90, 0x00, 0xa2, 0x2c, 0xce, 0xab, 0x00, 0xa2, 0x2e, 0xce, 0xab, 0xa2, 0x30,
+ 0xce, 0xab, 0xa2, 0x32, 0xce, 0xab, 0xa2, 0x34, 0xce, 0x8b, 0xa2, 0x35, 0xce, 0x8b,
+ 0xa2, 0x36, 0xce, 0x8b, 0xa2, 0x37, 0xce, 0x8b, 0xa2, 0x38, 0xce, 0x8b, 0xa2, 0x39,
+ 0xce, 0x8b, 0xa2, 0x3a, 0xce, 0x8b, 0xa2, 0x3b, 0xce, 0x8b, 0xa2, 0x3c, 0xce, 0x8b,
+ 0xa2, 0x3d, 0xce, 0x8b, 0xa2, 0x3e, 0xce, 0x8b, 0xa2, 0x3f, 0xce, 0x8b, 0xa2, 0x40,
+ 0xce, 0x8b, 0xa2, 0x41, 0xce, 0x8b, 0xa2, 0x42, 0xce, 0x8b, 0xa2, 0x44, 0xce, 0xab,
+ 0xa2, 0x46, 0xce, 0xab, 0xa2, 0x48, 0xce, 0xab, 0xa2, 0x4a, 0xce, 0xab, 0xa2, 0x4c,
+ 0xce, 0xab, 0x90, 0x00, 0xa2, 0x4e, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x50, 0xce, 0xab,
+ 0x90, 0x00, 0xa2, 0x54, 0xce, 0x8b, 0xa2, 0x55, 0xce, 0x8b, 0xa2, 0x57, 0xce, 0x8b,
+ 0xa2, 0x58, 0xce, 0x8b, 0xa2, 0x59, 0xce, 0x8b, 0xa2, 0x5a, 0xce, 0x8b, 0xa2, 0x5b,
+ 0xce, 0x8b, 0xa2, 0x5c, 0xce, 0x8b, 0xa2, 0x5d, 0xce, 0x8b, 0xa2, 0x5e, 0xce, 0x8b,
+ 0xa2, 0x5f, 0xce, 0x8b, 0xa2, 0x60, 0xce, 0x8b, 0xa2, 0x61, 0xce, 0x8b, 0xa2, 0x64,
+ 0xce, 0xab, 0x90, 0x00, 0xa2, 0x62, 0xce, 0xab, 0x90, 0x04, 0xa2, 0x66, 0xce, 0xab,
+ 0x00, 0xa2, 0x68, 0xce, 0xab, 0xa2, 0x6a, 0xce, 0xab, 0xa2, 0x6c, 0xce, 0xab, 0xa2,
+ 0x6e, 0xce, 0xab, 0xa2, 0x70, 0xce, 0xab, 0xa2, 0x72, 0xce, 0xab, 0xa2, 0x74, 0xce,
+ 0x8b, 0x90, 0x00, 0xa2, 0x76, 0xce, 0xab, 0x3c, 0xb0, 0xe4, 0x9c, 0xa2, 0x02, 0x72,
+ 0xab, 0x90, 0xa6, 0xa2, 0x04, 0x72, 0xab, 0x90, 0x00, 0xa2, 0x06, 0x72, 0xab, 0xb0,
+ 0xff, 0xff, 0xa2, 0x08, 0x72, 0xab, 0xb0, 0xff, 0xff, 0xa2, 0x36, 0x72, 0xab, 0x00,
+ 0xa2, 0x0a, 0x72, 0x8b, 0xa2, 0x0b, 0x72, 0x8b, 0xa2, 0x0c, 0x72, 0x8b, 0x90, 0x00,
+ 0xa2, 0x0d, 0x72, 0x8b, 0x00, 0xa2, 0x0e, 0x72, 0x8b, 0xa2, 0x0f, 0x72, 0x8b, 0x90,
+ 0x02, 0xa2, 0x10, 0x72, 0x8b, 0xa2, 0x11, 0x72, 0x8b, 0xa2, 0x12, 0x72, 0x8b, 0x90,
+ 0x01, 0xa2, 0x14, 0x72, 0x8b, 0x90, 0x00, 0xa2, 0x00, 0x72, 0x8b, 0xa2, 0x15, 0x72,
+ 0x8b, 0xa2, 0x16, 0x72, 0x8b, 0xa2, 0x18, 0x72, 0x8b, 0xa2, 0x1a, 0x72, 0x8b, 0xa2,
+ 0x1c, 0x72, 0x8b, 0x90, 0x02, 0xa2, 0x1e, 0x72, 0x8b, 0x90, 0x00, 0xa2, 0x20, 0x72,
+ 0xab, 0x90, 0x00, 0xa2, 0x22, 0x72, 0xab, 0x90, 0x01, 0xa2, 0x24, 0x72, 0x8b, 0xa2,
+ 0x25, 0x72, 0x8b, 0x90, 0x00, 0xa2, 0x26, 0x72, 0x8b, 0xa2, 0x27, 0x72, 0x8b, 0xa2,
+ 0x28, 0x72, 0xab, 0xa2, 0x2a, 0x72, 0xab, 0xa2, 0x2c, 0x72, 0xab, 0xa2, 0x2e, 0x72,
+ 0xab, 0xb7, 0x00, 0x00, 0x0c, 0xa8, 0x0c, 0x9c, 0x01, 0x56, 0xb5, 0xd7, 0x7d, 0xa8,
+ 0x0c, 0xa2, 0x22, 0x74, 0x8b, 0xac, 0x74, 0xce, 0x37, 0x28, 0xac, 0x74, 0xce, 0x36,
+ 0xc9, 0xa9, 0x0c, 0x7a, 0xb7, 0x00, 0x00, 0x0c, 0xa8, 0x0c, 0x96, 0x7e, 0xdc, 0x94,
+ 0x34, 0xb5, 0xd7, 0x84, 0xa8, 0x0c, 0xa2, 0x10, 0x78, 0xab, 0x00, 0xa2, 0x00, 0x78,
+ 0xab, 0xa2, 0x02, 0x78, 0xab, 0xa2, 0x04, 0x78, 0xab, 0xa2, 0x06, 0x78, 0xab, 0xa2,
+ 0x08, 0x78, 0xab, 0xa2, 0x0a, 0x78, 0xab, 0xa2, 0x0c, 0x78, 0xab, 0xa2, 0x0e, 0x78,
+ 0xab, 0x90, 0x00, 0xa2, 0x12, 0x78, 0xab, 0xa9, 0x0c, 0x95, 0x39, 0xb7, 0x00, 0x00,
+ 0x0c, 0xa8, 0x0c, 0x9c, 0x03, 0x56, 0xb5, 0xd7, 0x33, 0xa8, 0x0c, 0xa2, 0x22, 0x76,
+ 0x8b, 0xac, 0x76, 0xce, 0x36, 0x86, 0xac, 0x76, 0xce, 0x36, 0x19, 0xa9, 0x0c, 0x7a,
+ 0x00, 0x3c, 0xad, 0xca, 0x88, 0xc6, 0xa9, 0xca, 0xa9, 0xcc, 0xad, 0xca, 0x88, 0xc6,
+ 0xa9, 0xca, 0xa9, 0xcc, 0xad, 0xca, 0x88, 0xc6, 0xa9, 0xca, 0xa9, 0xcc, 0xad, 0xca,
+ 0x88, 0xc6, 0xa9, 0xca, 0xa9, 0xcc, 0xad, 0xca, 0x88, 0xc6, 0xa9, 0xca, 0xa9, 0xcc,
+ 0xad, 0xca, 0x88, 0xc6, 0x3c, 0x00, 0xb7, 0x00, 0x00, 0x2e, 0xb7, 0x00, 0x00, 0x30,
+ 0xb7, 0x00, 0x00, 0x2c, 0x91, 0x00, 0xb2, 0xce, 0x3e, 0x00, 0xe6, 0x82, 0x0e, 0xcc,
+ 0xf8, 0xa9, 0xca, 0x82, 0x10, 0xca, 0xfc, 0x6b, 0x3c, 0x91, 0x00, 0xb2, 0xce, 0x3e,
+ 0x00, 0xfc, 0xb6, 0xb8, 0x00, 0xa8, 0xa2, 0x0c, 0xcc, 0xfc, 0x4c, 0x82, 0x0e, 0xcc,
+ 0xf8, 0xa9, 0xca, 0x82, 0x10, 0xca, 0xfc, 0x75, 0x3c, 0xa0, 0x30, 0xca, 0xfc, 0x4c,
+ 0x00, 0xe6, 0xa2, 0x0c, 0xcc, 0xab, 0xa8, 0x2e, 0x05, 0xab, 0x2e, 0x7c, 0x00, 0xa2,
+ 0x06, 0xcc, 0xab, 0x95, 0x22, 0x83, 0x00, 0xb8, 0x00, 0xab, 0x48, 0xb7, 0x00, 0x00,
+ 0x02, 0xb7, 0x00, 0x00, 0x04, 0xab, 0x3c, 0xac, 0xce, 0x3e, 0xa8, 0xcc, 0xae, 0xce,
+ 0xb7, 0xff, 0xff, 0x38, 0xb7, 0xff, 0xff, 0x3a, 0x82, 0x00, 0x2e, 0xfd, 0x5e, 0x32,
+ 0x0f, 0x00, 0xab, 0x34, 0xab, 0x36, 0xb2, 0xce, 0x3e, 0xa0, 0x30, 0xcc, 0xf8, 0x94,
+ 0x52, 0xa2, 0x02, 0xcc, 0xa8, 0xab, 0x34, 0xa2, 0x04, 0xcc, 0xa8, 0xab, 0x36, 0x94,
+ 0x44, 0x31, 0xfe, 0xab, 0x34, 0xac, 0xcc, 0x36, 0x31, 0xea, 0xb2, 0xce, 0x3e, 0xa0,
+ 0x30, 0xcc, 0xf8, 0xa8, 0x36, 0xa2, 0x04, 0xcc, 0xfd, 0x95, 0x24, 0xa2, 0x04, 0xcc,
+ 0xfc, 0x41, 0x4e, 0xa8, 0x34, 0xa2, 0x02, 0xcc, 0xfd, 0x95, 0x32, 0xa2, 0x02, 0xcc,
+ 0xfc, 0x95, 0x38, 0xa2, 0x02, 0xcc, 0xa8, 0x02, 0x96, 0x34, 0xeb, 0xab, 0x34, 0xa2,
+ 0x04, 0xcc, 0xa8, 0x96, 0x36, 0xeb, 0xab, 0x36, 0x96, 0x37, 0x17, 0x95, 0x50, 0x97,
+ 0x00, 0x42, 0xa8, 0x02, 0x96, 0x04, 0xfa, 0x9d, 0x00, 0x97, 0x01, 0x42, 0xa8, 0xcc,
+ 0xae, 0xce, 0xab, 0xcc, 0x00, 0xe5, 0xab, 0x32, 0x82, 0x00, 0x42, 0xdd, 0x5d, 0xa8,
+ 0xcc, 0xae, 0xce, 0xab, 0xcc, 0x91, 0x00, 0xb2, 0xce, 0x3e, 0xb7, 0x00, 0x00, 0x40,
+ 0xa8, 0x32, 0x9c, 0x00, 0x94, 0x50, 0xa2, 0x0a, 0xcc, 0xfc, 0x94, 0x39, 0x94, 0x48,
+ 0xa8, 0x04, 0x9d, 0x00, 0x95, 0x21, 0x9c, 0x00, 0x41, 0x46, 0xa8, 0x02, 0x9d, 0x00,
+ 0x95, 0x2b, 0x90, 0x00, 0xaa, 0xc8, 0x62, 0xa8, 0x2c, 0x04, 0xbd, 0x7f, 0xff, 0x90,
+ 0x01, 0xab, 0x2c, 0xab, 0x04, 0xe6, 0xac, 0x06, 0x02, 0x82, 0xff, 0x02, 0xfa, 0xa8,
+ 0x3c, 0xb7, 0x00, 0x00, 0x06, 0xb5, 0xd7, 0x44, 0x97, 0x00, 0x42, 0x95, 0x52, 0xc4,
+ 0x9c, 0x00, 0x4d, 0x00, 0xc6, 0xa2, 0x06, 0xcc, 0x8b, 0xab, 0x32, 0xa8, 0x2e, 0x05,
+ 0xab, 0x2e, 0xc4, 0x9c, 0x00, 0x94, 0x9d, 0x02, 0xa2, 0x02, 0xcc, 0xa8, 0x96, 0x34,
+ 0xeb, 0xa2, 0x02, 0xcc, 0xab, 0xa2, 0x04, 0xcc, 0xa8, 0x96, 0x36, 0xeb, 0xa2, 0x04,
+ 0xcc, 0xab, 0xa2, 0x05, 0xcc, 0x17, 0x94, 0x38, 0xa2, 0x04, 0xcc, 0xa8, 0x9d, 0x00,
+ 0x4e, 0x9c, 0x00, 0x42, 0x94, 0x2c, 0xa2, 0x02, 0xcc, 0xa8, 0x9d, 0x00, 0x42, 0x94,
+ 0x23, 0x00, 0xdc, 0x4e, 0xa8, 0x3a, 0xa2, 0x04, 0xcc, 0xfd, 0x94, 0x4f, 0xa2, 0x04,
+ 0xcc, 0xfc, 0x94, 0x40, 0xa9, 0xca, 0x82, 0x10, 0xca, 0xfc, 0x94, 0x95, 0x82, 0x0e,
+ 0x40, 0xf8, 0x82, 0x0e, 0xcc, 0xf8, 0x95, 0xae, 0xa2, 0x0a, 0xcc, 0xa8, 0xaf, 0x04,
+ 0xab, 0x04, 0xa2, 0x06, 0xcc, 0xa8, 0xaf, 0x02, 0x9a, 0xff, 0xab, 0x02, 0xaf, 0x06,
+ 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x08, 0xcc, 0xa8, 0xb5, 0xd6, 0xb5, 0x3f, 0x06, 0x3f,
+ 0x02, 0x3f, 0x04, 0x00, 0xc6, 0xa8, 0x2e, 0x05, 0xab, 0x2e, 0x95, 0x4f, 0xa8, 0x38,
+ 0xa2, 0x02, 0xcc, 0xfd, 0x42, 0x95, 0x47, 0xac, 0x40, 0x30, 0xa2, 0x02, 0xcc, 0xa8,
+ 0xab, 0x38, 0xa2, 0x04, 0xcc, 0xa8, 0xab, 0x3a, 0x95, 0x58, 0x96, 0x42, 0xdc, 0x95,
+ 0x6e, 0xa8, 0x02, 0xa2, 0x02, 0xcc, 0xab, 0xa8, 0x04, 0xa2, 0x04, 0xcc, 0xab, 0x90,
+ 0x01, 0xc6, 0xa8, 0x06, 0xa2, 0x06, 0xcc, 0xab, 0xa8, 0x3c, 0xa2, 0x08, 0xcc, 0xab,
+ 0xa8, 0x2c, 0x04, 0xbd, 0x7f, 0xff, 0x90, 0x01, 0xab, 0x2c, 0xa2, 0x0a, 0xcc, 0xab,
+ 0xae, 0xce, 0xae, 0xcc, 0xae, 0xce, 0xe6, 0xae, 0xce, 0xab, 0xcc, 0xa8, 0x2e, 0x04,
+ 0xab, 0x2e, 0x00, 0x8b, 0x42, 0x95, 0xac, 0xa8, 0x2e, 0x9c, 0x00, 0x4e, 0xa8, 0x38,
+ 0x96, 0x3a, 0xfa, 0x9c, 0x00, 0x46, 0x30, 0x1c, 0xac, 0x3e, 0xce, 0x3c, 0xac, 0x3e,
+ 0xce, 0x3c, 0x83, 0x44, 0x01, 0x91, 0x8b, 0x40, 0x40, 0x83, 0xcc, 0x01, 0x91, 0x8b,
+ 0x40, 0x40, 0x83, 0x44, 0x01, 0x91, 0x8b, 0x3c, 0x34, 0x14, 0xa1, 0x38, 0x01, 0x8c,
+ 0xab, 0xa1, 0x3a, 0x01, 0x88, 0xab, 0x87, 0xff, 0xff, 0x01, 0x8a, 0xab, 0x87, 0xff,
+ 0xff, 0x01, 0x86, 0xab, 0x83, 0x01, 0x01, 0x91, 0x8b, 0x3c, 0x34, 0x32, 0x83, 0x00,
+ 0x01, 0x88, 0xab, 0x83, 0x00, 0x01, 0x8c, 0xab, 0x3c, 0xb6, 0x01, 0x8c, 0xa8, 0xa4,
+ 0x01, 0x88, 0xcc, 0xab, 0x3c, 0x82, 0x00, 0x2e, 0xfc, 0x94, 0xdc, 0xb2, 0xce, 0x3e,
+ 0xa0, 0x30, 0xcc, 0xf8, 0xa2, 0x02, 0xcc, 0xa8, 0xab, 0x34, 0xa2, 0x04, 0xcc, 0xa8,
+ 0xab, 0x36, 0xb7, 0xff, 0xff, 0x38, 0xb7, 0xff, 0xff, 0x3a, 0xb2, 0xce, 0x3e, 0x91,
+ 0x00, 0xb7, 0x00, 0x00, 0x40, 0x00, 0xdc, 0x94, 0x93, 0xa8, 0x36, 0xa2, 0x04, 0xcc,
+ 0xfd, 0x48, 0xa2, 0x04, 0xcc, 0xfc, 0x94, 0x37, 0x94, 0x43, 0xa2, 0x06, 0xcc, 0xa8,
+ 0x9c, 0x00, 0x94, 0x23, 0xac, 0x02, 0x3c, 0xac, 0x04, 0x3e, 0xa8, 0x50, 0xa2, 0x06,
+ 0xcc, 0xa8, 0x9a, 0xff, 0xab, 0x02, 0xa2, 0x0a, 0xcc, 0xa8, 0xab, 0x04, 0xa2, 0x08,
+ 0xcc, 0xa8, 0xb5, 0xd5, 0x87, 0xac, 0x3c, 0x02, 0xac, 0x3e, 0x04, 0x90, 0x00, 0xc6,
+ 0xa8, 0x2e, 0x05, 0xab, 0x2e, 0x94, 0x4f, 0xa8, 0x34, 0xa2, 0x02, 0xcc, 0xfd, 0x95,
+ 0x3b, 0xa2, 0x02, 0xcc, 0xfc, 0x95, 0x41, 0x02, 0xa2, 0x02, 0xcc, 0xa8, 0x96, 0x34,
+ 0xeb, 0xa2, 0x02, 0xcc, 0xab, 0xa2, 0x04, 0xcc, 0xa8, 0x96, 0x36, 0xeb, 0xa2, 0x04,
+ 0xcc, 0xab, 0xa2, 0x05, 0xcc, 0x17, 0x95, 0x5e, 0xa8, 0x3a, 0xa2, 0x04, 0xcc, 0xfd,
+ 0x4e, 0xa2, 0x04, 0xcc, 0xfc, 0x41, 0x57, 0xa8, 0x38, 0xa2, 0x02, 0xcc, 0xfd, 0x41,
+ 0x4f, 0xac, 0x40, 0x30, 0xa2, 0x02, 0xcc, 0xa8, 0xab, 0x38, 0xa2, 0x04, 0xcc, 0xa8,
+ 0xab, 0x3a, 0xa9, 0xca, 0x82, 0x10, 0xca, 0xfc, 0x4a, 0x82, 0x0e, 0x40, 0xf8, 0x82,
+ 0x0e, 0xcc, 0xf8, 0x95, 0xa6, 0xa8, 0x2e, 0x9c, 0x00, 0x4b, 0xa8, 0x3a, 0x96, 0x38,
+ 0xfa, 0x9c, 0x00, 0x43, 0x35, 0x14, 0x3c, 0x35, 0x2b, 0x3c, 0xb6, 0xf9, 0xfa, 0xa8,
+ 0xb6, 0xf9, 0xfc, 0xfc, 0x5d, 0xb6, 0xf9, 0xfa, 0xa9, 0xb3, 0x01, 0x84, 0xf2, 0xf6,
+ 0xb3, 0x01, 0x90, 0x90, 0x40, 0xd6, 0x40, 0x40, 0x90, 0xc8, 0xd6, 0x40, 0x40, 0x97,
+ 0xdf, 0xd2, 0x90, 0x98, 0xd6, 0x3c, 0xb6, 0xf9, 0xfe, 0x88, 0x96, 0xc8, 0x10, 0x47,
+ 0x30, 0x0a, 0x91, 0x01, 0xb4, 0x85, 0x58, 0x99, 0xfe, 0x9c, 0xa4, 0x51, 0xb3, 0x01,
+ 0x90, 0x90, 0x40, 0xd6, 0x40, 0x40, 0x90, 0xc8, 0xd6, 0x40, 0x40, 0x97, 0xdf, 0xd2,
+ 0x3c, 0x00, 0xb6, 0xf9, 0xfa, 0xab, 0x34, 0x43, 0x95, 0x22, 0xb6, 0xff, 0x20, 0xa9,
+ 0x90, 0x01, 0xb6, 0x10, 0x00, 0x8e, 0x8b, 0x03, 0xaf, 0xcc, 0xaf, 0xce, 0xa2, 0x0a,
+ 0x72, 0x88, 0x9d, 0x01, 0x53, 0xa2, 0x12, 0x72, 0x88, 0xb5, 0xd2, 0xfa, 0xac, 0x76,
+ 0xce, 0xa2, 0x22, 0xce, 0x88, 0xb2, 0x20, 0x00, 0x94, 0x30, 0xa2, 0x10, 0x72, 0x88,
+ 0xb5, 0xd2, 0xe7, 0xac, 0x76, 0xce, 0xa2, 0x22, 0xce, 0x88, 0xb2, 0x28, 0x00, 0x9c,
+ 0x00, 0xb2, 0x20, 0x00, 0xaf, 0xcc, 0xa2, 0x11, 0x72, 0x88, 0xb5, 0xd2, 0xcf, 0xac,
+ 0x76, 0xce, 0xa2, 0x22, 0xce, 0x88, 0xb3, 0x28, 0x00, 0x9c, 0x00, 0xb3, 0x20, 0x00,
+ 0x3f, 0xcc, 0x90, 0x52, 0xa2, 0x02, 0xcc, 0x8e, 0x8b, 0x02, 0xa2, 0x0a, 0x72, 0x88,
+ 0x9c, 0x01, 0x48, 0x90, 0x10, 0xa2, 0x02, 0xce, 0x8e, 0xab, 0x04, 0xc4, 0x9a, 0x04,
+ 0xc6, 0x90, 0x00, 0xa2, 0x08, 0xcc, 0x8e, 0xab, 0x06, 0x31, 0x03, 0x9c, 0x00, 0x94,
+ 0xa4, 0xa2, 0x0a, 0x72, 0x88, 0x9c, 0x01, 0x52, 0x90, 0xdb, 0xa2, 0x02, 0xcc, 0x8e,
+ 0x90, 0x52, 0xa2, 0x02, 0xce, 0x8e, 0x30, 0xea, 0x9c, 0x00, 0x94, 0xac, 0x33, 0xf8,
+ 0x90, 0x10, 0xb6, 0x10, 0x04, 0x8b, 0x91, 0x00, 0xb6, 0x10, 0x20, 0x88, 0x99, 0x08,
+ 0x9c, 0x00, 0x94, 0x60, 0x91, 0x00, 0xb6, 0x10, 0x24, 0x88, 0x99, 0x20, 0x9c, 0x00,
+ 0x94, 0x5a, 0x90, 0x08, 0xb6, 0x10, 0x04, 0x8b, 0x91, 0x00, 0xb6, 0x10, 0x20, 0x88,
+ 0x99, 0x04, 0x9c, 0x00, 0x94, 0x3c, 0x91, 0x00, 0xb6, 0x10, 0x24, 0x88, 0x99, 0x02,
+ 0x9c, 0x00, 0x94, 0x2a, 0xc4, 0x99, 0xfb, 0xc6, 0xa8, 0x06, 0xa2, 0x08, 0xcc, 0x8e,
+ 0xa2, 0x0a, 0x72, 0x88, 0x9c, 0x01, 0x46, 0xa8, 0x04, 0xa2, 0x02, 0xce, 0x8e, 0x88,
+ 0x02, 0xa2, 0x02, 0xcc, 0x8e, 0x88, 0x03, 0xb6, 0x10, 0x00, 0x8b, 0x90, 0x01, 0x3f,
+ 0xce, 0x3f, 0xcc, 0x3c, 0x30, 0x16, 0x94, 0x4c, 0x95, 0x38, 0x30, 0x10, 0x94, 0x46,
+ 0x95, 0x4a, 0x30, 0x0a, 0x94, 0x40, 0x95, 0x6e, 0x30, 0x04, 0x94, 0x3a, 0x95, 0x68,
+ 0xae, 0xca, 0x05, 0xae, 0xca, 0x82, 0x00, 0xca, 0xfc, 0x3c, 0x3d, 0xa2, 0x0a, 0x72,
+ 0x88, 0x9c, 0x01, 0x4d, 0xa2, 0x10, 0x72, 0x88, 0xb5, 0xd1, 0xe3, 0x00, 0xa2, 0x0e,
+ 0x76, 0xab, 0x5e, 0xa2, 0x12, 0x72, 0x88, 0xb5, 0xd1, 0xd6, 0x00, 0xa2, 0x0e, 0x76,
+ 0xab, 0x51, 0xa2, 0x11, 0x72, 0x88, 0xb5, 0xd1, 0xc9, 0x00, 0xa2, 0x0e, 0x76, 0xab,
+ 0x00, 0xa2, 0x00, 0x74, 0xab, 0xc4, 0x99, 0xfb, 0xc6, 0xa8, 0x06, 0xa2, 0x08, 0xcc,
+ 0x8e, 0xa2, 0x0a, 0x72, 0x88, 0x9c, 0x01, 0x46, 0xa8, 0x04, 0xa2, 0x02, 0xce, 0x8e,
+ 0x88, 0x02, 0xa2, 0x02, 0xcc, 0x8e, 0x88, 0x03, 0xb6, 0x10, 0x00, 0x8b, 0x00, 0x3f,
+ 0xce, 0x3f, 0xcc, 0x3c, 0x33, 0x12, 0x91, 0x00, 0xae, 0xca, 0xaa, 0xc8, 0x41, 0x3c,
+ 0xae, 0xca, 0xb6, 0x10, 0x1c, 0x88, 0x96, 0xc8, 0x17, 0x41, 0x70, 0x9a, 0x01, 0x3c,
+ 0xaf, 0xcc, 0x9c, 0x00, 0x94, 0x21, 0xb2, 0x20, 0x2e, 0x30, 0x0f, 0xa2, 0x0a, 0x72,
+ 0x88, 0x9c, 0x01, 0x45, 0xb2, 0x28, 0x2e, 0x30, 0x03, 0x3f, 0xcc, 0x3c, 0x90, 0x40,
+ 0x01, 0xd9, 0xc6, 0xa8, 0xe2, 0xba, 0x02, 0x00, 0xab, 0xe2, 0x3c, 0xb2, 0x20, 0x2e,
+ 0x30, 0x0f, 0xa2, 0x0a, 0x72, 0x88, 0x9c, 0x01, 0x7b, 0xb2, 0x28, 0x2e, 0x30, 0x03,
+ 0x3f, 0xcc, 0x3c, 0x90, 0x40, 0xda, 0xc6, 0xa8, 0xe2, 0xb9, 0xfd, 0xff, 0xab, 0xe2,
+ 0x3c, 0xb2, 0x20, 0x2e, 0xc4, 0x01, 0x99, 0x01, 0x3c, 0xaf, 0xcc, 0xab, 0xca, 0xa2,
+ 0x22, 0xce, 0x88, 0xb2, 0x28, 0x00, 0x9c, 0x00, 0xb2, 0x20, 0x00, 0x82, 0x02, 0xcc,
+ 0xf8, 0x82, 0x00, 0xca, 0xfc, 0x50, 0x82, 0x01, 0xca, 0xfc, 0x4e, 0x82, 0x02, 0xca,
+ 0xfc, 0x4c, 0x90, 0x00, 0xc5, 0x3f, 0xcc, 0x3c, 0x90, 0x53, 0x66, 0x90, 0x71, 0x69,
+ 0x90, 0x71, 0x6c, 0xaf, 0xcc, 0xab, 0xca, 0xa2, 0x22, 0xce, 0x88, 0xb2, 0x28, 0x00,
+ 0x9c, 0x00, 0xb2, 0x20, 0x00, 0x82, 0x02, 0xcc, 0xf8, 0xa2, 0x0a, 0x72, 0x88, 0x9d,
+ 0x01, 0x55, 0x82, 0x00, 0xca, 0xfc, 0x4a, 0x82, 0x08, 0xca, 0xfc, 0x48, 0x41, 0xc5,
+ 0x3f, 0xcc, 0x3c, 0x90, 0x5e, 0x66, 0x90, 0x61, 0x69, 0x82, 0x00, 0xca, 0xfc, 0x94,
+ 0x26, 0x82, 0x05, 0xca, 0xfc, 0x94, 0x25, 0x82, 0x09, 0xca, 0xfc, 0x94, 0x24, 0x82,
+ 0x06, 0xca, 0xfc, 0x94, 0x23, 0x82, 0x0a, 0xca, 0xfc, 0x5e, 0x82, 0x07, 0xca, 0xfc,
+ 0x94, 0x22, 0x82, 0x0c, 0xca, 0xfc, 0x94, 0x21, 0x3f, 0xcc, 0x3c, 0x91, 0x52, 0x90,
+ 0xdb, 0x5d, 0x90, 0x61, 0x91, 0x10, 0x58, 0x90, 0xf9, 0x91, 0x52, 0x53, 0x90, 0xdb,
+ 0x91, 0x61, 0x4e, 0x90, 0xdb, 0x91, 0xed, 0x49, 0x90, 0xdb, 0x91, 0x61, 0x44, 0x90,
+ 0xf9, 0x91, 0x61, 0xb2, 0x20, 0x02, 0xc5, 0xb2, 0x28, 0x02, 0xa8, 0xca, 0xc5, 0x3f,
+ 0xcc, 0x3c, 0xab, 0xca, 0xaf, 0xcc, 0xa2, 0x22, 0xce, 0x88, 0xb2, 0x28, 0x00, 0x9c,
+ 0x00, 0xb2, 0x20, 0x00, 0x82, 0x00, 0xcc, 0xf8, 0x82, 0x00, 0xca, 0xfc, 0x47, 0xc4,
+ 0x9a, 0x20, 0xc6, 0x3f, 0xcc, 0x3c, 0x90, 0x20, 0x01, 0xd9, 0xc6, 0x3f, 0xcc, 0x3c,
+ 0xab, 0xca, 0xaf, 0xcc, 0xa2, 0x22, 0xce, 0x88, 0xb2, 0x28, 0x00, 0x9c, 0x00, 0xb2,
+ 0x20, 0x00, 0xa8, 0xca, 0x99, 0x08, 0x9c, 0x00, 0x4b, 0xa2, 0x18, 0xcc, 0x88, 0x9a,
+ 0x40, 0xa2, 0x18, 0xcc, 0x8b, 0x4b, 0x90, 0x40, 0x01, 0xa2, 0x18, 0xcc, 0xd9, 0xa2,
+ 0x18, 0xcc, 0x8b, 0x94, 0x30, 0xae, 0xca, 0x91, 0x00, 0xaf, 0xc8, 0x99, 0x01, 0x9d,
+ 0x00, 0x82, 0x02, 0xca, 0xfa, 0x3f, 0xc8, 0xaf, 0xc8, 0x99, 0x02, 0x9d, 0x00, 0x82,
+ 0x04, 0xca, 0xfa, 0x3f, 0xc8, 0xaf, 0xc8, 0x99, 0x04, 0x9d, 0x00, 0x82, 0x08, 0xca,
+ 0xfa, 0x3f, 0xc8, 0x99, 0x10, 0x9d, 0x00, 0x82, 0x20, 0xca, 0xfa, 0xa2, 0x16, 0xcc,
+ 0x88, 0x3f, 0xcc, 0x3c, 0xaf, 0xcc, 0xe7, 0xab, 0xca, 0x82, 0x68, 0xca, 0xf8, 0xb2,
+ 0x28, 0x00, 0x9c, 0x00, 0xb2, 0x20, 0x00, 0x00, 0xa2, 0x12, 0xcc, 0x8e, 0x8b, 0x10,
+ 0x00, 0xa2, 0x14, 0xcc, 0x8e, 0x8b, 0x11, 0x96, 0x10, 0x11, 0x96, 0x10, 0x0a, 0x82,
+ 0x2e, 0x10, 0xd9, 0x82, 0x40, 0x11, 0xd9, 0x88, 0x10, 0x96, 0x11, 0xda, 0xad, 0xca,
+ 0xfa, 0xab, 0x12, 0xa2, 0x10, 0xcc, 0x88, 0x99, 0x07, 0x9c, 0x01, 0xa2, 0x10, 0xcc,
+ 0x88, 0x99, 0x07, 0x9c, 0x01, 0x4e, 0x9c, 0x02, 0x51, 0x9c, 0x06, 0x4e, 0x9c, 0x04,
+ 0x48, 0x9c, 0x05, 0x4b, 0x00, 0x4f, 0x90, 0x40, 0x49, 0x90, 0x08, 0x46, 0x90, 0x02,
+ 0x43, 0x90, 0x04, 0x40, 0xad, 0xca, 0xab, 0xa0, 0xc8, 0x12, 0xfa, 0xad, 0xca, 0xa8,
+ 0x99, 0x02, 0x9c, 0x00, 0x55, 0x90, 0x03, 0x01, 0xab, 0xca, 0xa2, 0x16, 0xcc, 0x88,
+ 0x96, 0xca, 0xf9, 0xa2, 0x16, 0xcc, 0x8b, 0x3f, 0xcc, 0xa8, 0x12, 0x3c, 0xa2, 0x16,
+ 0xcc, 0x88, 0x9a, 0x03, 0x6f, 0xaf, 0xcc, 0xb2, 0x28, 0x00, 0x9c, 0x00, 0xb2, 0x20,
+ 0x00, 0x82, 0x02, 0xca, 0xfc, 0x58, 0x82, 0x04, 0xca, 0xfc, 0x56, 0x82, 0x01, 0xca,
+ 0xfc, 0x54, 0x82, 0x08, 0xca, 0xfc, 0x52, 0x86, 0x01, 0x00, 0xca, 0xfc, 0x4f, 0x3f,
+ 0xcc, 0x3c, 0x90, 0x06, 0x4b, 0x90, 0x05, 0x48, 0x90, 0x01, 0x45, 0x90, 0x04, 0x42,
+ 0x90, 0x00, 0xab, 0xca, 0x9c, 0x06, 0x30, 0x17, 0x90, 0x07, 0x01, 0xa2, 0x08, 0xcc,
+ 0xd9, 0x96, 0xca, 0xfa, 0xa2, 0x08, 0xcc, 0x8b, 0x82, 0x06, 0xca, 0xfc, 0x30, 0x08,
+ 0x3f, 0xcc, 0x3c, 0xc4, 0x9a, 0x40, 0xc6, 0x3c, 0x90, 0x40, 0x01, 0xd9, 0xc6, 0x3c,
+ 0xaf, 0xcc, 0xb2, 0x10, 0x00, 0xab, 0xca, 0xa2, 0x22, 0xcc, 0x88, 0x8b, 0x12, 0xa2,
+ 0x26, 0xcc, 0x88, 0x8b, 0x10, 0xa2, 0x2a, 0xcc, 0x88, 0x8b, 0x11, 0x82, 0x03, 0x12,
+ 0xda, 0x90, 0x20, 0x01, 0xa0, 0xc8, 0x10, 0xd9, 0x82, 0x00, 0xca, 0xfd, 0x82, 0x20,
+ 0x10, 0xda, 0x90, 0x02, 0x01, 0xa0, 0xc8, 0x10, 0xd9, 0x82, 0x00, 0xca, 0xfd, 0x82,
+ 0x02, 0x10, 0xda, 0x90, 0x01, 0x01, 0xa0, 0xc8, 0x10, 0xd9, 0x82, 0x00, 0xca, 0xfd,
+ 0x82, 0x01, 0x10, 0xda, 0x90, 0x10, 0x01, 0xa0, 0xc8, 0x11, 0xd9, 0x82, 0x00, 0xca,
+ 0xfd, 0x82, 0x10, 0x11, 0xda, 0x00, 0xa2, 0x24, 0xcc, 0x8e, 0x00, 0xa2, 0x28, 0xcc,
+ 0x8e, 0x88, 0x12, 0xa2, 0x22, 0xcc, 0x8b, 0x88, 0x10, 0xa2, 0x26, 0xcc, 0x8b, 0x88,
+ 0x11, 0xa2, 0x2a, 0xcc, 0x8b, 0x3f, 0xcc, 0x3c, 0xaf, 0xcc, 0x90, 0x04, 0xb2, 0x10,
+ 0x04, 0xc6, 0xc4, 0x96, 0xc8, 0x12, 0x64, 0x3f, 0xcc, 0x3c, 0xaf, 0xcc, 0x90, 0x04,
+ 0xb2, 0x10, 0x04, 0xc6, 0xc4, 0x96, 0xc8, 0x12, 0x64, 0xb6, 0x10, 0x00, 0x88, 0x9a,
+ 0x01, 0xb6, 0x10, 0x00, 0x8b, 0x90, 0x10, 0xc6, 0x3f, 0xcc, 0x3c, 0xc4, 0xa2, 0x01,
+ 0xcc, 0xda, 0xa2, 0x02, 0xcc, 0xda, 0xa2, 0x03, 0xcc, 0xda, 0xa2, 0x04, 0xcc, 0xda,
+ 0xa2, 0x05, 0xcc, 0xda, 0x9c, 0x00, 0x94, 0x85, 0xc4, 0xb6, 0xfe, 0xe0, 0x8b, 0xa2,
+ 0x01, 0xcc, 0x88, 0xb6, 0xfe, 0xe1, 0x8b, 0xa2, 0x02, 0xcc, 0x88, 0xb6, 0xfe, 0xe2,
+ 0x8b, 0xa2, 0x03, 0xcc, 0x88, 0xb6, 0xfe, 0xe3, 0x8b, 0xa2, 0x04, 0xcc, 0x88, 0xb6,
+ 0xfe, 0xe4, 0x8b, 0xa2, 0x05, 0xcc, 0x88, 0xb6, 0xfe, 0xe5, 0x8b, 0xa8, 0x02, 0xb6,
+ 0xfe, 0xe6, 0x8b, 0xa8, 0x0a, 0xb6, 0xfe, 0xe7, 0x8b, 0xa8, 0x04, 0xb6, 0xfe, 0xe8,
+ 0xab, 0xa8, 0x06, 0xb6, 0xfe, 0xea, 0xab, 0xa8, 0x08, 0xb6, 0xfe, 0xec, 0xab, 0x3c,
+ 0xab, 0x6e, 0xae, 0xca, 0xab, 0x70, 0x91, 0x10, 0xb4, 0x80, 0x06, 0xb6, 0x11, 0x26,
+ 0x88, 0x8b, 0x6c, 0xa8, 0xcc, 0xb6, 0x11, 0x26, 0x8b, 0x3c, 0xb6, 0x10, 0x3a, 0x88,
+ 0x8b, 0x6d, 0xa8, 0xcc, 0xb6, 0x10, 0x3a, 0x8b, 0x3c, 0xab, 0xca, 0x90, 0x08, 0x01,
+ 0xb6, 0x10, 0x02, 0xd9, 0x82, 0x00, 0xca, 0xfd, 0x9a, 0x08, 0xb6, 0x10, 0x02, 0x8b,
+ 0x3c, 0x82, 0x00, 0x7d, 0xdc, 0x3c, 0x00, 0x8b, 0x7d, 0x34, 0xd3, 0xb6, 0x10, 0x00,
+ 0x88, 0x99, 0xfe, 0xb6, 0x10, 0x00, 0x8b, 0x00, 0x80, 0x6c, 0xcc, 0xab, 0x34, 0x47,
+ 0x80, 0x6d, 0xcc, 0xab, 0x34, 0x40, 0x00, 0x34, 0x36, 0xb6, 0x10, 0x00, 0x88, 0x9a,
+ 0x01, 0xb6, 0x10, 0x00, 0x8b, 0x34, 0xf9, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00,
+ 0x04, 0xb7, 0x00, 0x00, 0x06, 0xac, 0x74, 0xcc, 0x82, 0x34, 0xcc, 0xf8, 0x00, 0xb5,
+ 0xf6, 0xe1, 0x91, 0x20, 0xb4, 0x7f, 0x8c, 0x90, 0x04, 0x3c, 0xb6, 0x10, 0x00, 0x88,
+ 0xb7, 0x00, 0x00, 0x12, 0x96, 0xc8, 0x10, 0x30, 0x40, 0xb1, 0x10, 0x80, 0xc4, 0xad,
+ 0xca, 0x8b, 0xa2, 0x01, 0xcc, 0x88, 0xa2, 0x02, 0xca, 0x8b, 0xa2, 0x02, 0xcc, 0x88,
+ 0xa2, 0x04, 0xca, 0x8b, 0xa2, 0x03, 0xcc, 0x88, 0xa2, 0x06, 0xca, 0x8b, 0xa2, 0x04,
+ 0xcc, 0x88, 0xa2, 0x08, 0xca, 0x8b, 0xa2, 0x05, 0xcc, 0x88, 0xa2, 0x0a, 0xca, 0x8b,
+ 0x82, 0x00, 0x12, 0xfc, 0x4a, 0xb6, 0x10, 0x00, 0x88, 0x9a, 0x01, 0xb6, 0x10, 0x00,
+ 0x8b, 0x00, 0x3c, 0xb6, 0x10, 0x00, 0x88, 0x99, 0xfe, 0xb6, 0x10, 0x00, 0x8b, 0xb7,
+ 0x00, 0x01, 0x12, 0x3c, 0x3c, 0xaf, 0xcc, 0xb2, 0x10, 0x00, 0xc4, 0xab, 0xca, 0x82,
+ 0x01, 0x02, 0xfc, 0x94, 0x25, 0x82, 0x00, 0x7d, 0xdd, 0x46, 0xa8, 0xca, 0xc6, 0x3f,
+ 0xcc, 0x3c, 0xaf, 0xca, 0x34, 0xc7, 0x3f, 0xca, 0x90, 0x40, 0x01, 0xb6, 0x10, 0x2a,
+ 0xd9, 0xb6, 0x10, 0x2a, 0x8b, 0x90, 0x01, 0x01, 0x96, 0xca, 0xf9, 0xab, 0xca, 0x7f,
+ 0x00, 0xc5, 0x40, 0xad, 0x0a, 0xa8, 0xb6, 0xda, 0xd6, 0xab, 0xa2, 0xa6, 0xcc, 0x8b,
+ 0x8c, 0xc9, 0xc8, 0xa2, 0xa4, 0xcc, 0x8b, 0xa2, 0x02, 0x0a, 0xa8, 0xb6, 0xda, 0xd8,
+ 0xab, 0xa2, 0xa2, 0xcc, 0x8b, 0x8c, 0xc9, 0xc8, 0xa2, 0xa0, 0xcc, 0x8b, 0xb7, 0x02,
+ 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xa6, 0x01, 0x2e, 0xcc, 0x88, 0x9c, 0x00, 0x4e,
+ 0xae, 0x02, 0x03, 0xe7, 0xae, 0x02, 0xae, 0x04, 0xf7, 0xae, 0x04, 0xaa, 0xc8, 0x6d,
+ 0xa8, 0x02, 0x01, 0x03, 0xb8, 0x00, 0x01, 0xab, 0x02, 0xa8, 0x04, 0x01, 0x82, 0x00,
+ 0xc8, 0xe8, 0xab, 0x04, 0xa2, 0x02, 0x06, 0xfd, 0x94, 0x61, 0xa2, 0x02, 0x06, 0xfc,
+ 0x94, 0x53, 0xad, 0x06, 0xa8, 0x01, 0x03, 0xb8, 0x00, 0x01, 0xab, 0x02, 0xa2, 0x02,
+ 0x06, 0xa8, 0x01, 0x82, 0x00, 0xc8, 0xe8, 0xab, 0x04, 0xb7, 0x00, 0x00, 0x06, 0x82,
+ 0x0f, 0x06, 0xfc, 0x94, 0x2c, 0xb7, 0x02, 0x00, 0x10, 0xb7, 0x00, 0x00, 0x12, 0xa8,
+ 0x06, 0xae, 0x10, 0x03, 0xe7, 0xae, 0x10, 0xae, 0x12, 0xf7, 0xae, 0x12, 0xaa, 0xc8,
+ 0x6d, 0xa0, 0x04, 0x12, 0xfd, 0x4f, 0xa0, 0x04, 0x12, 0xfc, 0x44, 0xa9, 0x06, 0x95,
+ 0x2a, 0xa0, 0x02, 0x10, 0xfd, 0x41, 0x69, 0xa8, 0x06, 0xa6, 0x01, 0x2e, 0xcc, 0x8b,
+ 0x48, 0xa8, 0x02, 0xad, 0x06, 0xfd, 0x42, 0x95, 0x59, 0xb7, 0x02, 0x00, 0x10, 0xb7,
+ 0x00, 0x00, 0x12, 0xa6, 0x01, 0x26, 0xcc, 0x88, 0x9c, 0x00, 0x4e, 0xae, 0x10, 0x03,
+ 0xe7, 0xae, 0x10, 0xae, 0x12, 0xf7, 0xae, 0x12, 0xaa, 0xc8, 0x6d, 0xa8, 0x10, 0x03,
+ 0x01, 0xb8, 0x00, 0x01, 0xab, 0x10, 0xa8, 0x12, 0x01, 0x82, 0x00, 0xc8, 0xe8, 0xab,
+ 0x12, 0xa2, 0x02, 0x08, 0xfd, 0x94, 0x58, 0xa2, 0x02, 0x08, 0xfc, 0x94, 0x59, 0xad,
+ 0x08, 0xa8, 0x03, 0x01, 0xb8, 0x00, 0x01, 0xab, 0x02, 0xa2, 0x02, 0x08, 0xa8, 0x01,
+ 0x82, 0x00, 0xc8, 0xe8, 0xab, 0x04, 0xb7, 0x00, 0x00, 0x0a, 0x82, 0x0f, 0x0a, 0xfc,
+ 0x94, 0x2c, 0xb7, 0x02, 0x00, 0x06, 0xb7, 0x00, 0x00, 0x08, 0xa8, 0x0a, 0xae, 0x06,
+ 0x03, 0xe7, 0xae, 0x06, 0xae, 0x08, 0xf7, 0xae, 0x08, 0xaa, 0xc8, 0x6d, 0xa0, 0x04,
+ 0x08, 0xfd, 0x4f, 0xa0, 0x04, 0x08, 0xfc, 0x44, 0xa9, 0x0a, 0x95, 0x2a, 0xa0, 0x02,
+ 0x06, 0xfd, 0x41, 0x69, 0xa8, 0x0a, 0xa6, 0x01, 0x26, 0xcc, 0x8b, 0x82, 0x01, 0xca,
+ 0xfa, 0xb4, 0xfe, 0x8c, 0xa8, 0x10, 0xad, 0x08, 0xfd, 0x6c, 0x95, 0x5f, 0x90, 0x00,
+ 0x3c, 0xaf, 0xcc, 0xab, 0xca, 0xa2, 0x22, 0xce, 0x88, 0xe7, 0xb8, 0x00, 0x54, 0xae,
+ 0xca, 0xad, 0xca, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xb2, 0x28, 0x00, 0x9c, 0x00, 0xb2,
+ 0x20, 0x00, 0xad, 0xca, 0xa8, 0x9d, 0xff, 0x90, 0xff, 0xa2, 0x2a, 0xcc, 0x8b, 0xab,
+ 0xcc, 0xad, 0xca, 0xa8, 0x02, 0x96, 0xcc, 0xeb, 0xad, 0xca, 0xab, 0x3f, 0xcc, 0x3c,
+ 0xaf, 0xcc, 0xa2, 0x22, 0xce, 0x88, 0xb2, 0x28, 0x00, 0x9c, 0x00, 0xb2, 0x20, 0x00,
+ 0xa2, 0x2c, 0xcc, 0x88, 0xab, 0xca, 0xa2, 0x22, 0xce, 0x88, 0xe7, 0xb8, 0x00, 0x54,
+ 0xad, 0xc8, 0xa8, 0x96, 0xca, 0xf8, 0x3f, 0xcc, 0x3c, 0xaf, 0xcc, 0xab, 0xca, 0xa2,
+ 0x22, 0xce, 0x88, 0xb2, 0x28, 0x00, 0x9c, 0x00, 0xb2, 0x20, 0x00, 0x82, 0x01, 0xca,
+ 0xfc, 0x5c, 0x90, 0x10, 0x82, 0x00, 0xca, 0xfc, 0x4b, 0xa2, 0x06, 0xcc, 0xda, 0xa2,
+ 0x06, 0xcc, 0x8b, 0x3f, 0xcc, 0x3c, 0x01, 0xa2, 0x06, 0xcc, 0xd9, 0xa2, 0x06, 0xcc,
+ 0x8b, 0x6c, 0xa2, 0x2a, 0xcc, 0x88, 0xa2, 0x2a, 0xcc, 0x8b, 0x90, 0x10, 0x01, 0xa2,
+ 0x04, 0xcc, 0xd9, 0xa2, 0x04, 0xcc, 0x8b, 0x95, 0x2f, 0x3c, 0x3c, 0xaf, 0xcc, 0xab,
+ 0xca, 0xb7, 0x00, 0x00, 0x10, 0xb7, 0x00, 0x00, 0x12, 0x80, 0x7e, 0x12, 0xfc, 0x94,
+ 0x4f, 0xa8, 0x12, 0xb5, 0xca, 0xbe, 0xa2, 0x4e, 0x76, 0xa8, 0x9c, 0x08, 0x43, 0xa9,
+ 0x12, 0x74, 0xa2, 0x64, 0x76, 0xa8, 0x96, 0xca, 0xfc, 0x4f, 0x90, 0x02, 0x96, 0xca,
+ 0xfd, 0x41, 0x71, 0xa2, 0x62, 0x76, 0xa8, 0x9c, 0x04, 0x41, 0x79, 0x91, 0x01, 0xa2,
+ 0x22, 0x76, 0x88, 0xb5, 0xfb, 0x3d, 0xb7, 0x15, 0x5c, 0x02, 0xb7, 0x00, 0x00, 0x04,
+ 0xb7, 0x02, 0x00, 0x06, 0xac, 0x76, 0xcc, 0x82, 0x4c, 0xcc, 0xf8, 0xa2, 0x22, 0x76,
+ 0x88, 0xb5, 0xf3, 0xeb, 0xb7, 0x00, 0x01, 0x10, 0x95, 0x41, 0x82, 0x00, 0x10, 0xfd,
+ 0x4c, 0x00, 0xb5, 0xca, 0x57, 0xa2, 0x54, 0x74, 0xa8, 0x96, 0xca, 0xfc, 0x43, 0x3f,
+ 0xcc, 0x3c, 0xb5, 0xfb, 0xeb, 0x66, 0x97, 0x00, 0xd0, 0x83, 0x44, 0x01, 0x91, 0x8b,
+ 0x40, 0x40, 0x83, 0xcc, 0x01, 0x91, 0x8b, 0x83, 0x00, 0x01, 0x86, 0xab, 0x83, 0x00,
+ 0x01, 0x8a, 0xab, 0x83, 0x00, 0x01, 0x88, 0xab, 0x83, 0x00, 0x01, 0x8c, 0xab, 0x83,
+ 0x44, 0x01, 0x91, 0x8b, 0x96, 0xd0, 0x0d, 0xa8, 0xbe, 0x99, 0x03, 0x9c, 0x00, 0x94,
+ 0x4c, 0xb2, 0x10, 0x00, 0x00, 0xc5, 0xb2, 0x10, 0x04, 0x90, 0x04, 0xc6, 0xc4, 0x96,
+ 0xc8, 0x12, 0x64, 0xb2, 0x10, 0x5e, 0x90, 0x0b, 0xc5, 0xb2, 0x10, 0x22, 0x90, 0x03,
+ 0xc5, 0xb2, 0x10, 0x26, 0x90, 0x23, 0xc5, 0xb2, 0x10, 0x2a, 0x90, 0x10, 0xc5, 0xb2,
+ 0x10, 0x3a, 0x90, 0x3f, 0xc5, 0xb2, 0x10, 0x32, 0x90, 0x20, 0xc5, 0x91, 0x20, 0xb2,
+ 0x11, 0x7e, 0x00, 0xc5, 0x02, 0x82, 0x02, 0xcc, 0xeb, 0xaa, 0xca, 0x69, 0xb2, 0x10,
+ 0x00, 0x90, 0x01, 0xc6, 0x96, 0xd0, 0x0a, 0xb0, 0x07, 0xef, 0x9e, 0x32, 0x03, 0xae,
+ 0xce, 0xd7, 0xae, 0xce, 0xd7, 0x03, 0xae, 0xce, 0xd7, 0xae, 0xce, 0xd7, 0xab, 0x58,
+ 0xa8, 0xbe, 0x99, 0x0c, 0xc7, 0xc7, 0x99, 0x03, 0x04, 0xab, 0xca, 0xb2, 0x20, 0x00,
+ 0x96, 0xd0, 0x1b, 0x96, 0xd0, 0x1c, 0x97, 0xe7, 0xd2, 0xb7, 0x00, 0x00, 0x02, 0xac,
+ 0xca, 0x04, 0xa8, 0xcc, 0x30, 0x51, 0xa9, 0x02, 0xb2, 0x28, 0x00, 0xac, 0x04, 0xca,
+ 0xaa, 0xca, 0x71, 0x96, 0xd0, 0x0b, 0xa8, 0xbe, 0x99, 0x0c, 0x9d, 0x00, 0x96, 0xd0,
+ 0x0c, 0xb0, 0x50, 0x06, 0xb6, 0xff, 0xa6, 0xab, 0x05, 0xb6, 0xff, 0xa4, 0xab, 0xb0,
+ 0x50, 0x07, 0xb6, 0xff, 0xaa, 0xab, 0x05, 0xb6, 0xff, 0xa8, 0xab, 0xb0, 0x50, 0x08,
+ 0xb6, 0xff, 0xae, 0xab, 0x05, 0xb6, 0xff, 0xac, 0xab, 0xb0, 0x50, 0x09, 0xb6, 0xff,
+ 0xb2, 0xab, 0x05, 0xb6, 0xff, 0xb0, 0xab, 0x3c, 0xae, 0xca, 0x05, 0x9c, 0x00, 0x59,
+ 0xae, 0xca, 0x4e, 0xab, 0xca, 0x00, 0xc5, 0xaf, 0xca, 0x91, 0xff, 0x90, 0x01, 0xa2,
+ 0x3c, 0xcc, 0x8e, 0xa2, 0x3c, 0xcc, 0x88, 0x96, 0xc8, 0x10, 0x7e, 0x3f, 0xca, 0xac,
+ 0xcc, 0xce, 0x82, 0x30, 0xce, 0xf8, 0x92, 0xce, 0xd4, 0x9d, 0x11, 0x4a, 0xc7, 0x0e,
+ 0x90, 0x01, 0x06, 0xd6, 0x0b, 0x09, 0x8f, 0x0e, 0xac, 0xca, 0xcc, 0xb6, 0xff, 0x02,
+ 0xa8, 0x9c, 0x02, 0x54, 0xa2, 0x78, 0xcc, 0x88, 0x9a, 0x08, 0xa2, 0x78, 0xcc, 0x8b,
+ 0xa2, 0x76, 0xcc, 0x88, 0x9a, 0x08, 0xa2, 0x76, 0xcc, 0x8b, 0x00, 0xa2, 0x4c, 0xcc,
+ 0x8b, 0x88, 0x59, 0x99, 0x7f, 0xa2, 0x1a, 0xcc, 0x8e, 0x90, 0xff, 0xa2, 0x1c, 0xcc,
+ 0x8e, 0x88, 0x59, 0xa2, 0x22, 0xcc, 0x8e, 0x90, 0xff, 0xa2, 0x24, 0xcc, 0x8e, 0xa8,
+ 0x02, 0xb5, 0xf9, 0x00, 0x90, 0x2f, 0xa2, 0x16, 0xcc, 0x8e, 0x00, 0xa2, 0x18, 0xcc,
+ 0x8e, 0x00, 0xa2, 0x12, 0xcc, 0x8e, 0x00, 0xa2, 0x14, 0xcc, 0x8e, 0x00, 0xa2, 0x04,
+ 0xcc, 0x8e, 0x90, 0x60, 0xa2, 0x06, 0xcc, 0x8e, 0x90, 0xa6, 0xa2, 0x08, 0xcc, 0x8e,
+ 0x90, 0x98, 0xa2, 0x02, 0xcc, 0x8e, 0xc4, 0x9a, 0x21, 0xc5, 0x3c, 0xa2, 0x06, 0xcc,
+ 0xa8, 0xb2, 0x10, 0x00, 0x91, 0x5a, 0xbc, 0x20, 0x33, 0xb4, 0x01, 0x28, 0xbc, 0x20,
+ 0x35, 0xb4, 0x01, 0x30, 0xbc, 0x20, 0x48, 0x94, 0x69, 0xbc, 0x20, 0x47, 0x94, 0x47,
+ 0xbc, 0x20, 0x34, 0x94, 0x20, 0xbc, 0x20, 0x36, 0xb4, 0x01, 0x22, 0xbc, 0x20, 0x49,
+ 0x94, 0x72, 0xbc, 0x20, 0x4a, 0x94, 0x8b, 0xbc, 0x20, 0x51, 0x94, 0xa4, 0xbc, 0x20,
+ 0x52, 0x94, 0xbd, 0xbc, 0x20, 0x54, 0x94, 0xd6, 0x3c, 0xa6, 0x01, 0x36, 0xcc, 0x88,
+ 0xd6, 0xa6, 0x01, 0x34, 0xcc, 0x88, 0xa2, 0x01, 0xce, 0x8b, 0xa6, 0x01, 0x32, 0xcc,
+ 0x88, 0xa2, 0x02, 0xce, 0x8b, 0xa6, 0x01, 0x30, 0xcc, 0x88, 0xa2, 0x03, 0xce, 0x8b,
+ 0x3c, 0xa6, 0x01, 0x46, 0xcc, 0x88, 0xab, 0x02, 0xa6, 0x01, 0x44, 0xcc, 0x88, 0x8b,
+ 0x03, 0xa8, 0x02, 0xf1, 0xa6, 0x01, 0x42, 0xcc, 0x88, 0x99, 0x0f, 0xad, 0xca, 0xfa,
+ 0xf1, 0x3c, 0xa6, 0x01, 0x5e, 0xcc, 0x88, 0xab, 0x02, 0xa6, 0x01, 0x5c, 0xcc, 0x88,
+ 0x8b, 0x03, 0xa8, 0x02, 0xf1, 0xa6, 0x01, 0x5a, 0xcc, 0x88, 0x99, 0x0f, 0xa2, 0x06,
+ 0xca, 0xfa, 0xf1, 0x3c, 0xa6, 0x01, 0x6e, 0xcc, 0x88, 0xab, 0x02, 0xa6, 0x01, 0x6c,
+ 0xcc, 0x88, 0x8b, 0x03, 0xa8, 0x02, 0xf1, 0xa6, 0x01, 0x6a, 0xcc, 0x88, 0x99, 0x0f,
+ 0xa2, 0x0a, 0xca, 0xfa, 0xf1, 0x3c, 0xa6, 0x01, 0x76, 0xcc, 0x88, 0xab, 0x02, 0xa6,
+ 0x01, 0x74, 0xcc, 0x88, 0x8b, 0x03, 0xa8, 0x02, 0xf1, 0xa6, 0x01, 0x72, 0xcc, 0x88,
+ 0x99, 0x0f, 0xa2, 0x0c, 0xca, 0xfa, 0xf1, 0x3c, 0xa6, 0x01, 0x4e, 0xcc, 0x88, 0xab,
+ 0x02, 0xa6, 0x01, 0x4c, 0xcc, 0x88, 0x8b, 0x03, 0xa8, 0x02, 0xf1, 0xa6, 0x01, 0x4a,
+ 0xcc, 0x88, 0x99, 0x0f, 0xa2, 0x02, 0xca, 0xfa, 0xf1, 0x3c, 0xa6, 0x01, 0x56, 0xcc,
+ 0x88, 0xab, 0x02, 0xa6, 0x01, 0x54, 0xcc, 0x88, 0x8b, 0x03, 0xa8, 0x02, 0xf1, 0xa6,
+ 0x01, 0x52, 0xcc, 0x88, 0x99, 0x0f, 0xa2, 0x04, 0xca, 0xfa, 0xf1, 0x3c, 0xa6, 0x01,
+ 0x66, 0xcc, 0x88, 0xab, 0x02, 0xa6, 0x01, 0x64, 0xcc, 0x88, 0x8b, 0x03, 0xa8, 0x02,
+ 0xf1, 0xa6, 0x01, 0x62, 0xcc, 0x88, 0x99, 0x0f, 0xa2, 0x08, 0xca, 0xfa, 0xf1, 0x3c,
+ 0xb6, 0xda, 0xd6, 0xa8, 0xb9, 0xff, 0x00, 0xf1, 0xb6, 0xda, 0xd8, 0xa8, 0xf1, 0x3c,
+ 0xa6, 0x01, 0x26, 0xcc, 0x88, 0xf1, 0x3c, 0xa6, 0x01, 0x2e, 0xcc, 0x88, 0xf1, 0x3c,
+ 0xb7, 0x02, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0x9c, 0x00, 0x4e, 0xae, 0x02, 0x03,
+ 0xe7, 0xae, 0x02, 0xae, 0x04, 0xf7, 0xae, 0x04, 0xaa, 0xc8, 0x6d, 0xa8, 0x02, 0x01,
+ 0x03, 0xb8, 0x00, 0x01, 0xab, 0x02, 0xa8, 0x04, 0x01, 0x82, 0x00, 0xc8, 0xe8, 0xab,
+ 0x04, 0x3c, 0xa2, 0x2f, 0xce, 0xdc, 0x3c, 0xa2, 0x2f, 0xce, 0x8b, 0xab, 0xca, 0xa2,
+ 0x54, 0xce, 0xa8, 0x9c, 0x00, 0x94, 0x26, 0x9c, 0x01, 0x41, 0x3c, 0xa2, 0x1c, 0x72,
+ 0xa8, 0x82, 0x00, 0xca, 0xfd, 0x04, 0x82, 0x00, 0xca, 0xfc, 0x05, 0xa2, 0x1c, 0x72,
+ 0xab, 0xb7, 0x01, 0x06, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00,
+ 0xb4, 0xc8, 0x93, 0xa2, 0x1a, 0x72, 0xa8, 0x82, 0x00, 0xca, 0xfd, 0x04, 0x82, 0x00,
+ 0xca, 0xfc, 0x05, 0xa2, 0x1a, 0x72, 0xab, 0xb7, 0x01, 0x06, 0x02, 0xb7, 0x00, 0x00,
+ 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4, 0xc8, 0x71, 0xa2, 0x15, 0x72, 0x88, 0x9c,
+ 0x00, 0x4d, 0xa2, 0x2f, 0xce, 0x88, 0x9d, 0x00, 0x46, 0x90, 0x01, 0xb5, 0xf7, 0xcc,
+ 0x45, 0x90, 0x00, 0xb5, 0xf7, 0xc6, 0xb7, 0x62, 0x5a, 0x02, 0xb7, 0x00, 0x02, 0x04,
+ 0xb7, 0x04, 0x00, 0x06, 0xac, 0xce, 0xcc, 0x82, 0x34, 0xcc, 0xf8, 0xa2, 0x22, 0xce,
+ 0x88, 0xb4, 0xf0, 0x17, 0xa2, 0x32, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x25, 0xa2, 0x54,
+ 0xce, 0xa8, 0xb5, 0xc6, 0xab, 0xa2, 0x00, 0x78, 0xa8, 0xab, 0x02, 0xa2, 0x02, 0x78,
+ 0xa8, 0xab, 0x04, 0xb7, 0x04, 0x00, 0x06, 0xac, 0xce, 0xcc, 0x82, 0x34, 0xcc, 0xf8,
+ 0xa2, 0x22, 0xce, 0x88, 0xb4, 0xef, 0xea, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00,
+ 0x04, 0xb7, 0x04, 0x00, 0x06, 0xac, 0xce, 0xcc, 0x82, 0x34, 0xcc, 0xf8, 0xa2, 0x22,
+ 0xce, 0x88, 0xb4, 0xef, 0xd0, 0xa2, 0x2d, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x1f, 0xb7,
+ 0xbc, 0x20, 0x02, 0xb7, 0x00, 0xbe, 0x04, 0xb7, 0x04, 0x00, 0x06, 0xac, 0xce, 0xcc,
+ 0x82, 0x34, 0xcc, 0xf8, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xef, 0xae, 0x90, 0x01, 0xb4,
+ 0xf7, 0x3e, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x04, 0x00, 0x06,
+ 0xac, 0xce, 0xcc, 0x82, 0x34, 0xcc, 0xf8, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xef, 0x8f,
+ 0x7f, 0xa2, 0x2d, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x1f, 0xb7, 0xbc, 0x20, 0x02, 0xb7,
+ 0x00, 0xbe, 0x04, 0xb7, 0x04, 0x00, 0x06, 0xac, 0xce, 0xcc, 0x82, 0x34, 0xcc, 0xf8,
+ 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xef, 0x6c, 0x90, 0x01, 0xb4, 0xf6, 0xfc, 0xb7, 0x9a,
+ 0xca, 0x02, 0xb7, 0x00, 0x3b, 0x04, 0xb7, 0x04, 0x00, 0x06, 0xac, 0xce, 0xcc, 0x82,
+ 0x34, 0xcc, 0xf8, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xef, 0x4d, 0x7f, 0x90, 0x00, 0xb5,
+ 0xf8, 0xca, 0xb7, 0x00, 0x00, 0x02, 0xac, 0xce, 0xcc, 0x82, 0x12, 0xcc, 0xf8, 0xb7,
+ 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xb7, 0x00, 0x00, 0x08, 0xb7, 0x00, 0x00,
+ 0x0a, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xf7, 0x5f, 0x90, 0x29, 0xa2, 0x3a, 0xce, 0xab,
+ 0xb0, 0xd2, 0x4d, 0x91, 0x08, 0xb5, 0xf7, 0xb6, 0x3c, 0xb7, 0x00, 0x00, 0x02, 0xac,
+ 0xce, 0xcc, 0x82, 0x12, 0xcc, 0xf8, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06,
+ 0xb7, 0x00, 0x00, 0x08, 0xb7, 0x00, 0x00, 0x0a, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xf7,
+ 0x2e, 0x90, 0x2a, 0xa2, 0x3a, 0xce, 0xab, 0xb0, 0xf4, 0x7d, 0x91, 0x01, 0xb5, 0xf7,
+ 0x85, 0x3c, 0xaf, 0xc8, 0xb7, 0x00, 0x01, 0x02, 0xa2, 0x54, 0xce, 0xa8, 0xab, 0x04,
+ 0xac, 0xce, 0x06, 0x82, 0x42, 0x06, 0xf8, 0xac, 0xce, 0x08, 0x82, 0x3e, 0x08, 0xf8,
+ 0xac, 0xce, 0x0a, 0x82, 0x3e, 0x0a, 0xf8, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xf8, 0x41,
+ 0x90, 0x01, 0xa2, 0x2b, 0xce, 0x8b, 0x3f, 0xc8, 0x9c, 0x02, 0x95, 0x99, 0x95, 0x65,
+ 0xb7, 0x01, 0x02, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4,
+ 0xc6, 0xc6, 0xac, 0xce, 0xcc, 0x82, 0x12, 0xcc, 0xf8, 0xb5, 0xf7, 0xb9, 0x9d, 0x00,
+ 0x94, 0x2c, 0xa2, 0x54, 0xce, 0xa8, 0xb5, 0xfa, 0x44, 0xa2, 0x2c, 0xce, 0x88, 0x9c,
+ 0x00, 0x52, 0x90, 0x00, 0xa2, 0x2c, 0xce, 0x8b, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18,
+ 0xb0, 0x20, 0x70, 0xb5, 0x0a, 0x52, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20,
+ 0x29, 0xb4, 0x0a, 0x46, 0x9c, 0x02, 0x95, 0x82, 0x9c, 0x03, 0x95, 0x86, 0x95, 0x52,
+ 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xac, 0xce, 0x06, 0x82, 0x0e, 0x06,
+ 0xf8, 0xac, 0xce, 0x08, 0x82, 0x0a, 0x08, 0xf8, 0xac, 0xce, 0x0a, 0x82, 0x0a, 0x0a,
+ 0xf8, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xf7, 0xbb, 0x90, 0x00, 0xa2, 0x27, 0xce, 0x8b,
+ 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x74, 0xb5, 0x0a, 0x06, 0x90, 0x00,
+ 0xa2, 0x26, 0xce, 0x8b, 0xa2, 0x2c, 0xce, 0x8b, 0xa2, 0x33, 0xce, 0x8b, 0xa2, 0x22,
+ 0xce, 0x88, 0xab, 0x18, 0xb0, 0x2f, 0x05, 0xb5, 0x09, 0xec, 0x90, 0x01, 0x36, 0x9c,
+ 0x90, 0x00, 0xa2, 0x3a, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20,
+ 0x6f, 0xb5, 0x09, 0xd6, 0x90, 0x00, 0xb4, 0xf5, 0x85, 0xb7, 0x00, 0x01, 0x02, 0xa2,
+ 0x54, 0xce, 0xa8, 0xab, 0x04, 0xac, 0xce, 0x06, 0x82, 0x42, 0x06, 0xf8, 0xac, 0xce,
+ 0x08, 0x82, 0x3e, 0x08, 0xf8, 0xac, 0xce, 0x0a, 0x82, 0x46, 0x0a, 0xf8, 0xa2, 0x22,
+ 0xce, 0x88, 0xb5, 0xf7, 0x4e, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xf5, 0xd0, 0xb4, 0xf5,
+ 0xdd, 0x90, 0x00, 0xa2, 0x27, 0xce, 0x8b, 0xa2, 0x26, 0xce, 0x8b, 0xa2, 0x33, 0xce,
+ 0x8b, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x74, 0xb5, 0x09, 0x87, 0xa2,
+ 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x2f, 0x05, 0xb5, 0x09, 0x7b, 0x90, 0x01, 0xa2,
+ 0x3a, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x6f, 0xb5, 0x09,
+ 0x69, 0xa2, 0x2e, 0xce, 0x88, 0x9c, 0x00, 0xb4, 0xfd, 0x35, 0x40, 0x90, 0x00, 0x37,
+ 0x29, 0xa2, 0x29, 0xce, 0x88, 0x9c, 0x00, 0x46, 0x90, 0x01, 0xa2, 0x26, 0xce, 0x8b,
+ 0xa2, 0x2a, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x26, 0x90, 0x01, 0xa2, 0x33, 0xce, 0x8b,
+ 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x2f, 0x05, 0xb5, 0x09, 0x34, 0xa2, 0x02,
+ 0xce, 0x88, 0xa2, 0x27, 0xce, 0x8b, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20,
+ 0x74, 0xb5, 0x09, 0x20, 0x90, 0x02, 0xa2, 0x3a, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88,
+ 0xab, 0x18, 0xb0, 0x20, 0x6f, 0xb5, 0x09, 0x0e, 0x90, 0x00, 0xb5, 0xf4, 0xbd, 0xa2,
+ 0x22, 0xce, 0x88, 0xb5, 0xf8, 0x34, 0xa2, 0x32, 0xce, 0x8b, 0xb4, 0xfd, 0x03, 0x90,
+ 0x03, 0xa2, 0x3a, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x6f,
+ 0xb5, 0x08, 0xe9, 0xa2, 0x2e, 0xce, 0x88, 0x9c, 0x00, 0xb4, 0xfd, 0x2f, 0x95, 0x7f,
+ 0xa2, 0x2c, 0xce, 0x88, 0x9d, 0x00, 0x52, 0x90, 0x01, 0xa2, 0x2c, 0xce, 0x8b, 0xa2,
+ 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x70, 0xb5, 0x08, 0xc5, 0x90, 0x00, 0xa2,
+ 0x2d, 0xce, 0x8b, 0xa2, 0x2b, 0xce, 0x8b, 0xa2, 0x20, 0xce, 0xa8, 0x9c, 0x01, 0xb4,
+ 0xfe, 0x32, 0x9c, 0x04, 0xb4, 0xfe, 0x1d, 0xb4, 0xfd, 0xe4, 0xa2, 0x2b, 0xce, 0x88,
+ 0x9c, 0x00, 0x4c, 0xa2, 0x20, 0xce, 0xa8, 0x9c, 0x02, 0xb4, 0xfd, 0x6d, 0xb4, 0xfd,
+ 0xa0, 0x90, 0x01, 0x37, 0xf1, 0xa2, 0x20, 0xce, 0xa8, 0x9c, 0x01, 0xb4, 0xfe, 0x0a,
+ 0x9c, 0x04, 0xb4, 0xfd, 0xf5, 0xb4, 0xfd, 0xbc, 0x90, 0x04, 0xa2, 0x3a, 0xce, 0xab,
+ 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x6f, 0xb5, 0x08, 0x70, 0xa2, 0x2e,
+ 0xce, 0x88, 0x9d, 0x00, 0x95, 0xf5, 0xb4, 0xfc, 0xf6, 0x90, 0x05, 0xa2, 0x3a, 0xce,
+ 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x6f, 0xb5, 0x08, 0x53, 0x90,
+ 0x00, 0xb5, 0xf4, 0x02, 0xb5, 0xf7, 0x7d, 0xa2, 0x32, 0xce, 0x8b, 0xb4, 0xfc, 0x4c,
+ 0xb7, 0x00, 0x01, 0x02, 0xb2, 0x43, 0xb7, 0xa2, 0x18, 0xce, 0xa8, 0xab, 0x04, 0xa2,
+ 0x1a, 0xce, 0xa8, 0xab, 0x06, 0xa2, 0x1c, 0xce, 0xa8, 0xab, 0x08, 0xb7, 0x00, 0x06,
+ 0x0a, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xf4, 0x79, 0x90, 0x06, 0xa2, 0x3a, 0xce, 0xab,
+ 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x6f, 0xb5, 0x08, 0x0e, 0xb0, 0xd2,
+ 0x4d, 0x91, 0x08, 0xb5, 0xf4, 0xc4, 0x3c, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00,
+ 0x04, 0xb7, 0x04, 0x00, 0x06, 0xac, 0xce, 0xcc, 0x82, 0x34, 0xcc, 0xf8, 0xa2, 0x22,
+ 0xce, 0x88, 0xb5, 0xec, 0x0a, 0x90, 0x07, 0xa2, 0x3a, 0xce, 0xab, 0xa2, 0x22, 0xce,
+ 0x88, 0xab, 0x18, 0xb0, 0x20, 0x6f, 0xb5, 0x07, 0xd9, 0x90, 0x00, 0xb5, 0xf3, 0x88,
+ 0xb7, 0x01, 0x03, 0x02, 0xb7, 0x00, 0x02, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22,
+ 0xce, 0x88, 0xb4, 0xc4, 0x07, 0xb5, 0xf3, 0xea, 0xb4, 0xf3, 0xf7, 0xa8, 0x46, 0xb5,
+ 0xc2, 0x50, 0xa2, 0x00, 0x74, 0xa8, 0x9c, 0x00, 0x3c, 0xa8, 0x44, 0xbc, 0x04, 0x01,
+ 0x94, 0xbf, 0xbc, 0x04, 0x02, 0x94, 0xc2, 0xbc, 0x04, 0x0e, 0x94, 0xc5, 0xa2, 0x3a,
+ 0x74, 0xa8, 0x9c, 0x00, 0x54, 0xa2, 0x29, 0x74, 0x88, 0x9d, 0x00, 0x4d, 0xa2, 0x2a,
+ 0x74, 0x88, 0x9d, 0x00, 0x46, 0xac, 0x74, 0xce, 0xb4, 0xfd, 0x47, 0xa8, 0x44, 0xbc,
+ 0x04, 0x03, 0x94, 0xaf, 0xbc, 0x04, 0xff, 0x94, 0x3a, 0xa2, 0x3a, 0x74, 0xa8, 0x9c,
+ 0x00, 0x94, 0xae, 0x9c, 0x01, 0x94, 0xcb, 0x9c, 0x02, 0xb4, 0x01, 0x67, 0x9c, 0x03,
+ 0xb4, 0x02, 0x1a, 0x9c, 0x04, 0xb4, 0x02, 0xec, 0x9c, 0x05, 0xb4, 0x03, 0x96, 0x9c,
+ 0x06, 0xb4, 0x04, 0x40, 0x9c, 0x07, 0xb4, 0x04, 0x8f, 0x9c, 0x29, 0xb4, 0x04, 0x8b,
+ 0x9c, 0x2a, 0xb4, 0x04, 0xa0, 0x9c, 0x2b, 0xb4, 0x04, 0xd6, 0x3c, 0xa2, 0x3a, 0x74,
+ 0xa8, 0x9c, 0x03, 0x50, 0xa2, 0x34, 0x74, 0xa8, 0x96, 0x48, 0xfc, 0x41, 0x3c, 0x00,
+ 0xa2, 0x34, 0x74, 0xab, 0x95, 0x4f, 0xa2, 0x36, 0x74, 0xa8, 0x96, 0x48, 0xfc, 0x94,
+ 0x23, 0xa2, 0x38, 0x74, 0xa8, 0x96, 0x48, 0xfc, 0x42, 0x95, 0x21, 0xa2, 0x2d, 0x74,
+ 0x88, 0x9d, 0x00, 0x95, 0x6a, 0x90, 0x01, 0xa2, 0x31, 0x74, 0x8b, 0x00, 0xa2, 0x38,
+ 0x74, 0xab, 0xac, 0x74, 0xce, 0xb4, 0xfb, 0x49, 0x90, 0x01, 0xa2, 0x30, 0x74, 0x8b,
+ 0x00, 0xa2, 0x36, 0x74, 0x8b, 0xac, 0x74, 0xce, 0xb4, 0xfb, 0x38, 0xa8, 0x48, 0xa2,
+ 0x29, 0x74, 0x8b, 0x95, 0xbb, 0xa8, 0x48, 0xa2, 0x2a, 0x74, 0x8b, 0x95, 0xc3, 0xa8,
+ 0x48, 0xa2, 0x28, 0x74, 0x8b, 0xac, 0x74, 0xce, 0xb4, 0xe4, 0x87, 0xa8, 0x48, 0x9d,
+ 0x00, 0x90, 0x01, 0xa2, 0x2e, 0x74, 0x8b, 0x95, 0xb4, 0xa8, 0x44, 0xbc, 0x04, 0x01,
+ 0x45, 0xbc, 0x04, 0x02, 0x41, 0x3c, 0xa2, 0x29, 0x74, 0x88, 0x9d, 0x00, 0x47, 0xa2,
+ 0x2a, 0x74, 0x88, 0x9c, 0x00, 0x3c, 0xac, 0x74, 0xce, 0x37, 0x2c, 0xb4, 0xfc, 0xff,
+ 0xa8, 0x44, 0xbc, 0x04, 0x03, 0x4a, 0xbc, 0x04, 0xff, 0x53, 0xbc, 0x04, 0x04, 0x94,
+ 0x79, 0x3c, 0xa2, 0x2e, 0x74, 0x88, 0x9c, 0x00, 0x3c, 0xac, 0x74, 0xce, 0x36, 0xd9,
+ 0x3c, 0x90, 0x00, 0xa2, 0x27, 0x74, 0x8b, 0xa2, 0x26, 0x74, 0x8b, 0xa2, 0x33, 0x74,
+ 0x8b, 0xa2, 0x2d, 0x74, 0x8b, 0xa2, 0x30, 0x74, 0x8b, 0xa2, 0x31, 0x74, 0x8b, 0xa2,
+ 0x22, 0x74, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x74, 0xb5, 0x06, 0x5d, 0xa2, 0x22, 0x74,
+ 0x88, 0xab, 0x18, 0xb0, 0x2f, 0x05, 0xb5, 0x06, 0x51, 0xb7, 0x00, 0x00, 0x02, 0xb7,
+ 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xac, 0x74, 0xcc, 0x82, 0x36, 0xcc, 0xf8,
+ 0x00, 0xb5, 0xea, 0x59, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00,
+ 0x00, 0x06, 0xac, 0x74, 0xcc, 0x82, 0x38, 0xcc, 0xf8, 0x00, 0xb5, 0xea, 0x42, 0x90,
+ 0x01, 0xac, 0x74, 0xce, 0xb5, 0xf9, 0x95, 0xb4, 0xfd, 0x1d, 0xa2, 0x2f, 0x74, 0x88,
+ 0x9d, 0x00, 0x3c, 0xa8, 0x48, 0x99, 0x0e, 0x9c, 0x00, 0x3c, 0x90, 0x01, 0xac, 0x74,
+ 0xce, 0xb5, 0xf9, 0x7c, 0xb4, 0xf9, 0xd4, 0xa8, 0x44, 0xbc, 0x04, 0x03, 0x54, 0xbc,
+ 0x04, 0x06, 0x5e, 0xbc, 0x04, 0x04, 0x94, 0x54, 0xbc, 0x04, 0xff, 0x94, 0x82, 0xbc,
+ 0x04, 0x0d, 0x94, 0x93, 0x3c, 0xa2, 0x2e, 0x74, 0x88, 0x9d, 0x00, 0x46, 0xac, 0x74,
+ 0xce, 0xb4, 0xfc, 0x37, 0x3c, 0xa8, 0x48, 0x9c, 0x02, 0x41, 0x3c, 0x90, 0x00, 0xa2,
+ 0x27, 0x74, 0x8b, 0xa2, 0x26, 0x74, 0x8b, 0xa2, 0x22, 0x74, 0x88, 0xab, 0x18, 0xb0,
+ 0x20, 0x74, 0xb5, 0x05, 0xbb, 0xa2, 0x2c, 0x74, 0x88, 0x9d, 0x00, 0x52, 0x90, 0x01,
+ 0xa2, 0x2c, 0x74, 0x8b, 0xa2, 0x22, 0x74, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x70, 0xb5,
+ 0x05, 0xa2, 0xac, 0x74, 0xce, 0xb4, 0xfd, 0x37, 0xa8, 0x48, 0x99, 0x10, 0x9c, 0x00,
+ 0x3c, 0xa2, 0x54, 0x74, 0xa8, 0xb5, 0xc0, 0x50, 0xa2, 0x00, 0x78, 0xa8, 0xa2, 0x02,
+ 0x78, 0xfa, 0x9c, 0x00, 0x4c, 0x90, 0x01, 0xa2, 0x32, 0x74, 0x8b, 0xac, 0x74, 0xce,
+ 0xb4, 0xf9, 0x7f, 0x90, 0x00, 0xa2, 0x32, 0x74, 0x8b, 0xac, 0x74, 0xce, 0x36, 0x53,
+ 0xb4, 0xfb, 0xc8, 0x90, 0x00, 0xa2, 0x32, 0x74, 0x8b, 0xa8, 0x46, 0xb5, 0xf4, 0x90,
+ 0x9c, 0x00, 0x3c, 0xac, 0x74, 0xce, 0x36, 0x69, 0xb4, 0xfb, 0xb2, 0xa8, 0x48, 0xa2,
+ 0x02, 0x74, 0x8b, 0xac, 0x74, 0xce, 0xb4, 0xfb, 0xe8, 0xa8, 0x44, 0xbc, 0x04, 0x03,
+ 0x4a, 0xbc, 0x04, 0x04, 0x54, 0xbc, 0x04, 0xff, 0x94, 0xb4, 0x3c, 0xa2, 0x2e, 0x74,
+ 0x88, 0x9c, 0x00, 0x3c, 0xac, 0x74, 0xce, 0xb5, 0xfb, 0xcb, 0x3c, 0xa8, 0x48, 0x99,
+ 0x01, 0x9c, 0x00, 0x48, 0xa2, 0x30, 0x74, 0x88, 0x9d, 0x00, 0x94, 0x4b, 0xa8, 0x48,
+ 0x99, 0x02, 0x9c, 0x00, 0x48, 0xa2, 0x31, 0x74, 0x88, 0x9d, 0x00, 0x94, 0x3c, 0xa8,
+ 0x48, 0x99, 0x20, 0x9d, 0x00, 0x94, 0x34, 0xa8, 0x48, 0x99, 0x03, 0x9c, 0x00, 0x94,
+ 0x35, 0xa2, 0x2d, 0x74, 0x88, 0x9c, 0x00, 0x94, 0x2d, 0x90, 0x00, 0xa2, 0x2d, 0x74,
+ 0x8b, 0xb7, 0x15, 0xa4, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x04, 0x00, 0x06, 0xac,
+ 0x74, 0xcc, 0x82, 0x38, 0xcc, 0xf8, 0xa8, 0x46, 0xb5, 0xe8, 0xf4, 0xac, 0x74, 0xce,
+ 0xb4, 0xf9, 0x1e, 0xac, 0x74, 0xce, 0xb5, 0xfb, 0xeb, 0xb4, 0xfc, 0x44, 0xa8, 0x48,
+ 0x99, 0x08, 0x9c, 0x00, 0x94, 0x32, 0xa2, 0x2d, 0x74, 0x88, 0x9d, 0x00, 0x94, 0x2a,
+ 0xb7, 0x15, 0xa4, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x04, 0x00, 0x06, 0xac, 0x74,
+ 0xcc, 0x82, 0x36, 0xcc, 0xf8, 0xa8, 0x46, 0xb5, 0xe8, 0xbd, 0x90, 0x01, 0xa2, 0x2d,
+ 0x74, 0x8b, 0x90, 0x00, 0xa2, 0x31, 0x74, 0x8b, 0xac, 0x74, 0xce, 0xb4, 0xf8, 0xdb,
+ 0x90, 0x00, 0xac, 0x74, 0xce, 0xb4, 0xf0, 0x38, 0xa2, 0x2a, 0x74, 0x88, 0x9c, 0x00,
+ 0x3c, 0xa2, 0x2d, 0x74, 0x88, 0x9c, 0x00, 0x3c, 0xac, 0x74, 0xce, 0xb4, 0xfc, 0x2c,
+ 0xa8, 0x44, 0xbc, 0x04, 0x0b, 0x50, 0xbc, 0x04, 0x04, 0x94, 0x2f, 0xbc, 0x04, 0x03,
+ 0x94, 0x5e, 0xbc, 0x04, 0xff, 0x94, 0x7d, 0x3c, 0xa8, 0x48, 0xa2, 0x2c, 0x74, 0xdc,
+ 0x50, 0xa2, 0x2c, 0x74, 0x8b, 0xa2, 0x22, 0x74, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x70,
+ 0xb5, 0x04, 0x43, 0xa2, 0x2c, 0x74, 0x88, 0x9d, 0x00, 0x3c, 0xac, 0x74, 0xce, 0xb4,
+ 0xfa, 0x95, 0xa8, 0x48, 0x99, 0x06, 0x9c, 0x00, 0x53, 0xa2, 0x2d, 0x74, 0x88, 0x9c,
+ 0x00, 0x4c, 0x90, 0x00, 0xa2, 0x2d, 0x74, 0x8b, 0xac, 0x74, 0xce, 0xb4, 0xf8, 0xad,
+ 0xa8, 0x48, 0x99, 0x08, 0x9c, 0x00, 0x3c, 0xa2, 0x2d, 0x74, 0x88, 0x9d, 0x00, 0x3c,
+ 0x90, 0x01, 0xa2, 0x2d, 0x74, 0x8b, 0xac, 0x74, 0xce, 0xb4, 0xf8, 0x93, 0xa2, 0x2e,
+ 0x74, 0x88, 0x9c, 0x00, 0x3c, 0x90, 0x00, 0xac, 0x74, 0xce, 0xb5, 0xf7, 0x6d, 0x90,
+ 0x01, 0xa2, 0x33, 0x74, 0x8b, 0xa2, 0x22, 0x74, 0x88, 0xab, 0x18, 0xb0, 0x2f, 0x05,
+ 0xb5, 0x03, 0xe1, 0xb4, 0xfb, 0x79, 0xa2, 0x2d, 0x74, 0x88, 0x9d, 0x00, 0x49, 0xac,
+ 0x74, 0xce, 0xb5, 0xfb, 0x27, 0xb4, 0xf8, 0x5f, 0xa2, 0x2a, 0x74, 0x88, 0x9c, 0x00,
+ 0x3c, 0xac, 0x74, 0xce, 0xb4, 0xfb, 0x7d, 0xa8, 0x44, 0xbc, 0x04, 0x06, 0x50, 0xbc,
+ 0x04, 0x03, 0x94, 0x32, 0xbc, 0x04, 0x04, 0x94, 0x54, 0xbc, 0x04, 0xff, 0x94, 0x83,
+ 0x3c, 0xa8, 0x48, 0x9c, 0x01, 0x41, 0x3c, 0xac, 0x74, 0xce, 0xa2, 0x2c, 0x74, 0x88,
+ 0x9d, 0x00, 0x30, 0x04, 0xb5, 0xfa, 0x38, 0x3c, 0x90, 0x00, 0xa2, 0x2c, 0x74, 0x8b,
+ 0xa2, 0x22, 0x74, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x70, 0xb4, 0x03, 0x84, 0xa2, 0x2e,
+ 0x74, 0x88, 0x9d, 0x00, 0x3c, 0x90, 0x00, 0xa2, 0x2b, 0x74, 0x8b, 0xa2, 0x2d, 0x74,
+ 0x8b, 0xa2, 0x33, 0x74, 0x8b, 0xa2, 0x22, 0x74, 0x88, 0xab, 0x18, 0xb0, 0x2f, 0x05,
+ 0xb5, 0x03, 0x63, 0xac, 0x74, 0xce, 0xb4, 0xfa, 0xdb, 0xa8, 0x48, 0x99, 0x10, 0x9c,
+ 0x00, 0x3c, 0xa2, 0x54, 0x74, 0xa8, 0xb5, 0xbe, 0x11, 0xa2, 0x00, 0x74, 0xa8, 0xa2,
+ 0x00, 0x74, 0xfa, 0x9c, 0x00, 0x4c, 0x90, 0x01, 0xa2, 0x32, 0x74, 0x8b, 0xac, 0x74,
+ 0xce, 0xb4, 0xf7, 0x40, 0x90, 0x00, 0xa2, 0x32, 0x74, 0x8b, 0xac, 0x74, 0xce, 0xb5,
+ 0xfb, 0x6b, 0xb4, 0xfa, 0xa7, 0x90, 0x00, 0xa2, 0x32, 0x74, 0x8b, 0xa8, 0x46, 0xb5,
+ 0xf2, 0x50, 0x9c, 0x00, 0x3c, 0xac, 0x74, 0xce, 0xb5, 0xfb, 0x54, 0xb4, 0xfa, 0x90,
+ 0xa8, 0x44, 0xbc, 0x04, 0x04, 0x4b, 0xbc, 0x04, 0x0c, 0x94, 0x32, 0xbc, 0x04, 0xff,
+ 0x94, 0x2d, 0x3c, 0xa8, 0x48, 0x99, 0x06, 0x9c, 0x00, 0x3c, 0xa2, 0x2c, 0x74, 0x88,
+ 0x9d, 0x00, 0x4f, 0xb5, 0xef, 0xe7, 0x90, 0x00, 0xa2, 0x2d, 0x74, 0x8b, 0xac, 0x74,
+ 0xce, 0xb4, 0xf9, 0xe9, 0xb5, 0xef, 0xd8, 0x90, 0x00, 0xa2, 0x2d, 0x74, 0x8b, 0xac,
+ 0x74, 0xce, 0xb4, 0xfa, 0x53, 0xb5, 0xef, 0xc9, 0xa2, 0x14, 0x72, 0x88, 0x9c, 0x00,
+ 0x3c, 0xa2, 0x34, 0x74, 0xa8, 0x9d, 0x00, 0x3c, 0xac, 0x74, 0xce, 0xb4, 0xfa, 0xb9,
+ 0x3c, 0xa8, 0x44, 0xbc, 0x04, 0xff, 0x41, 0x3c, 0x90, 0x01, 0xac, 0x74, 0xce, 0xb5,
+ 0xf0, 0x50, 0xb5, 0xef, 0xa2, 0xa8, 0x46, 0xb5, 0xee, 0xd2, 0xb4, 0xfa, 0x21, 0xa8,
+ 0x44, 0xbc, 0x04, 0xff, 0x41, 0x3c, 0xb5, 0xef, 0x90, 0xb7, 0x00, 0x00, 0x02, 0xac,
+ 0xce, 0xcc, 0x82, 0x12, 0xcc, 0xf8, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06,
+ 0xb7, 0x00, 0x00, 0x08, 0xb7, 0x00, 0x00, 0x0a, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xee,
+ 0xd0, 0xb0, 0xd2, 0x4d, 0x91, 0x08, 0xb5, 0xef, 0x2d, 0x90, 0x2b, 0xa2, 0x3a, 0x74,
+ 0xab, 0x3c, 0xa8, 0x44, 0xbc, 0x04, 0xff, 0x41, 0x3c, 0xb5, 0xef, 0x55, 0xac, 0x74,
+ 0xce, 0xb4, 0xf9, 0xd6, 0xa2, 0x28, 0xce, 0x88, 0xab, 0xca, 0x90, 0x01, 0x82, 0x00,
+ 0xca, 0xfc, 0x44, 0xe7, 0xaa, 0xca, 0x63, 0xab, 0x02, 0xa2, 0x06, 0xce, 0xa8, 0xe7,
+ 0xe7, 0xab, 0xca, 0xa8, 0x02, 0x82, 0x00, 0xca, 0xfc, 0x44, 0xe7, 0xaa, 0xca, 0x63,
+ 0xab, 0x02, 0xa2, 0x08, 0x72, 0xa8, 0x96, 0x02, 0xf9, 0x9c, 0x00, 0x42, 0x00, 0x3c,
+ 0x90, 0x01, 0x3c, 0xab, 0xca, 0x9c, 0x03, 0xb4, 0x01, 0x80, 0x05, 0xab, 0x02, 0xb7,
+ 0x00, 0x04, 0x04, 0x92, 0x00, 0xb7, 0x00, 0x00, 0x06, 0x82, 0x00, 0x06, 0xfd, 0xb4,
+ 0x01, 0x69, 0x82, 0x04, 0x04, 0xfc, 0xb4, 0x01, 0x46, 0xaf, 0xcc, 0xa8, 0x02, 0xe7,
+ 0xe7, 0xa0, 0xc8, 0xcc, 0xf8, 0x82, 0x58, 0xcc, 0xf8, 0xa2, 0x01, 0xcc, 0x88, 0xab,
+ 0x04, 0xc4, 0xab, 0x08, 0x3f, 0xcc, 0x82, 0x04, 0x04, 0xfc, 0x94, 0x41, 0xa8, 0x08,
+ 0xb5, 0xbc, 0x75, 0xac, 0x74, 0xcc, 0x82, 0x01, 0xca, 0xfc, 0x4c, 0x82, 0x02, 0xca,
+ 0xfc, 0x54, 0x82, 0x03, 0xca, 0xfc, 0x57, 0x95, 0x44, 0xa2, 0x4a, 0xcc, 0x88, 0x9c,
+ 0x00, 0x72, 0xb7, 0x00, 0x02, 0x06, 0x95, 0x51, 0xa2, 0x4b, 0xcc, 0x88, 0x9c, 0x00,
+ 0x7a, 0x6d, 0xa2, 0x4c, 0xcc, 0x88, 0x9c, 0x00, 0x95, 0x61, 0xa2, 0x06, 0xcc, 0xa8,
+ 0x05, 0x96, 0x02, 0xfc, 0x7e, 0x95, 0x6c, 0xa8, 0x08, 0xb5, 0xbc, 0x48, 0xac, 0x76,
+ 0xce, 0x82, 0x01, 0xca, 0xfc, 0x4e, 0x82, 0x02, 0xca, 0xfc, 0x94, 0x5d, 0x82, 0x03,
+ 0xca, 0xfc, 0x94, 0x76, 0x95, 0x87, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x00, 0x4d, 0x9c,
+ 0x01, 0x58, 0x9c, 0x02, 0x94, 0x2b, 0x9c, 0x03, 0x94, 0x39, 0x95, 0x9b, 0xa2, 0x56,
+ 0xce, 0x88, 0x9c, 0x00, 0x95, 0xa3, 0xb7, 0x00, 0x04, 0x06, 0x95, 0xa9, 0xa2, 0x57,
+ 0xce, 0x88, 0x9d, 0x00, 0x6c, 0xa2, 0x58, 0xce, 0x88, 0x9d, 0x00, 0x73, 0xa2, 0x56,
+ 0xce, 0x88, 0x9d, 0x00, 0x7a, 0x95, 0xc0, 0xa2, 0x57, 0xce, 0x88, 0x9d, 0x00, 0x95,
+ 0x23, 0xa2, 0x58, 0xce, 0x88, 0x9d, 0x00, 0x95, 0x2b, 0x95, 0xd2, 0xa2, 0x59, 0xce,
+ 0x88, 0x9c, 0x00, 0x95, 0xda, 0x95, 0x37, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x00, 0x95,
+ 0x39, 0x9c, 0x01, 0x95, 0x2f, 0x9c, 0x02, 0x95, 0x22, 0x9c, 0x03, 0x42, 0x95, 0xf1,
+ 0xa2, 0x5a, 0xce, 0x88, 0x9c, 0x00, 0x95, 0xf9, 0x95, 0x56, 0xa2, 0x0a, 0xce, 0xa8,
+ 0x05, 0x96, 0x02, 0xfc, 0x43, 0xb4, 0xfe, 0xf7, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x03,
+ 0x94, 0x26, 0xa2, 0x5e, 0xce, 0x88, 0x9c, 0x00, 0xb4, 0xfe, 0xe6, 0xa2, 0x56, 0xce,
+ 0x88, 0x9d, 0x00, 0xb4, 0xfe, 0xdd, 0xa2, 0x57, 0xce, 0x88, 0x9d, 0x00, 0xb4, 0xfe,
+ 0xd4, 0xa2, 0x58, 0xce, 0x88, 0x9d, 0x00, 0xb4, 0xfe, 0xcb, 0x95, 0x90, 0xa2, 0x5e,
+ 0xce, 0x88, 0x9c, 0x00, 0xb4, 0xfe, 0xc0, 0xa2, 0x59, 0xce, 0x88, 0x9d, 0x00, 0xb4,
+ 0xfe, 0xb7, 0xa2, 0x5a, 0xce, 0x88, 0x9d, 0x00, 0xb4, 0xfe, 0xae, 0x95, 0xad, 0xaf,
+ 0xce, 0xa8, 0x02, 0xe7, 0xe7, 0xa0, 0xc8, 0xce, 0xf8, 0x82, 0x1a, 0xce, 0xf8, 0xa2,
+ 0x01, 0xce, 0x88, 0xab, 0x04, 0xd4, 0xab, 0x08, 0x3f, 0xce, 0xb4, 0xfe, 0xb7, 0xa8,
+ 0x06, 0x3c, 0xa2, 0x0a, 0xce, 0xa8, 0x05, 0xab, 0x02, 0xb4, 0xfe, 0x79, 0xb7, 0x00,
+ 0x01, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x00, 0x94, 0x24,
+ 0x9c, 0x01, 0x94, 0x47, 0x9c, 0x02, 0x94, 0x43, 0x9c, 0x03, 0x94, 0x59, 0x82, 0x00,
+ 0x04, 0xfc, 0x50, 0xaf, 0xce, 0xa8, 0x04, 0x35, 0xb6, 0x3f, 0xce, 0x9c, 0x02, 0x42,
+ 0x00, 0x3c, 0x90, 0x01, 0x3c, 0xa8, 0x02, 0x3c, 0xa2, 0x5b, 0xce, 0x88, 0xa2, 0x5f,
+ 0xce, 0xda, 0xa2, 0x5d, 0xce, 0xda, 0xa2, 0x61, 0xce, 0xda, 0x9c, 0x00, 0x46, 0xb7,
+ 0x00, 0x02, 0x04, 0x95, 0x2f, 0xa2, 0x5e, 0xce, 0x88, 0x9c, 0x00, 0x95, 0x37, 0xb7,
+ 0x00, 0x03, 0x04, 0x95, 0x3d, 0xa2, 0x5b, 0xce, 0x88, 0xa2, 0x5f, 0xce, 0xda, 0xa2,
+ 0x5c, 0xce, 0xda, 0xa2, 0x60, 0xce, 0xda, 0x9c, 0x00, 0x95, 0x20, 0xb7, 0x00, 0x01,
+ 0x04, 0x95, 0x57, 0xa2, 0x59, 0xce, 0x88, 0x9c, 0x00, 0x46, 0xb7, 0x00, 0x01, 0x04,
+ 0x95, 0x64, 0xa2, 0x5a, 0xce, 0x88, 0x9c, 0x00, 0x95, 0x3b, 0xb7, 0x00, 0x02, 0x04,
+ 0x95, 0x72, 0xab, 0x1a, 0xbc, 0x10, 0x29, 0x94, 0x80, 0xbc, 0x10, 0x2a, 0x94, 0x86,
+ 0xbc, 0x10, 0x2b, 0x94, 0x86, 0xbc, 0x10, 0x2e, 0x94, 0xad, 0xbc, 0x20, 0x17, 0x94,
+ 0xae, 0xbc, 0x20, 0x6f, 0x94, 0xb4, 0xbc, 0x20, 0x70, 0x94, 0xba, 0xbc, 0x20, 0x74,
+ 0x94, 0xc5, 0xbc, 0x20, 0x29, 0x94, 0xca, 0xbc, 0x2f, 0x07, 0x94, 0xe3, 0xbc, 0x2f,
+ 0x05, 0x94, 0xe9, 0xbc, 0x32, 0x0e, 0x94, 0xef, 0xbc, 0x40, 0x0d, 0xb4, 0x01, 0x00,
+ 0xbc, 0x40, 0x0f, 0xb4, 0x01, 0x11, 0xbc, 0x40, 0x10, 0xb4, 0x01, 0x4f, 0xbc, 0x40,
+ 0x21, 0xb4, 0x01, 0x61, 0xbc, 0x40, 0x2a, 0xb4, 0x01, 0x72, 0xbc, 0x40, 0x33, 0xb4,
+ 0x01, 0x8d, 0xbc, 0x40, 0x40, 0xb4, 0x01, 0x9f, 0xbc, 0x40, 0x34, 0xb4, 0x01, 0xb1,
+ 0xbc, 0x40, 0x35, 0xb4, 0x01, 0xcc, 0xbc, 0x40, 0x3d, 0xb4, 0x01, 0xe7, 0xbc, 0x40,
+ 0x3e, 0xb4, 0x01, 0xf9, 0xbc, 0x40, 0x3f, 0xb4, 0x02, 0x0b, 0x3c, 0xa2, 0x20, 0x72,
+ 0xa8, 0xab, 0x1c, 0x90, 0x02, 0xb4, 0x02, 0x17, 0xa2, 0x22, 0x72, 0xa8, 0x6b, 0xa2,
+ 0x14, 0x72, 0x88, 0x9c, 0x00, 0x43, 0x90, 0x00, 0x75, 0xb7, 0x00, 0x00, 0x1c, 0xa2,
+ 0x1a, 0x72, 0xa8, 0x9c, 0x00, 0x46, 0x90, 0x01, 0xa0, 0xc8, 0x1c, 0xfa, 0xa2, 0x1c,
+ 0x72, 0xa8, 0x9c, 0x00, 0x46, 0x90, 0x02, 0xa0, 0xc8, 0x1c, 0xfa, 0xa8, 0x1c, 0x95,
+ 0x36, 0xa2, 0x27, 0x72, 0x88, 0x94, 0x1f, 0xa8, 0x18, 0xb5, 0xb9, 0xcc, 0xa2, 0x52,
+ 0x74, 0xa8, 0x95, 0x47, 0xa8, 0x18, 0xb5, 0xb9, 0xc1, 0xa2, 0x3a, 0x74, 0xa8, 0x95,
+ 0x52, 0xa8, 0x18, 0xb5, 0xb9, 0xb6, 0xa2, 0x2c, 0x74, 0x88, 0xab, 0x1c, 0x90, 0x01,
+ 0xb4, 0x01, 0xba, 0xa8, 0x18, 0xb5, 0xb9, 0xa6, 0xa2, 0x27, 0x74, 0x88, 0x70, 0xa8,
+ 0x18, 0xb5, 0xb9, 0x9c, 0xaf, 0xcc, 0xaf, 0xca, 0xac, 0x74, 0xca, 0x82, 0x12, 0xca,
+ 0xf8, 0x92, 0x1c, 0xb5, 0xe2, 0x82, 0x3f, 0xca, 0x3f, 0xcc, 0x90, 0x06, 0xb4, 0x01,
+ 0x92, 0xa8, 0x18, 0xb5, 0xb9, 0x7e, 0xa2, 0x24, 0x74, 0xa8, 0x95, 0x95, 0xa8, 0x18,
+ 0xb5, 0xb9, 0x73, 0xa2, 0x33, 0x74, 0x88, 0x95, 0x43, 0x8c, 0x7c, 0x3c, 0xac, 0x78,
+ 0x3e, 0xa8, 0x18, 0xb5, 0xb9, 0x8a, 0xa2, 0x12, 0x78, 0xa8, 0x8c, 0x3c, 0x7c, 0xac,
+ 0x3e, 0x78, 0x95, 0xb7, 0x8c, 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8, 0x18, 0xb5, 0xb9,
+ 0x5f, 0xa2, 0x28, 0x76, 0x88, 0x8c, 0x3c, 0x7b, 0xac, 0x3e, 0x76, 0x95, 0xce, 0x8c,
+ 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8, 0x18, 0xb5, 0xb9, 0x48, 0xaf, 0xcc, 0xaf, 0xce,
+ 0xac, 0x76, 0xcc, 0x82, 0x46, 0xcc, 0xf8, 0x90, 0x09, 0xb5, 0x21, 0xe6, 0x94, 0x20,
+ 0x00, 0xab, 0x1c, 0xac, 0x76, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0x90, 0x09, 0xb5, 0x21,
+ 0xd5, 0x54, 0x00, 0x96, 0x1c, 0xfa, 0x3f, 0xce, 0x3f, 0xcc, 0x8c, 0x3c, 0x7b, 0xac,
+ 0x3e, 0x76, 0x95, 0xae, 0x90, 0x02, 0x95, 0x21, 0x90, 0x01, 0x75, 0x8c, 0x7b, 0x3c,
+ 0xac, 0x76, 0x3e, 0xa8, 0x18, 0xb5, 0xb9, 0x04, 0xa2, 0x62, 0x76, 0xa8, 0x8c, 0x3c,
+ 0x7b, 0xac, 0x3e, 0x76, 0xb4, 0xfe, 0xd4, 0x8c, 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8,
+ 0x18, 0xb5, 0xb8, 0xec, 0xa2, 0x3f, 0x76, 0x88, 0x8c, 0x3c, 0x7b, 0xac, 0x3e, 0x76,
+ 0x95, 0xe4, 0x8c, 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8, 0x18, 0xb5, 0xb8, 0xd5, 0xa2,
+ 0x30, 0x76, 0xa8, 0xab, 0x1c, 0xa2, 0x32, 0x76, 0xa8, 0xab, 0x1e, 0x8c, 0x3c, 0x7b,
+ 0xac, 0x3e, 0x76, 0x90, 0x04, 0x94, 0xba, 0x8c, 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8,
+ 0x18, 0xb5, 0xb8, 0xb4, 0xa2, 0x74, 0x76, 0x88, 0x8c, 0x3c, 0x7b, 0xac, 0x3e, 0x76,
+ 0xb4, 0xfe, 0xe1, 0x8c, 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8, 0x18, 0xb5, 0xb8, 0x9c,
+ 0xa2, 0x12, 0x76, 0x88, 0x8c, 0x3c, 0x7b, 0xac, 0x3e, 0x76, 0xb4, 0xfe, 0xc9, 0x8c,
+ 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8, 0x18, 0xb5, 0xb8, 0x84, 0xa2, 0x68, 0x76, 0xa8,
+ 0xab, 0x1c, 0xa2, 0x6a, 0x76, 0xa8, 0xab, 0x1e, 0x8c, 0x3c, 0x7b, 0xac, 0x3e, 0x76,
+ 0x90, 0x04, 0x94, 0x69, 0x8c, 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8, 0x18, 0xb5, 0xb8,
+ 0x63, 0xa2, 0x6c, 0x76, 0xa8, 0xab, 0x1c, 0xa2, 0x6e, 0x76, 0xa8, 0xab, 0x1e, 0x8c,
+ 0x3c, 0x7b, 0xac, 0x3e, 0x76, 0x90, 0x04, 0x94, 0x48, 0x8c, 0x7b, 0x3c, 0xac, 0x76,
+ 0x3e, 0xa8, 0x18, 0xb5, 0xb8, 0x42, 0xa2, 0x2c, 0x76, 0xa8, 0x8c, 0x3c, 0x7b, 0xac,
+ 0x3e, 0x76, 0xb4, 0xfe, 0x12, 0x8c, 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8, 0x18, 0xb5,
+ 0xb8, 0x2a, 0xa2, 0x4e, 0x76, 0xa8, 0x8c, 0x3c, 0x7b, 0xac, 0x3e, 0x76, 0xb4, 0xfd,
+ 0xfa, 0x8c, 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8, 0x18, 0xb5, 0xb8, 0x12, 0xa2, 0x2a,
+ 0x76, 0x88, 0x8c, 0x3c, 0x7b, 0xac, 0x3e, 0x76, 0xb4, 0xfd, 0xe2, 0x9a, 0x20, 0xb4,
+ 0x11, 0x76, 0xa2, 0x06, 0xcc, 0xa8, 0xbc, 0x10, 0x1a, 0x94, 0x8b, 0xbc, 0x10, 0x1b,
+ 0x94, 0xa9, 0xbc, 0x10, 0x1f, 0x94, 0xed, 0xbc, 0x1f, 0x07, 0xb4, 0x00, 0xff, 0xbc,
+ 0x1f, 0x08, 0xb4, 0x01, 0x02, 0xbc, 0x20, 0x20, 0xb4, 0x01, 0x05, 0xbc, 0x20, 0x76,
+ 0xb4, 0x01, 0x47, 0xbc, 0x20, 0x18, 0xb4, 0x01, 0x56, 0xbc, 0x20, 0x1d, 0xb4, 0x01,
+ 0x6d, 0xbc, 0x2f, 0x01, 0xb4, 0x01, 0x7c, 0xbc, 0x2f, 0x03, 0xb4, 0x01, 0x86, 0xbc,
+ 0x32, 0x15, 0xb4, 0x01, 0xba, 0xbc, 0x32, 0x16, 0xb4, 0x01, 0xb4, 0xbc, 0x32, 0x17,
+ 0xb4, 0x01, 0xae, 0xbc, 0x32, 0x13, 0xb4, 0x02, 0x21, 0xbc, 0x40, 0x0e, 0xb4, 0x02,
+ 0x3a, 0xbc, 0x40, 0x11, 0xb4, 0x02, 0x44, 0xbc, 0x40, 0x3a, 0xb4, 0x02, 0x69, 0xbc,
+ 0x40, 0x3b, 0xb4, 0x02, 0x73, 0xbc, 0x40, 0x15, 0xb4, 0x02, 0x7d, 0xbc, 0x40, 0x1f,
+ 0xb4, 0x02, 0x96, 0xbc, 0x4f, 0x02, 0xb4, 0x02, 0xca, 0xbc, 0x60, 0x00, 0x41, 0x3c,
+ 0xb5, 0xed, 0x17, 0x90, 0x01, 0xa2, 0x0e, 0x72, 0x8b, 0x3c, 0xa2, 0x0e, 0xcc, 0xa8,
+ 0x99, 0x01, 0xa2, 0x15, 0x72, 0x8b, 0xb7, 0x01, 0x06, 0x02, 0xb7, 0x00, 0x00, 0x04,
+ 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb5, 0xb8, 0xea, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x06,
+ 0x72, 0xab, 0x3c, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x08, 0x72, 0xab, 0xb7, 0x00, 0x00,
+ 0x0a, 0x80, 0x7e, 0x0a, 0xfc, 0x3c, 0xa8, 0x0a, 0xb5, 0xb7, 0x33, 0xa2, 0x4e, 0x76,
+ 0xa8, 0x9c, 0x00, 0x4d, 0x9c, 0x01, 0x4a, 0x9c, 0x02, 0x47, 0x9c, 0x03, 0x44, 0x9c,
+ 0x09, 0x41, 0x44, 0xa9, 0x0a, 0x95, 0x20, 0xac, 0x76, 0xce, 0xb5, 0xfa, 0x0f, 0x9d,
+ 0x00, 0x6c, 0xb7, 0x02, 0x01, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06,
+ 0xa8, 0x0a, 0xb5, 0xb8, 0x99, 0x7e, 0xa2, 0x10, 0xcc, 0xa8, 0xab, 0xca, 0xa2, 0x0e,
+ 0xcc, 0xa8, 0xb5, 0x10, 0x45, 0xa2, 0x02, 0x72, 0xab, 0xa8, 0xca, 0xa2, 0x04, 0x72,
+ 0xab, 0x3c, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x34, 0x72, 0xab, 0x3c, 0xa2, 0x0e, 0xcc,
+ 0x88, 0xa2, 0x30, 0x72, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xb6, 0xbb, 0xa2,
+ 0x0e, 0xcc, 0x88, 0xa2, 0x08, 0x74, 0x8b, 0xac, 0x74, 0xce, 0xb5, 0xd9, 0xcd, 0xa2,
+ 0x12, 0x72, 0x88, 0x9c, 0x02, 0x4a, 0xb5, 0xb6, 0xb7, 0xac, 0x76, 0xce, 0xb5, 0xd2,
+ 0xe6, 0x3c, 0xa2, 0x10, 0x72, 0x88, 0x9c, 0x02, 0x4a, 0xb5, 0xb6, 0xa6, 0xac, 0x76,
+ 0xce, 0xb5, 0xd2, 0xd5, 0x3c, 0xa2, 0x11, 0x72, 0x88, 0x9c, 0x02, 0x3c, 0xb5, 0xb6,
+ 0x95, 0xac, 0x76, 0xce, 0xb5, 0xd2, 0xc4, 0x3c, 0xb7, 0x04, 0x0d, 0x02, 0xa2, 0x0e,
+ 0xcc, 0x88, 0xab, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x0a, 0xcc, 0xa8, 0xb4, 0xb8,
+ 0x11, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xb6, 0x5e, 0xac, 0xcc, 0xca, 0xaf, 0xcc, 0xac,
+ 0x74, 0xcc, 0x82, 0x18, 0xcc, 0xf8, 0x82, 0x0e, 0xca, 0xf8, 0xb5, 0xdf, 0x41, 0x3f,
+ 0xcc, 0x3c, 0xb7, 0x04, 0x06, 0x02, 0xa2, 0x0e, 0xcc, 0xa8, 0xab, 0x04, 0xb7, 0x00,
+ 0x00, 0x06, 0xa2, 0x0a, 0xcc, 0xa8, 0xb4, 0xb7, 0xdf, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5,
+ 0xb6, 0x2c, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x20, 0x74, 0xab, 0x3c, 0xa2, 0x0a, 0xcc,
+ 0xa8, 0xb5, 0xb6, 0x1c, 0xa2, 0x06, 0x74, 0xa8, 0xa2, 0x0e, 0xcc, 0xfc, 0x3c, 0xa2,
+ 0x06, 0x74, 0xab, 0xa2, 0x52, 0x74, 0xa8, 0x9c, 0x03, 0x44, 0x9d, 0x03, 0x41, 0x3c,
+ 0xa2, 0x1e, 0x74, 0xa8, 0xaf, 0xc8, 0x90, 0x00, 0xa2, 0x1e, 0x74, 0xab, 0xac, 0x74,
+ 0xce, 0xb5, 0xd9, 0x12, 0x3f, 0xc8, 0xa2, 0x1e, 0x74, 0xab, 0xb4, 0xd9, 0x09, 0xa2,
+ 0x0a, 0xcc, 0xa8, 0xb5, 0xb6, 0x0a, 0xb7, 0x00, 0x00, 0x0c, 0x82, 0x01, 0x0c, 0xfc,
+ 0x3c, 0xa8, 0x0c, 0xb5, 0xb5, 0xd4, 0xa2, 0x54, 0x74, 0xa8, 0xa2, 0x0a, 0xcc, 0xfc,
+ 0x43, 0xa9, 0x0c, 0x75, 0xa2, 0x0c, 0x78, 0xa8, 0xa2, 0x42, 0x74, 0xab, 0xa2, 0x0e,
+ 0x78, 0xa8, 0xa2, 0x44, 0x74, 0xab, 0xa2, 0x08, 0x78, 0xa8, 0xa2, 0x3e, 0x74, 0xab,
+ 0xa2, 0x0a, 0x78, 0xa8, 0xa2, 0x40, 0x74, 0xab, 0xa2, 0x04, 0x78, 0xa8, 0xa2, 0x46,
+ 0x74, 0xab, 0xa2, 0x04, 0x78, 0xa8, 0xa2, 0x46, 0x74, 0xab, 0x90, 0x01, 0xab, 0x02,
+ 0xa2, 0x54, 0x74, 0xa8, 0xab, 0x04, 0xac, 0x74, 0x06, 0x82, 0x42, 0x06, 0xf8, 0xac,
+ 0x74, 0x08, 0x82, 0x3e, 0x08, 0xf8, 0xac, 0x74, 0x0a, 0x82, 0x46, 0x0a, 0xf8, 0xa2,
+ 0x22, 0x74, 0x88, 0xb5, 0xe8, 0x7b, 0x95, 0x59, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xb5,
+ 0x91, 0xa2, 0x10, 0xcc, 0xa8, 0xab, 0xca, 0xa2, 0x0e, 0xcc, 0xa8, 0xb5, 0x0e, 0xc2,
+ 0xa2, 0x00, 0x78, 0xab, 0xa8, 0xca, 0xa2, 0x02, 0x78, 0xab, 0x3c, 0xa2, 0x0a, 0xcc,
+ 0xa8, 0xb5, 0xb5, 0x5e, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x0c, 0x76, 0xab, 0x3c, 0xa2,
+ 0x0a, 0xcc, 0xa8, 0xb5, 0xb5, 0x4e, 0xac, 0xcc, 0xce, 0x82, 0x0e, 0xce, 0xf8, 0xa9,
+ 0xce, 0xd4, 0xa2, 0x13, 0x76, 0x8b, 0xa2, 0x01, 0xce, 0x88, 0xa2, 0x14, 0x76, 0x8b,
+ 0xa2, 0x02, 0xce, 0x88, 0xa2, 0x15, 0x76, 0x8b, 0xac, 0x76, 0xce, 0xb4, 0xd1, 0x5f,
+ 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xb5, 0x23, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x10, 0x76,
+ 0x8b, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xb5, 0x13, 0xa2, 0x0e, 0xcc, 0x88, 0xa2,
+ 0x11, 0x76, 0x8b, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xb5, 0x03, 0xa2, 0x10, 0xcc,
+ 0xa8, 0xab, 0xca, 0xa2, 0x0e, 0xcc, 0xa8, 0xb5, 0x0e, 0x48, 0xa2, 0x00, 0x76, 0xab,
+ 0xa8, 0xca, 0xa2, 0x02, 0x76, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xb4, 0xe4,
+ 0xa2, 0x0e, 0xcc, 0x88, 0x91, 0x00, 0x9c, 0x00, 0x91, 0x02, 0x9c, 0x01, 0x91, 0x01,
+ 0x9c, 0x02, 0x91, 0x08, 0x9c, 0x03, 0x91, 0x04, 0x9c, 0x04, 0xb1, 0x01, 0x00, 0xa8,
+ 0xca, 0xa2, 0x04, 0x76, 0xab, 0xb7, 0x02, 0x02, 0x02, 0xac, 0xca, 0x04, 0xb7, 0x00,
+ 0x00, 0x06, 0xa2, 0x0a, 0xcc, 0xa8, 0xb4, 0xb6, 0x49, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5,
+ 0xb4, 0xaa, 0xa2, 0x0a, 0x76, 0xa8, 0xa2, 0x0e, 0xcc, 0xfc, 0x3c, 0xa2, 0x0e, 0xcc,
+ 0xa8, 0xa2, 0x0a, 0x76, 0xab, 0xa2, 0x62, 0x76, 0xa8, 0x9d, 0x03, 0x44, 0x9c, 0x03,
+ 0x41, 0x3c, 0xa2, 0x17, 0x76, 0xa8, 0xaf, 0xc8, 0x90, 0x00, 0xa2, 0x17, 0x76, 0xab,
+ 0xac, 0x76, 0xce, 0xb5, 0xd0, 0xb1, 0x3f, 0xc8, 0xa2, 0x17, 0x76, 0xab, 0xb4, 0xd0,
+ 0xa8, 0xa2, 0x0c, 0xcc, 0xa8, 0xb6, 0xb8, 0x00, 0xab, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5,
+ 0xb4, 0x50, 0xa2, 0x04, 0xcc, 0xa8, 0xbc, 0x13, 0x13, 0x53, 0xbc, 0x13, 0x14, 0xb4,
+ 0x01, 0x5c, 0xbc, 0x13, 0x15, 0xb4, 0x01, 0xc3, 0xbc, 0x13, 0x16, 0xb4, 0x02, 0xf5,
+ 0x3c, 0xac, 0xcc, 0xce, 0x82, 0x16, 0xce, 0xf8, 0xa2, 0x04, 0x74, 0xa8, 0x9d, 0x00,
+ 0x3c, 0xd4, 0xa2, 0x08, 0x74, 0x8b, 0xa2, 0x02, 0xce, 0x88, 0xab, 0xca, 0xa2, 0x01,
+ 0xce, 0x88, 0x8c, 0xca, 0xc9, 0xa2, 0x04, 0x74, 0xab, 0xa2, 0x03, 0xce, 0x88, 0xa2,
+ 0x02, 0x74, 0x8b, 0xa2, 0x04, 0xce, 0xa8, 0xa2, 0x20, 0x74, 0xab, 0xa2, 0x06, 0xce,
+ 0xa8, 0xa2, 0x06, 0x74, 0xab, 0xb7, 0x00, 0x00, 0x02, 0xaf, 0xcc, 0xaf, 0xce, 0xac,
+ 0x74, 0xcc, 0x82, 0x58, 0xcc, 0xf8, 0x82, 0x08, 0xce, 0xf8, 0x80, 0x7e, 0x02, 0xfc,
+ 0x94, 0xf1, 0xd4, 0xc6, 0xa2, 0x01, 0xce, 0x88, 0xa2, 0x01, 0xcc, 0x8b, 0xa2, 0x02,
+ 0xce, 0x88, 0xa2, 0x02, 0xcc, 0x8b, 0xa2, 0x03, 0xce, 0x88, 0xa2, 0x03, 0xcc, 0x8b,
+ 0xa2, 0x01, 0xcc, 0x88, 0xa2, 0x22, 0x74, 0xdc, 0x94, 0x74, 0xa2, 0x01, 0xcc, 0x88,
+ 0x9c, 0x02, 0x94, 0xa0, 0xaf, 0x76, 0x88, 0x7b, 0xaf, 0xc8, 0xc4, 0xb5, 0xb3, 0xbe,
+ 0xac, 0x76, 0xca, 0x82, 0x1a, 0xca, 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04,
+ 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, 0x8b, 0x7b, 0x3f, 0x76, 0xa2, 0x22, 0x74,
+ 0x88, 0xa2, 0x02, 0xca, 0x8b, 0x90, 0x02, 0xa2, 0x03, 0xca, 0x8b, 0xa2, 0x03, 0xcc,
+ 0x88, 0x9c, 0x02, 0x94, 0x3f, 0xaf, 0x76, 0x88, 0x7b, 0xaf, 0xc8, 0xa2, 0x02, 0xcc,
+ 0x88, 0xb5, 0xb3, 0x82, 0xac, 0x76, 0xca, 0x82, 0x1a, 0xca, 0xf8, 0xa8, 0x02, 0x9c,
+ 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, 0x8b, 0x7b, 0x3f,
+ 0x76, 0xa2, 0x22, 0x74, 0x88, 0xad, 0xca, 0x8b, 0x90, 0x02, 0xa2, 0x01, 0xca, 0x8b,
+ 0xa9, 0x02, 0x82, 0x04, 0xcc, 0xf8, 0x82, 0x04, 0xce, 0xf8, 0x95, 0xa8, 0xaf, 0x74,
+ 0x88, 0x7a, 0xaf, 0xc8, 0xa2, 0x02, 0xcc, 0x88, 0xb5, 0xb3, 0x2f, 0xac, 0x74, 0xca,
+ 0x82, 0x58, 0xca, 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa,
+ 0xc8, 0x66, 0x3f, 0xc8, 0x8b, 0x7a, 0x3f, 0x74, 0x95, 0x3f, 0xaf, 0x74, 0x88, 0x7a,
+ 0xaf, 0xc8, 0xc4, 0xb5, 0xb3, 0x0a, 0xac, 0x74, 0xca, 0x82, 0x58, 0xca, 0xf8, 0xa8,
+ 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, 0x8b,
+ 0x7a, 0x3f, 0x74, 0x95, 0xa0, 0x3f, 0xce, 0x3f, 0xcc, 0xa2, 0x0c, 0x72, 0x89, 0x3c,
+ 0xac, 0xcc, 0xce, 0x82, 0x16, 0xce, 0xf8, 0xa2, 0x04, 0x74, 0xa8, 0x9c, 0x00, 0x3c,
+ 0x90, 0x01, 0xa2, 0x00, 0x74, 0x8b, 0xf4, 0xa2, 0x0a, 0x74, 0xab, 0xa2, 0x02, 0xce,
+ 0xa8, 0xa2, 0x0c, 0x74, 0xab, 0xa2, 0x04, 0xce, 0xa8, 0xa2, 0x0e, 0x74, 0xab, 0xa2,
+ 0x06, 0xce, 0xa8, 0xa2, 0x10, 0x74, 0xab, 0xa2, 0x08, 0xce, 0x88, 0xa2, 0x1e, 0x74,
+ 0x8b, 0xac, 0xce, 0xca, 0x82, 0x09, 0xca, 0xf8, 0xaf, 0xcc, 0xac, 0x74, 0xcc, 0x82,
+ 0x12, 0xcc, 0xf8, 0xb5, 0xdb, 0x90, 0xa2, 0x20, 0x72, 0xa8, 0x9c, 0x01, 0x41, 0x3c,
+ 0xb7, 0x04, 0x0e, 0x02, 0xb7, 0x00, 0x01, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22,
+ 0x74, 0x88, 0xb5, 0xb4, 0x2b, 0xac, 0x74, 0xce, 0xb4, 0xd5, 0x99, 0x90, 0x01, 0xa2,
+ 0x00, 0x74, 0xab, 0xa2, 0x52, 0x74, 0xa8, 0x9c, 0x03, 0xb4, 0x00, 0xff, 0x9c, 0x02,
+ 0xb4, 0x01, 0x0f, 0xb7, 0x00, 0x00, 0x02, 0xaf, 0xcc, 0xac, 0xcc, 0xce, 0x82, 0x16,
+ 0xcc, 0xf8, 0xac, 0x74, 0xcc, 0x82, 0x58, 0xcc, 0xf8, 0x82, 0x08, 0xce, 0xf8, 0x80,
+ 0x7e, 0x02, 0xfc, 0x94, 0xd4, 0xa2, 0x01, 0xcc, 0x88, 0xa2, 0x22, 0x74, 0xdc, 0x94,
+ 0x71, 0x9c, 0x02, 0x94, 0xa1, 0xaf, 0x76, 0x88, 0x7b, 0xaf, 0xc8, 0xc4, 0xb5, 0xb2,
+ 0x43, 0xac, 0x76, 0xca, 0x82, 0x1a, 0xca, 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82,
+ 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, 0x8b, 0x7b, 0x3f, 0x76, 0xa2, 0x02,
+ 0xcc, 0x88, 0xa2, 0x02, 0xca, 0x8b, 0xa2, 0x03, 0xcc, 0x88, 0xa2, 0x03, 0xca, 0x8b,
+ 0xa2, 0x03, 0xcc, 0x88, 0x9c, 0x02, 0x94, 0x3e, 0xaf, 0x76, 0x88, 0x7b, 0xaf, 0xc8,
+ 0xa2, 0x02, 0xcc, 0x88, 0xb5, 0xb2, 0x05, 0xac, 0x76, 0xca, 0x82, 0x1a, 0xca, 0xf8,
+ 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8,
+ 0x8b, 0x7b, 0x3f, 0x76, 0xc4, 0xad, 0xca, 0x8b, 0xa2, 0x01, 0xcc, 0x88, 0xa2, 0x01,
+ 0xca, 0x8b, 0xa9, 0x02, 0x82, 0x04, 0xcc, 0xf8, 0x82, 0x04, 0xce, 0xf8, 0x95, 0x8b,
+ 0xaf, 0x74, 0x88, 0x7a, 0xaf, 0xc8, 0xa2, 0x02, 0xcc, 0x88, 0xb5, 0xb1, 0xb3, 0xac,
+ 0x74, 0xca, 0x82, 0x58, 0xca, 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca,
+ 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, 0x8b, 0x7a, 0x3f, 0x74, 0x95, 0x3e, 0xaf, 0x74,
+ 0x88, 0x7a, 0xaf, 0xc8, 0xc4, 0xb5, 0xb1, 0x8e, 0xac, 0x74, 0xca, 0x82, 0x58, 0xca,
+ 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f,
+ 0xc8, 0x8b, 0x7a, 0x3f, 0x74, 0x95, 0xa1, 0x3f, 0xcc, 0xac, 0x74, 0xce, 0xb4, 0xd7,
+ 0x33, 0xa2, 0x2c, 0x72, 0xa8, 0x05, 0xa2, 0x2c, 0x72, 0xab, 0x04, 0x9d, 0x00, 0xb4,
+ 0xfe, 0xf7, 0xb5, 0xd5, 0x76, 0xb4, 0xfe, 0xf1, 0xa2, 0x2e, 0x72, 0xa8, 0x05, 0xa2,
+ 0x2e, 0x72, 0xab, 0x04, 0x9d, 0x00, 0xb4, 0xfe, 0xe2, 0xb5, 0xd5, 0x61, 0xb4, 0xfe,
+ 0xdc, 0xac, 0x74, 0xce, 0xb5, 0xd6, 0x9f, 0xa2, 0x0c, 0x72, 0x88, 0x05, 0xa2, 0x0c,
+ 0x72, 0x8b, 0x3c, 0xa2, 0x0c, 0xcc, 0xa8, 0xb6, 0xb8, 0x00, 0xab, 0xa2, 0x0a, 0xcc,
+ 0xa8, 0xb5, 0xb1, 0x36, 0xa2, 0x04, 0xcc, 0xa8, 0xbc, 0x13, 0x17, 0x53, 0xbc, 0x13,
+ 0x18, 0xb4, 0x02, 0x2c, 0xbc, 0x13, 0x19, 0xb4, 0x02, 0x48, 0xbc, 0x13, 0x1a, 0xb4,
+ 0x02, 0x85, 0x3c, 0xac, 0xcc, 0xce, 0x82, 0x16, 0xce, 0xf8, 0xa2, 0x08, 0x76, 0xa8,
+ 0x9d, 0x00, 0x3c, 0xf4, 0xa2, 0x06, 0x76, 0xab, 0x9c, 0x00, 0x4e, 0x9c, 0x01, 0x94,
+ 0x3c, 0x9c, 0x02, 0x94, 0x6a, 0x9c, 0x03, 0x94, 0x98, 0x94, 0xb6, 0xaf, 0xcc, 0xac,
+ 0x76, 0xcc, 0xa2, 0x03, 0xce, 0x88, 0x99, 0x01, 0xa2, 0x13, 0xcc, 0x8b, 0xa2, 0x04,
+ 0xce, 0x88, 0x99, 0x5b, 0xa2, 0x14, 0xcc, 0x8b, 0xa2, 0x05, 0xce, 0x88, 0x99, 0xdb,
+ 0xa2, 0x15, 0xcc, 0x8b, 0xa2, 0x0a, 0x72, 0x89, 0xa2, 0x22, 0x76, 0x88, 0xa2, 0x10,
+ 0x72, 0x8b, 0x3f, 0xcc, 0x3c, 0xaf, 0xcc, 0xac, 0x76, 0xcc, 0xa2, 0x03, 0xce, 0x88,
+ 0x99, 0x01, 0xa2, 0x13, 0xcc, 0x8b, 0xa2, 0x04, 0xce, 0x88, 0x99, 0x6d, 0xa2, 0x14,
+ 0xcc, 0x8b, 0xa2, 0x05, 0xce, 0x88, 0x99, 0xed, 0xa2, 0x15, 0xcc, 0x8b, 0xa2, 0x0a,
+ 0x72, 0x89, 0xa2, 0x22, 0x76, 0x88, 0xa2, 0x11, 0x72, 0x8b, 0x3f, 0xcc, 0x3c, 0xaf,
+ 0xcc, 0xac, 0x76, 0xcc, 0xa2, 0x03, 0xce, 0x88, 0x99, 0x01, 0xa2, 0x13, 0xcc, 0x8b,
+ 0xa2, 0x04, 0xce, 0x88, 0x99, 0x6d, 0xa2, 0x14, 0xcc, 0x8b, 0xa2, 0x05, 0xce, 0x88,
+ 0x99, 0x6d, 0xa2, 0x15, 0xcc, 0x8b, 0xa2, 0x0a, 0x72, 0x89, 0xa2, 0x22, 0x76, 0x88,
+ 0xa2, 0x12, 0x72, 0x8b, 0x3f, 0xcc, 0x3c, 0xaf, 0xcc, 0xac, 0x76, 0xcc, 0xa2, 0x03,
+ 0xce, 0x88, 0x99, 0x01, 0xa2, 0x13, 0xcc, 0x8b, 0xa2, 0x04, 0xce, 0x88, 0x99, 0x37,
+ 0xa2, 0x14, 0xcc, 0x8b, 0xa2, 0x0b, 0x72, 0x89, 0x3f, 0xcc, 0x3c, 0xa2, 0x06, 0xce,
+ 0xa8, 0xa2, 0x08, 0x76, 0xab, 0xa2, 0x08, 0xce, 0x88, 0xa2, 0x10, 0x76, 0x8b, 0xa2,
+ 0x09, 0xce, 0x88, 0xa2, 0x11, 0x76, 0x8b, 0xa2, 0x0a, 0xce, 0xa8, 0xa2, 0x0a, 0x76,
+ 0xab, 0xb7, 0x00, 0x00, 0x02, 0xaf, 0xcc, 0xaf, 0xce, 0xac, 0x76, 0xcc, 0x82, 0x1a,
+ 0xcc, 0xf8, 0x82, 0x0c, 0xce, 0xf8, 0x80, 0x7e, 0x02, 0xfc, 0x94, 0xef, 0xd4, 0xc6,
+ 0xa2, 0x01, 0xce, 0x88, 0xa2, 0x01, 0xcc, 0x8b, 0xa2, 0x02, 0xce, 0x88, 0xa2, 0x02,
+ 0xcc, 0x8b, 0xa2, 0x03, 0xce, 0x88, 0xa2, 0x03, 0xcc, 0x8b, 0xa2, 0x01, 0xcc, 0x88,
+ 0xa2, 0x22, 0x76, 0xdc, 0x94, 0xbf, 0x9c, 0x02, 0x94, 0x96, 0xaf, 0x76, 0x88, 0x7b,
+ 0xaf, 0xc8, 0xc4, 0xb5, 0xaf, 0xd6, 0xac, 0x76, 0xca, 0x82, 0x1a, 0xca, 0xf8, 0xa8,
+ 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, 0x8b,
+ 0x7b, 0x3f, 0x76, 0xa2, 0x22, 0x76, 0x88, 0xa2, 0x02, 0xca, 0x8b, 0x90, 0x04, 0xa2,
+ 0x03, 0xca, 0x8b, 0xa2, 0x03, 0xcc, 0x88, 0x9c, 0x02, 0x94, 0x35, 0xaf, 0x76, 0x88,
+ 0x7b, 0xaf, 0xc8, 0xa2, 0x02, 0xcc, 0x88, 0xb5, 0xaf, 0x9a, 0xac, 0x76, 0xca, 0x82,
+ 0x1a, 0xca, 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8,
+ 0x66, 0x3f, 0xc8, 0x8b, 0x7b, 0x3f, 0x76, 0xa2, 0x22, 0x76, 0x88, 0xad, 0xca, 0x8b,
+ 0x90, 0x04, 0xa2, 0x01, 0xca, 0x8b, 0x94, 0x4d, 0xaf, 0x74, 0x88, 0x7a, 0xaf, 0xc8,
+ 0xa2, 0x02, 0xcc, 0x88, 0xb5, 0xaf, 0x51, 0xac, 0x74, 0xca, 0x82, 0x58, 0xca, 0xf8,
+ 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8,
+ 0x8b, 0x7a, 0x3f, 0x74, 0x95, 0x35, 0xaf, 0x74, 0x88, 0x7a, 0xaf, 0xc8, 0xc4, 0xb5,
+ 0xaf, 0x2c, 0xac, 0x74, 0xca, 0x82, 0x58, 0xca, 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47,
+ 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, 0x8b, 0x7a, 0x3f, 0x74, 0x95,
+ 0x96, 0xa9, 0x02, 0x82, 0x04, 0xcc, 0xf8, 0x82, 0x04, 0xce, 0xf8, 0x95, 0xf3, 0x3f,
+ 0xce, 0x3f, 0xcc, 0xa2, 0x14, 0xce, 0xa8, 0xa2, 0x00, 0x76, 0xab, 0xa2, 0x16, 0xce,
+ 0xa8, 0xa2, 0x02, 0x76, 0xab, 0xa2, 0x18, 0xce, 0xa8, 0xa2, 0x04, 0x76, 0xab, 0x3c,
+ 0xac, 0xcc, 0xce, 0x82, 0x16, 0xce, 0xf8, 0xa2, 0x08, 0x76, 0xa8, 0x9c, 0x00, 0x3c,
+ 0x90, 0x01, 0xa2, 0x0e, 0x76, 0xab, 0xd4, 0xa2, 0x17, 0x76, 0x8b, 0xa2, 0x01, 0xce,
+ 0x88, 0xa2, 0x18, 0x76, 0x8b, 0x3c, 0x90, 0x00, 0xa2, 0x0e, 0x76, 0xab, 0xa2, 0x06,
+ 0x76, 0xa8, 0x9c, 0x03, 0x46, 0xac, 0x76, 0xce, 0xb4, 0xd5, 0x89, 0xa2, 0x62, 0x76,
+ 0xa8, 0x9c, 0x03, 0x44, 0x9c, 0x02, 0x53, 0x70, 0xa2, 0x28, 0x72, 0xa8, 0x05, 0xa2,
+ 0x28, 0x72, 0xab, 0x04, 0x9d, 0x00, 0x7d, 0xb5, 0xd2, 0xb5, 0x95, 0x21, 0xa2, 0x2a,
+ 0x72, 0xa8, 0x05, 0xa2, 0x2a, 0x72, 0xab, 0x04, 0x9d, 0x00, 0x95, 0x2f, 0xb5, 0xd2,
+ 0xa2, 0x95, 0x34, 0xac, 0xcc, 0xce, 0x82, 0x16, 0xce, 0xf8, 0xa2, 0x06, 0x76, 0xa8,
+ 0x9c, 0x00, 0x4d, 0x9c, 0x01, 0x5b, 0x9c, 0x02, 0x94, 0x27, 0x9c, 0x03, 0x94, 0x33,
+ 0x94, 0x3a, 0x88, 0x7e, 0xa2, 0x10, 0x72, 0x8b, 0xa2, 0x0a, 0x72, 0x88, 0x05, 0xa2,
+ 0x0a, 0x72, 0x8b, 0x94, 0x29, 0x88, 0x7e, 0xa2, 0x11, 0x72, 0x8b, 0xa2, 0x0a, 0x72,
+ 0x88, 0x05, 0xa2, 0x0a, 0x72, 0x8b, 0x59, 0x88, 0x7e, 0xa2, 0x12, 0x72, 0x8b, 0xa2,
+ 0x0a, 0x72, 0x88, 0x05, 0xa2, 0x0a, 0x72, 0x8b, 0x49, 0xa2, 0x0b, 0x72, 0x88, 0x05,
+ 0xa2, 0x0b, 0x72, 0x8b, 0xb7, 0x00, 0x00, 0x02, 0xaf, 0xcc, 0xaf, 0xce, 0xac, 0x76,
+ 0xcc, 0x82, 0x1a, 0xcc, 0xf8, 0x82, 0x0c, 0xce, 0xf8, 0x80, 0x7e, 0x02, 0xfc, 0x94,
+ 0xd6, 0xa2, 0x01, 0xcc, 0x88, 0xa2, 0x22, 0x76, 0xdc, 0x94, 0xc0, 0x9c, 0x02, 0x94,
+ 0x97, 0xaf, 0x76, 0x88, 0x7b, 0xaf, 0xc8, 0xc4, 0xb5, 0xae, 0x11, 0xac, 0x76, 0xca,
+ 0x82, 0x1a, 0xca, 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa,
+ 0xc8, 0x66, 0x3f, 0xc8, 0x8b, 0x7b, 0x3f, 0x76, 0xa2, 0x02, 0xcc, 0x88, 0xa2, 0x02,
+ 0xca, 0x8b, 0xa2, 0x03, 0xcc, 0x88, 0xa2, 0x03, 0xca, 0x8b, 0xa2, 0x03, 0xcc, 0x88,
+ 0x9c, 0x02, 0x94, 0x34, 0xaf, 0x76, 0x88, 0x7b, 0xaf, 0xc8, 0xa2, 0x02, 0xcc, 0x88,
+ 0xb5, 0xad, 0xd3, 0xac, 0x76, 0xca, 0x82, 0x1a, 0xca, 0xf8, 0xa8, 0x02, 0x9c, 0x00,
+ 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, 0x8b, 0x7b, 0x3f, 0x76,
+ 0xc4, 0xad, 0xca, 0x8b, 0xa2, 0x01, 0xcc, 0x88, 0xa2, 0x01, 0xca, 0x8b, 0x94, 0x4d,
+ 0xaf, 0x74, 0x88, 0x7a, 0xaf, 0xc8, 0xa2, 0x02, 0xcc, 0x88, 0xb5, 0xad, 0x8b, 0xac,
+ 0x74, 0xca, 0x82, 0x58, 0xca, 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca,
+ 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, 0x8b, 0x7a, 0x3f, 0x74, 0x95, 0x34, 0xaf, 0x74,
+ 0x88, 0x7a, 0xaf, 0xc8, 0xc4, 0xb5, 0xad, 0x66, 0xac, 0x74, 0xca, 0x82, 0x58, 0xca,
+ 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f,
+ 0xc8, 0x8b, 0x7a, 0x3f, 0x74, 0x95, 0x97, 0xa9, 0x02, 0x82, 0x04, 0xcc, 0xf8, 0x82,
+ 0x04, 0xce, 0xf8, 0x95, 0xda, 0x3f, 0xce, 0x3f, 0xcc, 0xac, 0x76, 0xce, 0xb4, 0xd3,
+ 0x99, 0xa2, 0x0c, 0xcc, 0xa8, 0xb6, 0xb8, 0x00, 0xab, 0x91, 0x00, 0xe4, 0x99, 0x3f,
+ 0x82, 0x02, 0xcc, 0xf8, 0x9c, 0x01, 0xb4, 0x01, 0xe0, 0xa2, 0x04, 0xcc, 0xa8, 0xbc,
+ 0x13, 0x11, 0x94, 0x35, 0xbc, 0x13, 0x13, 0x94, 0x33, 0xbc, 0x13, 0x14, 0x94, 0x2e,
+ 0xbc, 0x13, 0x15, 0x94, 0x29, 0xbc, 0x13, 0x16, 0x94, 0x24, 0xbc, 0x13, 0x17, 0x94,
+ 0x22, 0xbc, 0x13, 0x18, 0x5e, 0xbc, 0x13, 0x19, 0x5a, 0xbc, 0x13, 0x1a, 0x56, 0xbc,
+ 0x24, 0x11, 0x55, 0xbc, 0x24, 0x12, 0x3c, 0xbc, 0x24, 0x13, 0x94, 0xb6, 0xb4, 0x01,
+ 0xa1, 0xb4, 0xf4, 0xf2, 0xb4, 0xf8, 0x7c, 0xb4, 0xfb, 0xa7, 0xa2, 0x06, 0xcc, 0xa8,
+ 0x9c, 0x00, 0x55, 0x9c, 0x01, 0x94, 0x21, 0x9c, 0x02, 0x94, 0x2d, 0x9c, 0x04, 0x94,
+ 0x39, 0x9c, 0x05, 0x94, 0x57, 0x9c, 0x06, 0x94, 0x65, 0x3c, 0xb7, 0x01, 0x01, 0x02,
+ 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4, 0xae, 0x58, 0xb7, 0x01,
+ 0x02, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4, 0xae, 0x48,
+ 0xb7, 0x01, 0x04, 0x02, 0xb7, 0x00, 0x04, 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4,
+ 0xae, 0x38, 0xa2, 0x10, 0x72, 0x88, 0xab, 0xca, 0xb5, 0xac, 0x97, 0xa2, 0x29, 0x76,
+ 0x88, 0x9c, 0x02, 0x41, 0x3c, 0xb7, 0x02, 0x10, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7,
+ 0x00, 0x00, 0x06, 0xa8, 0xca, 0xb4, 0xae, 0x16, 0xa2, 0x11, 0x72, 0x88, 0xab, 0xca,
+ 0xb5, 0xac, 0x75, 0xa2, 0x29, 0x76, 0x88, 0x9c, 0x02, 0x95, 0x20, 0x3c, 0xb7, 0x00,
+ 0x00, 0x02, 0x80, 0x7e, 0x02, 0xfc, 0x3c, 0xa8, 0x02, 0xb5, 0xac, 0x5e, 0xa2, 0x06,
+ 0x76, 0xa8, 0x9c, 0x03, 0x43, 0xa9, 0x02, 0x73, 0xb7, 0x02, 0x10, 0x02, 0xb7, 0x00,
+ 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x02, 0xb5, 0xad, 0xdb, 0x74, 0xa2, 0x06,
+ 0xcc, 0xa8, 0x9c, 0x00, 0x51, 0x9c, 0x01, 0x94, 0x7e, 0x9c, 0x02, 0x94, 0x8d, 0x9c,
+ 0x03, 0x94, 0x9c, 0x9c, 0x04, 0x94, 0xb3, 0x3c, 0xa2, 0x0e, 0xcc, 0x88, 0x9c, 0x00,
+ 0x51, 0x9c, 0x01, 0x5d, 0x9c, 0x02, 0x94, 0x28, 0x9c, 0x03, 0x94, 0x32, 0x9c, 0x04,
+ 0x94, 0x3c, 0x94, 0x48, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xac, 0x0b, 0x90, 0x02, 0xa2,
+ 0x04, 0x76, 0xab, 0x94, 0x39, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xab, 0xfc, 0x90, 0x01,
+ 0xa2, 0x04, 0x76, 0xab, 0x94, 0x2a, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xab, 0xed, 0x90,
+ 0x08, 0xa2, 0x04, 0x76, 0xab, 0x5c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xab, 0xdf, 0x90,
+ 0x04, 0xa2, 0x04, 0x76, 0xab, 0x4e, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xab, 0xd1, 0xb0,
+ 0x01, 0x00, 0xa2, 0x04, 0x76, 0xab, 0xab, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xb7, 0x02,
+ 0x02, 0x02, 0xa2, 0x0a, 0xcc, 0xa8, 0xb4, 0xad, 0x51, 0xb7, 0x00, 0x00, 0x04, 0xb7,
+ 0x00, 0x00, 0x06, 0xb7, 0x02, 0x0f, 0x02, 0xa2, 0x0a, 0xcc, 0xa8, 0xb4, 0xad, 0x3e,
+ 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xb7, 0x02, 0x10, 0x02, 0xa2, 0x0a,
+ 0xcc, 0xa8, 0xb4, 0xad, 0x2b, 0xa2, 0x20, 0x72, 0xa8, 0x9c, 0x01, 0x41, 0x3c, 0xb7,
+ 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xb7, 0x02, 0x01, 0x02, 0xa2, 0x0a, 0xcc,
+ 0xa8, 0xb4, 0xad, 0x10, 0xa2, 0x20, 0x72, 0xa8, 0x9c, 0x01, 0x41, 0x3c, 0xb7, 0x00,
+ 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xb7, 0x02, 0x04, 0x02, 0xa2, 0x0a, 0xcc, 0xa8,
+ 0xb4, 0xac, 0xf5, 0x90, 0x00, 0xa2, 0x0c, 0xcc, 0xab, 0xb4, 0xfe, 0x0b, 0x3c, 0xa2,
+ 0x02, 0xcc, 0xa8, 0xbc, 0x10, 0x15, 0xb4, 0x01, 0x40, 0xbc, 0x10, 0x16, 0xb4, 0x01,
+ 0x43, 0xbc, 0x10, 0x17, 0xb4, 0x01, 0x46, 0xbc, 0x10, 0x18, 0xb4, 0x01, 0x49, 0xbc,
+ 0x10, 0x1a, 0xb4, 0x01, 0x4c, 0xbc, 0x10, 0x1b, 0xb4, 0x01, 0x4f, 0xbc, 0x10, 0x22,
+ 0xb4, 0x01, 0x52, 0xbc, 0x10, 0x1f, 0xb4, 0x01, 0x5e, 0xbc, 0xf4, 0x07, 0xb4, 0x01,
+ 0x70, 0xbc, 0xf4, 0x08, 0xb4, 0x01, 0x73, 0xbc, 0x20, 0x20, 0xb4, 0x01, 0x7c, 0xbc,
+ 0x20, 0x22, 0xb4, 0x01, 0x88, 0xbc, 0x20, 0x75, 0xb4, 0x01, 0x92, 0xbc, 0x20, 0x76,
+ 0xb4, 0x01, 0x9c, 0xbc, 0xf4, 0x01, 0x94, 0x83, 0xbc, 0xf4, 0x04, 0xb4, 0x03, 0xd6,
+ 0xbc, 0x20, 0x0d, 0xb4, 0x01, 0x9b, 0xbc, 0x20, 0x0e, 0xb4, 0x01, 0xad, 0xbc, 0x20,
+ 0x16, 0xb4, 0x01, 0xbf, 0xbc, 0x20, 0x29, 0x94, 0x86, 0xbc, 0x32, 0x15, 0xb4, 0x01,
+ 0xe1, 0xbc, 0x32, 0x16, 0xb4, 0x01, 0xf3, 0xbc, 0x32, 0x17, 0xb4, 0x02, 0x05, 0xbc,
+ 0x32, 0x13, 0xb4, 0x02, 0x17, 0xbc, 0x40, 0x0c, 0xb4, 0x02, 0x29, 0xbc, 0x40, 0x0e,
+ 0xb4, 0x02, 0x33, 0xbc, 0x40, 0x11, 0xb4, 0x02, 0x3d, 0xbc, 0x40, 0x1d, 0xb4, 0x02,
+ 0xdc, 0xbc, 0x40, 0x3a, 0xb4, 0x02, 0xe6, 0xbc, 0x40, 0x3b, 0xb4, 0x02, 0xf0, 0xbc,
+ 0x40, 0x41, 0xb4, 0x02, 0xfa, 0xbc, 0x40, 0x15, 0xb4, 0x03, 0x04, 0xbc, 0xf4, 0x02,
+ 0x94, 0x29, 0xbc, 0x40, 0x1f, 0xb4, 0x03, 0x23, 0xbc, 0x40, 0x13, 0xb4, 0x03, 0x46,
+ 0xbc, 0x40, 0x17, 0xb4, 0x03, 0x50, 0xbc, 0xf4, 0x03, 0xb4, 0x03, 0x90, 0x3c, 0xa2,
+ 0x0a, 0xcc, 0xa8, 0xb5, 0xaa, 0x58, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x20, 0x74, 0x8b,
+ 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xaa, 0x5c, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x0a,
+ 0x76, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xaa, 0x38, 0xaf, 0xcc, 0xac, 0xcc,
+ 0xca, 0x82, 0x0e, 0xca, 0xf8, 0xac, 0x74, 0xcc, 0x82, 0x12, 0xcc, 0xf8, 0xb5, 0xd3,
+ 0x1b, 0xac, 0x74, 0xcc, 0x82, 0x12, 0xcc, 0xf8, 0xac, 0x74, 0xce, 0xb5, 0xdc, 0xc4,
+ 0xb2, 0x12, 0x10, 0xa8, 0xcc, 0xb8, 0xfe, 0x80, 0xab, 0xcc, 0x9a, 0x0c, 0xab, 0xca,
+ 0x30, 0x0b, 0x97, 0xb0, 0xcc, 0x97, 0xfe, 0xca, 0x30, 0x03, 0x3f, 0xcc, 0x3c, 0x00,
+ 0x96, 0xcc, 0x14, 0x05, 0xe1, 0x66, 0x3c, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x0c, 0x72,
+ 0x8b, 0x3c, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x0a, 0x72, 0x8b, 0x3c, 0xa2, 0x0e, 0xcc,
+ 0x88, 0xa2, 0x0b, 0x72, 0x8b, 0x3c, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x0d, 0x72, 0x8b,
+ 0x3c, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x06, 0x72, 0xab, 0x3c, 0xa2, 0x0e, 0xcc, 0xa8,
+ 0xa2, 0x08, 0x72, 0xab, 0x3c, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x00, 0x72, 0x8b, 0x9d,
+ 0x00, 0xb5, 0xd8, 0x8f, 0xa2, 0x00, 0x72, 0x8b, 0x3c, 0xa2, 0x10, 0xcc, 0xa8, 0xab,
+ 0xca, 0xa2, 0x0e, 0xcc, 0xa8, 0xb5, 0x03, 0x06, 0xa2, 0x02, 0x72, 0xab, 0xa8, 0xca,
+ 0xa2, 0x04, 0x72, 0xab, 0x3c, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x34, 0x72, 0x8b, 0x3c,
+ 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x30, 0x72, 0xab, 0x90, 0x00, 0xa2, 0x0f, 0x72, 0x8b,
+ 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa9, 0x76, 0xa2, 0x0e, 0xcc, 0x88, 0x99, 0x37,
+ 0xa2, 0x08, 0x74, 0x8b, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa9, 0x64, 0xa2, 0x0e,
+ 0xcc, 0xa8, 0xa2, 0x04, 0x74, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa9, 0x54,
+ 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x00, 0x74, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5,
+ 0xa9, 0x44, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x02, 0x74, 0x8b, 0x3c, 0xa2, 0x0a, 0xcc,
+ 0xa8, 0xb5, 0xa9, 0x34, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x0a, 0x74, 0xab, 0xa2, 0x10,
+ 0xcc, 0xa8, 0xa2, 0x0c, 0x74, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa9, 0x1c,
+ 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x0e, 0x74, 0xab, 0xa2, 0x10, 0xcc, 0xa8, 0xa2, 0x10,
+ 0x74, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa9, 0x04, 0xa2, 0x0e, 0xcc, 0x88,
+ 0xa2, 0x1e, 0x74, 0x8b, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa8, 0xf4, 0xaf, 0xcc,
+ 0xac, 0xcc, 0xca, 0x82, 0x0e, 0xcc, 0xf8, 0xac, 0x74, 0xca, 0x82, 0x12, 0xca, 0xf8,
+ 0xb5, 0xd1, 0xd7, 0x3f, 0xcc, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa8, 0xff, 0xa2,
+ 0x0e, 0xcc, 0xa8, 0xa2, 0x0c, 0x78, 0xab, 0xa2, 0x10, 0xcc, 0xa8, 0xa2, 0x0e, 0x78,
+ 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa8, 0xe7, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2,
+ 0x08, 0x78, 0xab, 0xa2, 0x10, 0xcc, 0xa8, 0xa2, 0x0a, 0x78, 0xab, 0x3c, 0xa2, 0x0a,
+ 0xcc, 0xa8, 0xb5, 0xa8, 0xcf, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x04, 0x78, 0xab, 0xa2,
+ 0x10, 0xcc, 0xa8, 0xa2, 0x06, 0x78, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa8,
+ 0xb7, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x00, 0x78, 0xab, 0xa2, 0x10, 0xcc, 0xa8, 0xa2,
+ 0x02, 0x78, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa8, 0x8b, 0xa2, 0x0e, 0xcc,
+ 0xa8, 0xa2, 0x06, 0x76, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa8, 0x7b, 0xa2,
+ 0x0e, 0xcc, 0x88, 0xa2, 0x0c, 0x76, 0x8b, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa8,
+ 0x6b, 0xa2, 0x06, 0x76, 0xa8, 0x9c, 0x00, 0x4d, 0x9c, 0x01, 0x94, 0x30, 0x9c, 0x02,
+ 0x94, 0x53, 0x9c, 0x03, 0x94, 0x76, 0x3c, 0xa2, 0x0f, 0xcc, 0x88, 0x99, 0x01, 0xa2,
+ 0x13, 0x76, 0x8b, 0xa2, 0x10, 0xcc, 0x88, 0x99, 0x5b, 0xa2, 0x14, 0x76, 0x8b, 0xa2,
+ 0x11, 0xcc, 0x88, 0x99, 0xdb, 0xa2, 0x15, 0x76, 0x8b, 0xa2, 0x22, 0x76, 0x88, 0xa2,
+ 0x10, 0x72, 0x8b, 0x3c, 0xa2, 0x0f, 0xcc, 0x88, 0x99, 0x01, 0xa2, 0x13, 0x76, 0x8b,
+ 0xa2, 0x10, 0xcc, 0x88, 0x99, 0x6d, 0xa2, 0x14, 0x76, 0x8b, 0xa2, 0x11, 0xcc, 0x88,
+ 0x99, 0xed, 0xa2, 0x15, 0x76, 0x8b, 0xa2, 0x22, 0x76, 0x88, 0xa2, 0x11, 0x72, 0x8b,
+ 0x3c, 0xa2, 0x0f, 0xcc, 0x88, 0x99, 0x01, 0xa2, 0x13, 0x76, 0x8b, 0xa2, 0x10, 0xcc,
+ 0x88, 0x99, 0x6d, 0xa2, 0x14, 0x76, 0x8b, 0xa2, 0x11, 0xcc, 0x88, 0x99, 0x6d, 0xa2,
+ 0x15, 0x76, 0x8b, 0xa2, 0x22, 0x76, 0x88, 0xa2, 0x12, 0x72, 0x8b, 0x3c, 0xa2, 0x0f,
+ 0xcc, 0x88, 0x99, 0x01, 0xa2, 0x13, 0x76, 0x8b, 0xa2, 0x10, 0xcc, 0x88, 0x99, 0x37,
+ 0xa2, 0x14, 0x76, 0x8b, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa7, 0xc6, 0xa2, 0x0e,
+ 0xcc, 0xa8, 0xa2, 0x08, 0x76, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa7, 0xb6,
+ 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x10, 0x76, 0x8b, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5,
+ 0xa7, 0xa6, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x11, 0x76, 0x8b, 0x3c, 0xa2, 0x0a, 0xcc,
+ 0xa8, 0xb5, 0xa7, 0x96, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x0e, 0x76, 0xab, 0x3c, 0xa2,
+ 0x0a, 0xcc, 0xa8, 0xb5, 0xa7, 0x86, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x00, 0x76, 0xab,
+ 0xa2, 0x10, 0xcc, 0xa8, 0xa2, 0x02, 0x76, 0xab, 0xa2, 0x0e, 0xcc, 0xfa, 0x9d, 0x00,
+ 0x3c, 0x90, 0x02, 0x01, 0xa2, 0x0c, 0x76, 0xd9, 0xa2, 0x0c, 0x76, 0x8b, 0x3c, 0xa2,
+ 0x0a, 0xcc, 0xa8, 0xb5, 0xa7, 0x5c, 0x91, 0x02, 0xa2, 0x0e, 0xcc, 0x88, 0x9c, 0x00,
+ 0x91, 0x02, 0x9c, 0x01, 0x91, 0x01, 0x9c, 0x02, 0x91, 0x08, 0x9c, 0x03, 0x91, 0x04,
+ 0x9c, 0x04, 0xb1, 0x01, 0x00, 0xa8, 0xca, 0xa2, 0x04, 0x76, 0xab, 0x3c, 0xa2, 0x0a,
+ 0xcc, 0xa8, 0xb5, 0xa7, 0x33, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x17, 0x76, 0x8b, 0x3c,
+ 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa7, 0x23, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x18, 0x76,
+ 0x8b, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa6, 0xff, 0xac, 0x74, 0xce, 0x82, 0x58,
+ 0xce, 0xf8, 0xa2, 0x12, 0xcc, 0x88, 0xe7, 0xe7, 0xa0, 0xc8, 0xce, 0xf8, 0xa2, 0x0e,
+ 0xcc, 0x88, 0xd6, 0xa2, 0x0f, 0xcc, 0x88, 0xa2, 0x01, 0xce, 0x8b, 0xa2, 0x10, 0xcc,
+ 0x88, 0xa2, 0x02, 0xce, 0x8b, 0xa2, 0x11, 0xcc, 0x88, 0xa2, 0x03, 0xce, 0x8b, 0x3c,
+ 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa6, 0xdd, 0xac, 0x76, 0xce, 0x82, 0x1a, 0xce, 0xf8,
+ 0xa2, 0x12, 0xcc, 0x88, 0xe7, 0xe7, 0xa0, 0xc8, 0xce, 0xf8, 0xa2, 0x0e, 0xcc, 0x88,
+ 0xd6, 0xa2, 0x0f, 0xcc, 0x88, 0xa2, 0x01, 0xce, 0x8b, 0xa2, 0x10, 0xcc, 0x88, 0xa2,
+ 0x02, 0xce, 0x8b, 0xa2, 0x11, 0xcc, 0x88, 0xa2, 0x03, 0xce, 0x8b, 0x3c, 0x03, 0xae,
+ 0xca, 0xd7, 0xae, 0xca, 0xd7, 0x03, 0xae, 0xca, 0xd7, 0xae, 0xca, 0xd7, 0x03, 0xae,
+ 0xca, 0xd7, 0xae, 0xca, 0xd7, 0x3c, 0x00, 0xb6, 0xfe, 0x20, 0xab, 0xb6, 0xfe, 0x00,
+ 0xab, 0xb6, 0xfe, 0x90, 0xab, 0x3c, 0xac, 0xcc, 0x28, 0xac, 0xce, 0x2a, 0xab, 0xce,
+ 0xa8, 0x16, 0xb8, 0x00, 0x0a, 0x96, 0x14, 0xfc, 0x94, 0x45, 0xb2, 0xcf, 0x1e, 0xa0,
+ 0x16, 0xcc, 0xf8, 0xa8, 0x1a, 0xa2, 0x02, 0xcc, 0xab, 0xa8, 0x18, 0xa2, 0x01, 0xcc,
+ 0x8b, 0xa8, 0xce, 0xc6, 0xa8, 0x1c, 0xa2, 0x04, 0xcc, 0xab, 0xa8, 0x1e, 0xa2, 0x06,
+ 0xcc, 0xab, 0xa8, 0x20, 0xa2, 0x08, 0xcc, 0xab, 0xa8, 0x16, 0xb8, 0x00, 0x0a, 0xbc,
+ 0x0b, 0xae, 0x00, 0xab, 0x16, 0xaf, 0xca, 0xb1, 0x04, 0x00, 0xb5, 0x58, 0x5e, 0x3f,
+ 0xca, 0x00, 0xac, 0x28, 0xcc, 0xac, 0x2a, 0xce, 0x3c, 0xb6, 0xff, 0x04, 0xa9, 0x76,
+ 0x00, 0xab, 0x14, 0xab, 0x16, 0xa7, 0xcf, 0x1e, 0xda, 0xd6, 0x00, 0xe1, 0x62, 0x3c,
+ 0xa8, 0x14, 0x96, 0x16, 0xfc, 0x42, 0x00, 0x3c, 0x90, 0x01, 0x63, 0x34, 0x0b, 0x9c,
+ 0x00, 0x44, 0xb0, 0xf0, 0x00, 0x3c, 0xb6, 0xfe, 0x00, 0x88, 0x96, 0xc8, 0x17, 0x94,
+ 0x35, 0xaf, 0xce, 0xaf, 0xcc, 0xa7, 0xfe, 0x00, 0xfe, 0x0e, 0xb3, 0xcf, 0x1e, 0xa0,
+ 0x14, 0xce, 0xf8, 0xf0, 0xb9, 0xff, 0x7f, 0xe1, 0xf0, 0xe1, 0xf0, 0xe1, 0xf0, 0xe1,
+ 0xf0, 0xe1, 0xa8, 0x14, 0xb8, 0x00, 0x0a, 0xbc, 0x0b, 0xae, 0x00, 0xab, 0x14, 0xb3,
+ 0xfe, 0x00, 0xd4, 0x9a, 0x80, 0xd6, 0x3f, 0xcc, 0x3f, 0xce, 0x00, 0x3c, 0xb0, 0xf0,
+ 0x01, 0x3c, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xac, 0xce, 0xcc, 0x82,
+ 0x48, 0xcc, 0xf8, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4, 0xcf, 0x24, 0xa2, 0x35, 0xce,
+ 0x88, 0x9c, 0x00, 0x94, 0x22, 0xa2, 0x3f, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x23, 0xb7,
+ 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xac, 0xce, 0xcc, 0x82, 0x48, 0xcc, 0xf8,
+ 0xb7, 0x02, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, 0xce, 0xfa, 0xb7, 0x1e, 0x84,
+ 0x02, 0xb7, 0x00, 0x00, 0x04, 0x7a, 0xb7, 0x31, 0x2d, 0x02, 0xb7, 0x00, 0x01, 0x04,
+ 0x95, 0x23, 0xa2, 0x35, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xb7, 0x09, 0xc4, 0x02, 0xb7,
+ 0x00, 0x00, 0x04, 0xac, 0xce, 0xcc, 0x82, 0x48, 0xcc, 0xf8, 0xb7, 0x02, 0x00, 0x06,
+ 0xa2, 0x22, 0xce, 0x88, 0xb4, 0xce, 0xc6, 0xb7, 0x62, 0x5a, 0x08, 0xb7, 0x00, 0x02,
+ 0x0a, 0xa2, 0x35, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x35, 0xa2, 0x36, 0xce, 0x88, 0x9c,
+ 0x00, 0x94, 0x77, 0xa2, 0x38, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x25, 0xa2, 0x37, 0xce,
+ 0x88, 0x9d, 0x00, 0x5e, 0xa2, 0x34, 0xce, 0x88, 0x9c, 0x07, 0x4a, 0xf4, 0xab, 0x08,
+ 0xa2, 0x02, 0xce, 0xa8, 0xab, 0x0a, 0x4d, 0xa2, 0x24, 0xce, 0xa8, 0xab, 0x08, 0xa2,
+ 0x26, 0xce, 0xa8, 0xab, 0x0a, 0x40, 0xa2, 0x54, 0xce, 0x88, 0x9c, 0x00, 0x47, 0xa2,
+ 0x34, 0xce, 0x88, 0x9c, 0x07, 0x58, 0xac, 0x08, 0x02, 0xac, 0x0a, 0x04, 0xac, 0xce,
+ 0xcc, 0x82, 0x48, 0xcc, 0xf8, 0xb7, 0x02, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4,
+ 0xce, 0x5b, 0x86, 0x12, 0x60, 0x08, 0xfc, 0x41, 0x7e, 0x86, 0x04, 0xa8, 0x0a, 0xfc,
+ 0x42, 0x95, 0x25, 0xb7, 0x01, 0x07, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00,
+ 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xa6, 0x60, 0x95, 0x3a, 0xb7, 0x00, 0x4e, 0x08,
+ 0xb7, 0x00, 0x00, 0x0a, 0x95, 0x52, 0xa2, 0x35, 0xce, 0x88, 0x9c, 0x00, 0x5a, 0xb7,
+ 0x00, 0x4e, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xac, 0xce, 0xcc, 0x82, 0x48, 0xcc, 0xf8,
+ 0xb7, 0x02, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, 0xce, 0x0c, 0xb7, 0x62, 0x5a,
+ 0x02, 0xb7, 0x00, 0x02, 0x04, 0x7a, 0x95, 0x2a, 0x95, 0x2c, 0xa2, 0x35, 0xce, 0x88,
+ 0x9c, 0x00, 0x94, 0x21, 0xa2, 0x3a, 0xce, 0x88, 0x9d, 0x00, 0x5a, 0xb7, 0x00, 0x4e,
+ 0x02, 0xb7, 0x00, 0x00, 0x04, 0xac, 0xce, 0xcc, 0x82, 0x48, 0xcc, 0xf8, 0xb7, 0x02,
+ 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, 0xcd, 0xd6, 0xa2, 0x35, 0xce, 0x88, 0x9c,
+ 0x00, 0x4a, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0x95, 0x21, 0xb7, 0x62,
+ 0x5a, 0x02, 0xb7, 0x00, 0x02, 0x04, 0x95, 0x2b, 0x90, 0x00, 0xa2, 0x50, 0xce, 0xab,
+ 0xb5, 0xd3, 0xf7, 0x90, 0x00, 0xb5, 0xd3, 0xca, 0x91, 0x02, 0xa2, 0x22, 0xce, 0x88,
+ 0xb5, 0xd4, 0xdc, 0xb7, 0x03, 0x01, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00,
+ 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xa5, 0xb8, 0xb7, 0x03, 0x02, 0x02, 0xb7, 0x00,
+ 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xa5, 0xa5, 0x90,
+ 0x00, 0xa2, 0x3f, 0xce, 0x8b, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x21,
+ 0xb5, 0xe9, 0x4d, 0x90, 0x00, 0xa2, 0x2a, 0xce, 0x8b, 0xa2, 0x22, 0xce, 0x88, 0xab,
+ 0x18, 0xb0, 0x40, 0x3f, 0xb5, 0xe9, 0x3b, 0x90, 0x00, 0xa2, 0x2c, 0xce, 0xab, 0xa2,
+ 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3d, 0xb5, 0xe9, 0x29, 0x90, 0x00, 0xa2,
+ 0x4e, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3e, 0xb5, 0xe9,
+ 0x17, 0xb7, 0x05, 0x02, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2,
+ 0x22, 0xce, 0x88, 0xb5, 0xa5, 0x4a, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04,
+ 0xb7, 0x02, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xac, 0xce, 0xcc, 0x82, 0x4c, 0xcc,
+ 0xf8, 0xb5, 0xcd, 0x09, 0xb4, 0xfd, 0xcb, 0x90, 0x00, 0xb5, 0xd3, 0x1e, 0x91, 0x02,
+ 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xd4, 0x30, 0x90, 0x07, 0xa2, 0x50, 0xce, 0xab, 0xb5,
+ 0xd3, 0x34, 0xb7, 0x03, 0x01, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06,
+ 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xa5, 0x03, 0xb7, 0x03, 0x02, 0x02, 0xb7, 0x00, 0x00,
+ 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xa4, 0xf0, 0x90, 0x00,
+ 0xa2, 0x3f, 0xce, 0x8b, 0xa2, 0x35, 0xce, 0x8b, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18,
+ 0xb0, 0x40, 0x21, 0xb5, 0xe8, 0x94, 0x90, 0x00, 0xa2, 0x29, 0xce, 0x8b, 0xa2, 0x54,
+ 0xce, 0x88, 0x9c, 0x00, 0x94, 0x1f, 0xa2, 0x34, 0x72, 0xa8, 0x9c, 0x01, 0x41, 0x44,
+ 0xa2, 0x22, 0xce, 0x88, 0xb7, 0x01, 0x07, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00,
+ 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xa4, 0xad, 0xa2, 0x2a, 0xce, 0x88, 0x9c,
+ 0x00, 0x94, 0x42, 0x90, 0x01, 0xa2, 0x4e, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab,
+ 0x18, 0xb0, 0x40, 0x3e, 0xb5, 0xe8, 0x4d, 0xb7, 0x05, 0x02, 0x02, 0xb7, 0x00, 0x00,
+ 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xa4, 0x80, 0xb7, 0x00,
+ 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x02, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88,
+ 0xac, 0xce, 0xcc, 0x82, 0x4c, 0xcc, 0xf8, 0xb5, 0xcc, 0x3f, 0xb4, 0xfd, 0x18, 0xa2,
+ 0x2c, 0xce, 0xa8, 0x9c, 0x01, 0x95, 0x48, 0x90, 0x01, 0xa2, 0x2c, 0xce, 0xab, 0xa2,
+ 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3d, 0xb5, 0xe8, 0x03, 0x95, 0x5c, 0xa2,
+ 0x14, 0x72, 0x88, 0x9d, 0x00, 0x95, 0xec, 0x90, 0x01, 0xa2, 0x41, 0xce, 0x8b, 0x3c,
+ 0x90, 0x30, 0x01, 0xab, 0xca, 0xa2, 0x50, 0xce, 0xa8, 0x96, 0xca, 0xf9, 0xa2, 0x50,
+ 0xce, 0xab, 0xb5, 0xd2, 0x43, 0x00, 0xa2, 0x35, 0xce, 0x8b, 0x90, 0x02, 0xa2, 0x4e,
+ 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3e, 0xb5, 0xe7, 0xc8,
+ 0xb7, 0x05, 0x02, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22,
+ 0xce, 0x88, 0xb5, 0xa3, 0xfb, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7,
+ 0x02, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xac, 0xce, 0xcc, 0x82, 0x4c, 0xcc, 0xf8,
+ 0xb5, 0xcb, 0xba, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xd2, 0x5e, 0xa2, 0x2e, 0xce, 0xab,
+ 0xab, 0xca, 0x99, 0x06, 0x9c, 0x00, 0x48, 0xa2, 0x1e, 0x72, 0x88, 0x9c, 0x02, 0x94,
+ 0x2c, 0x82, 0x08, 0xca, 0xf9, 0x82, 0x00, 0xca, 0xfc, 0x94, 0x32, 0xa2, 0x3b, 0xce,
+ 0x88, 0x9d, 0x00, 0x94, 0x2a, 0x90, 0x01, 0xa2, 0x35, 0xce, 0x8b, 0xb7, 0x01, 0x03,
+ 0x02, 0xb7, 0x00, 0x04, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5,
+ 0xa3, 0x9c, 0x50, 0xb7, 0x01, 0x04, 0x02, 0xb7, 0x00, 0x04, 0x04, 0xb7, 0x00, 0x00,
+ 0x06, 0x00, 0xb5, 0xa3, 0x8b, 0x91, 0x08, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xd2, 0x93,
+ 0x3c, 0x90, 0x00, 0xa2, 0x35, 0xce, 0x8b, 0xa2, 0x3f, 0xce, 0x8b, 0xa2, 0x22, 0xce,
+ 0x88, 0xab, 0x18, 0xb0, 0x40, 0x21, 0xb5, 0xe7, 0x25, 0x90, 0x08, 0xa2, 0x50, 0xce,
+ 0xfa, 0xa2, 0x50, 0xce, 0xab, 0xb5, 0xd1, 0x7c, 0x90, 0x03, 0xa2, 0x4e, 0xce, 0xab,
+ 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3e, 0xb5, 0xe7, 0x06, 0xa2, 0x22,
+ 0xce, 0x88, 0xb5, 0xd1, 0xc9, 0xa2, 0x2e, 0xce, 0xab, 0xab, 0xca, 0x99, 0x04, 0x9d,
+ 0x00, 0x5c, 0x82, 0x40, 0xca, 0xf9, 0x82, 0x00, 0xca, 0xfd, 0xb4, 0xfe, 0x02, 0x90,
+ 0x01, 0xb5, 0xd1, 0x20, 0x91, 0x04, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xd2, 0x32, 0xb4,
+ 0xfc, 0x10, 0x90, 0x01, 0xa2, 0x35, 0xce, 0x8b, 0x77, 0x00, 0xa2, 0x35, 0xce, 0x8b,
+ 0xa2, 0x36, 0xce, 0x8b, 0xa2, 0x37, 0xce, 0x8b, 0xa2, 0x38, 0xce, 0x8b, 0x90, 0x04,
+ 0xa2, 0x4e, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3e, 0xb5,
+ 0xe6, 0xae, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xd1, 0x71, 0xa2, 0x2e, 0xce, 0xab, 0x99,
+ 0x40, 0x9c, 0x00, 0x54, 0x90, 0x08, 0x01, 0xa2, 0x50, 0xce, 0xf9, 0xa2, 0x50, 0xce,
+ 0xab, 0xb5, 0xd0, 0xf4, 0x90, 0x01, 0xa2, 0x35, 0xce, 0x8b, 0xa2, 0x22, 0xce, 0x88,
+ 0xb5, 0xd1, 0x4d, 0xa2, 0x2e, 0xce, 0xab, 0x99, 0x02, 0x9d, 0x00, 0xb4, 0xfd, 0x91,
+ 0xa2, 0x34, 0xce, 0x88, 0x9d, 0x04, 0x4c, 0x91, 0x01, 0xa2, 0x22, 0xce, 0x88, 0xb5,
+ 0xd1, 0xbf, 0xb4, 0xfb, 0xbe, 0xa2, 0x3c, 0xce, 0x88, 0x9d, 0x00, 0x72, 0xac, 0xce,
+ 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0x90, 0x04, 0xb5, 0x09, 0xae, 0x42, 0x95, 0x20, 0x90,
+ 0x05, 0xb5, 0x09, 0xa6, 0x42, 0x95, 0x28, 0x90, 0x03, 0xb5, 0x09, 0x9e, 0xb4, 0xfd,
+ 0x58, 0x95, 0x32, 0x00, 0xa2, 0x35, 0xce, 0x8b, 0x90, 0x05, 0xa2, 0x4e, 0xce, 0xab,
+ 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3e, 0xb5, 0xe6, 0x26, 0xa2, 0x22,
+ 0xce, 0x88, 0xb5, 0xd0, 0xe9, 0xa2, 0x2e, 0xce, 0xab, 0xab, 0xca, 0x99, 0x04, 0x9c,
+ 0x00, 0x94, 0x40, 0x90, 0x01, 0xa2, 0x35, 0xce, 0x8b, 0xac, 0xce, 0xcc, 0x82, 0x46,
+ 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, 0xb5, 0x09, 0x4b, 0xac, 0xce, 0xcc, 0x82, 0x44,
+ 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, 0xb5, 0x09, 0x4d, 0x5a, 0x91, 0x08, 0xa2, 0x22,
+ 0xce, 0x88, 0xb5, 0xd1, 0x3e, 0xb5, 0xfb, 0xd6, 0x90, 0x08, 0xa2, 0x50, 0xce, 0xfa,
+ 0xa2, 0x50, 0xce, 0xab, 0xb5, 0xd0, 0x3b, 0x3c, 0x91, 0x04, 0x7a, 0x82, 0x08, 0xca,
+ 0xf9, 0x82, 0x00, 0xca, 0xfc, 0x95, 0x34, 0x90, 0x01, 0xa2, 0x35, 0xce, 0x8b, 0xac,
+ 0xce, 0xcc, 0x82, 0x46, 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, 0xb5, 0x09, 0x09, 0x95,
+ 0x4a, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x03, 0x42, 0x94, 0x3e, 0xa2, 0x30, 0x72, 0xa8,
+ 0x9c, 0x00, 0x94, 0x36, 0xa2, 0x54, 0xce, 0x88, 0x9d, 0x00, 0x94, 0x2e, 0xa2, 0x24,
+ 0xce, 0xa8, 0xbc, 0x12, 0x60, 0x42, 0x94, 0x24, 0xa2, 0x26, 0xce, 0xa8, 0xbc, 0x04,
+ 0xa8, 0x41, 0x5b, 0xa2, 0x30, 0x72, 0xa8, 0xa2, 0x32, 0x72, 0xfd, 0x43, 0xb4, 0xfc,
+ 0x94, 0xa2, 0x32, 0x72, 0xa8, 0x04, 0xa2, 0x32, 0x72, 0xab, 0x90, 0x01, 0xa2, 0x54,
+ 0xce, 0x8b, 0x90, 0x00, 0xa2, 0x35, 0xce, 0x8b, 0x90, 0x06, 0xa2, 0x4e, 0xce, 0xab,
+ 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3e, 0xb5, 0xe5, 0x54, 0xa2, 0x22,
+ 0xce, 0x88, 0xb5, 0xd0, 0x17, 0xa2, 0x2e, 0xce, 0xab, 0x99, 0x04, 0x9d, 0x00, 0x59,
+ 0x90, 0x08, 0xa2, 0x50, 0xce, 0xfa, 0xa2, 0x50, 0xce, 0xab, 0xb5, 0xcf, 0x9b, 0x91,
+ 0x04, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xd0, 0x85, 0xb4, 0xfb, 0x47, 0x90, 0x01, 0xa2,
+ 0x35, 0xce, 0x8b, 0x7f, 0x00, 0xa2, 0x35, 0xce, 0x8b, 0x90, 0x07, 0xa2, 0x4e, 0xce,
+ 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3e, 0xb5, 0xe5, 0x0d, 0xa2,
+ 0x22, 0xce, 0x88, 0xb5, 0xcf, 0xd0, 0xa2, 0x2e, 0xce, 0xab, 0xab, 0xca, 0x99, 0x08,
+ 0x9d, 0x00, 0x4c, 0x82, 0x40, 0xca, 0xf9, 0x82, 0x00, 0xca, 0xfc, 0x4a, 0xb4, 0xfc,
+ 0x08, 0x90, 0x01, 0xa2, 0x35, 0xce, 0x8b, 0x40, 0x91, 0x08, 0xa2, 0x22, 0xce, 0x88,
+ 0xb5, 0xd0, 0x36, 0xb4, 0xfa, 0xfa, 0x00, 0xa2, 0x35, 0xce, 0x8b, 0xa2, 0x3b, 0xce,
+ 0x8b, 0x90, 0x03, 0xa2, 0x2c, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0,
+ 0x40, 0x3d, 0xb5, 0xe4, 0xc1, 0x00, 0xa2, 0x41, 0xce, 0x8b, 0xa2, 0x4c, 0xce, 0xab,
+ 0x90, 0x08, 0xa2, 0x4e, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40,
+ 0x3e, 0xb5, 0xe4, 0xa6, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xcf, 0x69, 0xa2, 0x2e, 0xce,
+ 0xab, 0x99, 0x40, 0x9c, 0x00, 0x54, 0x90, 0x01, 0xa2, 0x35, 0xce, 0x8b, 0x90, 0x08,
+ 0x01, 0xa2, 0x50, 0xce, 0xf9, 0xa2, 0x50, 0xce, 0xab, 0xb5, 0xce, 0xe6, 0x91, 0x01,
+ 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xcf, 0xd0, 0xb4, 0xfa, 0x96, 0x90, 0x00, 0xa2, 0x50,
+ 0xce, 0xab, 0xb5, 0xce, 0xd1, 0x90, 0x09, 0xa2, 0x4e, 0xce, 0xab, 0xa2, 0x22, 0xce,
+ 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3e, 0xb5, 0xe4, 0x5b, 0xa2, 0x04, 0xce, 0xa8, 0x9c,
+ 0x02, 0x94, 0x2c, 0x90, 0x01, 0xb5, 0xce, 0x8a, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00,
+ 0x00, 0x04, 0xb7, 0x02, 0x00, 0x06, 0xac, 0xce, 0xcc, 0x82, 0x4c, 0xcc, 0xf8, 0xa2,
+ 0x22, 0xce, 0x88, 0xb5, 0xc8, 0x53, 0xa2, 0x04, 0xce, 0xa8, 0xab, 0xca, 0xa2, 0x22,
+ 0xce, 0x88, 0xb4, 0xcf, 0x7e, 0x90, 0x00, 0xb5, 0xce, 0x5e, 0x95, 0x2c, 0xa8, 0x46,
+ 0xb5, 0x9e, 0xc9, 0xa2, 0x0e, 0x76, 0xa8, 0x9c, 0x00, 0x3c, 0xa8, 0x44, 0xbc, 0x02,
+ 0x09, 0x55, 0xbc, 0x02, 0xff, 0x59, 0xbc, 0x02, 0x01, 0x94, 0x46, 0xbc, 0x02, 0x04,
+ 0x94, 0x4e, 0xbc, 0x02, 0x10, 0x94, 0x79, 0x94, 0xb2, 0xa8, 0x48, 0xa2, 0x2e, 0x76,
+ 0xab, 0x94, 0xaa, 0xa8, 0x48, 0xa2, 0x48, 0x76, 0xfc, 0x94, 0x23, 0xa2, 0x4a, 0x76,
+ 0xfc, 0x46, 0xa2, 0x4c, 0x76, 0xfc, 0x4d, 0x3c, 0xb0, 0x02, 0x0b, 0xab, 0x44, 0x00,
+ 0xa2, 0x4a, 0x76, 0xab, 0x94, 0x8b, 0xb0, 0x02, 0x0d, 0xab, 0x44, 0x00, 0xa2, 0x4c,
+ 0x76, 0xab, 0x94, 0x7f, 0x00, 0xa2, 0x48, 0x76, 0xab, 0x94, 0x78, 0xa2, 0x4e, 0x76,
+ 0xa8, 0x9c, 0x09, 0x3c, 0xac, 0x76, 0xce, 0xb4, 0xfa, 0xcb, 0xa2, 0x4e, 0x76, 0xa8,
+ 0x9c, 0x09, 0x3c, 0xac, 0x76, 0xce, 0xb5, 0xfa, 0x09, 0xa2, 0x54, 0x76, 0x88, 0x9c,
+ 0x00, 0x3c, 0xa2, 0x34, 0x72, 0xa8, 0x9c, 0x01, 0x41, 0x43, 0xac, 0x76, 0xce, 0xb7,
+ 0x01, 0x07, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x46, 0xb4,
+ 0x9f, 0xc8, 0xac, 0x76, 0xce, 0xb5, 0xf9, 0xe0, 0xa2, 0x54, 0x76, 0x88, 0x9c, 0x00,
+ 0x5c, 0xa2, 0x34, 0x72, 0xa8, 0x9c, 0x01, 0x41, 0x43, 0xac, 0x76, 0xce, 0xb7, 0x01,
+ 0x07, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x46, 0xb5, 0x9f,
+ 0x9f, 0x90, 0x09, 0xa2, 0x4e, 0x76, 0xab, 0xa2, 0x22, 0x76, 0x88, 0xab, 0x18, 0xb0,
+ 0x40, 0x3e, 0xb4, 0xe3, 0x47, 0xa2, 0x4e, 0x76, 0xa8, 0x9c, 0x00, 0x94, 0x2b, 0x9c,
+ 0x01, 0x94, 0x3a, 0x9c, 0x02, 0x94, 0x93, 0x9c, 0x03, 0x94, 0xec, 0x9c, 0x04, 0xb4,
+ 0x01, 0x74, 0x9c, 0x05, 0xb4, 0x02, 0xad, 0x9c, 0x06, 0xb4, 0x03, 0x42, 0x9c, 0x07,
+ 0xb4, 0x03, 0xba, 0x9c, 0x08, 0xb4, 0x04, 0x1f, 0x9c, 0x09, 0xb4, 0x06, 0x44, 0x3c,
+ 0xa8, 0x44, 0xbc, 0x02, 0x02, 0x41, 0x3c, 0xa8, 0x48, 0xa2, 0x04, 0x76, 0xab, 0xac,
+ 0x76, 0xce, 0xb4, 0xfe, 0x8b, 0xa8, 0x44, 0xbc, 0x02, 0xff, 0x46, 0xbc, 0x02, 0x09,
+ 0x94, 0x35, 0x3c, 0xa2, 0x35, 0x76, 0x88, 0x9c, 0x00, 0x4f, 0x90, 0x01, 0xa2, 0x3f,
+ 0x76, 0x8b, 0xac, 0x46, 0x18, 0xb0, 0x40, 0x21, 0xb4, 0xe2, 0xdf, 0x90, 0x01, 0xa2,
+ 0x35, 0x76, 0x8b, 0xac, 0x76, 0xce, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xcd, 0x99, 0xa2,
+ 0x2e, 0x76, 0xab, 0x99, 0x06, 0x9c, 0x00, 0xb4, 0xf7, 0xbf, 0xb4, 0xfb, 0x86, 0xa2,
+ 0x35, 0x76, 0x88, 0x9c, 0x00, 0x3c, 0xac, 0x76, 0xce, 0xa2, 0x22, 0xce, 0x88, 0xb5,
+ 0xcd, 0x7a, 0xa2, 0x2e, 0x76, 0xab, 0x99, 0x06, 0x9c, 0x00, 0x3c, 0xb4, 0xfb, 0x69,
+ 0xa8, 0x44, 0xbc, 0x02, 0x09, 0x41, 0x3c, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x06, 0x9c,
+ 0x00, 0x4f, 0xa2, 0x35, 0x76, 0x88, 0x9d, 0x00, 0x48, 0xa2, 0x1e, 0x72, 0x88, 0x9c,
+ 0x02, 0x94, 0x2e, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x08, 0x9c, 0x00, 0x3c, 0xa2, 0x35,
+ 0x76, 0x88, 0x9d, 0x00, 0x3c, 0xa2, 0x3b, 0x76, 0x88, 0x9d, 0x00, 0x3c, 0x90, 0x01,
+ 0xa2, 0x35, 0x76, 0x8b, 0xb7, 0x01, 0x03, 0x02, 0xb7, 0x00, 0x04, 0x04, 0xb7, 0x00,
+ 0x00, 0x06, 0xa8, 0x46, 0xb4, 0x9e, 0x9d, 0xb7, 0x01, 0x04, 0x02, 0xb7, 0x00, 0x04,
+ 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4, 0x9e, 0x8d, 0xa8, 0x44, 0xbc, 0x02, 0x09,
+ 0x4b, 0xbc, 0x02, 0x14, 0x94, 0x31, 0xbc, 0x02, 0xff, 0x94, 0x33, 0x3c, 0xa2, 0x35,
+ 0x76, 0x88, 0x9d, 0x00, 0x3c, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x04, 0x9c, 0x00, 0x4c,
+ 0x90, 0x01, 0xa2, 0x35, 0x76, 0x8b, 0xac, 0x76, 0xce, 0xb4, 0xf7, 0x52, 0xa2, 0x2e,
+ 0x76, 0xa8, 0x99, 0x40, 0x9c, 0x00, 0x3c, 0xac, 0x76, 0xce, 0xb4, 0xf9, 0x24, 0xa2,
+ 0x48, 0x76, 0xa8, 0x9d, 0x00, 0x3c, 0xa2, 0x35, 0x76, 0x88, 0x9c, 0x00, 0x3c, 0xa2,
+ 0x06, 0x76, 0xa8, 0x9c, 0x03, 0x4b, 0x00, 0xa2, 0x34, 0x76, 0x8b, 0xac, 0x76, 0xce,
+ 0xb4, 0xfb, 0x1a, 0xa2, 0x30, 0x72, 0x88, 0x9c, 0x00, 0x71, 0xa2, 0x30, 0x72, 0xa8,
+ 0xa2, 0x32, 0x72, 0xfd, 0x47, 0x90, 0x01, 0xa2, 0x55, 0x76, 0x8b, 0x3c, 0xa2, 0x32,
+ 0x72, 0xa8, 0x04, 0xa2, 0x32, 0x72, 0xab, 0x90, 0x01, 0xa2, 0x55, 0x76, 0x8b, 0x00,
+ 0xa2, 0x34, 0x76, 0x8b, 0xac, 0x76, 0xce, 0xb4, 0xfa, 0xe9, 0xa8, 0x44, 0xbc, 0x02,
+ 0x09, 0x94, 0x3c, 0xbc, 0x02, 0xff, 0x94, 0x92, 0xbc, 0x02, 0x06, 0x4e, 0xbc, 0x02,
+ 0x05, 0x5a, 0xbc, 0x02, 0x07, 0x5c, 0xbc, 0x02, 0x13, 0x94, 0xf0, 0x3c, 0xb1, 0x01,
+ 0x00, 0xac, 0x76, 0xce, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xcc, 0xe6, 0xb4, 0xf6, 0xe5,
+ 0xac, 0x76, 0xce, 0xb4, 0xfb, 0x49, 0xac, 0x76, 0xce, 0xa2, 0x3c, 0x76, 0x88, 0x9d,
+ 0x00, 0xb4, 0xf8, 0x93, 0xb4, 0xfb, 0xc4, 0xac, 0x76, 0xce, 0xa2, 0x2e, 0x76, 0xa8,
+ 0x99, 0x02, 0x9d, 0x00, 0xb4, 0xf8, 0x82, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x40, 0x9c,
+ 0x00, 0x5e, 0xa2, 0x35, 0x76, 0x88, 0x9d, 0x00, 0x57, 0x90, 0x08, 0x01, 0xa2, 0x50,
+ 0x76, 0xf9, 0xa2, 0x50, 0x76, 0xab, 0xb5, 0xcb, 0xaf, 0x90, 0x01, 0xa2, 0x35, 0x76,
+ 0x8b, 0xb4, 0xf6, 0x9b, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x0c, 0x9c, 0x00, 0x3c, 0xa2,
+ 0x38, 0x76, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x37, 0x76, 0x88, 0x9d, 0x00, 0x3c, 0x90,
+ 0x01, 0xa2, 0x37, 0x76, 0x8b, 0xac, 0x76, 0xce, 0xb5, 0x04, 0x87, 0xb4, 0xf6, 0x75,
+ 0xa2, 0x35, 0x76, 0x88, 0x9d, 0x00, 0x4d, 0xa2, 0x34, 0x76, 0x88, 0x9c, 0x00, 0x3c,
+ 0xac, 0x76, 0xce, 0xb4, 0xf8, 0x21, 0xac, 0x76, 0xce, 0xa2, 0x36, 0xce, 0x88, 0x9d,
+ 0x00, 0x94, 0x36, 0x90, 0x01, 0xa2, 0x36, 0xce, 0x8b, 0xb5, 0x07, 0x8e, 0xb5, 0xf6,
+ 0x4a, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xcb, 0xb4, 0xa2, 0x2e, 0xce, 0xab, 0x99, 0x0c,
+ 0x9c, 0x00, 0x3c, 0xa2, 0x38, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x37, 0xce, 0x88,
+ 0x9d, 0x00, 0x3c, 0x90, 0x01, 0xa2, 0x37, 0xce, 0x8b, 0xb5, 0x04, 0x32, 0xb4, 0xf6,
+ 0x20, 0xa2, 0x38, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x37, 0xce, 0x88, 0x9d, 0x00,
+ 0x3c, 0x90, 0x01, 0xa2, 0x37, 0xce, 0x8b, 0xb5, 0x04, 0x18, 0xb4, 0xf6, 0x06, 0xa2,
+ 0x34, 0x76, 0x88, 0x9d, 0x04, 0x41, 0x3c, 0xa2, 0x3c, 0x76, 0x88, 0x9d, 0x00, 0x3c,
+ 0xac, 0x76, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0x90, 0x04, 0xb5, 0x03, 0xee, 0x41, 0x3c,
+ 0x90, 0x05, 0xb5, 0x03, 0xe7, 0x41, 0x3c, 0x90, 0x03, 0xb5, 0x03, 0xe0, 0x41, 0x3c,
+ 0xac, 0x76, 0xce, 0xb4, 0xf7, 0x95, 0xa8, 0x44, 0xbc, 0x02, 0x09, 0x46, 0xbc, 0x02,
+ 0xff, 0x94, 0x77, 0x3c, 0xac, 0x76, 0xce, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x02, 0x9d,
+ 0x00, 0xb4, 0xf7, 0x7b, 0xa2, 0x35, 0x76, 0x88, 0x9d, 0x00, 0x3c, 0xa2, 0x2e, 0x76,
+ 0xa8, 0x99, 0x04, 0x9c, 0x00, 0x94, 0x27, 0x90, 0x01, 0xa2, 0x35, 0x76, 0x8b, 0xa2,
+ 0x34, 0x76, 0x88, 0xac, 0x76, 0xcc, 0x82, 0x46, 0xcc, 0xf8, 0xb5, 0x03, 0x89, 0xa2,
+ 0x50, 0x76, 0xa8, 0x9a, 0x08, 0xa2, 0x50, 0x76, 0xab, 0xac, 0x76, 0xce, 0xb5, 0xca,
+ 0x91, 0xb5, 0xf6, 0x1c, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x08, 0x9c, 0x00, 0x3c, 0x90,
+ 0x01, 0xa2, 0x35, 0x76, 0x8b, 0xa2, 0x34, 0x76, 0x88, 0xac, 0x76, 0xcc, 0x82, 0x46,
+ 0xcc, 0xf8, 0xb5, 0x03, 0x61, 0xa2, 0x50, 0x76, 0xa8, 0x9a, 0x08, 0xa2, 0x50, 0x76,
+ 0xab, 0xac, 0x76, 0xce, 0xb5, 0xca, 0x61, 0xb4, 0xf5, 0xec, 0xac, 0x76, 0xce, 0xa2,
+ 0x35, 0x76, 0x88, 0x9c, 0x00, 0xb4, 0xf7, 0x07, 0xa2, 0x34, 0x76, 0x88, 0x04, 0xa2,
+ 0x34, 0x76, 0x8b, 0xb4, 0xf9, 0x11, 0xa8, 0x44, 0xbc, 0x02, 0x09, 0x4b, 0xbc, 0x02,
+ 0xff, 0x94, 0x4d, 0xbc, 0x02, 0x13, 0x94, 0x60, 0x3c, 0xac, 0x76, 0xce, 0xa2, 0x2e,
+ 0x76, 0xa8, 0x99, 0x02, 0x9d, 0x00, 0xb4, 0xf6, 0xdc, 0xa2, 0x35, 0x76, 0x88, 0x9d,
+ 0x00, 0x55, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x04, 0x9c, 0x00, 0x4c, 0x90, 0x01, 0xa2,
+ 0x35, 0x76, 0x8b, 0xac, 0x76, 0xce, 0xb4, 0xf5, 0xc3, 0xac, 0x76, 0xce, 0xa2, 0x22,
+ 0xce, 0x88, 0xb5, 0xca, 0x67, 0xa2, 0x2e, 0x76, 0xab, 0x99, 0x40, 0x9c, 0x00, 0x3c,
+ 0xa2, 0x35, 0x76, 0x88, 0x9c, 0x00, 0x3c, 0xb4, 0xf6, 0xa3, 0xac, 0x76, 0xce, 0xa2,
+ 0x35, 0x76, 0x88, 0x9c, 0x00, 0xb4, 0xf6, 0x97, 0xa2, 0x3c, 0x76, 0x88, 0x9c, 0x00,
+ 0xb4, 0xfa, 0x51, 0xb4, 0xf6, 0x8b, 0xa2, 0x3c, 0x76, 0x88, 0x9c, 0x00, 0x3c, 0xac,
+ 0x76, 0xce, 0xb4, 0xf6, 0x7e, 0xa8, 0x44, 0xbc, 0x02, 0x09, 0x4b, 0xbc, 0x02, 0xff,
+ 0x94, 0x3a, 0xbc, 0x02, 0x13, 0x94, 0x4d, 0x3c, 0xac, 0x76, 0xce, 0xa2, 0x2e, 0x76,
+ 0xa8, 0x99, 0x02, 0x9c, 0x00, 0x43, 0xb4, 0xf6, 0x5e, 0xa2, 0x35, 0x76, 0x88, 0x9d,
+ 0x00, 0x3c, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x08, 0x9c, 0x00, 0x49, 0x90, 0x01, 0xa2,
+ 0x35, 0x76, 0x8b, 0xb4, 0xf5, 0x4a, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x40, 0x9c, 0x00,
+ 0x3c, 0xb4, 0xf6, 0x39, 0xac, 0x76, 0xce, 0xa2, 0x35, 0x76, 0x88, 0x9c, 0x00, 0xb4,
+ 0xf6, 0x2d, 0xa2, 0x3c, 0x76, 0x88, 0x9d, 0x00, 0xb4, 0xf6, 0x24, 0xb4, 0xfa, 0x2c,
+ 0xa2, 0x3c, 0x76, 0x88, 0x9c, 0x00, 0x3c, 0xac, 0x76, 0xce, 0xb4, 0xf6, 0x14, 0xa8,
+ 0x44, 0xbc, 0x02, 0xff, 0x94, 0x36, 0xbc, 0x02, 0x09, 0x94, 0x98, 0xbc, 0x02, 0x03,
+ 0xb4, 0x01, 0x6e, 0xbc, 0x02, 0x0a, 0xb4, 0x01, 0x6e, 0xbc, 0x02, 0x0b, 0xb4, 0x01,
+ 0x75, 0xbc, 0x02, 0x06, 0xb4, 0x01, 0x93, 0xbc, 0x02, 0x13, 0xb4, 0x01, 0xc2, 0xbc,
+ 0x02, 0x0d, 0xb4, 0x01, 0xc9, 0xbc, 0x02, 0x11, 0xb4, 0x01, 0xda, 0xbc, 0x02, 0x12,
+ 0xb4, 0x01, 0xe1, 0x3c, 0xa2, 0x35, 0x76, 0x88, 0x9d, 0x00, 0x4d, 0xa2, 0x3b, 0x76,
+ 0x88, 0x9d, 0x00, 0x46, 0xac, 0x76, 0xce, 0xb4, 0xf6, 0xa9, 0xa2, 0x35, 0x76, 0x88,
+ 0x9c, 0x00, 0x3c, 0xa2, 0x3a, 0x76, 0x88, 0x9d, 0x00, 0x3c, 0xac, 0x76, 0xce, 0xa2,
+ 0x22, 0xce, 0x88, 0xb5, 0xc9, 0x5c, 0xa2, 0x2e, 0x76, 0xab, 0x99, 0x08, 0x9c, 0x00,
+ 0x4d, 0xa2, 0x3b, 0x76, 0x88, 0x9d, 0x00, 0x46, 0xb5, 0xf5, 0x98, 0xb4, 0xf4, 0x9c,
+ 0xb7, 0x03, 0x02, 0x02, 0xb7, 0x00, 0x01, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x46,
+ 0xb5, 0x9a, 0xb1, 0xb7, 0x02, 0x06, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00,
+ 0x06, 0xa8, 0x46, 0xb5, 0x9a, 0xa0, 0xb4, 0xf4, 0x77, 0xa2, 0x3b, 0x76, 0x88, 0x9d,
+ 0x00, 0x51, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x04, 0x9c, 0x00, 0x48, 0xac, 0x76, 0xce,
+ 0xb5, 0xf5, 0x5a, 0x94, 0x50, 0xa2, 0x3b, 0x76, 0x88, 0x9d, 0x00, 0x51, 0xa2, 0x2e,
+ 0x76, 0xa8, 0x99, 0x02, 0x9c, 0x00, 0x48, 0xac, 0x76, 0xce, 0xb5, 0xf6, 0x28, 0x94,
+ 0x38, 0xa2, 0x3a, 0x76, 0x88, 0x9c, 0x00, 0x94, 0x58, 0xa2, 0x3b, 0x76, 0x88, 0x9d,
+ 0x00, 0x94, 0x50, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x08, 0x9c, 0x00, 0x94, 0x46, 0x90,
+ 0x01, 0xa2, 0x3b, 0x76, 0x8b, 0xb7, 0x01, 0x03, 0x02, 0xb7, 0x00, 0x04, 0x04, 0xb7,
+ 0x00, 0x00, 0x06, 0xa8, 0x46, 0xb5, 0x9a, 0x3c, 0xac, 0x76, 0xce, 0xb5, 0xf4, 0x10,
+ 0x40, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x20, 0x9c, 0x00, 0x3c, 0xa2, 0x4a, 0x76, 0xa8,
+ 0x9d, 0x00, 0x3c, 0xb7, 0x07, 0xef, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x02, 0x00,
+ 0x06, 0xac, 0x76, 0xcc, 0x82, 0x4a, 0xcc, 0xf8, 0xa8, 0x46, 0xb4, 0xc1, 0xe6, 0xa2,
+ 0x2e, 0x76, 0xa8, 0x99, 0x40, 0x9c, 0x00, 0x95, 0x30, 0xa2, 0x4a, 0x76, 0xa8, 0x9c,
+ 0x00, 0x57, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06,
+ 0xac, 0x76, 0xcc, 0x82, 0x4a, 0xcc, 0xf8, 0x00, 0xb5, 0xc1, 0xbe, 0xa2, 0x35, 0x76,
+ 0x88, 0x9d, 0x00, 0x95, 0x56, 0x90, 0x01, 0xa2, 0x35, 0x76, 0x8b, 0x90, 0x08, 0x01,
+ 0xa2, 0x50, 0x76, 0xf9, 0xa2, 0x50, 0x76, 0xab, 0xac, 0x76, 0xce, 0xb5, 0xc7, 0xe4,
+ 0xb5, 0xf3, 0x9d, 0x95, 0x72, 0xac, 0x76, 0xce, 0xb4, 0xf5, 0x83, 0xa2, 0x3b, 0x76,
+ 0x88, 0x9d, 0x00, 0x3c, 0xac, 0x76, 0xce, 0xb4, 0xf5, 0x67, 0xac, 0x76, 0xce, 0xa2,
+ 0x22, 0xce, 0x88, 0xb5, 0xc8, 0x28, 0xa2, 0x2e, 0x76, 0xab, 0x99, 0x41, 0x9d, 0x00,
+ 0x3c, 0xb7, 0x02, 0x0a, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8,
+ 0x46, 0xb4, 0x99, 0x8a, 0xa2, 0x4c, 0x76, 0xa8, 0x9d, 0x00, 0x3c, 0xb1, 0x01, 0x00,
+ 0xac, 0x76, 0xce, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xc8, 0x87, 0x90, 0x30, 0xa2, 0x50,
+ 0x76, 0xfa, 0xa2, 0x50, 0x76, 0xab, 0xb5, 0xc7, 0x87, 0xb7, 0x05, 0x01, 0x02, 0xb7,
+ 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x46, 0xb5, 0x99, 0x58, 0xb4, 0xf3,
+ 0x2f, 0xa2, 0x3c, 0x76, 0x88, 0x9c, 0x00, 0x3c, 0xac, 0x76, 0xce, 0xb4, 0xf4, 0x1b,
+ 0xb7, 0x02, 0x06, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x46,
+ 0xb5, 0x99, 0x37, 0xac, 0x76, 0xce, 0xb4, 0xf3, 0x0b, 0xa2, 0x3b, 0x76, 0x88, 0x9d,
+ 0x00, 0x3c, 0xac, 0x76, 0xce, 0xb4, 0xf4, 0xdd, 0xa2, 0x41, 0x76, 0x88, 0x9c, 0x00,
+ 0x3c, 0xac, 0x76, 0xce, 0xb4, 0xf3, 0xea, 0xa8, 0x44, 0xbc, 0x02, 0x02, 0x45, 0xbc,
+ 0x02, 0x0f, 0x4d, 0x3c, 0xa8, 0x48, 0xa2, 0x04, 0x76, 0xab, 0xac, 0x76, 0xce, 0xb4,
+ 0xf8, 0x44, 0xac, 0x76, 0xce, 0xb4, 0xf3, 0x18, 0xaf, 0xce, 0xab, 0xce, 0x39, 0x3f,
+ 0xce, 0x3c, 0xaf, 0xce, 0xab, 0xce, 0x38, 0x3f, 0xce, 0x3c, 0xaf, 0xce, 0xab, 0xce,
+ 0x3a, 0x43, 0x3f, 0xce, 0x3d, 0x3f, 0xce, 0x3c, 0xa2, 0x34, 0xce, 0x88, 0x9c, 0x00,
+ 0x94, 0x2a, 0x9c, 0x01, 0x94, 0x42, 0x9c, 0x02, 0x94, 0x79, 0x9c, 0x03, 0x94, 0xb0,
+ 0x9c, 0x04, 0x94, 0xe2, 0x9c, 0x05, 0xb4, 0x01, 0x89, 0x9c, 0x06, 0xb4, 0x01, 0x97,
+ 0x9c, 0x07, 0xb4, 0x01, 0xd0, 0x9c, 0x08, 0xb4, 0x02, 0x45, 0x9c, 0x09, 0xb4, 0x02,
+ 0x81, 0x3c, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0x34, 0x4d, 0xb7, 0x02, 0x05,
+ 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4,
+ 0x98, 0x90, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x02, 0x94, 0x24, 0x9c, 0x03, 0x94, 0x20,
+ 0xa2, 0x34, 0xce, 0x88, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0x34, 0x79, 0xb7,
+ 0x02, 0x05, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce,
+ 0x88, 0xb4, 0x98, 0x64, 0xa2, 0x34, 0xce, 0x88, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc,
+ 0xf8, 0x34, 0xa1, 0x95, 0x20, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x01, 0x94, 0x24, 0x9c,
+ 0x03, 0x94, 0x20, 0xa2, 0x34, 0xce, 0x88, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8,
+ 0x34, 0xb4, 0xb7, 0x02, 0x05, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06,
+ 0xa2, 0x22, 0xce, 0x88, 0xb4, 0x98, 0x29, 0xa2, 0x34, 0xce, 0x88, 0xac, 0xce, 0xcc,
+ 0x82, 0x44, 0xcc, 0xf8, 0x34, 0xdc, 0x95, 0x20, 0xb5, 0xd9, 0x79, 0x9c, 0x00, 0x94,
+ 0x20, 0xa2, 0x34, 0xce, 0x88, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0x34, 0xf2,
+ 0xb7, 0x02, 0x05, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22,
+ 0xce, 0x88, 0xb4, 0x97, 0xf3, 0xa2, 0x34, 0xce, 0x88, 0xac, 0xce, 0xcc, 0x82, 0x44,
+ 0xcc, 0xf8, 0x35, 0x0a, 0x95, 0x20, 0xa2, 0x2a, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x44,
+ 0xa2, 0x2c, 0xce, 0xa8, 0x9c, 0x02, 0x94, 0x26, 0x53, 0xb7, 0x02, 0x05, 0x02, 0xb7,
+ 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, 0x97, 0xc0,
+ 0x90, 0x02, 0xa2, 0x2c, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40,
+ 0x3d, 0xb5, 0xdb, 0x68, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce,
+ 0x88, 0x35, 0x57, 0xa2, 0x34, 0xce, 0x88, 0x04, 0x35, 0x5e, 0x95, 0x39, 0xa2, 0x2c,
+ 0xce, 0xa8, 0x9c, 0x02, 0x52, 0x90, 0x01, 0xa2, 0x2c, 0xce, 0xab, 0xa2, 0x22, 0xce,
+ 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3d, 0xb5, 0xdb, 0x39, 0xa2, 0x40, 0xce, 0x88, 0x9c,
+ 0x00, 0x56, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, 0x35,
+ 0x8d, 0xa2, 0x34, 0xce, 0x88, 0x04, 0x35, 0x8c, 0x95, 0x6f, 0xac, 0xce, 0xcc, 0x82,
+ 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, 0x35, 0x9b, 0xa2, 0x30, 0xce, 0xa8, 0xa2,
+ 0x32, 0xce, 0xfa, 0x9c, 0x00, 0x49, 0xa2, 0x34, 0xce, 0x88, 0x04, 0x35, 0xb5, 0x95,
+ 0x90, 0xa2, 0x34, 0xce, 0x88, 0x04, 0x35, 0xb6, 0x95, 0x99, 0xb7, 0x02, 0x05, 0x02,
+ 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, 0x97,
+ 0x25, 0xa2, 0x0c, 0xce, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x55, 0xa2, 0x0f, 0x72, 0x88,
+ 0x9c, 0x00, 0x4e, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88,
+ 0x35, 0xf0, 0x4d, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88,
+ 0x35, 0xf6, 0xb7, 0x02, 0x05, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06,
+ 0xa2, 0x22, 0xce, 0x88, 0xb4, 0x96, 0xe7, 0xaf, 0xce, 0xb7, 0x05, 0x04, 0x44, 0xb7,
+ 0x00, 0x00, 0x48, 0xb7, 0x00, 0x00, 0x4a, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x46, 0xb5,
+ 0x9b, 0xc3, 0x3f, 0xce, 0x90, 0x20, 0x01, 0xab, 0xca, 0xa2, 0x50, 0xce, 0xa8, 0x96,
+ 0xca, 0xf9, 0xa2, 0x50, 0xce, 0xab, 0xb5, 0xc4, 0xd9, 0xb7, 0x03, 0x01, 0x02, 0xb7,
+ 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0x96, 0xa8,
+ 0xa2, 0x30, 0xce, 0xa8, 0xa2, 0x32, 0xce, 0xfa, 0x9c, 0x00, 0x94, 0x20, 0xac, 0xce,
+ 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, 0x36, 0x69, 0xb7, 0x02, 0x05,
+ 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4,
+ 0x96, 0x7c, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, 0x36,
+ 0x81, 0x95, 0x20, 0xa2, 0x0c, 0xce, 0x88, 0x99, 0x02, 0x9c, 0x00, 0x94, 0x28, 0xa2,
+ 0x0f, 0x72, 0x88, 0x9c, 0x00, 0x94, 0x20, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8,
+ 0xa2, 0x34, 0xce, 0x88, 0x36, 0xaa, 0xb7, 0x02, 0x05, 0x02, 0xb7, 0x00, 0x00, 0x04,
+ 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, 0x96, 0x3b, 0xac, 0xce, 0xcc,
+ 0x82, 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, 0x36, 0xc2, 0x95, 0x20, 0xb7, 0x03,
+ 0x01, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88,
+ 0xb5, 0x96, 0x19, 0x90, 0x20, 0x01, 0xab, 0xca, 0xa2, 0x50, 0xce, 0xa8, 0x96, 0xca,
+ 0xf9, 0xa2, 0x50, 0xce, 0xab, 0xb5, 0xc4, 0x24, 0xa2, 0x3c, 0xce, 0x88, 0x9d, 0x00,
+ 0x94, 0x30, 0xa2, 0x2a, 0xce, 0x88, 0x9c, 0x00, 0x42, 0x94, 0x27, 0xb5, 0xd9, 0x1c,
+ 0x9c, 0x00, 0x94, 0x33, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce,
+ 0x88, 0x37, 0x17, 0xb7, 0x02, 0x05, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00,
+ 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, 0x95, 0xce, 0xb7, 0x02, 0x01, 0x02, 0xb7, 0x00,
+ 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, 0x95, 0xbb, 0xac,
+ 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, 0x37, 0x42, 0x95, 0x33,
+ 0xa2, 0x34, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x2d, 0x9c, 0x01, 0x94, 0x2c, 0x9c, 0x02,
+ 0x94, 0x2b, 0x9c, 0x03, 0x94, 0x2a, 0x9c, 0x04, 0x94, 0x5b, 0x9c, 0x05, 0x94, 0xcb,
+ 0x9c, 0x06, 0x94, 0xca, 0x9c, 0x07, 0xb4, 0x01, 0x65, 0x9c, 0x08, 0xb4, 0x01, 0xc1,
+ 0x9c, 0x09, 0xb4, 0x01, 0xec, 0x9c, 0x0a, 0xb4, 0x02, 0x4d, 0x3c, 0xb4, 0xfc, 0x96,
+ 0xb4, 0xfc, 0x93, 0xb4, 0xfc, 0x90, 0xac, 0xce, 0xcc, 0x82, 0x46, 0xcc, 0xf8, 0x90,
+ 0x01, 0x37, 0x85, 0x4c, 0x90, 0x02, 0x37, 0x8a, 0x94, 0x20, 0x90, 0x00, 0x4a, 0x90,
+ 0x03, 0x47, 0x90, 0x02, 0x37, 0x96, 0x67, 0x90, 0x02, 0xa2, 0x28, 0xce, 0x8b, 0xa2,
+ 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x0d, 0xb5, 0xd8, 0xf9, 0xb4, 0xfc, 0x5e,
+ 0x90, 0x01, 0x75, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x03, 0x5c, 0xac, 0xce, 0xcc, 0x82,
+ 0x44, 0xcc, 0xf8, 0x90, 0x03, 0x37, 0xc1, 0x94, 0x30, 0xac, 0xce, 0xcc, 0x82, 0x46,
+ 0xcc, 0xf8, 0x90, 0x03, 0x37, 0xce, 0x94, 0x23, 0x94, 0x4e, 0xa2, 0x28, 0xce, 0x88,
+ 0x9c, 0x03, 0x42, 0x95, 0x23, 0x90, 0x01, 0xa2, 0x2a, 0xce, 0x8b, 0xa2, 0x22, 0xce,
+ 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3f, 0xb5, 0xd8, 0xb5, 0xb4, 0xfc, 0x1a, 0x90, 0x01,
+ 0x50, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x03, 0x69, 0xa2, 0x28, 0xce, 0x88, 0x9c, 0x03,
+ 0x70, 0x90, 0x02, 0xa2, 0x29, 0xce, 0x8b, 0xaf, 0xce, 0xb5, 0xaf, 0x79, 0x3f, 0xce,
+ 0xa2, 0x3c, 0xce, 0x88, 0x9c, 0x00, 0x48, 0x90, 0x03, 0xa2, 0x2a, 0xce, 0x8b, 0x95,
+ 0x3a, 0x90, 0x00, 0x68, 0x90, 0x02, 0x6b, 0xb4, 0xfb, 0xe4, 0xac, 0xce, 0xcc, 0x82,
+ 0x44, 0xcc, 0xf8, 0x90, 0x04, 0xb5, 0xfb, 0xcc, 0x94, 0x48, 0xac, 0xce, 0xcc, 0x82,
+ 0x46, 0xcc, 0xf8, 0x90, 0x04, 0xb5, 0xfb, 0xbe, 0x94, 0x3a, 0xac, 0xce, 0xcc, 0x82,
+ 0x44, 0xcc, 0xf8, 0x90, 0x05, 0xb5, 0xfb, 0xb0, 0x5d, 0xac, 0xce, 0xcc, 0x82, 0x46,
+ 0xcc, 0xf8, 0x90, 0x05, 0xb5, 0xfb, 0xa3, 0x50, 0xb0, 0x2b, 0xc4, 0xa2, 0x24, 0xce,
+ 0xab, 0x90, 0x01, 0xa2, 0x26, 0xce, 0xab, 0xb4, 0xfb, 0x9e, 0xb0, 0xe6, 0x59, 0xa2,
+ 0x24, 0xce, 0xab, 0x90, 0x0b, 0xa2, 0x26, 0xce, 0xab, 0xb4, 0xfb, 0x8e, 0xac, 0xce,
+ 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0x90, 0x04, 0xb5, 0xfb, 0x76, 0x94, 0x23, 0xac, 0xce,
+ 0xcc, 0x82, 0x46, 0xcc, 0xf8, 0x90, 0x04, 0xb5, 0xfb, 0x68, 0x90, 0x05, 0xb5, 0xfb,
+ 0x63, 0x59, 0x40, 0xb0, 0x35, 0x20, 0xa2, 0x24, 0xce, 0xab, 0x90, 0x77, 0xa2, 0x26,
+ 0xce, 0xab, 0xb4, 0xfb, 0x5d, 0x90, 0x05, 0xb5, 0xfb, 0x4c, 0x42, 0x95, 0x29, 0xb0,
+ 0x12, 0x60, 0xa2, 0x24, 0xce, 0xab, 0xb0, 0x04, 0xa8, 0xa2, 0x26, 0xce, 0xab, 0xb4,
+ 0xfb, 0x44, 0xb7, 0x02, 0x06, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06,
+ 0xa2, 0x22, 0xce, 0x88, 0xb5, 0x94, 0x0f, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8,
+ 0x90, 0x06, 0xb5, 0xfb, 0x19, 0x41, 0x53, 0xb7, 0x03, 0x01, 0x02, 0xb7, 0x00, 0x01,
+ 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0x93, 0xee, 0xaf, 0xce,
+ 0xb7, 0x05, 0x03, 0x44, 0xb7, 0x00, 0x00, 0x48, 0xb7, 0x00, 0x00, 0x4a, 0xa2, 0x22,
+ 0xce, 0x88, 0xab, 0x46, 0xb5, 0x98, 0xca, 0x3f, 0xce, 0xa2, 0x50, 0xce, 0xa8, 0x9a,
+ 0x20, 0xa2, 0x50, 0xce, 0xab, 0xb5, 0xc1, 0xe6, 0x90, 0x01, 0xa2, 0x38, 0xce, 0x8b,
+ 0x3c, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0x90, 0x07, 0xb5, 0xfa, 0xcb, 0x50,
+ 0xac, 0xce, 0xcc, 0x82, 0x46, 0xcc, 0xf8, 0x90, 0x07, 0xb5, 0xfa, 0xbe, 0x43, 0xb4,
+ 0xfa, 0xc6, 0xb7, 0x02, 0x01, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06,
+ 0xa2, 0x22, 0xce, 0x88, 0xb4, 0x93, 0x91, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8,
+ 0x90, 0x08, 0xb5, 0xfa, 0x9b, 0x50, 0xac, 0xce, 0xcc, 0x82, 0x46, 0xcc, 0xf8, 0x90,
+ 0x08, 0xb5, 0xfa, 0x8e, 0x43, 0xb4, 0xfa, 0x96, 0xb7, 0x02, 0x06, 0x02, 0xb7, 0x00,
+ 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0x93, 0x61, 0xac,
+ 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0x90, 0x08, 0xb5, 0xfa, 0x6b, 0x54, 0xa2, 0x50,
+ 0xce, 0xa8, 0x9a, 0x20, 0xa2, 0x50, 0xce, 0xab, 0xb5, 0xc1, 0x65, 0x90, 0x01, 0xa2,
+ 0x38, 0xce, 0x8b, 0x3c, 0xb7, 0x03, 0x01, 0x02, 0xb7, 0x00, 0x01, 0x04, 0xb7, 0x00,
+ 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0x93, 0x2d, 0x95, 0x27, 0xa2, 0x22, 0xce,
+ 0x88, 0xab, 0x18, 0xb0, 0x40, 0x0f, 0xb5, 0xd6, 0xd9, 0xb7, 0x02, 0x07, 0x02, 0xb7,
+ 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, 0x93, 0x0c,
+ 0x00, 0x00
+};
+
+#endif /* !(_PTIFDDI_ASM_H) */
diff --git a/drivers/net/scc.c b/drivers/net/scc.c
index 3c5e81848..63362ac70 100644
--- a/drivers/net/scc.c
+++ b/drivers/net/scc.c
@@ -1586,11 +1586,6 @@ static int scc_net_init(struct device *dev)
memcpy(dev->dev_addr, ax25_nocall, AX25_ADDR_LEN);
dev->flags = 0;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = 4;
dev->type = ARPHRD_AX25;
dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
diff --git a/drivers/net/sdla.c b/drivers/net/sdla.c
index feaeef4cc..c9684f678 100644
--- a/drivers/net/sdla.c
+++ b/drivers/net/sdla.c
@@ -872,7 +872,7 @@ static void sdla_isr(int irq, void *dev_id, struct pt_regs * regs)
struct frad_local *flp;
char byte;
- dev = irq2dev_map[irq];
+ dev = dev_id;
if (dev == NULL)
{
@@ -1293,7 +1293,6 @@ NOTE: This is rather a useless action right now, as the
{
case ARPHRD_FRAD:
dev->type = ifr->ifr_flags;
- dev->family = AF_UNSPEC;
break;
default:
return(-ENOPROTOOPT);
@@ -1475,11 +1474,9 @@ int sdla_set_config(struct device *dev, struct ifmap *map)
}
dev->irq = map->irq;
- if (request_irq(dev->irq, &sdla_isr, 0, dev->name, NULL))
+ if (request_irq(dev->irq, &sdla_isr, 0, dev->name, dev))
return(-EAGAIN);
- irq2dev_map[dev->irq] = dev;
-
if (flp->type == SDLA_S507)
{
switch(dev->irq)
@@ -1645,12 +1642,6 @@ __initfunc(int sdla_init(struct device *dev))
dev->change_mtu = sdla_change_mtu;
dev->type = 0xFFFF;
- dev->family = AF_UNSPEC;
- dev->pa_alen = 0;
- dev->pa_addr = 0;
- dev->pa_dstaddr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
dev->hard_header_len = 0;
dev->addr_len = 0;
dev->mtu = SDLA_MAX_MTU;
@@ -1696,6 +1687,6 @@ void cleanup_module(void)
if (sdla0.priv)
kfree(sdla0.priv);
if (sdla0.irq)
- free_irq(sdla0.irq, NULL);
+ free_irq(sdla0.irq, &sdla0);
}
#endif /* MODULE */
diff --git a/drivers/net/sdla_fr.c b/drivers/net/sdla_fr.c
index 95f1ae739..fd7173b45 100644
--- a/drivers/net/sdla_fr.c
+++ b/drivers/net/sdla_fr.c
@@ -78,37 +78,35 @@
/****** Defines & Macros ****************************************************/
-#define CMD_OK 0 /* normal firmware return code */
-#define CMD_TIMEOUT 0xFF /* firmware command timed out */
-#define MAX_CMD_RETRY 10 /* max number of firmware retries */
+#define CMD_OK 0 /* normal firmware return code */
+#define CMD_TIMEOUT 0xFF /* firmware command timed out */
+#define MAX_CMD_RETRY 10 /* max number of firmware retries */
-#define FR_HEADER_LEN 8 /* max encapsulation header size */
-#define FR_CHANNEL_MTU 1500 /* unfragmented logical channel MTU */
+#define FR_HEADER_LEN 8 /* max encapsulation header size */
+#define FR_CHANNEL_MTU 1500 /* unfragmented logical channel MTU */
/* Q.922 frame types */
-#define Q922_UI 0x03 /* Unnumbered Info frame */
-#define Q922_XID 0xAF /* ??? */
+#define Q922_UI 0x03 /* Unnumbered Info frame */
+#define Q922_XID 0xAF /* ??? */
/****** Data Structures *****************************************************/
/* This is an extention of the 'struct device' we create for each network
* interface to keep the rest of channel-specific data.
*/
-typedef struct fr_channel
-{
- char name[WAN_IFNAME_SZ+1]; /* interface name, ASCIIZ */
- unsigned dlci; /* logical channel number */
- unsigned cir; /* committed information rate */
- char state; /* channel state */
+typedef struct fr_channel {
+ char name[WAN_IFNAME_SZ + 1]; /* interface name, ASCIIZ */
+ unsigned dlci; /* logical channel number */
+ unsigned cir; /* committed information rate */
+ char state; /* channel state */
unsigned long state_tick; /* time of the last state change */
- sdla_t* card; /* -> owner */
- struct enet_statistics ifstats; /* interface statistics */
+ sdla_t *card; /* -> owner */
+ struct enet_statistics ifstats; /* interface statistics */
} fr_channel_t;
-typedef struct dlci_status
-{
- unsigned short dlci PACKED;
- unsigned char state PACKED;
+typedef struct dlci_status {
+ unsigned short dlci PACKED;
+ unsigned char state PACKED;
} dlci_status_t;
static char TracingEnabled;
@@ -117,60 +115,60 @@ static int int_occur = 0;
/****** Function Prototypes *************************************************/
/* WAN link driver entry points. These are called by the WAN router module. */
-static int update (wan_device_t* wandev);
-static int new_if (wan_device_t* wandev, struct device* dev,
- wanif_conf_t* conf);
-static int del_if (wan_device_t* wandev, struct device* dev);
+static int update(wan_device_t * wandev);
+static int new_if(wan_device_t * wandev, struct device *dev,
+ wanif_conf_t * conf);
+static int del_if(wan_device_t * wandev, struct device *dev);
/* WANPIPE-specific entry points */
-static int wpf_exec (struct sdla* card, void* u_cmd, void* u_data);
+static int wpf_exec(struct sdla *card, void *u_cmd, void *u_data);
/* Network device interface */
-static int if_init (struct device* dev);
-static int if_open (struct device* dev);
-static int if_close (struct device* dev);
-static int if_header (struct sk_buff* skb, struct device* dev,
- unsigned short type, void* daddr, void* saddr, unsigned len);
-static int if_rebuild_hdr (struct sk_buff* skb);
-static int if_send (struct sk_buff* skb, struct device* dev);
-static struct enet_statistics* if_stats (struct device* dev);
+static int if_init(struct device *dev);
+static int if_open(struct device *dev);
+static int if_close(struct device *dev);
+static int if_header(struct sk_buff *skb, struct device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len);
+static int if_rebuild_hdr(struct sk_buff *skb);
+static int if_send(struct sk_buff *skb, struct device *dev);
+static struct enet_statistics *if_stats(struct device *dev);
/* Interrupt handlers */
-static void fr502_isr (sdla_t* card);
-static void fr508_isr (sdla_t* card);
-static void fr502_rx_intr (sdla_t* card);
-static void fr508_rx_intr (sdla_t* card);
-static void tx_intr (sdla_t* card);
-static void spur_intr (sdla_t* card);
+static void fr502_isr(sdla_t * card);
+static void fr508_isr(sdla_t * card);
+static void fr502_rx_intr(sdla_t * card);
+static void fr508_rx_intr(sdla_t * card);
+static void tx_intr(sdla_t * card);
+static void spur_intr(sdla_t * card);
/* Background polling routines */
-static void wpf_poll (sdla_t* card);
+static void wpf_poll(sdla_t * card);
/* Frame relay firmware interface functions */
-static int fr_read_version (sdla_t* card, char* str);
-static int fr_configure (sdla_t* card, fr_conf_t *conf);
-static int fr_set_intr_mode (sdla_t* card, unsigned mode, unsigned mtu);
-static int fr_comm_enable (sdla_t* card);
-static int fr_comm_disable (sdla_t* card);
-static int fr_get_err_stats (sdla_t* card);
-static int fr_get_stats (sdla_t* card);
-static int fr_add_dlci (sdla_t* card, int dlci, int num);
-static int fr_activate_dlci (sdla_t* card, int dlci, int num);
-static int fr_issue_isf (sdla_t* card, int isf);
-static int fr502_send (sdla_t* card, int dlci, int attr, int len, void *buf);
-static int fr508_send (sdla_t* card, int dlci, int attr, int len, void *buf);
+static int fr_read_version(sdla_t * card, char *str);
+static int fr_configure(sdla_t * card, fr_conf_t * conf);
+static int fr_set_intr_mode(sdla_t * card, unsigned mode, unsigned mtu);
+static int fr_comm_enable(sdla_t * card);
+static int fr_comm_disable(sdla_t * card);
+static int fr_get_err_stats(sdla_t * card);
+static int fr_get_stats(sdla_t * card);
+static int fr_add_dlci(sdla_t * card, int dlci, int num);
+static int fr_activate_dlci(sdla_t * card, int dlci, int num);
+static int fr_issue_isf(sdla_t * card, int isf);
+static int fr502_send(sdla_t * card, int dlci, int attr, int len, void *buf);
+static int fr508_send(sdla_t * card, int dlci, int attr, int len, void *buf);
/* Firmware asynchronous event handlers */
-static int fr_event (sdla_t* card, int event, fr_mbox_t* mbox);
-static int fr_modem_failure (sdla_t *card, fr_mbox_t* mbox);
-static int fr_dlci_change (sdla_t *card, fr_mbox_t* mbox);
+static int fr_event(sdla_t * card, int event, fr_mbox_t * mbox);
+static int fr_modem_failure(sdla_t * card, fr_mbox_t * mbox);
+static int fr_dlci_change(sdla_t * card, fr_mbox_t * mbox);
/* Miscellaneous functions */
-static int update_chan_state (struct device* dev);
-static void set_chan_state (struct device* dev, int state);
-static struct device* find_channel (sdla_t* card, unsigned dlci);
-static int is_tx_ready (sdla_t* card);
-static unsigned int dec_to_uint (unsigned char* str, int len);
+static int update_chan_state(struct device *dev);
+static void set_chan_state(struct device *dev, int state);
+static struct device *find_channel(sdla_t * card, unsigned dlci);
+static int is_tx_ready(sdla_t * card);
+static unsigned int dec_to_uint(unsigned char *str, int len);
/****** Public Functions ****************************************************/
@@ -186,36 +184,31 @@ static unsigned int dec_to_uint (unsigned char* str, int len);
* Return: 0 o.k.
* < 0 failure.
*/
-__initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf))
+__initfunc(int wpf_init(sdla_t * card, wandev_conf_t * conf))
{
- union
- {
+ union {
char str[80];
fr_conf_t cfg;
} u;
/* Verify configuration ID */
- if (conf->config_id != WANCONFIG_FR)
- {
+ if (conf->config_id != WANCONFIG_FR) {
printk(KERN_INFO "%s: invalid configuration ID %u!\n",
- card->devname, conf->config_id)
- ;
+ card->devname, conf->config_id);
return -EINVAL;
}
-
/* Initialize protocol-specific fields of adapter data space */
- switch (card->hw.fwid)
- {
+ switch (card->hw.fwid) {
case SFID_FR502:
- card->mbox = (void*)(card->hw.dpmbase + FR502_MBOX_OFFS);
- card->rxmb = (void*)(card->hw.dpmbase + FR502_RXMB_OFFS);
- card->flags = (void*)(card->hw.dpmbase + FR502_FLAG_OFFS);
+ card->mbox = (void *) (card->hw.dpmbase + FR502_MBOX_OFFS);
+ card->rxmb = (void *) (card->hw.dpmbase + FR502_RXMB_OFFS);
+ card->flags = (void *) (card->hw.dpmbase + FR502_FLAG_OFFS);
card->isr = &fr502_isr;
break;
case SFID_FR508:
- card->mbox = (void*)(card->hw.dpmbase + FR508_MBOX_OFFS);
- card->flags = (void*)(card->hw.dpmbase + FR508_FLAG_OFFS);
+ card->mbox = (void *) (card->hw.dpmbase + FR508_MBOX_OFFS);
+ card->flags = (void *) (card->hw.dpmbase + FR508_FLAG_OFFS);
card->isr = &fr508_isr;
break;
@@ -230,10 +223,9 @@ __initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf))
*/
if (fr_read_version(card, NULL) || fr_read_version(card, u.str))
return -EIO
- ;
+ ;
printk(KERN_INFO "%s: running frame relay firmware v%s\n",
- card->devname, u.str)
- ;
+ card->devname, u.str);
/* Adjust configuration */
conf->mtu = max(min(conf->mtu, 4080), FR_CHANNEL_MTU + FR_HEADER_LEN);
@@ -241,93 +233,83 @@ __initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf))
/* Configure adapter firmware */
memset(&u.cfg, 0, sizeof(u.cfg));
- u.cfg.mtu = conf->mtu;
- u.cfg.t391 = 10;
- u.cfg.t392 = 15;
- u.cfg.n391 = 6;
- u.cfg.n392 = 3;
- u.cfg.n393 = 4;
- u.cfg.kbps = conf->bps / 1000;
- u.cfg.cir_fwd = 16;
+ u.cfg.mtu = conf->mtu;
+ u.cfg.t391 = 10;
+ u.cfg.t392 = 15;
+ u.cfg.n391 = 6;
+ u.cfg.n392 = 3;
+ u.cfg.n393 = 4;
+ u.cfg.kbps = conf->bps / 1000;
+ u.cfg.cir_fwd = 16;
u.cfg.cir_bwd = u.cfg.bc_fwd = u.cfg.bc_bwd = u.cfg.cir_fwd;
- u.cfg.options = 0x0081; /* direct Rx, no CIR check */
- switch (conf->u.fr.signalling)
- {
- case WANOPT_FR_Q933: u.cfg.options |= 0x0200; break;
- case WANOPT_FR_LMI: u.cfg.options |= 0x0400; break;
+ u.cfg.options = 0x0081; /* direct Rx, no CIR check */
+ switch (conf->u.fr.signalling) {
+ case WANOPT_FR_Q933:
+ u.cfg.options |= 0x0200;
+ break;
+ case WANOPT_FR_LMI:
+ u.cfg.options |= 0x0400;
+ break;
}
- if (conf->station == WANOPT_CPE)
- {
+ if (conf->station == WANOPT_CPE) {
u.cfg.options |= 0x8000; /* auto config DLCI */
- }
- else
- {
+ } else {
u.cfg.station = 1; /* switch emulation mode */
card->u.f.node_dlci = conf->u.fr.dlci ? conf->u.fr.dlci : 16;
- card->u.f.dlci_num = min(max(conf->u.fr.dlci_num, 1), 100);
+ card->u.f.dlci_num = min(max(conf->u.fr.dlci_num, 1), 100);
}
if (conf->clocking == WANOPT_INTERNAL)
u.cfg.port |= 0x0001
- ;
+ ;
if (conf->interface == WANOPT_RS232)
u.cfg.port |= 0x0002
- ;
+ ;
if (conf->u.fr.t391)
- u.cfg.t391 = min(conf->u.fr.t391, 30)
- ;
+ u.cfg.t391 = min(conf->u.fr.t391, 30);
if (conf->u.fr.t392)
- u.cfg.t392 = min(conf->u.fr.t392, 30)
- ;
+ u.cfg.t392 = min(conf->u.fr.t392, 30);
if (conf->u.fr.n391)
- u.cfg.n391 = min(conf->u.fr.n391, 255)
- ;
+ u.cfg.n391 = min(conf->u.fr.n391, 255);
if (conf->u.fr.n392)
- u.cfg.n392 = min(conf->u.fr.n392, 10)
- ;
+ u.cfg.n392 = min(conf->u.fr.n392, 10);
if (conf->u.fr.n393)
- u.cfg.n393 = min(conf->u.fr.n393, 10)
- ;
+ u.cfg.n393 = min(conf->u.fr.n393, 10);
if (fr_configure(card, &u.cfg))
return -EIO
- ;
+ ;
- if (card->hw.fwid == SFID_FR508)
- {
- fr_buf_info_t* buf_info =
- (void*)(card->hw.dpmbase + FR508_RXBC_OFFS)
- ;
+ if (card->hw.fwid == SFID_FR508) {
+ fr_buf_info_t *buf_info =
+ (void *) (card->hw.dpmbase + FR508_RXBC_OFFS);
card->rxmb =
- (void*)(buf_info->rse_next -
- FR_MB_VECTOR + card->hw.dpmbase)
- ;
+ (void *) (buf_info->rse_next -
+ FR_MB_VECTOR + card->hw.dpmbase);
card->u.f.rxmb_base =
- (void*)(buf_info->rse_base -
- FR_MB_VECTOR + card->hw.dpmbase)
- ;
+ (void *) (buf_info->rse_base -
+ FR_MB_VECTOR + card->hw.dpmbase);
card->u.f.rxmb_last =
- (void*)(buf_info->rse_base +
- (buf_info->rse_num - 1) * sizeof(fr_buf_ctl_t) -
- FR_MB_VECTOR + card->hw.dpmbase)
- ;
+ (void *) (buf_info->rse_base +
+ (buf_info->rse_num - 1) * sizeof(fr_buf_ctl_t) -
+ FR_MB_VECTOR + card->hw.dpmbase);
card->u.f.rx_base = buf_info->buf_base;
- card->u.f.rx_top = buf_info->buf_top;
+ card->u.f.rx_top = buf_info->buf_top;
}
- card->wandev.mtu = conf->mtu;
- card->wandev.bps = conf->bps;
- card->wandev.interface = conf->interface;
- card->wandev.clocking = conf->clocking;
- card->wandev.station = conf->station;
- card->poll = &wpf_poll;
- card->exec = &wpf_exec;
- card->wandev.update = &update;
- card->wandev.new_if = &new_if;
- card->wandev.del_if = &del_if;
- card->wandev.state = WAN_DISCONNECTED;
- card->wandev.udp_port = conf->udp_port;
- TracingEnabled = '0';
- return 0;
+ card->wandev.mtu = conf->mtu;
+ card->wandev.bps = conf->bps;
+ card->wandev.interface = conf->interface;
+ card->wandev.clocking = conf->clocking;
+ card->wandev.station = conf->station;
+ card->poll = &wpf_poll;
+ card->exec = &wpf_exec;
+ card->wandev.update = &update;
+ card->wandev.new_if = &new_if;
+ card->wandev.del_if = &del_if;
+ card->wandev.state = WAN_DISCONNECTED;
+ card->wandev.udp_port = conf->udp_port;
+ TracingEnabled = '0';
+ return 0;
}
/******* WAN Device Driver Entry Points *************************************/
@@ -335,20 +317,20 @@ __initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf))
/*============================================================================
* Update device status & statistics.
*/
-static int update (wan_device_t* wandev)
+static int update(wan_device_t * wandev)
{
- sdla_t* card;
+ sdla_t *card;
/* sanity checks */
if ((wandev == NULL) || (wandev->private == NULL))
return -EFAULT
- ;
+ ;
if (wandev->state == WAN_UNCONFIGURED)
return -ENODEV
- ;
- if (test_and_set_bit(0, (void*)&wandev->critical))
+ ;
+ if (test_and_set_bit(0, (void *) &wandev->critical))
return -EAGAIN
- ;
+ ;
card = wandev->private;
fr_get_err_stats(card);
fr_get_stats(card);
@@ -368,61 +350,48 @@ static int update (wan_device_t* wandev)
* Return: 0 o.k.
* < 0 failure (channel will not be created)
*/
-static int new_if (wan_device_t* wandev, struct device* dev, wanif_conf_t* conf)
+static int new_if(wan_device_t * wandev, struct device *dev, wanif_conf_t * conf)
{
- sdla_t* card = wandev->private;
- fr_channel_t* chan;
+ sdla_t *card = wandev->private;
+ fr_channel_t *chan;
int err = 0;
- if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ))
- {
+ if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) {
printk(KERN_INFO "%s: invalid interface name!\n",
- card->devname)
- ;
+ card->devname);
return -EINVAL;
}
-
/* allocate and initialize private data */
chan = kmalloc(sizeof(fr_channel_t), GFP_KERNEL);
if (chan == NULL)
return -ENOMEM
- ;
+ ;
memset(chan, 0, sizeof(fr_channel_t));
strcpy(chan->name, conf->name);
chan->card = card;
/* verify media address */
- if (is_digit(conf->addr[0]))
- {
+ if (is_digit(conf->addr[0])) {
int dlci = dec_to_uint(conf->addr, 0);
- if (dlci && (dlci <= 4095))
- {
+ if (dlci && (dlci <= 4095)) {
chan->dlci = dlci;
- }
- else
- {
+ } else {
printk(KERN_ERR
- "%s: invalid DLCI %u on interface %s!\n",
- wandev->name, dlci, chan->name)
- ;
+ "%s: invalid DLCI %u on interface %s!\n",
+ wandev->name, dlci, chan->name);
err = -EINVAL;
}
- }
- else
- {
+ } else {
printk(KERN_ERR
- "%s: invalid media address on interface %s!\n",
- wandev->name, chan->name)
- ;
+ "%s: invalid media address on interface %s!\n",
+ wandev->name, chan->name);
err = -EINVAL;
}
- if (err)
- {
+ if (err) {
kfree(chan);
return err;
}
-
/* prepare network device data space for registration */
dev->name = chan->name;
dev->init = &if_init;
@@ -433,10 +402,9 @@ static int new_if (wan_device_t* wandev, struct device* dev, wanif_conf_t* conf)
/*============================================================================
* Delete logical channel.
*/
-static int del_if (wan_device_t* wandev, struct device* dev)
+static int del_if(wan_device_t * wandev, struct device *dev)
{
- if (dev->priv)
- {
+ if (dev->priv) {
kfree(dev->priv);
dev->priv = NULL;
}
@@ -448,21 +416,20 @@ static int del_if (wan_device_t* wandev, struct device* dev)
/*============================================================================
* Execute adapter interface command.
*/
-static int wpf_exec (struct sdla* card, void* u_cmd, void* u_data)
+static int wpf_exec(struct sdla *card, void *u_cmd, void *u_data)
{
- fr_mbox_t* mbox = card->mbox;
+ fr_mbox_t *mbox = card->mbox;
int retry = MAX_CMD_RETRY;
int err, len;
fr_cmd_t cmd;
- if(copy_from_user((void*)&cmd, u_cmd, sizeof(cmd)))
+ if (copy_from_user((void *) &cmd, u_cmd, sizeof(cmd)))
return -EFAULT;
/* execute command */
- do
- {
+ do {
memcpy(&mbox->cmd, &cmd, sizeof(cmd));
if (cmd.length)
- if(copy_from_user((void*)&mbox->data, u_data, cmd.length))
+ if (copy_from_user((void *) &mbox->data, u_data, cmd.length))
return -EFAULT;
if (sdla_exec(mbox))
err = mbox->cmd.result;
@@ -472,10 +439,10 @@ static int wpf_exec (struct sdla* card, void* u_cmd, void* u_data)
while (err && retry-- && fr_event(card, err, mbox));
/* return result */
- if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(fr_cmd_t)))
+ if (copy_to_user(u_cmd, (void *) &mbox->cmd, sizeof(fr_cmd_t)))
return -EFAULT;
len = mbox->cmd.length;
- if (len && u_data && copy_to_user(u_data, (void*)&mbox->data, len))
+ if (len && u_data && copy_to_user(u_data, (void *) &mbox->data, len))
return -EFAULT;
return 0;
}
@@ -489,43 +456,42 @@ static int wpf_exec (struct sdla* card, void* u_cmd, void* u_data)
* interface registration. Returning anything but zero will fail interface
* registration.
*/
-static int if_init (struct device* dev)
+static int if_init(struct device *dev)
{
- fr_channel_t* chan = dev->priv;
- sdla_t* card = chan->card;
- wan_device_t* wandev = &card->wandev;
+ fr_channel_t *chan = dev->priv;
+ sdla_t *card = chan->card;
+ wan_device_t *wandev = &card->wandev;
int i;
/* Initialize device driver entry points */
- dev->open = &if_open;
- dev->stop = &if_close;
- dev->hard_header = &if_header;
- dev->rebuild_header = &if_rebuild_hdr;
- dev->hard_start_xmit = &if_send;
- dev->get_stats = &if_stats;
+ dev->open = &if_open;
+ dev->stop = &if_close;
+ dev->hard_header = &if_header;
+ dev->rebuild_header = &if_rebuild_hdr;
+ dev->hard_start_xmit = &if_send;
+ dev->get_stats = &if_stats;
/* Initialize media-specific parameters */
- dev->family = AF_INET; /* address family */
- dev->type = ARPHRD_DLCI; /* ARP h/w type */
- dev->mtu = FR_CHANNEL_MTU;
- dev->hard_header_len = FR_HEADER_LEN;/* media header length */
- dev->addr_len = 2; /* hardware address length */
- *(unsigned short*)dev->dev_addr = htons(chan->dlci);
+ dev->family = AF_INET; /* address family */
+ dev->type = ARPHRD_DLCI; /* ARP h/w type */
+ dev->mtu = FR_CHANNEL_MTU;
+ dev->hard_header_len = FR_HEADER_LEN; /* media header length */
+ dev->addr_len = 2; /* hardware address length */
+ *(unsigned short *) dev->dev_addr = htons(chan->dlci);
/* Initialize hardware parameters (just for reference) */
- dev->irq = wandev->irq;
- dev->dma = wandev->dma;
- dev->base_addr = wandev->ioport;
- dev->mem_start = wandev->maddr;
- dev->mem_end = wandev->maddr + wandev->msize - 1;
-
- /* Set transmit buffer queue length */
- dev->tx_queue_len = 30;
-
+ dev->irq = wandev->irq;
+ dev->dma = wandev->dma;
+ dev->base_addr = wandev->ioport;
+ dev->mem_start = wandev->maddr;
+ dev->mem_end = wandev->maddr + wandev->msize - 1;
+
+ /* Set transmit buffer queue length */
+ dev->tx_queue_len = 30;
+
/* Initialize socket buffers */
for (i = 0; i < DEV_NUMBUFFS; ++i)
- skb_queue_head_init(&dev->buffs[i])
- ;
+ skb_queue_head_init(&dev->buffs[i]);
set_chan_state(dev, WAN_DISCONNECTED);
return 0;
}
@@ -537,41 +503,34 @@ static int if_init (struct device* dev)
*
* Return 0 if O.k. or errno.
*/
-static int if_open (struct device* dev)
+static int if_open(struct device *dev)
{
- fr_channel_t* chan = dev->priv;
- sdla_t* card = chan->card;
+ fr_channel_t *chan = dev->priv;
+ sdla_t *card = chan->card;
int err = 0;
if (dev->start)
- return -EBUSY /* only one open is allowed */
- ;
- if (test_and_set_bit(0, (void*)&card->wandev.critical))
+ return -EBUSY /* only one open is allowed */
+ ;
+ if (test_and_set_bit(0, (void *) &card->wandev.critical))
return -EAGAIN;
;
- if (!card->open_cnt)
- {
+ if (!card->open_cnt) {
if ((fr_comm_enable(card)) ||
- (fr_set_intr_mode(card, 0x03, card->wandev.mtu)))
- {
+ (fr_set_intr_mode(card, 0x03, card->wandev.mtu))) {
err = -EIO;
goto done;
}
wanpipe_set_state(card, WAN_CONNECTED);
- if (card->wandev.station == WANOPT_CPE)
- {
+ if (card->wandev.station == WANOPT_CPE) {
/* CPE: issue full status enquiry */
fr_issue_isf(card, FR_ISF_FSE);
- }
- else /* FR switch: activate DLCI(s) */
- {
+ } else { /* FR switch: activate DLCI(s) */
fr_add_dlci(card,
- card->u.f.node_dlci, card->u.f.dlci_num)
- ;
+ card->u.f.node_dlci, card->u.f.dlci_num);
fr_activate_dlci(card,
- card->u.f.node_dlci, card->u.f.dlci_num)
- ;
+ card->u.f.node_dlci, card->u.f.dlci_num);
}
}
dev->mtu = min(dev->mtu, card->wandev.mtu - FR_HEADER_LEN);
@@ -581,7 +540,7 @@ static int if_open (struct device* dev)
wanpipe_open(card);
update_chan_state(dev);
-done:
+ done:
card->wandev.critical = 0;
return err;
}
@@ -591,18 +550,17 @@ done:
* o if this is the last open, then disable communications and interrupts.
* o reset flags.
*/
-static int if_close (struct device* dev)
+static int if_close(struct device *dev)
{
- fr_channel_t* chan = dev->priv;
- sdla_t* card = chan->card;
+ fr_channel_t *chan = dev->priv;
+ sdla_t *card = chan->card;
- if (test_and_set_bit(0, (void*)&card->wandev.critical))
+ if (test_and_set_bit(0, (void *) &card->wandev.critical))
return -EAGAIN;
;
dev->start = 0;
wanpipe_close(card);
- if (!card->open_cnt)
- {
+ if (!card->open_cnt) {
wanpipe_set_state(card, WAN_DISCONNECTED);
fr_set_intr_mode(card, 0, 0);
fr_comm_disable(card);
@@ -621,15 +579,14 @@ static int if_close (struct device* dev)
*
* Return: media header length.
*/
-static int if_header (struct sk_buff* skb, struct device* dev,
- unsigned short type, void* daddr, void* saddr, unsigned len)
+static int if_header(struct sk_buff *skb, struct device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
{
int hdr_len = 0;
skb->protocol = type;
hdr_len = wan_encapsulate(skb, dev);
- if (hdr_len < 0)
- {
+ if (hdr_len < 0) {
hdr_len = 0;
skb->protocol = 0;
}
@@ -645,14 +602,13 @@ static int if_header (struct sk_buff* skb, struct device* dev,
* Return: 1 physical address resolved.
* 0 physical address not resolved
*/
-static int if_rebuild_hdr (struct sk_buff* skb)
+static int if_rebuild_hdr(struct sk_buff *skb)
{
- fr_channel_t* chan = skb->dev->priv;
- sdla_t* card = chan->card;
+ fr_channel_t *chan = skb->dev->priv;
+ sdla_t *card = chan->card;
printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n",
- card->devname, skb->dev->name)
- ;
+ card->devname, skb->dev->name);
return 1;
}
@@ -674,84 +630,62 @@ static int if_rebuild_hdr (struct sk_buff* skb)
* 2. Setting tbusy flag will inhibit further transmit requests from the
* protocol stack and can be used for flow control with protocol layer.
*/
-static int if_send (struct sk_buff* skb, struct device* dev)
+static int if_send(struct sk_buff *skb, struct device *dev)
{
fr_channel_t *chan = dev->priv;
sdla_t *card = chan->card;
- int retry=0, err;
+ int retry = 0, err;
struct device *dev2;
-
- if (test_and_set_bit(0, (void*)&card->wandev.critical))
- {
+
+ if (test_and_set_bit(0, (void *) &card->wandev.critical)) {
#ifdef _DEBUG_
printk(KERN_INFO "%s: if_send() hit critical section!\n",
- card->devname)
- ;
+ card->devname);
#endif
dev_kfree_skb(skb, FREE_WRITE);
return 0;
}
-
- if (test_and_set_bit(0, (void*)&dev->tbusy))
- {
+ if (test_and_set_bit(0, (void *) &dev->tbusy)) {
#ifdef _DEBUG_
printk(KERN_INFO "%s: Tx collision on interface %s!\n",
- card->devname, dev->name)
- ;
+ card->devname, dev->name);
#endif
++chan->ifstats.collisions;
++card->wandev.stats.collisions;
retry = 1;
- if(card->wandev.tx_int_enabled)
- {
- for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave)
- {
- dev2->tbusy = 1;
- }
+ if (card->wandev.tx_int_enabled) {
+ for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) {
+ dev2->tbusy = 1;
+ }
}
- }
- else if (card->wandev.state != WAN_CONNECTED)
- {
+ } else if (card->wandev.state != WAN_CONNECTED) {
++chan->ifstats.tx_dropped;
++card->wandev.stats.tx_dropped;
- }
- else if (chan->state != WAN_CONNECTED)
- {
+ } else if (chan->state != WAN_CONNECTED) {
update_chan_state(dev);
++chan->ifstats.tx_dropped;
++card->wandev.stats.tx_dropped;
- }
- else if (!is_tx_ready(card))
- {
+ } else if (!is_tx_ready(card)) {
retry = 1;
- if(card->wandev.tx_int_enabled)
- {
- for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave)
- {
- dev2->tbusy = 1;
- }
+ if (card->wandev.tx_int_enabled) {
+ for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) {
+ dev2->tbusy = 1;
+ }
}
- }
- else
- {
- err = (card->hw.fwid == SFID_FR508) ?
- fr508_send(card, chan->dlci, 0, skb->len, skb->data) :
- fr502_send(card, chan->dlci, 0, skb->len, skb->data)
- ;
- if (err)
- {
+ } else {
+ err = (card->hw.fwid == SFID_FR508) ?
+ fr508_send(card, chan->dlci, 0, skb->len, skb->data) :
+ fr502_send(card, chan->dlci, 0, skb->len, skb->data);
+ if (err) {
++chan->ifstats.tx_errors;
++card->wandev.stats.tx_errors;
- }
- else
- {
+ } else {
++chan->ifstats.tx_packets;
++card->wandev.stats.tx_packets;
}
}
- if (!retry)
- {
+ if (!retry) {
dev_kfree_skb(skb, FREE_WRITE);
dev->tbusy = 0;
}
@@ -764,9 +698,9 @@ static int if_send (struct sk_buff* skb, struct device* dev)
* Get ethernet-style interface statistics.
* Return a pointer to struct enet_statistics.
*/
-static struct enet_statistics* if_stats (struct device* dev)
+static struct enet_statistics *if_stats(struct device *dev)
{
- fr_channel_t* chan = dev->priv;
+ fr_channel_t *chan = dev->priv;
return &chan->ifstats;
}
@@ -776,17 +710,16 @@ static struct enet_statistics* if_stats (struct device* dev)
/*============================================================================
* S502 frame relay interrupt service routine.
*/
-static void fr502_isr (sdla_t* card)
+static void fr502_isr(sdla_t * card)
{
- fr502_flags_t* flags = card->flags;
+ fr502_flags_t *flags = card->flags;
- switch (flags->iflag)
- {
- case 0x01: /* receive interrupt */
+ switch (flags->iflag) {
+ case 0x01: /* receive interrupt */
fr502_rx_intr(card);
break;
- case 0x02: /* transmit interrupt */
+ case 0x02: /* transmit interrupt */
flags->imask &= ~0x02;
tx_intr(card);
break;
@@ -800,28 +733,26 @@ static void fr502_isr (sdla_t* card)
/*============================================================================
* S508 frame relay interrupt service routine.
*/
-static void fr508_isr (sdla_t* card)
+static void fr508_isr(sdla_t * card)
{
- fr508_flags_t* flags = card->flags;
- fr_buf_ctl_t* bctl;
-
- if(int_occur){
+ fr508_flags_t *flags = card->flags;
+ fr_buf_ctl_t *bctl;
+
+ if (int_occur) {
#ifdef _DEBUG_
- printk(KERN_INFO "%s:Interrupt Occurred within an ISR\n",card->devname);
+ printk(KERN_INFO "%s:Interrupt Occurred within an ISR\n", card->devname);
#endif
return;
}
- int_occur=1;
- switch (flags->iflag)
- {
- case 0x01: /* receive interrupt */
+ int_occur = 1;
+ switch (flags->iflag) {
+ case 0x01: /* receive interrupt */
fr508_rx_intr(card);
break;
- case 0x02: /* transmit interrupt */
- bctl = (void*)(flags->tse_offs - FR_MB_VECTOR +
- card->hw.dpmbase)
- ;
+ case 0x02: /* transmit interrupt */
+ bctl = (void *) (flags->tse_offs - FR_MB_VECTOR +
+ card->hw.dpmbase);
bctl->flag = 0x90; /* disable further Tx interrupts */
tx_intr(card);
break;
@@ -836,48 +767,41 @@ static void fr508_isr (sdla_t* card)
/*============================================================================
* Receive interrupt handler.
*/
-static void fr502_rx_intr (sdla_t* card)
+static void fr502_rx_intr(sdla_t * card)
{
- fr_mbox_t* mbox = card->rxmb;
+ fr_mbox_t *mbox = card->rxmb;
struct sk_buff *skb;
struct device *dev;
fr_channel_t *chan;
unsigned dlci, len;
- void* buf;
-
+ void *buf;
+
sdla_mapmem(&card->hw, FR502_RX_VECTOR);
dlci = mbox->cmd.dlci;
- len = mbox->cmd.length;
+ len = mbox->cmd.length;
/* Find network interface for this packet */
dev = find_channel(card, dlci);
- if (dev == NULL)
- {
+ if (dev == NULL) {
/* Invalid channel, discard packet */
printk(KERN_INFO "%s: receiving on orphaned DLCI %d!\n",
- card->devname, dlci)
- ;
+ card->devname, dlci);
goto rx_done;
}
chan = dev->priv;
- if (!dev->start)
- {
+ if (!dev->start) {
++chan->ifstats.rx_dropped;
goto rx_done;
}
-
/* Allocate socket buffer */
skb = dev_alloc_skb(len);
- if (skb == NULL)
- {
+ if (skb == NULL) {
printk(KERN_INFO "%s: no socket buffers available!\n",
- card->devname)
- ;
+ card->devname);
++chan->ifstats.rx_dropped;
goto rx_done;
}
-
/* Copy data to the socket buffer */
buf = skb_put(skb, len);
memcpy(buf, mbox->data, len);
@@ -886,77 +810,64 @@ static void fr502_rx_intr (sdla_t* card)
/* Decapsulate packet and pass it up the protocol stack */
skb->dev = dev;
buf = skb_pull(skb, 1); /* remove hardware header */
- if (!wan_type_trans(skb, dev))
- {
+ if (!wan_type_trans(skb, dev)) {
/* can't decapsulate packet */
dev_kfree_skb(skb, FREE_READ);
++chan->ifstats.rx_errors;
++card->wandev.stats.rx_errors;
- }
- else
- {
+ } else {
netif_rx(skb);
++chan->ifstats.rx_packets;
++card->wandev.stats.rx_packets;
}
-rx_done:
+ rx_done:
sdla_mapmem(&card->hw, FR_MB_VECTOR);
}
/*============================================================================
* Receive interrupt handler.
*/
-static void fr508_rx_intr (sdla_t* card)
+static void fr508_rx_intr(sdla_t * card)
{
- fr_buf_ctl_t* frbuf = card->rxmb;
- struct sk_buff* skb;
- struct device* dev;
- fr_channel_t* chan;
+ fr_buf_ctl_t *frbuf = card->rxmb;
+ struct sk_buff *skb;
+ struct device *dev;
+ fr_channel_t *chan;
unsigned dlci, len, offs;
- void* buf;
-
- if (frbuf->flag != 0x01)
- {
+ void *buf;
+
+ if (frbuf->flag != 0x01) {
printk(KERN_INFO "%s: corrupted Rx buffer @ 0x%X!\n",
- card->devname, (unsigned)frbuf)
- ;
+ card->devname, (unsigned) frbuf);
return;
}
- len = frbuf->length;
+ len = frbuf->length;
dlci = frbuf->dlci;
offs = frbuf->offset;
/* Find network interface for this packet */
dev = find_channel(card, dlci);
- if (dev == NULL)
- {
+ if (dev == NULL) {
/* Invalid channel, discard packet */
printk(KERN_INFO "%s: receiving on orphaned DLCI %d!\n",
- card->devname, dlci)
- ;
+ card->devname, dlci);
goto rx_done;
}
chan = dev->priv;
- if (!dev->start)
- {
+ if (!dev->start) {
++chan->ifstats.rx_dropped;
goto rx_done;
}
-
/* Allocate socket buffer */
skb = dev_alloc_skb(len);
- if (skb == NULL)
- {
+ if (skb == NULL) {
printk(KERN_INFO "%s: no socket buffers available!\n",
- card->devname)
- ;
+ card->devname);
++chan->ifstats.rx_dropped;
goto rx_done;
}
-
/* Copy data to the socket buffer */
- if ((offs + len) > card->u.f.rx_top + 1)
- {
+ if ((offs + len) > card->u.f.rx_top + 1) {
unsigned tmp = card->u.f.rx_top - offs + 1;
buf = skb_put(skb, tmp);
@@ -965,30 +876,27 @@ static void fr508_rx_intr (sdla_t* card)
len -= tmp;
}
buf = skb_put(skb, len);
- sdla_peek(&card->hw, offs, buf, len);
+ sdla_peek(&card->hw, offs, buf, len);
/* Decapsulate packet and pass it up the protocol stack */
skb->dev = dev;
buf = skb_pull(skb, 1); /* remove hardware header */
- if (!wan_type_trans(skb, dev))
- {
+ if (!wan_type_trans(skb, dev)) {
/* can't decapsulate packet */
dev_kfree_skb(skb, FREE_READ);
++chan->ifstats.rx_errors;
++card->wandev.stats.rx_errors;
- }
- else
- {
+ } else {
netif_rx(skb);
++chan->ifstats.rx_packets;
++card->wandev.stats.rx_packets;
}
-rx_done:
+ rx_done:
/* Release buffer element and calculate a pointer to the next one */
frbuf->flag = 0;
card->rxmb = ++frbuf;
- if ((void*)frbuf > card->u.f.rxmb_last)
+ if ((void *) frbuf > card->u.f.rxmb_last)
card->rxmb = card->u.f.rxmb_base
- ;
+ ;
}
/*============================================================================
@@ -997,19 +905,23 @@ rx_done:
* o
* If number of spurious interrupts exceeded some limit, then ???
*/
-static void tx_intr (sdla_t* card)
+static void tx_intr(sdla_t * card)
{
- struct device* dev = card->wandev.dev;
-
- for (; dev; dev = dev->slave) {
- if( !dev || !dev->start ) continue;
- dev->tbusy = 0;
- dev_tint(dev);
- }
- card->wandev.tx_int_enabled = 0;
+ struct device *dev = card->wandev.dev;
+ int v = 0;
+
+ for (; dev; dev = dev->slave) {
+ if (!dev || !dev->start)
+ continue;
+ v += dev->tbusy;
+ dev->tbusy = 0;
+ }
+ card->wandev.tx_int_enabled = 0;
+ if (v)
+ mark_bh(NET_BH);
/*
- printk(KERN_INFO "%s: transmit interrupt!\n", card->devname);
-*/
+ printk(KERN_INFO "%s: transmit interrupt!\n", card->devname);
+ */
}
/*============================================================================
@@ -1018,7 +930,7 @@ static void tx_intr (sdla_t* card)
* o
* If number of spurious interrupts exceeded some limit, then ???
*/
-static void spur_intr (sdla_t* card)
+static void spur_intr(sdla_t * card)
{
printk(KERN_INFO "%s: spurious interrupt!\n", card->devname);
}
@@ -1036,25 +948,25 @@ static void spur_intr (sdla_t* card)
* 1. This routine may be called on interrupt context with all interrupts
* enabled. Beware!
*/
-static void wpf_poll (sdla_t* card)
+static void wpf_poll(sdla_t * card)
{
static unsigned long last_poll;
- fr502_flags_t* flags;
+ fr502_flags_t *flags;
if ((jiffies - last_poll) < HZ)
return
- ;
+ ;
flags = card->flags;
- if (flags->event)
- {
- fr_mbox_t* mbox = card->mbox;
+ if (flags->event) {
+ fr_mbox_t *mbox = card->mbox;
int err;
memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
mbox->cmd.command = FR_READ_STATUS;
err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
- if (err) fr_event(card, err, mbox);
+ if (err)
+ fr_event(card, err, mbox);
}
last_poll = jiffies;
}
@@ -1065,25 +977,23 @@ static void wpf_poll (sdla_t* card)
* Read firmware code version.
* o fill string str with firmware version info.
*/
-static int fr_read_version (sdla_t* card, char* str)
+static int fr_read_version(sdla_t * card, char *str)
{
- fr_mbox_t* mbox = card->mbox;
+ fr_mbox_t *mbox = card->mbox;
int retry = MAX_CMD_RETRY;
int err;
- do
- {
+ do {
memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
mbox->cmd.command = FR_READ_CODE_VERSION;
err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
}
while (err && retry-- && fr_event(card, err, mbox));
- if (!err && str)
- {
+ if (!err && str) {
int len = mbox->cmd.length;
memcpy(str, mbox->data, len);
- str[len] = '\0';
+ str[len] = '\0';
}
return err;
}
@@ -1091,25 +1001,24 @@ static int fr_read_version (sdla_t* card, char* str)
/*============================================================================
* Set global configuration.
*/
-static int fr_configure (sdla_t* card, fr_conf_t *conf)
+static int fr_configure(sdla_t * card, fr_conf_t * conf)
{
- fr_mbox_t* mbox = card->mbox;
+ fr_mbox_t *mbox = card->mbox;
int retry = MAX_CMD_RETRY;
int dlci = card->u.f.node_dlci;
int dlci_num = card->u.f.dlci_num;
int err, i;
- do
- {
+ do {
memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
memcpy(mbox->data, conf, sizeof(fr_conf_t));
- if (dlci_num) for (i = 0; i < dlci_num; ++i)
- ((fr_conf_t*)mbox->data)->dlci[i] = dlci + i
- ;
+ if (dlci_num)
+ for (i = 0; i < dlci_num; ++i)
+ ((fr_conf_t *) mbox->data)->dlci[i] = dlci + i
+ ;
mbox->cmd.command = FR_SET_CONFIG;
mbox->cmd.length =
- sizeof(fr_conf_t) + dlci_num * sizeof(short)
- ;
+ sizeof(fr_conf_t) + dlci_num * sizeof(short);
err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
}
while (err && retry-- && fr_event(card, err, mbox));
@@ -1119,32 +1028,28 @@ static int fr_configure (sdla_t* card, fr_conf_t *conf)
/*============================================================================
* Set interrupt mode.
*/
-static int fr_set_intr_mode (sdla_t* card, unsigned mode, unsigned mtu)
+static int fr_set_intr_mode(sdla_t * card, unsigned mode, unsigned mtu)
{
- fr_mbox_t* mbox = card->mbox;
+ fr_mbox_t *mbox = card->mbox;
int retry = MAX_CMD_RETRY;
int err;
- do
- {
+ do {
memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
- if (card->hw.fwid == SFID_FR502)
- {
- fr502_intr_ctl_t* ictl = (void*)mbox->data;
+ if (card->hw.fwid == SFID_FR502) {
+ fr502_intr_ctl_t *ictl = (void *) mbox->data;
memset(ictl, 0, sizeof(fr502_intr_ctl_t));
- ictl->mode = mode;
+ ictl->mode = mode;
ictl->tx_len = mtu;
mbox->cmd.length = sizeof(fr502_intr_ctl_t);
- }
- else
- {
- fr508_intr_ctl_t* ictl = (void*)mbox->data;
+ } else {
+ fr508_intr_ctl_t *ictl = (void *) mbox->data;
memset(ictl, 0, sizeof(fr508_intr_ctl_t));
- ictl->mode = mode;
+ ictl->mode = mode;
ictl->tx_len = mtu;
- ictl->irq = card->hw.irq;
+ ictl->irq = card->hw.irq;
mbox->cmd.length = sizeof(fr508_intr_ctl_t);
}
mbox->cmd.command = FR_SET_INTR_MODE;
@@ -1157,14 +1062,13 @@ static int fr_set_intr_mode (sdla_t* card, unsigned mode, unsigned mtu)
/*============================================================================
* Enable communications.
*/
-static int fr_comm_enable (sdla_t* card)
+static int fr_comm_enable(sdla_t * card)
{
- fr_mbox_t* mbox = card->mbox;
+ fr_mbox_t *mbox = card->mbox;
int retry = MAX_CMD_RETRY;
int err;
- do
- {
+ do {
memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
mbox->cmd.command = FR_COMM_ENABLE;
err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
@@ -1176,14 +1080,13 @@ static int fr_comm_enable (sdla_t* card)
/*============================================================================
* Disable communications.
*/
-static int fr_comm_disable (sdla_t* card)
+static int fr_comm_disable(sdla_t * card)
{
- fr_mbox_t* mbox = card->mbox;
+ fr_mbox_t *mbox = card->mbox;
int retry = MAX_CMD_RETRY;
int err;
- do
- {
+ do {
memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
mbox->cmd.command = FR_COMM_DISABLE;
err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
@@ -1195,28 +1098,26 @@ static int fr_comm_disable (sdla_t* card)
/*============================================================================
* Get communications error statistics.
*/
-static int fr_get_err_stats (sdla_t* card)
+static int fr_get_err_stats(sdla_t * card)
{
- fr_mbox_t* mbox = card->mbox;
+ fr_mbox_t *mbox = card->mbox;
int retry = MAX_CMD_RETRY;
int err;
- do
- {
+ do {
memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
mbox->cmd.command = FR_READ_ERROR_STATS;
err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
}
while (err && retry-- && fr_event(card, err, mbox));
- if (!err)
- {
- fr_comm_stat_t* stats = (void*)mbox->data;
+ if (!err) {
+ fr_comm_stat_t *stats = (void *) mbox->data;
- card->wandev.stats.rx_over_errors = stats->rx_overruns;
- card->wandev.stats.rx_crc_errors = stats->rx_bad_crc;
- card->wandev.stats.rx_missed_errors = stats->rx_aborts;
- card->wandev.stats.rx_length_errors = stats->rx_too_long;
+ card->wandev.stats.rx_over_errors = stats->rx_overruns;
+ card->wandev.stats.rx_crc_errors = stats->rx_bad_crc;
+ card->wandev.stats.rx_missed_errors = stats->rx_aborts;
+ card->wandev.stats.rx_length_errors = stats->rx_too_long;
card->wandev.stats.tx_aborted_errors = stats->tx_aborts;
}
return err;
@@ -1225,28 +1126,26 @@ static int fr_get_err_stats (sdla_t* card)
/*============================================================================
* Get statistics.
*/
-static int fr_get_stats (sdla_t* card)
+static int fr_get_stats(sdla_t * card)
{
- fr_mbox_t* mbox = card->mbox;
+ fr_mbox_t *mbox = card->mbox;
int retry = MAX_CMD_RETRY;
int err;
- do
- {
+ do {
memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
mbox->cmd.command = FR_READ_STATISTICS;
err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
}
while (err && retry-- && fr_event(card, err, mbox));
- if (!err)
- {
- fr_link_stat_t* stats = (void*)mbox->data;
+ if (!err) {
+ fr_link_stat_t *stats = (void *) mbox->data;
card->wandev.stats.rx_frame_errors = stats->rx_bad_format;
card->wandev.stats.rx_dropped =
- stats->rx_dropped + stats->rx_dropped2
- ;
+ stats->rx_dropped + stats->rx_dropped2
+ ;
}
return err;
}
@@ -1254,21 +1153,20 @@ static int fr_get_stats (sdla_t* card)
/*============================================================================
* Add DLCI(s) (Access Node only!).
*/
-static int fr_add_dlci (sdla_t* card, int dlci, int num)
+static int fr_add_dlci(sdla_t * card, int dlci, int num)
{
- fr_mbox_t* mbox = card->mbox;
+ fr_mbox_t *mbox = card->mbox;
int retry = MAX_CMD_RETRY;
int err, i;
- do
- {
- unsigned short* dlci_list = (void*)mbox->data;
+ do {
+ unsigned short *dlci_list = (void *) mbox->data;
memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
for (i = 0; i < num; ++i)
dlci_list[i] = dlci + i
- ;
- mbox->cmd.length = num * sizeof(short);
+ ;
+ mbox->cmd.length = num * sizeof(short);
mbox->cmd.command = FR_ADD_DLCI;
err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
}
@@ -1279,21 +1177,20 @@ static int fr_add_dlci (sdla_t* card, int dlci, int num)
/*============================================================================
* Activate DLCI(s) (Access Node only!).
*/
-static int fr_activate_dlci (sdla_t* card, int dlci, int num)
+static int fr_activate_dlci(sdla_t * card, int dlci, int num)
{
- fr_mbox_t* mbox = card->mbox;
+ fr_mbox_t *mbox = card->mbox;
int retry = MAX_CMD_RETRY;
int err, i;
- do
- {
- unsigned short* dlci_list = (void*)mbox->data;
+ do {
+ unsigned short *dlci_list = (void *) mbox->data;
memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
for (i = 0; i < num; ++i)
dlci_list[i] = dlci + i
- ;
- mbox->cmd.length = num * sizeof(short);
+ ;
+ mbox->cmd.length = num * sizeof(short);
mbox->cmd.command = FR_ACTIVATE_DLCI;
err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
}
@@ -1304,17 +1201,16 @@ static int fr_activate_dlci (sdla_t* card, int dlci, int num)
/*============================================================================
* Issue in-channel signalling frame.
*/
-static int fr_issue_isf (sdla_t* card, int isf)
+static int fr_issue_isf(sdla_t * card, int isf)
{
- fr_mbox_t* mbox = card->mbox;
+ fr_mbox_t *mbox = card->mbox;
int retry = MAX_CMD_RETRY;
int err;
- do
- {
+ do {
memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
mbox->data[0] = isf;
- mbox->cmd.length = 1;
+ mbox->cmd.length = 1;
mbox->cmd.command = FR_ISSUE_IS_FRAME;
err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
}
@@ -1325,19 +1221,18 @@ static int fr_issue_isf (sdla_t* card, int isf)
/*============================================================================
* Send a frame (S502 version).
*/
-static int fr502_send (sdla_t* card, int dlci, int attr, int len, void *buf)
+static int fr502_send(sdla_t * card, int dlci, int attr, int len, void *buf)
{
- fr_mbox_t* mbox = card->mbox;
+ fr_mbox_t *mbox = card->mbox;
int retry = MAX_CMD_RETRY;
int err;
- do
- {
+ do {
memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
memcpy(mbox->data, buf, len);
- mbox->cmd.dlci = dlci;
- mbox->cmd.attr = attr;
- mbox->cmd.length = len;
+ mbox->cmd.dlci = dlci;
+ mbox->cmd.attr = attr;
+ mbox->cmd.length = len;
mbox->cmd.command = FR_WRITE;
err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
}
@@ -1348,28 +1243,25 @@ static int fr502_send (sdla_t* card, int dlci, int attr, int len, void *buf)
/*============================================================================
* Send a frame (S508 version).
*/
-static int fr508_send (sdla_t* card, int dlci, int attr, int len, void *buf)
+static int fr508_send(sdla_t * card, int dlci, int attr, int len, void *buf)
{
- fr_mbox_t* mbox = card->mbox;
+ fr_mbox_t *mbox = card->mbox;
int retry = MAX_CMD_RETRY;
int err;
- do
- {
+ do {
memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
- mbox->cmd.dlci = dlci;
- mbox->cmd.attr = attr;
- mbox->cmd.length = len;
+ mbox->cmd.dlci = dlci;
+ mbox->cmd.attr = attr;
+ mbox->cmd.length = len;
mbox->cmd.command = FR_WRITE;
err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
}
while (err && retry-- && fr_event(card, err, mbox));
- if (!err)
- {
- fr_buf_ctl_t* frbuf = (void*)(*(unsigned long*)mbox->data -
- FR_MB_VECTOR + card->hw.dpmbase)
- ;
+ if (!err) {
+ fr_buf_ctl_t *frbuf = (void *) (*(unsigned long *) mbox->data -
+ FR_MB_VECTOR + card->hw.dpmbase);
unsigned long flags;
save_flags(flags);
@@ -1390,10 +1282,9 @@ static int fr508_send (sdla_t* card, int dlci, int attr, int len, void *buf)
*
* Return zero if previous command has to be cancelled.
*/
-static int fr_event (sdla_t *card, int event, fr_mbox_t* mbox)
+static int fr_event(sdla_t * card, int event, fr_mbox_t * mbox)
{
- switch (event)
- {
+ switch (event) {
case FRRES_MODEM_FAILURE:
return fr_modem_failure(card, mbox);
@@ -1414,14 +1305,12 @@ static int fr_event (sdla_t *card, int event, fr_mbox_t* mbox)
case CMD_TIMEOUT:
printk(KERN_ERR "%s: command 0x%02X timed out!\n",
- card->devname, mbox->cmd.command)
- ;
+ card->devname, mbox->cmd.command);
break;
default:
printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n",
- card->devname, mbox->cmd.command, event)
- ;
+ card->devname, mbox->cmd.command, event);
}
return 0;
}
@@ -1431,13 +1320,11 @@ static int fr_event (sdla_t *card, int event, fr_mbox_t* mbox)
*
* Return zero if previous command has to be cancelled.
*/
-static int fr_modem_failure (sdla_t *card, fr_mbox_t* mbox)
+static int fr_modem_failure(sdla_t * card, fr_mbox_t * mbox)
{
printk(KERN_INFO "%s: physical link down! (modem error 0x%02X)\n",
- card->devname, mbox->data[0])
- ;
- switch (mbox->cmd.command)
- {
+ card->devname, mbox->data[0]);
+ switch (mbox->cmd.command) {
case FR_WRITE:
case FR_READ:
return 0;
@@ -1450,35 +1337,27 @@ static int fr_modem_failure (sdla_t *card, fr_mbox_t* mbox)
*
* Return zero if previous command has to be cancelled.
*/
-static int fr_dlci_change (sdla_t *card, fr_mbox_t* mbox)
+static int fr_dlci_change(sdla_t * card, fr_mbox_t * mbox)
{
- dlci_status_t* status = (void*)mbox->data;
+ dlci_status_t *status = (void *) mbox->data;
int cnt = mbox->cmd.length / sizeof(dlci_status_t);
- for (; cnt; --cnt, ++status)
- {
+ for (; cnt; --cnt, ++status) {
unsigned short dlci = status->dlci;
- struct device* dev = find_channel(card, dlci);
+ struct device *dev = find_channel(card, dlci);
- if (status->state & 0x01)
- {
+ if (status->state & 0x01) {
printk(KERN_INFO
- "%s: DLCI %u has been deleted!\n",
- card->devname, dlci)
- ;
+ "%s: DLCI %u has been deleted!\n",
+ card->devname, dlci);
if (dev && dev->start)
- set_chan_state(dev, WAN_DISCONNECTED)
- ;
- }
- else if (status->state & 0x02)
- {
+ set_chan_state(dev, WAN_DISCONNECTED);
+ } else if (status->state & 0x02) {
printk(KERN_INFO
- "%s: DLCI %u becomes active!\n",
- card->devname, dlci)
- ;
+ "%s: DLCI %u becomes active!\n",
+ card->devname, dlci);
if (dev && dev->start)
- set_chan_state(dev, WAN_CONNECTED)
- ;
+ set_chan_state(dev, WAN_CONNECTED);
}
}
return 1;
@@ -1489,32 +1368,28 @@ static int fr_dlci_change (sdla_t *card, fr_mbox_t* mbox)
/*============================================================================
* Update channel state.
*/
-static int update_chan_state (struct device* dev)
+static int update_chan_state(struct device *dev)
{
- fr_channel_t* chan = dev->priv;
- sdla_t* card = chan->card;
- fr_mbox_t* mbox = card->mbox;
+ fr_channel_t *chan = dev->priv;
+ sdla_t *card = chan->card;
+ fr_mbox_t *mbox = card->mbox;
int retry = MAX_CMD_RETRY;
int err;
- do
- {
+ do {
memset(&mbox->cmd, 0, sizeof(fr_cmd_t));
mbox->cmd.command = FR_LIST_ACTIVE_DLCI;
err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
}
while (err && retry-- && fr_event(card, err, mbox));
- if (!err)
- {
- unsigned short* list = (void*)mbox->data;
+ if (!err) {
+ unsigned short *list = (void *) mbox->data;
int cnt = mbox->cmd.length / sizeof(short);
- for (; cnt; --cnt, ++list)
- {
- if (*list == chan->dlci)
- {
- set_chan_state(dev, WAN_CONNECTED);
+ for (; cnt; --cnt, ++list) {
+ if (*list == chan->dlci) {
+ set_chan_state(dev, WAN_CONNECTED);
break;
}
}
@@ -1525,34 +1400,29 @@ static int update_chan_state (struct device* dev)
/*============================================================================
* Set channel state.
*/
-static void set_chan_state (struct device* dev, int state)
+static void set_chan_state(struct device *dev, int state)
{
- fr_channel_t* chan = dev->priv;
- sdla_t* card = chan->card;
+ fr_channel_t *chan = dev->priv;
+ sdla_t *card = chan->card;
unsigned long flags;
save_flags(flags);
cli();
- if (chan->state != state)
- {
- switch (state)
- {
+ if (chan->state != state) {
+ switch (state) {
case WAN_CONNECTED:
- printk (KERN_INFO "%s: interface %s connected!\n",
- card->devname, dev->name)
- ;
+ printk(KERN_INFO "%s: interface %s connected!\n",
+ card->devname, dev->name);
break;
case WAN_CONNECTING:
- printk (KERN_INFO "%s: interface %s connecting...\n",
- card->devname, dev->name)
- ;
+ printk(KERN_INFO "%s: interface %s connecting...\n",
+ card->devname, dev->name);
break;
case WAN_DISCONNECTED:
- printk (KERN_INFO "%s: interface %s disconnected!\n",
- card->devname, dev->name)
- ;
+ printk(KERN_INFO "%s: interface %s disconnected!\n",
+ card->devname, dev->name);
break;
}
chan->state = state;
@@ -1564,13 +1434,14 @@ static void set_chan_state (struct device* dev, int state)
/*============================================================================
* Find network device by its channel number.
*/
-static struct device* find_channel (sdla_t* card, unsigned dlci)
+static struct device *find_channel(sdla_t * card, unsigned dlci)
{
- struct device* dev;
+ struct device *dev;
for (dev = card->wandev.dev; dev; dev = dev->slave)
- if (((fr_channel_t*)dev->priv)->dlci == dlci) break
- ;
+ if (((fr_channel_t *) dev->priv)->dlci == dlci)
+ break
+ ;
return dev;
}
@@ -1581,22 +1452,21 @@ static struct device* find_channel (sdla_t* card, unsigned dlci)
* Return: 1 - Tx buffer(s) available
* 0 - no buffers available
*/
-static int is_tx_ready (sdla_t* card)
+static int is_tx_ready(sdla_t * card)
{
- if (card->hw.fwid == SFID_FR508)
- {
- fr508_flags_t* flags = card->flags;
+ if (card->hw.fwid == SFID_FR508) {
+ fr508_flags_t *flags = card->flags;
unsigned char sb = inb(card->hw.port);
- if (sb & 0x02) return 1;
+ if (sb & 0x02)
+ return 1;
flags->imask |= 0x02;
- card->wandev.tx_int_enabled = 1;
- }
- else
- {
- fr502_flags_t* flags = card->flags;
+ card->wandev.tx_int_enabled = 1;
+ } else {
+ fr502_flags_t *flags = card->flags;
- if (flags->tx_ready) return 1;
+ if (flags->tx_ready)
+ return 1;
flags->imask |= 0x02;
}
return 0;
@@ -1606,14 +1476,14 @@ static int is_tx_ready (sdla_t* card)
* Convert decimal string to unsigned integer.
* If len != 0 then only 'len' characters of the string are converted.
*/
-static unsigned int dec_to_uint (unsigned char* str, int len)
+static unsigned int dec_to_uint(unsigned char *str, int len)
{
unsigned val;
- if (!len) len = strlen(str);
+ if (!len)
+ len = strlen(str);
for (val = 0; len && is_digit(*str); ++str, --len)
- val = (val * 10) + (*str - (unsigned)'0')
- ;
+ val = (val * 10) + (*str - (unsigned) '0');
return val;
}
diff --git a/drivers/net/sdla_ppp.c b/drivers/net/sdla_ppp.c
index fa636040b..9158c0d09 100644
--- a/drivers/net/sdla_ppp.c
+++ b/drivers/net/sdla_ppp.c
@@ -49,7 +49,7 @@
#include <asm/uaccess.h>
#define _GNUC_
-#include <linux/sdla_ppp.h> /* PPP firmware API definitions */
+#include <linux/sdla_ppp.h> /* PPP firmware API definitions */
/****** Defines & Macros ****************************************************/
@@ -59,63 +59,63 @@
#define STATIC static
#endif
-#define CMD_OK 0 /* normal firmware return code */
-#define CMD_TIMEOUT 0xFF /* firmware command timed out */
+#define CMD_OK 0 /* normal firmware return code */
+#define CMD_TIMEOUT 0xFF /* firmware command timed out */
-#define PPP_DFLT_MTU 1500 /* default MTU */
-#define PPP_MAX_MTU 4000 /* maximum MTU */
+#define PPP_DFLT_MTU 1500 /* default MTU */
+#define PPP_MAX_MTU 4000 /* maximum MTU */
#define PPP_HDR_LEN 1
-#define CONNECT_TIMEOUT (90*HZ) /* link connection timeout */
-#define HOLD_DOWN_TIME (30*HZ) /* link hold down time */
+#define CONNECT_TIMEOUT (90*HZ) /* link connection timeout */
+#define HOLD_DOWN_TIME (30*HZ) /* link hold down time */
/****** Function Prototypes *************************************************/
/* WAN link driver entry points. These are called by the WAN router module. */
-static int update (wan_device_t* wandev);
-static int new_if (wan_device_t* wandev, struct device* dev,
- wanif_conf_t* conf);
-static int del_if (wan_device_t* wandev, struct device* dev);
+static int update(wan_device_t * wandev);
+static int new_if(wan_device_t * wandev, struct device *dev,
+ wanif_conf_t * conf);
+static int del_if(wan_device_t * wandev, struct device *dev);
/* WANPIPE-specific entry points */
-static int wpp_exec (struct sdla* card, void* u_cmd, void* u_data);
+static int wpp_exec(struct sdla *card, void *u_cmd, void *u_data);
/* Network device interface */
-static int if_init (struct device* dev);
-static int if_open (struct device* dev);
-static int if_close (struct device* dev);
-static int if_header (struct sk_buff* skb, struct device* dev,
- unsigned short type, void* daddr, void* saddr, unsigned len);
-static int if_rebuild_hdr (struct sk_buff* skb);
-static int if_send (struct sk_buff* skb, struct device* dev);
-static struct enet_statistics* if_stats (struct device* dev);
+static int if_init(struct device *dev);
+static int if_open(struct device *dev);
+static int if_close(struct device *dev);
+static int if_header(struct sk_buff *skb, struct device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len);
+static int if_rebuild_hdr(struct sk_buff *skb);
+static int if_send(struct sk_buff *skb, struct device *dev);
+static struct enet_statistics *if_stats(struct device *dev);
/* PPP firmware interface functions */
-static int ppp_read_version (sdla_t* card, char* str);
-static int ppp_configure (sdla_t* card, void* data);
-static int ppp_set_intr_mode (sdla_t* card, unsigned mode);
-static int ppp_comm_enable (sdla_t* card);
-static int ppp_comm_disable (sdla_t* card);
-static int ppp_get_err_stats (sdla_t* card);
-static int ppp_send (sdla_t* card, void* data, unsigned len, unsigned proto);
-static int ppp_error (sdla_t *card, int err, ppp_mbox_t* mb);
+static int ppp_read_version(sdla_t * card, char *str);
+static int ppp_configure(sdla_t * card, void *data);
+static int ppp_set_intr_mode(sdla_t * card, unsigned mode);
+static int ppp_comm_enable(sdla_t * card);
+static int ppp_comm_disable(sdla_t * card);
+static int ppp_get_err_stats(sdla_t * card);
+static int ppp_send(sdla_t * card, void *data, unsigned len, unsigned proto);
+static int ppp_error(sdla_t * card, int err, ppp_mbox_t * mb);
/* Interrupt handlers */
-STATIC void wpp_isr (sdla_t* card);
-static void rx_intr (sdla_t* card);
-static void tx_intr (sdla_t* card);
+STATIC void wpp_isr(sdla_t * card);
+static void rx_intr(sdla_t * card);
+static void tx_intr(sdla_t * card);
/* Background polling routines */
-static void wpp_poll (sdla_t* card);
-static void poll_active (sdla_t* card);
-static void poll_connecting (sdla_t* card);
-static void poll_disconnected (sdla_t* card);
+static void wpp_poll(sdla_t * card);
+static void poll_active(sdla_t * card);
+static void poll_connecting(sdla_t * card);
+static void poll_disconnected(sdla_t * card);
/* Miscellaneous functions */
-static int config502 (sdla_t* card);
-static int config508 (sdla_t* card);
-static void show_disc_cause (sdla_t* card, unsigned cause);
-static unsigned char bps_to_speed_code (unsigned long bps);
+static int config502(sdla_t * card);
+static int config508(sdla_t * card);
+static void show_disc_cause(sdla_t * card, unsigned cause);
+static unsigned char bps_to_speed_code(unsigned long bps);
static char TracingEnabled;
/****** Public Functions ****************************************************/
@@ -132,33 +132,28 @@ static char TracingEnabled;
* Return: 0 o.k.
* < 0 failure.
*/
-__initfunc(int wpp_init (sdla_t* card, wandev_conf_t* conf))
+__initfunc(int wpp_init(sdla_t * card, wandev_conf_t * conf))
{
- union
- {
+ union {
char str[80];
} u;
/* Verify configuration ID */
- if (conf->config_id != WANCONFIG_PPP)
- {
+ if (conf->config_id != WANCONFIG_PPP) {
printk(KERN_INFO "%s: invalid configuration ID %u!\n",
- card->devname, conf->config_id)
- ;
+ card->devname, conf->config_id);
return -EINVAL;
}
-
/* Initialize protocol-specific fields */
- switch (card->hw.fwid)
- {
+ switch (card->hw.fwid) {
case SFID_PPP502:
- card->mbox = (void*)(card->hw.dpmbase + PPP502_MB_OFFS);
- card->flags = (void*)(card->hw.dpmbase + PPP502_FLG_OFFS);
+ card->mbox = (void *) (card->hw.dpmbase + PPP502_MB_OFFS);
+ card->flags = (void *) (card->hw.dpmbase + PPP502_FLG_OFFS);
break;
case SFID_PPP508:
- card->mbox = (void*)(card->hw.dpmbase + PPP508_MB_OFFS);
- card->flags = (void*)(card->hw.dpmbase + PPP508_FLG_OFFS);
+ card->mbox = (void *) (card->hw.dpmbase + PPP508_MB_OFFS);
+ card->flags = (void *) (card->hw.dpmbase + PPP508_FLG_OFFS);
break;
default:
@@ -172,28 +167,27 @@ __initfunc(int wpp_init (sdla_t* card, wandev_conf_t* conf))
*/
if (ppp_read_version(card, NULL) || ppp_read_version(card, u.str))
return -EIO
- ;
+ ;
printk(KERN_INFO "%s: running PPP firmware v%s\n",
- card->devname, u.str)
- ;
+ card->devname, u.str);
/* Adjust configuration and set defaults */
card->wandev.mtu = (conf->mtu) ?
- min(conf->mtu, PPP_MAX_MTU) : PPP_DFLT_MTU
- ;
- card->wandev.bps = conf->bps;
- card->wandev.interface = conf->interface;
- card->wandev.clocking = conf->clocking;
- card->wandev.station = conf->station;
- card->isr = &wpp_isr;
- card->poll = &wpp_poll;
- card->exec = &wpp_exec;
- card->wandev.update = &update;
- card->wandev.new_if = &new_if;
- card->wandev.del_if = &del_if;
- card->wandev.state = WAN_DISCONNECTED;
- card->wandev.udp_port = conf->udp_port;
- TracingEnabled = '0';
+ min(conf->mtu, PPP_MAX_MTU) : PPP_DFLT_MTU
+ ;
+ card->wandev.bps = conf->bps;
+ card->wandev.interface = conf->interface;
+ card->wandev.clocking = conf->clocking;
+ card->wandev.station = conf->station;
+ card->isr = &wpp_isr;
+ card->poll = &wpp_poll;
+ card->exec = &wpp_exec;
+ card->wandev.update = &update;
+ card->wandev.new_if = &new_if;
+ card->wandev.del_if = &del_if;
+ card->wandev.state = WAN_DISCONNECTED;
+ card->wandev.udp_port = conf->udp_port;
+ TracingEnabled = '0';
return 0;
}
@@ -202,20 +196,20 @@ __initfunc(int wpp_init (sdla_t* card, wandev_conf_t* conf))
/*============================================================================
* Update device status & statistics.
*/
-static int update (wan_device_t* wandev)
+static int update(wan_device_t * wandev)
{
- sdla_t* card;
+ sdla_t *card;
/* sanity checks */
if ((wandev == NULL) || (wandev->private == NULL))
return -EFAULT
- ;
+ ;
if (wandev->state == WAN_UNCONFIGURED)
return -ENODEV
- ;
- if (test_and_set_bit(0, (void*)&wandev->critical))
+ ;
+ if (test_and_set_bit(0, (void *) &wandev->critical))
return -EAGAIN
- ;
+ ;
card = wandev->private;
ppp_get_err_stats(card);
@@ -235,21 +229,18 @@ static int update (wan_device_t* wandev)
* Return: 0 o.k.
* < 0 failure (channel will not be created)
*/
-static int new_if (wan_device_t* wandev, struct device* dev, wanif_conf_t* conf)
+static int new_if(wan_device_t * wandev, struct device *dev, wanif_conf_t * conf)
{
- sdla_t* card = wandev->private;
+ sdla_t *card = wandev->private;
if (wandev->ndev)
return -EEXIST
- ;
- if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ))
- {
+ ;
+ if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) {
printk(KERN_INFO "%s: invalid interface name!\n",
- card->devname)
- ;
+ card->devname);
return -EINVAL;
}
-
/* initialize data */
strcpy(card->u.p.if_name, conf->name);
@@ -263,7 +254,7 @@ static int new_if (wan_device_t* wandev, struct device* dev, wanif_conf_t* conf)
/*============================================================================
* Delete logical channel.
*/
-static int del_if (wan_device_t* wandev, struct device* dev)
+static int del_if(wan_device_t * wandev, struct device *dev)
{
return 0;
}
@@ -273,30 +264,28 @@ static int del_if (wan_device_t* wandev, struct device* dev)
/*============================================================================
* Execute adapter interface command.
*/
-static int wpp_exec (struct sdla* card, void* u_cmd, void* u_data)
+static int wpp_exec(struct sdla *card, void *u_cmd, void *u_data)
{
- ppp_mbox_t* mbox = card->mbox;
+ ppp_mbox_t *mbox = card->mbox;
int len;
- if(copy_from_user((void*)&mbox->cmd, u_cmd, sizeof(ppp_cmd_t)))
+ if (copy_from_user((void *) &mbox->cmd, u_cmd, sizeof(ppp_cmd_t)))
return -EFAULT;
len = mbox->cmd.length;
- if (len)
- {
- if(copy_from_user((void*)&mbox->data, u_data, len))
+ if (len) {
+ if (copy_from_user((void *) &mbox->data, u_data, len))
return -EFAULT;
}
-
/* execute command */
if (!sdla_exec(mbox))
return -EIO;
/* return result */
- if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(ppp_cmd_t)))
+ if (copy_to_user(u_cmd, (void *) &mbox->cmd, sizeof(ppp_cmd_t)))
return -EFAULT;
len = mbox->cmd.length;
- if (len && u_data && copy_to_user(u_data, (void*)&mbox->data, len))
- return -EFAULT;
+ if (len && u_data && copy_to_user(u_data, (void *) &mbox->data, len))
+ return -EFAULT;
return 0;
}
@@ -309,40 +298,39 @@ static int wpp_exec (struct sdla* card, void* u_cmd, void* u_data)
* interface registration. Returning anything but zero will fail interface
* registration.
*/
-static int if_init (struct device* dev)
+static int if_init(struct device *dev)
{
- sdla_t* card = dev->priv;
- wan_device_t* wandev = &card->wandev;
+ sdla_t *card = dev->priv;
+ wan_device_t *wandev = &card->wandev;
int i;
/* Initialize device driver entry points */
- dev->open = &if_open;
- dev->stop = &if_close;
- dev->hard_header = &if_header;
- dev->rebuild_header = &if_rebuild_hdr;
- dev->hard_start_xmit = &if_send;
- dev->get_stats = &if_stats;
+ dev->open = &if_open;
+ dev->stop = &if_close;
+ dev->hard_header = &if_header;
+ dev->rebuild_header = &if_rebuild_hdr;
+ dev->hard_start_xmit = &if_send;
+ dev->get_stats = &if_stats;
/* Initialize media-specific parameters */
- dev->family = AF_INET; /* address family */
- dev->type = ARPHRD_PPP; /* ARP h/w type */
- dev->mtu = wandev->mtu;
- dev->hard_header_len = PPP_HDR_LEN; /* media header length */
+ dev->family = AF_INET; /* address family */
+ dev->type = ARPHRD_PPP; /* ARP h/w type */
+ dev->mtu = wandev->mtu;
+ dev->hard_header_len = PPP_HDR_LEN; /* media header length */
/* Initialize hardware parameters (just for reference) */
- dev->irq = wandev->irq;
- dev->dma = wandev->dma;
- dev->base_addr = wandev->ioport;
- dev->mem_start = wandev->maddr;
- dev->mem_end = wandev->maddr + wandev->msize - 1;
-
- /* Set transmit buffer queue length */
- dev->tx_queue_len = 30;
-
+ dev->irq = wandev->irq;
+ dev->dma = wandev->dma;
+ dev->base_addr = wandev->ioport;
+ dev->mem_start = wandev->maddr;
+ dev->mem_end = wandev->maddr + wandev->msize - 1;
+
+ /* Set transmit buffer queue length */
+ dev->tx_queue_len = 30;
+
/* Initialize socket buffers */
for (i = 0; i < DEV_NUMBUFFS; ++i)
- skb_queue_head_init(&dev->buffs[i])
- ;
+ skb_queue_head_init(&dev->buffs[i]);
return 0;
}
@@ -353,69 +341,53 @@ static int if_init (struct device* dev)
*
* Return 0 if O.k. or errno.
*/
-static int if_open (struct device* dev)
+static int if_open(struct device *dev)
{
- sdla_t* card = dev->priv;
+ sdla_t *card = dev->priv;
int err = 0;
if (dev->start)
- return -EBUSY /* only one open is allowed */
- ;
- if (test_and_set_bit(0, (void*)&card->wandev.critical))
+ return -EBUSY /* only one open is allowed */
+ ;
+ if (test_and_set_bit(0, (void *) &card->wandev.critical))
return -EAGAIN;
;
- if ((card->hw.fwid == SFID_PPP502) ? config502(card) : config508(card))
- {
+ if ((card->hw.fwid == SFID_PPP502) ? config502(card) : config508(card)) {
err = -EIO;
goto split;
}
-
/* Initialize Rx/Tx buffer control fields */
- if (card->hw.fwid == SFID_PPP502)
- {
- ppp502_buf_info_t* info =
- (void*)(card->hw.dpmbase + PPP502_BUF_OFFS)
- ;
-
- card->u.p.txbuf_base = (void*)(card->hw.dpmbase +
- info->txb_offs)
- ;
- card->u.p.txbuf_last = (ppp_buf_ctl_t*)card->u.p.txbuf_base +
- (info->txb_num - 1)
- ;
- card->u.p.rxbuf_base = (void*)(card->hw.dpmbase +
- info->rxb_offs)
- ;
- card->u.p.rxbuf_last = (ppp_buf_ctl_t*)card->u.p.rxbuf_base +
- (info->rxb_num - 1)
- ;
- }
- else
- {
- ppp508_buf_info_t* info =
- (void*)(card->hw.dpmbase + PPP508_BUF_OFFS)
- ;
-
- card->u.p.txbuf_base = (void*)(card->hw.dpmbase +
- (info->txb_ptr - PPP508_MB_VECT))
- ;
- card->u.p.txbuf_last = (ppp_buf_ctl_t*)card->u.p.txbuf_base +
- (info->txb_num - 1)
- ;
- card->u.p.rxbuf_base = (void*)(card->hw.dpmbase +
- (info->rxb_ptr - PPP508_MB_VECT))
- ;
- card->u.p.rxbuf_last = (ppp_buf_ctl_t*)card->u.p.rxbuf_base +
- (info->rxb_num - 1)
- ;
+ if (card->hw.fwid == SFID_PPP502) {
+ ppp502_buf_info_t *info =
+ (void *) (card->hw.dpmbase + PPP502_BUF_OFFS);
+
+ card->u.p.txbuf_base = (void *) (card->hw.dpmbase +
+ info->txb_offs);
+ card->u.p.txbuf_last = (ppp_buf_ctl_t *) card->u.p.txbuf_base +
+ (info->txb_num - 1);
+ card->u.p.rxbuf_base = (void *) (card->hw.dpmbase +
+ info->rxb_offs);
+ card->u.p.rxbuf_last = (ppp_buf_ctl_t *) card->u.p.rxbuf_base +
+ (info->rxb_num - 1);
+ } else {
+ ppp508_buf_info_t *info =
+ (void *) (card->hw.dpmbase + PPP508_BUF_OFFS);
+
+ card->u.p.txbuf_base = (void *) (card->hw.dpmbase +
+ (info->txb_ptr - PPP508_MB_VECT));
+ card->u.p.txbuf_last = (ppp_buf_ctl_t *) card->u.p.txbuf_base +
+ (info->txb_num - 1);
+ card->u.p.rxbuf_base = (void *) (card->hw.dpmbase +
+ (info->rxb_ptr - PPP508_MB_VECT));
+ card->u.p.rxbuf_last = (ppp_buf_ctl_t *) card->u.p.rxbuf_base +
+ (info->rxb_num - 1);
card->u.p.rx_base = info->rxb_base;
- card->u.p.rx_top = info->rxb_end;
+ card->u.p.rx_top = info->rxb_end;
}
card->u.p.txbuf = card->u.p.txbuf_base;
card->rxmb = card->u.p.rxbuf_base;
- if (ppp_set_intr_mode(card, 0x03) || ppp_comm_enable(card))
- {
+ if (ppp_set_intr_mode(card, 0x03) || ppp_comm_enable(card)) {
err = -EIO;
goto split;
}
@@ -426,7 +398,7 @@ static int if_open (struct device* dev)
dev->tbusy = 0;
dev->start = 1;
-split:
+ split:
card->wandev.critical = 0;
return err;
}
@@ -436,11 +408,11 @@ split:
* o if this is the last open, then disable communications and interrupts.
* o reset flags.
*/
-static int if_close (struct device* dev)
+static int if_close(struct device *dev)
{
- sdla_t* card = dev->priv;
+ sdla_t *card = dev->priv;
- if (test_and_set_bit(0, (void*)&card->wandev.critical))
+ if (test_and_set_bit(0, (void *) &card->wandev.critical))
return -EAGAIN;
;
dev->start = 0;
@@ -461,11 +433,10 @@ static int if_close (struct device* dev)
*
* Return: media header length.
*/
-static int if_header (struct sk_buff* skb, struct device* dev,
- unsigned short type, void* daddr, void* saddr, unsigned len)
+static int if_header(struct sk_buff *skb, struct device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
{
- switch (type)
- {
+ switch (type) {
case ETH_P_IP:
case ETH_P_IPX:
skb->protocol = type;
@@ -483,13 +454,12 @@ static int if_header (struct sk_buff* skb, struct device* dev,
* Return: 1 physical address resolved.
* 0 physical address not resolved
*/
-static int if_rebuild_hdr (struct sk_buff* skb)
+static int if_rebuild_hdr(struct sk_buff *skb)
{
- sdla_t* card = skb->dev->priv;
+ sdla_t *card = skb->dev->priv;
printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n",
- card->devname, skb->dev->name)
- ;
+ card->devname, skb->dev->name);
return 1;
}
@@ -510,47 +480,40 @@ static int if_rebuild_hdr (struct sk_buff* skb)
* 2. Setting tbusy flag will inhibit further transmit requests from the
* protocol stack and can be used for flow control with protocol layer.
*/
-static int if_send (struct sk_buff* skb, struct device* dev)
+static int if_send(struct sk_buff *skb, struct device *dev)
{
- sdla_t* card = dev->priv;
+ sdla_t *card = dev->priv;
int retry = 0;
- if (test_and_set_bit(0, (void*)&card->wandev.critical))
- {
+ if (test_and_set_bit(0, (void *) &card->wandev.critical)) {
#ifdef _DEBUG_
printk(KERN_INFO "%s: if_send() hit critical section!\n",
- card->devname)
- ;
+ card->devname);
#endif
return 1;
}
-
- if (test_and_set_bit(0, (void*)&dev->tbusy))
- {
+ if (test_and_set_bit(0, (void *) &dev->tbusy)) {
#ifdef _DEBUG_
printk(KERN_INFO "%s: Tx collision on interface %s!\n",
- card->devname, dev->name)
- ;
+ card->devname, dev->name);
#endif
++card->wandev.stats.collisions;
retry = 1;
} else if (card->wandev.state != WAN_CONNECTED)
++card->wandev.stats.tx_dropped
- ;
+ ;
else if (!skb->protocol)
++card->wandev.stats.tx_errors
- ;
- else if (ppp_send(card, skb->data, skb->len, skb->protocol))
- {
- ppp_flags_t* flags = card->flags;
+ ;
+ else if (ppp_send(card, skb->data, skb->len, skb->protocol)) {
+ ppp_flags_t *flags = card->flags;
flags->imask |= 0x02; /* unmask Tx interrupts */
retry = 1;
- }
- else ++card->wandev.stats.tx_packets;
+ } else
+ ++card->wandev.stats.tx_packets;
- if (!retry)
- {
+ if (!retry) {
dev_kfree_skb(skb, FREE_WRITE);
dev->tbusy = 0;
}
@@ -563,9 +526,9 @@ static int if_send (struct sk_buff* skb, struct device* dev)
* Return a pointer to struct enet_statistics.
*/
-static struct enet_statistics* if_stats (struct device* dev)
+static struct enet_statistics *if_stats(struct device *dev)
{
- sdla_t* card = dev->priv;
+ sdla_t *card = dev->priv;
return &card->wandev.stats;
}
@@ -576,17 +539,17 @@ static struct enet_statistics* if_stats (struct device* dev)
* Read firmware code version.
* Put code version as ASCII string in str.
*/
-static int ppp_read_version (sdla_t* card, char* str)
+static int ppp_read_version(sdla_t * card, char *str)
{
- ppp_mbox_t* mb = card->mbox;
+ ppp_mbox_t *mb = card->mbox;
int err;
memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
mb->cmd.command = PPP_READ_CODE_VERSION;
err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
- if (err != CMD_OK) ppp_error(card, err, mb);
- else if (str)
- {
+ if (err != CMD_OK)
+ ppp_error(card, err, mb);
+ else if (str) {
int len = mb->cmd.length;
memcpy(str, mb->data, len);
@@ -598,37 +561,36 @@ static int ppp_read_version (sdla_t* card, char* str)
/*============================================================================
* Configure PPP firmware.
*/
-static int ppp_configure (sdla_t* card, void* data)
+static int ppp_configure(sdla_t * card, void *data)
{
- ppp_mbox_t* mb = card->mbox;
+ ppp_mbox_t *mb = card->mbox;
int data_len = (card->hw.fwid == SFID_PPP502) ?
- sizeof(ppp502_conf_t) : sizeof(ppp508_conf_t)
- ;
+ sizeof(ppp502_conf_t) : sizeof(ppp508_conf_t);
int err;
memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
memcpy(mb->data, data, data_len);
- mb->cmd.length = data_len;
+ mb->cmd.length = data_len;
mb->cmd.command = PPP_SET_CONFIG;
err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
- if (err != CMD_OK) ppp_error(card, err, mb);
+ if (err != CMD_OK)
+ ppp_error(card, err, mb);
return err;
}
/*============================================================================
* Set interrupt mode.
*/
-static int ppp_set_intr_mode (sdla_t* card, unsigned mode)
+static int ppp_set_intr_mode(sdla_t * card, unsigned mode)
{
- ppp_mbox_t* mb = card->mbox;
+ ppp_mbox_t *mb = card->mbox;
int err;
memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
mb->data[0] = mode;
- switch (card->hw.fwid)
- {
+ switch (card->hw.fwid) {
case SFID_PPP502:
- mb->cmd.length = 1;
+ mb->cmd.length = 1;
break;
case SFID_PPP508:
@@ -638,62 +600,64 @@ static int ppp_set_intr_mode (sdla_t* card, unsigned mode)
}
mb->cmd.command = PPP_SET_INTR_FLAGS;
err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
- if (err != CMD_OK) ppp_error(card, err, mb);
+ if (err != CMD_OK)
+ ppp_error(card, err, mb);
return err;
}
/*============================================================================
* Enable communications.
*/
-static int ppp_comm_enable (sdla_t* card)
+static int ppp_comm_enable(sdla_t * card)
{
- ppp_mbox_t* mb = card->mbox;
+ ppp_mbox_t *mb = card->mbox;
int err;
memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
mb->cmd.command = PPP_COMM_ENABLE;
err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
- if (err != CMD_OK) ppp_error(card, err, mb);
+ if (err != CMD_OK)
+ ppp_error(card, err, mb);
return err;
}
/*============================================================================
* Disable communications.
*/
-static int ppp_comm_disable (sdla_t* card)
+static int ppp_comm_disable(sdla_t * card)
{
- ppp_mbox_t* mb = card->mbox;
+ ppp_mbox_t *mb = card->mbox;
int err;
memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
mb->cmd.command = PPP_COMM_DISABLE;
err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
- if (err != CMD_OK) ppp_error(card, err, mb);
+ if (err != CMD_OK)
+ ppp_error(card, err, mb);
return err;
}
/*============================================================================
* Get communications error statistics.
*/
-static int ppp_get_err_stats (sdla_t* card)
+static int ppp_get_err_stats(sdla_t * card)
{
- ppp_mbox_t* mb = card->mbox;
+ ppp_mbox_t *mb = card->mbox;
int err;
memset(&mb->cmd, 0, sizeof(ppp_cmd_t));
mb->cmd.command = PPP_READ_ERROR_STATS;
err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT;
- if (err == CMD_OK)
- {
- ppp_err_stats_t* stats = (void*)mb->data;
-
- card->wandev.stats.rx_over_errors = stats->rx_overrun;
- card->wandev.stats.rx_crc_errors = stats->rx_bad_crc;
- card->wandev.stats.rx_missed_errors = stats->rx_abort;
- card->wandev.stats.rx_length_errors = stats->rx_lost;
+ if (err == CMD_OK) {
+ ppp_err_stats_t *stats = (void *) mb->data;
+
+ card->wandev.stats.rx_over_errors = stats->rx_overrun;
+ card->wandev.stats.rx_crc_errors = stats->rx_bad_crc;
+ card->wandev.stats.rx_missed_errors = stats->rx_abort;
+ card->wandev.stats.rx_length_errors = stats->rx_lost;
card->wandev.stats.tx_aborted_errors = stats->tx_abort;
- }
- else ppp_error(card, err, mb);
+ } else
+ ppp_error(card, err, mb);
return err;
}
@@ -702,34 +666,34 @@ static int ppp_get_err_stats (sdla_t* card)
* Return: 0 - o.k.
* 1 - no transmit buffers available
*/
-static int ppp_send (sdla_t* card, void* data, unsigned len, unsigned proto)
+static int ppp_send(sdla_t * card, void *data, unsigned len, unsigned proto)
{
- ppp_buf_ctl_t* txbuf = card->u.p.txbuf;
+ ppp_buf_ctl_t *txbuf = card->u.p.txbuf;
unsigned long addr, cpu_flags;
if (txbuf->flag)
return 1
- ;
+ ;
if (card->hw.fwid == SFID_PPP502)
- addr = (txbuf->buf.o_p[1] << 8) + txbuf->buf.o_p[0]
- ;
- else addr = txbuf->buf.ptr;
+ addr = (txbuf->buf.o_p[1] << 8) + txbuf->buf.o_p[0];
+ else
+ addr = txbuf->buf.ptr;
save_flags(cpu_flags);
cli();
sdla_poke(&card->hw, addr, data, len);
restore_flags(cpu_flags);
- txbuf->length = len; /* frame length */
+ txbuf->length = len; /* frame length */
if (proto == ETH_P_IPX)
txbuf->proto = 0x01 /* protocol ID */
- ;
- txbuf->flag = 1; /* start transmission */
+ ;
+ txbuf->flag = 1; /* start transmission */
/* Update transmit buffer control fields */
card->u.p.txbuf = ++txbuf;
- if ((void*)txbuf > card->u.p.txbuf_last)
+ if ((void *) txbuf > card->u.p.txbuf_last)
card->u.p.txbuf = card->u.p.txbuf_base
- ;
+ ;
return 0;
}
@@ -742,22 +706,19 @@ static int ppp_send (sdla_t* card, void* data, unsigned len, unsigned proto)
*
* Return zero if previous command has to be cancelled.
*/
-static int ppp_error (sdla_t *card, int err, ppp_mbox_t* mb)
+static int ppp_error(sdla_t * card, int err, ppp_mbox_t * mb)
{
unsigned cmd = mb->cmd.command;
- switch (err)
- {
+ switch (err) {
case CMD_TIMEOUT:
printk(KERN_ERR "%s: command 0x%02X timed out!\n",
- card->devname, cmd)
- ;
+ card->devname, cmd);
break;
default:
printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n",
- card->devname, cmd, err)
- ;
+ card->devname, cmd, err);
}
return 0;
}
@@ -767,25 +728,23 @@ static int ppp_error (sdla_t *card, int err, ppp_mbox_t* mb)
/*============================================================================
* PPP interrupt service routine.
*/
-STATIC void wpp_isr (sdla_t* card)
+STATIC void wpp_isr(sdla_t * card)
{
- ppp_flags_t* flags = card->flags;
+ ppp_flags_t *flags = card->flags;
- switch (flags->iflag)
- {
- case 0x01: /* receive interrupt */
+ switch (flags->iflag) {
+ case 0x01: /* receive interrupt */
rx_intr(card);
break;
- case 0x02: /* transmit interrupt */
+ case 0x02: /* transmit interrupt */
flags->imask &= ~0x02;
tx_intr(card);
break;
- default: /* unexpected interrupt */
+ default: /* unexpected interrupt */
printk(KERN_INFO "%s: spurious interrupt 0x%02X!\n",
- card->devname, flags->iflag)
- ;
+ card->devname, flags->iflag);
}
flags->iflag = 0;
}
@@ -793,52 +752,42 @@ STATIC void wpp_isr (sdla_t* card)
/*============================================================================
* Receive interrupt handler.
*/
-static void rx_intr (sdla_t* card)
+static void rx_intr(sdla_t * card)
{
- ppp_buf_ctl_t* rxbuf = card->rxmb;
- struct device* dev = card->wandev.dev;
- struct sk_buff* skb;
+ ppp_buf_ctl_t *rxbuf = card->rxmb;
+ struct device *dev = card->wandev.dev;
+ struct sk_buff *skb;
unsigned len;
- void* buf;
-
- if (rxbuf->flag != 0x01)
- {
+ void *buf;
+
+ if (rxbuf->flag != 0x01) {
printk(KERN_INFO "%s: corrupted Rx buffer @ 0x%X!\n",
- card->devname, (unsigned)rxbuf)
- ;
+ card->devname, (unsigned) rxbuf);
return;
}
-
if (!dev || !dev->start)
goto rx_done
- ;
- len = rxbuf->length;
+ ;
+ len = rxbuf->length;
/* Allocate socket buffer */
skb = dev_alloc_skb(len);
- if (skb == NULL)
- {
+ if (skb == NULL) {
printk(KERN_INFO "%s: no socket buffers available!\n",
- card->devname)
- ;
+ card->devname);
++card->wandev.stats.rx_dropped;
goto rx_done;
}
-
/* Copy data to the socket buffer */
- if (card->hw.fwid == SFID_PPP502)
- {
- unsigned addr = (rxbuf->buf.o_p[1] << 8) + rxbuf->buf.o_p[0];
+ if (card->hw.fwid == SFID_PPP502) {
+ unsigned addr = (rxbuf->buf.o_p[1] << 8) + rxbuf->buf.o_p[0];
buf = skb_put(skb, len);
sdla_peek(&card->hw, addr, buf, len);
- }
- else
- {
+ } else {
unsigned addr = rxbuf->buf.ptr;
- if ((addr + len) > card->u.p.rx_top + 1)
- {
+ if ((addr + len) > card->u.p.rx_top + 1) {
unsigned tmp = card->u.p.rx_top - addr + 1;
buf = skb_put(skb, tmp);
@@ -851,8 +800,7 @@ static void rx_intr (sdla_t* card)
}
/* Decapsulate packet */
- switch (rxbuf->proto)
- {
+ switch (rxbuf->proto) {
case 0x00:
skb->protocol = htons(ETH_P_IP);
break;
@@ -866,27 +814,26 @@ static void rx_intr (sdla_t* card)
skb->dev = dev;
netif_rx(skb);
++card->wandev.stats.rx_packets;
-rx_done:
+ rx_done:
/* Release buffer element and calculate a pointer to the next one */
rxbuf->flag = (card->hw.fwid == SFID_PPP502) ? 0xFF : 0x00;
card->rxmb = ++rxbuf;
- if ((void*)rxbuf > card->u.p.rxbuf_last)
+ if ((void *) rxbuf > card->u.p.rxbuf_last)
card->rxmb = card->u.p.rxbuf_base
- ;
+ ;
}
/*============================================================================
* Transmit interrupt handler.
*/
-static void tx_intr (sdla_t* card)
+static void tx_intr(sdla_t * card)
{
- struct device* dev = card->wandev.dev;
+ struct device *dev = card->wandev.dev;
if (!dev || !dev->start)
- return
- ;
+ return;
dev->tbusy = 0;
- dev_tint(dev);
+ mark_bh(NET_BH);
}
/****** Background Polling Routines ****************************************/
@@ -900,10 +847,9 @@ static void tx_intr (sdla_t* card)
* 1. This routine may be called on interrupt context with all interrupts
* enabled. Beware!
*/
-static void wpp_poll (sdla_t* card)
+static void wpp_poll(sdla_t * card)
{
- switch(card->wandev.state)
- {
+ switch (card->wandev.state) {
case WAN_CONNECTED:
poll_active(card);
break;
@@ -921,12 +867,11 @@ static void wpp_poll (sdla_t* card)
/*============================================================================
* Monitor active link phase.
*/
-static void poll_active (sdla_t* card)
+static void poll_active(sdla_t * card)
{
- ppp_flags_t* flags = card->flags;
+ ppp_flags_t *flags = card->flags;
- if (flags->disc_cause & 0x03)
- {
+ if (flags->disc_cause & 0x03) {
wanpipe_set_state(card, WAN_DISCONNECTED);
show_disc_cause(card, flags->disc_cause);
}
@@ -936,16 +881,13 @@ static void poll_active (sdla_t* card)
* Monitor link establishment phase.
* o if connection timed out, disconnect the link.
*/
-static void poll_connecting (sdla_t* card)
+static void poll_connecting(sdla_t * card)
{
- ppp_flags_t* flags = card->flags;
+ ppp_flags_t *flags = card->flags;
- if (flags->lcp_state == 0x09)
- {
+ if (flags->lcp_state == 0x09) {
wanpipe_set_state(card, WAN_CONNECTED);
- }
- else if (flags->disc_cause & 0x03)
- {
+ } else if (flags->disc_cause & 0x03) {
wanpipe_set_state(card, WAN_DISCONNECTED);
show_disc_cause(card, flags->disc_cause);
}
@@ -956,13 +898,12 @@ static void poll_connecting (sdla_t* card)
* o if interface is up and the hold-down timeout has expired, then retry
* connection.
*/
-static void poll_disconnected (sdla_t* card)
+static void poll_disconnected(sdla_t * card)
{
- struct device* dev = card->wandev.dev;
+ struct device *dev = card->wandev.dev;
if (dev && dev->start &&
- ((jiffies - card->state_tick) > HOLD_DOWN_TIME))
- {
+ ((jiffies - card->state_tick) > HOLD_DOWN_TIME)) {
wanpipe_set_state(card, WAN_CONNECTING);
ppp_comm_enable(card);
}
@@ -973,7 +914,7 @@ static void poll_disconnected (sdla_t* card)
/*============================================================================
* Configure S502 adapter.
*/
-static int config502 (sdla_t* card)
+static int config502(sdla_t * card)
{
ppp502_conf_t cfg;
@@ -981,35 +922,34 @@ static int config502 (sdla_t* card)
memset(&cfg, 0, sizeof(ppp502_conf_t));
if (card->wandev.clocking)
- cfg.line_speed = bps_to_speed_code(card->wandev.bps)
- ;
- cfg.txbuf_num = 4;
- cfg.mtu_local = card->wandev.mtu;
- cfg.mtu_remote = card->wandev.mtu;
- cfg.restart_tmr = 30;
- cfg.auth_rsrt_tmr = 30;
- cfg.auth_wait_tmr = 300;
- cfg.mdm_fail_tmr = 5;
- cfg.dtr_drop_tmr = 1;
- cfg.connect_tmout = 0; /* changed it from 900 */
- cfg.conf_retry = 10;
- cfg.term_retry = 2;
- cfg.fail_retry = 5;
- cfg.auth_retry = 10;
- cfg.ip_options = 0x80;
- cfg.ipx_options = 0xA0;
- cfg.conf_flags |= 0x0E;
+ cfg.line_speed = bps_to_speed_code(card->wandev.bps);
+ cfg.txbuf_num = 4;
+ cfg.mtu_local = card->wandev.mtu;
+ cfg.mtu_remote = card->wandev.mtu;
+ cfg.restart_tmr = 30;
+ cfg.auth_rsrt_tmr = 30;
+ cfg.auth_wait_tmr = 300;
+ cfg.mdm_fail_tmr = 5;
+ cfg.dtr_drop_tmr = 1;
+ cfg.connect_tmout = 0; /* changed it from 900 */
+ cfg.conf_retry = 10;
+ cfg.term_retry = 2;
+ cfg.fail_retry = 5;
+ cfg.auth_retry = 10;
+ cfg.ip_options = 0x80;
+ cfg.ipx_options = 0xA0;
+ cfg.conf_flags |= 0x0E;
/*
- cfg.ip_local = dev->pa_addr;
- cfg.ip_remote = dev->pa_dstaddr;
-*/
+ cfg.ip_local = dev->pa_addr;
+ cfg.ip_remote = dev->pa_dstaddr;
+ */
return ppp_configure(card, &cfg);
}
/*============================================================================
* Configure S508 adapter.
*/
-static int config508 (sdla_t* card)
+static int config508(sdla_t * card)
{
ppp508_conf_t cfg;
@@ -1018,95 +958,108 @@ static int config508 (sdla_t* card)
if (card->wandev.clocking)
cfg.line_speed = card->wandev.bps
- ;
+ ;
if (card->wandev.interface == WANOPT_RS232)
cfg.conf_flags |= 0x0020;
;
- cfg.conf_flags |= 0x300; /*send Configure-Request packets forever*/
- cfg.txbuf_percent = 60; /* % of Tx bufs */
- cfg.mtu_local = card->wandev.mtu;
- cfg.mtu_remote = card->wandev.mtu;
- cfg.restart_tmr = 30;
- cfg.auth_rsrt_tmr = 30;
- cfg.auth_wait_tmr = 300;
- cfg.mdm_fail_tmr = 5;
- cfg.dtr_drop_tmr = 1;
- cfg.connect_tmout = 0; /* changed it from 900 */
- cfg.conf_retry = 10;
- cfg.term_retry = 2;
- cfg.fail_retry = 5;
- cfg.auth_retry = 10;
- cfg.ip_options = 0x80;
- cfg.ipx_options = 0xA0;
+ cfg.conf_flags |= 0x300; /*send Configure-Request packets forever */
+ cfg.txbuf_percent = 60; /* % of Tx bufs */
+ cfg.mtu_local = card->wandev.mtu;
+ cfg.mtu_remote = card->wandev.mtu;
+ cfg.restart_tmr = 30;
+ cfg.auth_rsrt_tmr = 30;
+ cfg.auth_wait_tmr = 300;
+ cfg.mdm_fail_tmr = 5;
+ cfg.dtr_drop_tmr = 1;
+ cfg.connect_tmout = 0; /* changed it from 900 */
+ cfg.conf_retry = 10;
+ cfg.term_retry = 2;
+ cfg.fail_retry = 5;
+ cfg.auth_retry = 10;
+ cfg.ip_options = 0x80;
+ cfg.ipx_options = 0xA0;
/*
- cfg.ip_local = dev->pa_addr;
- cfg.ip_remote = dev->pa_dstaddr;
-*/
+ cfg.ip_local = dev->pa_addr;
+ cfg.ip_remote = dev->pa_dstaddr;
+ */
return ppp_configure(card, &cfg);
}
/*============================================================================
* Show disconnection cause.
*/
-static void show_disc_cause (sdla_t* card, unsigned cause)
+static void show_disc_cause(sdla_t * card, unsigned cause)
{
- if (cause & 0x0002) printk(KERN_INFO
- "%s: link terminated by peer\n", card->devname)
- ;
- else if (cause & 0x0004) printk(KERN_INFO
- "%s: link terminated by user\n", card->devname)
- ;
- else if (cause & 0x0008) printk(KERN_INFO
- "%s: authentication failed\n", card->devname)
- ;
- else if (cause & 0x0010) printk(KERN_INFO
- "%s: authentication protocol negotiation failed\n",
- card->devname)
- ;
- else if (cause & 0x0020) printk(KERN_INFO
- "%s: peer's request for authentication rejected\n",
- card->devname)
- ;
- else if (cause & 0x0040) printk(KERN_INFO
- "%s: MRU option rejected by peer\n", card->devname)
- ;
- else if (cause & 0x0080) printk(KERN_INFO
- "%s: peer's MRU was too small\n", card->devname)
- ;
- else if (cause & 0x0100) printk(KERN_INFO
- "%s: failed to negotiate peer's LCP options\n",
- card->devname)
- ;
- else if (cause & 0x0200) printk(KERN_INFO
- "%s: failed to negotiate peer's IPCP options\n",
- card->devname)
- ;
- else if (cause & 0x0400) printk(KERN_INFO
- "%s: failed to negotiate peer's IPXCP options\n",
- card->devname)
- ;
+ if (cause & 0x0002)
+ printk(KERN_INFO
+ "%s: link terminated by peer\n", card->devname);
+ else if (cause & 0x0004)
+ printk(KERN_INFO
+ "%s: link terminated by user\n", card->devname);
+ else if (cause & 0x0008)
+ printk(KERN_INFO
+ "%s: authentication failed\n", card->devname);
+ else if (cause & 0x0010)
+ printk(KERN_INFO
+ "%s: authentication protocol negotiation failed\n",
+ card->devname);
+ else if (cause & 0x0020)
+ printk(KERN_INFO
+ "%s: peer's request for authentication rejected\n",
+ card->devname);
+ else if (cause & 0x0040)
+ printk(KERN_INFO
+ "%s: MRU option rejected by peer\n", card->devname);
+ else if (cause & 0x0080)
+ printk(KERN_INFO
+ "%s: peer's MRU was too small\n", card->devname);
+ else if (cause & 0x0100)
+ printk(KERN_INFO
+ "%s: failed to negotiate peer's LCP options\n",
+ card->devname);
+ else if (cause & 0x0200)
+ printk(KERN_INFO
+ "%s: failed to negotiate peer's IPCP options\n",
+ card->devname);
+ else if (cause & 0x0400)
+ printk(KERN_INFO
+ "%s: failed to negotiate peer's IPXCP options\n",
+ card->devname);
}
/*============================================================================
* Convert line speed in bps to a number used by S502 code.
*/
-static unsigned char bps_to_speed_code (unsigned long bps)
+static unsigned char bps_to_speed_code(unsigned long bps)
{
- unsigned char number;
-
- if (bps <= 1200) number = 0x01 ;
- else if (bps <= 2400) number = 0x02;
- else if (bps <= 4800) number = 0x03;
- else if (bps <= 9600) number = 0x04;
- else if (bps <= 19200) number = 0x05;
- else if (bps <= 38400) number = 0x06;
- else if (bps <= 45000) number = 0x07;
- else if (bps <= 56000) number = 0x08;
- else if (bps <= 64000) number = 0x09;
- else if (bps <= 74000) number = 0x0A;
- else if (bps <= 112000) number = 0x0B;
- else if (bps <= 128000) number = 0x0C;
- else number = 0x0D;
+ unsigned char number;
+
+ if (bps <= 1200)
+ number = 0x01;
+ else if (bps <= 2400)
+ number = 0x02;
+ else if (bps <= 4800)
+ number = 0x03;
+ else if (bps <= 9600)
+ number = 0x04;
+ else if (bps <= 19200)
+ number = 0x05;
+ else if (bps <= 38400)
+ number = 0x06;
+ else if (bps <= 45000)
+ number = 0x07;
+ else if (bps <= 56000)
+ number = 0x08;
+ else if (bps <= 64000)
+ number = 0x09;
+ else if (bps <= 74000)
+ number = 0x0A;
+ else if (bps <= 112000)
+ number = 0x0B;
+ else if (bps <= 128000)
+ number = 0x0C;
+ else
+ number = 0x0D;
return number;
}
diff --git a/drivers/net/seeq8005.c b/drivers/net/seeq8005.c
index 1a41960c4..d8372bf52 100644
--- a/drivers/net/seeq8005.c
+++ b/drivers/net/seeq8005.c
@@ -305,7 +305,7 @@ __initfunc(static int seeq8005_probe1(struct device *dev, int ioaddr))
#if 0
{
- int irqval = request_irq(dev->irq, &seeq8005_interrupt, 0, "seeq8005", NULL);
+ int irqval = request_irq(dev->irq, &seeq8005_interrupt, 0, "seeq8005", dev);
if (irqval) {
printk ("%s: unable to get IRQ %d (irqval=%d).\n", dev->name,
dev->irq, irqval);
@@ -351,14 +351,13 @@ seeq8005_open(struct device *dev)
struct net_local *lp = (struct net_local *)dev->priv;
{
- int irqval = request_irq(dev->irq, &seeq8005_interrupt, 0, "seeq8005", NULL);
+ int irqval = request_irq(dev->irq, &seeq8005_interrupt, 0, "seeq8005", dev);
if (irqval) {
printk ("%s: unable to get IRQ %d (irqval=%d).\n", dev->name,
dev->irq, irqval);
return EAGAIN;
}
}
- irq2dev_map[dev->irq] = dev;
/* Reset the hardware here. Don't forget to set the station address. */
seeq8005_init(dev, 1);
@@ -413,7 +412,7 @@ seeq8005_send_packet(struct sk_buff *skb, struct device *dev)
static void
seeq8005_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
- struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct device *dev = dev_id;
struct net_local *lp;
int ioaddr, status, boguscount = 0;
@@ -574,9 +573,7 @@ seeq8005_close(struct device *dev)
/* Flush the Tx and disable Rx here. */
outw( SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
- free_irq(dev->irq, NULL);
-
- irq2dev_map[dev->irq] = 0;
+ free_irq(dev->irq, dev);
/* Update the statistics here. */
diff --git a/drivers/net/sgiseeq.c b/drivers/net/sgiseeq.c
index 0ea951254..0602cb4ab 100644
--- a/drivers/net/sgiseeq.c
+++ b/drivers/net/sgiseeq.c
@@ -1,4 +1,4 @@
-/* $Id: sgiseeq.c,v 1.3 1997/09/16 14:44:21 marks Exp $
+/* $Id: sgiseeq.c,v 1.4 1997/12/06 04:11:41 ralf Exp $
* sgiseeq.c: Seeq8003 ethernet driver for SGI machines.
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
@@ -474,7 +474,7 @@ static int sgiseeq_close(struct device *dev)
/* Shutdown the Seeq. */
reset_hpc3_and_seeq(sp->hregs, sregs);
- free_irq(dev->irq, NULL);
+ free_irq(dev->irq, dev);
return 0;
}
@@ -515,17 +515,6 @@ static inline int verify_tx(struct sgiseeq_private *sp,
sgiseeq_reset(dev);
return 0;
}
- /* Is the skippy buf even reasonable? */
- if(skb == NULL) {
- dev_tint(dev);
- printk("%s: skb is NULL\n", dev->name);
- return -1;
- }
-
- if(skb->len <= 0) {
- printk("%s: skb len is %d\n", dev->name, skb->len);
- return -1;
- }
/* Are we getting in someone else's way? */
if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) {
printk("%s: Transmitter access conflict.\n", dev->name);
diff --git a/drivers/net/shaper.c b/drivers/net/shaper.c
index fff408c42..9bfcb0792 100644
--- a/drivers/net/shaper.c
+++ b/drivers/net/shaper.c
@@ -598,11 +598,6 @@ __initfunc(int shaper_probe(struct device *dev))
dev->addr_len = 0;
dev->tx_queue_len = 10;
dev->flags = 0;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = 4;
/*
* Shaper is ok
diff --git a/drivers/net/sk_g16.c b/drivers/net/sk_g16.c
index 51e8fe8ba..c33e033e8 100644
--- a/drivers/net/sk_g16.c
+++ b/drivers/net/sk_g16.c
@@ -451,8 +451,6 @@ struct priv
/* IRQ map used to reserve a IRQ (see SK_open()) */
-/* extern void *irq2dev_map[16]; */ /* Declared in <linux/ioport.h> */
-
/* static variables */
static SK_RAM *board; /* pointer to our memory mapped board components */
@@ -610,8 +608,7 @@ __initfunc(int SK_init(struct device *dev))
* Return Value : 0 = Initialization done
* Errors : ENODEV - No SK_G16 found
* -1 - Configuration problem
- * Globals : irq2dev_map - Which device uses which IRQ
- * : board - pointer to SK_RAM
+ * Globals : board - pointer to SK_RAM
* Update History :
* YY/MM/DD uid Description
* 94/06/30 pwe SK_ADDR now checked and at the correct place
@@ -838,7 +835,6 @@ __initfunc(int SK_probe(struct device *dev, short ioaddr))
* Parameters : I : struct device *dev - SK_G16 device structure
* Return Value : 0 - Device opened
* Errors : -EAGAIN - Open failed
- * Globals : irq2dev_map - which device uses which irq
* Side Effects : None
* Update History :
* YY/MM/DD uid Description
@@ -872,7 +868,7 @@ static int SK_open(struct device *dev)
do
{
- irqval = request_irq(irqtab[i], &SK_interrupt, 0, "sk_g16", NULL);
+ irqval = request_irq(irqtab[i], &SK_interrupt, 0, "sk_g16", dev);
i++;
} while (irqval && irqtab[i]);
@@ -889,7 +885,7 @@ static int SK_open(struct device *dev)
}
else if (dev->irq == 2) /* IRQ2 is always IRQ9 */
{
- if (request_irq(9, &SK_interrupt, 0, "sk_g16", NULL))
+ if (request_irq(9, &SK_interrupt, 0, "sk_g16", dev))
{
printk("%s: unable to get IRQ 9\n", dev->name);
return -EAGAIN;
@@ -910,7 +906,7 @@ static int SK_open(struct device *dev)
/* check if IRQ free and valid. Then install Interrupt handler */
- if (request_irq(dev->irq, &SK_interrupt, 0, "sk_g16", NULL))
+ if (request_irq(dev->irq, &SK_interrupt, 0, "sk_g16", dev))
{
printk("%s: unable to get selected IRQ\n", dev->name);
return -EAGAIN;
@@ -937,8 +933,6 @@ static int SK_open(struct device *dev)
outb(i<<2, SK_POS4); /* Set IRQ on card */
}
- irq2dev_map[dev->irq] = dev; /* Set IRQ as used by us */
-
printk("%s: Schneider & Koch G16 at %#3x, IRQ %d, shared mem at %#08x\n",
dev->name, (unsigned int)dev->base_addr,
(int) dev->irq, (unsigned int) p->ram);
@@ -1282,7 +1276,7 @@ static int SK_send_packet(struct sk_buff *skb, struct device *dev)
static void SK_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
int csr0;
- struct device *dev = (struct device *) irq2dev_map[irq];
+ struct device *dev = dev_id;
struct priv *p = (struct priv *) dev->priv;
@@ -1636,8 +1630,7 @@ static int SK_close(struct device *dev)
SK_write_reg(CSR0, CSR0_STOP); /* STOP the LANCE */
- free_irq(dev->irq, NULL); /* Free IRQ */
- irq2dev_map[dev->irq] = 0; /* Mark IRQ as unused */
+ free_irq(dev->irq, dev); /* Free IRQ */
return 0; /* always succeed */
diff --git a/drivers/net/skeleton.c b/drivers/net/skeleton.c
index 0df5feaae..b30e62b32 100644
--- a/drivers/net/skeleton.c
+++ b/drivers/net/skeleton.c
@@ -221,7 +221,7 @@ __initfunc(static int netcard_probe1(struct device *dev, int ioaddr))
dev->irq = 9;
{
- int irqval = request_irq(dev->irq, &net_interrupt, 0, cardname, NULL);
+ int irqval = request_irq(dev->irq, &net_interrupt, 0, cardname, dev);
if (irqval) {
printk("%s: unable to get IRQ %d (irqval=%d).\n",
dev->name, dev->irq, irqval);
@@ -314,7 +314,7 @@ net_open(struct device *dev)
* This is used if the interrupt line can turned off (shared).
* See 3c503.c for an example of selecting the IRQ at config-time.
*/
- if (request_irq(dev->irq, &net_interrupt, 0, cardname, NULL)) {
+ if (request_irq(dev->irq, &net_interrupt, 0, cardname, dev)) {
return -EAGAIN;
}
/*
@@ -322,10 +322,9 @@ net_open(struct device *dev)
* and clean up on failure.
*/
if (request_dma(dev->dma, cardname)) {
- free_irq(dev->irq, NULL);
+ free_irq(dev->irq, dev);
return -EAGAIN;
}
- irq2dev_map[dev->irq] = dev;
/* Reset the hardware here. Don't forget to set the station address. */
/*chipset_init(dev, 1);*/
@@ -390,7 +389,7 @@ static int net_send_packet(struct sk_buff *skb, struct device *dev)
*/
static void net_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
- struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct device *dev = dev_id;
struct net_local *lp;
int ioaddr, status, boguscount = 0;
@@ -498,11 +497,9 @@ net_close(struct device *dev)
/* If not IRQ or DMA jumpered, free up the line. */
outw(0x00, ioaddr+0); /* Release the physical interrupt line. */
- free_irq(dev->irq, NULL);
+ free_irq(dev->irq, dev);
free_dma(dev->dma);
- irq2dev_map[dev->irq] = 0;
-
/* Update the statistics here. */
MOD_DEC_USE_COUNT;
@@ -607,7 +604,7 @@ cleanup_module(void)
* allocate them in net_probe1().
*/
/*
- free_irq(this_device.irq, NULL);
+ free_irq(this_device.irq, dev);
free_dma(this_device.dma);
*/
release_region(this_device.base_addr, NETCARD_IO_EXTENT);
diff --git a/drivers/net/slhc.c b/drivers/net/slhc.c
index 7e946b78f..187e51616 100644
--- a/drivers/net/slhc.c
+++ b/drivers/net/slhc.c
@@ -246,6 +246,14 @@ slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
struct iphdr *ip;
struct tcphdr *th, *oth;
+
+ /*
+ * Don't play with runt packets.
+ */
+
+ if(isize<sizeof(struct iphdr))
+ return isize;
+
ip = (struct iphdr *) icp;
/* Bail if this packet isn't TCP, or is an IP fragment */
@@ -264,9 +272,10 @@ slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
hlen = ip->ihl*4 + th->doff*4;
/* Bail if the TCP packet isn't `compressible' (i.e., ACK isn't set or
- * some other control bit is set).
+ * some other control bit is set). Also uncompressible if
+ * its a runt.
*/
- if(th->syn || th->fin || th->rst ||
+ if(hlen > isize || th->syn || th->fin || th->rst ||
! (th->ack)){
/* TCP connection stuff; send as regular IP */
comp->sls_o_tcp++;
@@ -697,7 +706,7 @@ slhc_toss(struct slcompress *comp)
void slhc_i_status(struct slcompress *comp)
{
if (comp != NULLSLCOMPR) {
- printk("\t%ld Cmp, %ld Uncmp, %ld Bad, %ld Tossed\n",
+ printk("\t%d Cmp, %d Uncmp, %d Bad, %d Tossed\n",
comp->sls_i_compressed,
comp->sls_i_uncompressed,
comp->sls_i_error,
@@ -709,12 +718,12 @@ void slhc_i_status(struct slcompress *comp)
void slhc_o_status(struct slcompress *comp)
{
if (comp != NULLSLCOMPR) {
- printk("\t%ld Cmp, %ld Uncmp, %ld AsIs, %ld NotTCP\n",
+ printk("\t%d Cmp, %d Uncmp, %d AsIs, %d NotTCP\n",
comp->sls_o_compressed,
comp->sls_o_uncompressed,
comp->sls_o_tcp,
comp->sls_o_nontcp);
- printk("\t%10ld Searches, %10ld Misses\n",
+ printk("\t%10d Searches, %10d Misses\n",
comp->sls_o_searches,
comp->sls_o_misses);
}
diff --git a/drivers/net/slip.c b/drivers/net/slip.c
index 435cd629a..76b0aec81 100644
--- a/drivers/net/slip.c
+++ b/drivers/net/slip.c
@@ -589,12 +589,7 @@ sl_open(struct device *dev)
sl->outfill_timer.data=(unsigned long)sl;
sl->outfill_timer.function=sl_outfill;
#endif
- /* Needed because address '0' is special */
- if (dev->pa_addr == 0) {
- dev->pa_addr=ntohl(0xC0A80001);
- }
dev->tbusy = 0;
-/* dev->flags |= IFF_UP; */
dev->start = 1;
return 0;
@@ -626,8 +621,6 @@ sl_close(struct device *dev)
dev->tbusy = 1;
dev->start = 0;
-/* dev->flags &= ~IFF_UP; */
-
return 0;
}
@@ -740,7 +733,11 @@ slip_close(struct tty_struct *tty)
return;
}
- (void) dev_close(sl->dev);
+ if (sl->dev->flags & IFF_UP)
+ {
+ /* STRONG layering violation! --ANK */
+ (void) dev_close(sl->dev);
+ }
tty->disc_data = 0;
sl->tty = NULL;
@@ -752,7 +749,6 @@ slip_close(struct tty_struct *tty)
(void)del_timer (&sl->outfill_timer);
#endif
sl_free(sl);
- unregister_netdev(sl->dev);
MOD_DEC_USE_COUNT;
}
@@ -1237,12 +1233,7 @@ int slip_init(struct device *dev)
dev_init_buffers(dev);
/* New-style flags. */
- dev->flags = IFF_NOARP|IFF_MULTICAST;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = 4;
+ dev->flags = IFF_NOARP|IFF_POINTOPOINT|IFF_MULTICAST;
return 0;
}
diff --git a/drivers/net/smc-mca.c b/drivers/net/smc-mca.c
index 43872c529..d3fe3084a 100644
--- a/drivers/net/smc-mca.c
+++ b/drivers/net/smc-mca.c
@@ -192,7 +192,7 @@ static int ultramca_open(struct device *dev)
{
int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
- if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, NULL))
+ if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, dev))
return -EAGAIN;
outb(ULTRA_MEMENB, ioaddr); /* Enable memory */
@@ -289,8 +289,7 @@ static int ultramca_close_card(struct device *dev)
printk("%s: Shutting down ethercard.\n", dev->name);
outb(0x00, ioaddr + 6); /* Disable interrupts. */
- free_irq(dev->irq, NULL);
- irq2dev_map[dev->irq] = 0;
+ free_irq(dev->irq, dev);
NS8390_init(dev, 0);
/* We should someday disable shared memory and change to 8-bit mode
@@ -366,7 +365,7 @@ void cleanup_module(void)
struct device *dev = &dev_ultra[this_dev];
if (dev->priv != NULL)
{
- /* NB: ultra_close_card() does free_irq + irq2dev */
+ /* NB: ultra_close_card() does free_irq */
int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET;
kfree(dev->priv);
dev->priv = NULL;
diff --git a/drivers/net/smc-ultra.c b/drivers/net/smc-ultra.c
index 30c6b4907..d922eb17c 100644
--- a/drivers/net/smc-ultra.c
+++ b/drivers/net/smc-ultra.c
@@ -253,7 +253,7 @@ ultra_open(struct device *dev)
{
int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
- if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, NULL))
+ if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, dev))
return -EAGAIN;
outb(0x00, ioaddr); /* Disable shared memory for safety. */
@@ -405,8 +405,7 @@ ultra_close_card(struct device *dev)
printk("%s: Shutting down ethercard.\n", dev->name);
outb(0x00, ioaddr + 6); /* Disable interrupts. */
- free_irq(dev->irq, NULL);
- irq2dev_map[dev->irq] = 0;
+ free_irq(dev->irq, dev);
NS8390_init(dev, 0);
@@ -474,7 +473,7 @@ cleanup_module(void)
for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
struct device *dev = &dev_ultra[this_dev];
if (dev->priv != NULL) {
- /* NB: ultra_close_card() does free_irq + irq2dev */
+ /* NB: ultra_close_card() does free_irq */
int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET;
kfree(dev->priv);
dev->priv = NULL;
diff --git a/drivers/net/smc9194.c b/drivers/net/smc9194.c
index 55f04121f..3db278b38 100644
--- a/drivers/net/smc9194.c
+++ b/drivers/net/smc9194.c
@@ -1102,13 +1102,12 @@ __initfunc(static int smc_initcard(struct device *dev, int ioaddr))
ether_setup(dev);
/* Grab the IRQ */
- irqval = request_irq(dev->irq, &smc_interrupt, 0, CARDNAME, NULL);
+ irqval = request_irq(dev->irq, &smc_interrupt, 0, CARDNAME, dev);
if (irqval) {
printk(CARDNAME": unable to get IRQ %d (irqval=%d).\n",
dev->irq, irqval);
return -EAGAIN;
}
- irq2dev_map[dev->irq] = dev;
/* Grab the region so that no one else tries to probe our ioports. */
request_region(ioaddr, SMC_IO_EXTENT, CARDNAME);
@@ -1274,7 +1273,7 @@ static void smc_interrupt(int irq, void * dev_id, struct pt_regs * regs)
static void smc_interrupt(int irq, struct pt_regs * regs)
#endif
{
- struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct device *dev = dev_id;
int ioaddr = dev->base_addr;
struct smc_local *lp = (struct smc_local *)dev->priv;
@@ -1764,8 +1763,7 @@ void cleanup_module(void)
/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
unregister_netdev(&devSMC9194);
- free_irq(devSMC9194.irq, NULL );
- irq2dev_map[devSMC9194.irq] = NULL;
+ free_irq(devSMC9194.irq, &devSMC9194);
release_region(devSMC9194.base_addr, SMC_IO_EXTENT);
if (devSMC9194.priv)
diff --git a/drivers/net/sonic.c b/drivers/net/sonic.c
index 4a6362c1d..d785cf8dd 100644
--- a/drivers/net/sonic.c
+++ b/drivers/net/sonic.c
@@ -390,16 +390,6 @@ static int sonic_send_packet(struct sk_buff *skb, struct device *dev)
dev->trans_start = jiffies;
}
- /*
- * If some higher layer thinks we've missed an tx-done interrupt
- * we are passed NULL. Caution: dev_tint() handles the cli()/sti()
- * itself.
- */
- if (skb == NULL) {
- dev_tint(dev);
- return 0;
- }
-
/*
* Block a timer-based transmit from overlapping. This could better be
* done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
diff --git a/drivers/net/strip.c b/drivers/net/strip.c
index 1ba68cf78..6d927ab4e 100644
--- a/drivers/net/strip.c
+++ b/drivers/net/strip.c
@@ -1,3 +1,5 @@
+#warning "will not compile until the networking is merged"
+#if 0
/*
* Copyright 1996 The Board of Trustees of The Leland Stanford
* Junior University. All Rights Reserved.
@@ -106,6 +108,7 @@ static const char StripVersion[] = "1.2-STUART.CHESHIRE";
#include <linux/tty.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
@@ -1505,16 +1508,18 @@ static void strip_send(struct strip *strip_info, struct sk_buff *skb)
memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)) &&
*strip_info->dev.broadcast!=0xFF)
{
+ struct in_device *in_dev = strip_info->dev.ip_ptr;
/*printk(KERN_INFO "%s: Sending gratuitous ARP with interval %ld\n",
strip_info->dev.name, strip_info->arp_interval / HZ);*/
strip_info->gratuitous_arp = jiffies + strip_info->arp_interval;
strip_info->arp_interval *= 2;
if (strip_info->arp_interval > MaxARPInterval)
strip_info->arp_interval = MaxARPInterval;
+ if (in_dev && in_dev->ifa_list)
arp_send(ARPOP_REPLY, ETH_P_ARP,
- strip_info->dev.pa_addr, /* Target address of ARP packet is our address */
+ in_dev->ifa_list->ifa_address,/* Target address of ARP packet is our address */
&strip_info->dev, /* Device to send packet on */
- strip_info->dev.pa_addr, /* Source IP address this ARP packet comes from */
+ in_dev->ifa_list->ifa_address,/* Source IP address this ARP packet comes from */
NULL, /* Destination HW address is NULL (broadcast it) */
strip_info->dev.dev_addr, /* Source HW address is our HW address */
strip_info->dev.dev_addr); /* Target HW address is our HW address (redundant) */
@@ -2430,7 +2435,6 @@ static int strip_dev_init(struct device *dev)
dev->tx_queue_len = 30; /* Drop after 30 frames queued */
dev->flags = 0;
- dev->family = AF_INET;
dev->metric = 0;
dev->mtu = DEFAULT_STRIP_MTU;
dev->type = ARPHRD_METRICOM; /* dtang */
@@ -2442,10 +2446,6 @@ static int strip_dev_init(struct device *dev)
*(MetricomAddress*)&dev->broadcast = broadcast_address;
dev->dev_addr[0] = 0;
dev->addr_len = sizeof(MetricomAddress);
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = sizeof(unsigned long);
/*
* Pointer to the interface buffers.
@@ -2634,7 +2634,6 @@ static void strip_close(struct tty_struct *tty)
if (!strip_info || strip_info->magic != STRIP_MAGIC)
return;
- dev_close(&strip_info->dev);
unregister_netdev(&strip_info->dev);
tty->disc_data = 0;
@@ -2783,3 +2782,4 @@ void cleanup_module(void)
printk(KERN_INFO "STRIP: Module Unloaded\n");
}
#endif /* MODULE */
+#endif
diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c
index 8411595a0..4e299218e 100644
--- a/drivers/net/sunhme.c
+++ b/drivers/net/sunhme.c
@@ -974,16 +974,20 @@ static void happy_meal_init_rings(struct happy_meal *hp, int from_irq)
/* Because we reserve afterwards. */
skb_put(skb, (ETH_FRAME_LEN + RX_OFFSET));
+#ifdef CONFIG_PCI
if(hp->happy_flags & HFLAG_PCI) {
pcihme_write_rxd(&hb->happy_meal_rxd[i],
(RXFLAG_OWN |
((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)),
(u32)virt_to_bus((volatile void *)skb->data));
} else {
+#endif
hb->happy_meal_rxd[i].rx_addr = (u32)((unsigned long) skb->data);
hb->happy_meal_rxd[i].rx_flags =
(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16));
+#ifdef CONFIG_PCI
}
+#endif
skb_reserve(skb, RX_OFFSET);
}
@@ -2301,12 +2305,6 @@ static int sun4c_happy_meal_start_xmit(struct sk_buff *skb, struct device *dev)
}
}
- if(skb == NULL || skb->len <= 0) {
- printk("%s: skb is NULL\n", dev->name);
- dev_tint(dev);
- return 0;
- }
-
if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) {
printk("happy meal: Transmitter access conflict.\n");
return 1;
@@ -2547,7 +2545,7 @@ static inline int happy_meal_ether_init(struct device *dev, struct linux_sbus_de
dev->get_stats = &happy_meal_get_stats;
dev->set_multicast_list = &happy_meal_set_multicast;
- dev->irq = (unsigned char) sdev->irqs[0].pri;
+ dev->irq = sdev->irqs[0].pri;
dev->dma = 0;
ether_setup(dev);
#ifdef MODULE
diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c
index 556ca044b..46b6bd7e7 100644
--- a/drivers/net/tlan.c
+++ b/drivers/net/tlan.c
@@ -27,10 +27,8 @@
#include <linux/module.h>
-
#include "tlan.h"
-
#include <linux/bios32.h>
#include <linux/ioport.h>
#include <linux/pci.h>
@@ -39,932 +37,1428 @@
+typedef u32 (TLanIntVectorFunc)( struct device *, u16 );
+
#ifdef MODULE
- static struct device *TLanDevices = NULL;
- static int TLanDevicesInstalled = 0;
+
+static struct device *TLanDevices = NULL;
+static int TLanDevicesInstalled = 0;
+
#endif
- static int debug = 0;
- static int aui = 0;
- static u8 *TLanPadBuffer;
- static char TLanSignature[] = "TLAN";
- static int TLanVersionMajor = 0;
- static int TLanVersionMinor = 32;
-
- static TLanPciId TLanDeviceList[] = {
- { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10, "Compaq Netelligent 10" },
- { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_100, "Compaq Netelligent 10/100" },
- { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED, "Compaq Integrated NetFlex-3/P" },
- { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETFLEX_3P, "Compaq NetFlex-3/P" },
- { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETFLEX_3P_BNC, "Compaq NetFlex-3/P w/ BNC" },
- { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT, "Compaq ProLiant Netelligent 10/100" },
- { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL, "Compaq Dual Port Netelligent 10/100" },
- { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_DESKPRO_4000_5233MMX, "Compaq Deskpro 4000 5233MMX" },
- { 0, 0, NULL } /* End of List */
- };
- static int TLan_MiiReadReg(u16, u16, u16, u16 *);
- static void TLan_MiiSendData( u16, u32, unsigned );
- static void TLan_MiiSync(u16);
- static void TLan_MiiWriteReg(u16, u16, u16, u16);
+static int debug = 0;
+static int aui = 0;
+static u8 *TLanPadBuffer;
+static char TLanSignature[] = "TLAN";
+static int TLanVersionMajor = 0;
+static int TLanVersionMinor = 38;
+
+
+static TLanPciId TLanDeviceList[] = {
+ { PCI_VENDOR_ID_COMPAQ,
+ PCI_DEVICE_ID_NETELLIGENT_10,
+ "Compaq Netelligent 10"
+ },
+ { PCI_VENDOR_ID_COMPAQ,
+ PCI_DEVICE_ID_NETELLIGENT_10_100,
+ "Compaq Netelligent 10/100"
+ },
+ { PCI_VENDOR_ID_COMPAQ,
+ PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED,
+ "Compaq Integrated NetFlex-3/P"
+ },
+ { PCI_VENDOR_ID_COMPAQ,
+ PCI_DEVICE_ID_NETFLEX_3P,
+ "Compaq NetFlex-3/P"
+ },
+ { PCI_VENDOR_ID_COMPAQ,
+ PCI_DEVICE_ID_NETFLEX_3P_BNC,
+ "Compaq NetFlex-3/P"
+ },
+ { PCI_VENDOR_ID_COMPAQ,
+ PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT,
+ "Compaq ProLiant Netelligent 10/100"
+ },
+ { PCI_VENDOR_ID_COMPAQ,
+ PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL,
+ "Compaq Dual Port Netelligent 10/100"
+ },
+ { PCI_VENDOR_ID_COMPAQ,
+ PCI_DEVICE_ID_DESKPRO_4000_5233MMX,
+ "Compaq Deskpro 4000 5233MMX"
+ },
+ { 0,
+ 0,
+ NULL
+ } /* End of List */
+};
+
+
+static int TLan_PciProbe( u8 *, u8 *, u8 *, u8 *, u32 *, u32 * );
+static int TLan_Init( struct device * );
+static int TLan_Open(struct device *dev);
+static int TLan_StartTx(struct sk_buff *, struct device *);
+static void TLan_HandleInterrupt(int, void *, struct pt_regs *);
+static int TLan_Close(struct device *);
+static struct net_device_stats *TLan_GetStats( struct device * );
+static void TLan_SetMulticastList( struct device * );
+
+static u32 TLan_HandleInvalid( struct device *, u16 );
+static u32 TLan_HandleTxEOF( struct device *, u16 );
+static u32 TLan_HandleStatOverflow( struct device *, u16 );
+static u32 TLan_HandleRxEOF( struct device *, u16 );
+static u32 TLan_HandleDummy( struct device *, u16 );
+static u32 TLan_HandleTxEOC( struct device *, u16 );
+static u32 TLan_HandleStatusCheck( struct device *, u16 );
+static u32 TLan_HandleRxEOC( struct device *, u16 );
+
+static void TLan_Timer( unsigned long );
+
+static void TLan_ResetLists( struct device * );
+static void TLan_PrintDio( u16 );
+static void TLan_PrintList( TLanList *, char *, int );
+static void TLan_ReadAndClearStats( struct device *, int );
+static int TLan_Reset( struct device * );
+static void TLan_SetMac( struct device *, int areg, char *mac );
+
+static int TLan_PhyNop( struct device * );
+static void TLan_PhyPrint( struct device * );
+static void TLan_PhySelect( struct device * );
+static int TLan_PhyInternalCheck( struct device * );
+static int TLan_PhyInternalService( struct device * );
+static int TLan_PhyDp83840aCheck( struct device * );
+
+static int TLan_MiiReadReg(u16, u16, u16, u16 *);
+static void TLan_MiiSendData( u16, u32, unsigned );
+static void TLan_MiiSync(u16);
+static void TLan_MiiWriteReg(u16, u16, u16, u16);
+
+static void TLan_EeSendStart( u16 );
+static int TLan_EeSendByte( u16, u8, int );
+static void TLan_EeReceiveByte( u16, u8 *, int );
+static int TLan_EeReadByte( u16, u8, u8 * );
+
+
+static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = {
+ TLan_HandleInvalid,
+ TLan_HandleTxEOF,
+ TLan_HandleStatOverflow,
+ TLan_HandleRxEOF,
+ TLan_HandleDummy,
+ TLan_HandleTxEOC,
+ TLan_HandleStatusCheck,
+ TLan_HandleRxEOC
+};
/*****************************************************************************
******************************************************************************
- ThunderLAN Driver MII Routines
+ ThunderLAN Driver Primary Functions
- These routines are based on the information in Chap. 2 of the
- "ThunderLAN Programmer's Guide", pp. 15-24.
+ These functions are more or less common to all Linux network drivers.
******************************************************************************
*****************************************************************************/
- /*************************************************************************
- * TLan_MiiReadReg
- *
- * Returns: 0 if ack received ok, 1 otherwise.
- * Parms: base_port The base IO port of the adapter in question.
- * dev The address of the PHY to be queried.
- * reg The register whose contents are to be
- * retreived.
- * val A pointer to a variable to store the retrieved
- * value.
- *
- * This function uses the TLAN's MII bus to retreive the contents of a
- * given register on a PHY. It sends the appropriate info and then
- * reads the 16-bit register value from the MII bus via the TLAN SIO
- * register.
- *
- ************************************************************************/
-
- int TLan_MiiReadReg(u16 base_port, u16 dev, u16 reg, u16 *val)
- {
- u8 nack;
- u16 sio, tmp;
- u32 i;
- int err;
- int minten;
-
- err = FALSE;
- outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
- sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
-
- cli();
+#ifdef MODULE
- TLan_MiiSync(base_port);
-
- minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio );
- if ( minten )
- TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio);
+ /***************************************************************
+ * init_module
+ *
+ * Returns:
+ * 0 if module installed ok, non-zero if not.
+ * Parms:
+ * None
+ *
+ * This function begins the setup of the driver creating a
+ * pad buffer, finding all TLAN devices (matching
+ * TLanDeviceList entries), and creating and initializing a
+ * device structure for each adapter.
+ *
+ **************************************************************/
+
+extern int init_module(void)
+{
+ TLanPrivateInfo *priv;
+ u8 bus;
+ struct device *dev;
+ size_t dev_size;
+ u8 dfn;
+ u32 dl_ix;
+ int failed;
+ int found;
+ u32 io_base;
+ u8 irq;
+ u8 rev;
+
+ printk( "TLAN driver, v%d.%d, (C) 1997 Caldera, Inc.\n",
+ TLanVersionMajor,
+ TLanVersionMinor
+ );
+ TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE,
+ ( GFP_KERNEL | GFP_DMA )
+ );
+ if ( TLanPadBuffer == NULL ) {
+ printk( "TLAN: Could not allocate memory for pad buffer.\n" );
+ return -ENOMEM;
+ }
+
+ memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE );
+
+ dev_size = sizeof(struct device) + sizeof(TLanPrivateInfo);
+
+ while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix ) ) )
+ {
+ dev = (struct device *) kmalloc( dev_size, GFP_KERNEL );
+ if ( dev == NULL ) {
+ printk( "TLAN: Could not allocate memory for device.\n" );
+ continue;
+ }
+ memset( dev, 0, dev_size );
+
+ dev->priv = priv = ( (void *) dev ) + sizeof(struct device);
+ dev->name = priv->devName;
+ strcpy( priv->devName, " " );
+ dev->base_addr = io_base;
+ dev->irq = irq;
+ dev->init = TLan_Init;
+
+ priv->pciBus = bus;
+ priv->pciDeviceFn = dfn;
+ priv->pciRevision = rev;
+ priv->pciEntry = dl_ix;
+
+ ether_setup( dev );
+
+ failed = register_netdev( dev );
+
+ if ( failed ) {
+ printk( "TLAN: Could not register network device. Freeing struct.\n" );
+ kfree( dev );
+ } else {
+ priv->nextDevice = TLanDevices;
+ TLanDevices = dev;
+ TLanDevicesInstalled++;
+ printk("TLAN: %s irq=%2d io=%04x, %s\n", dev->name, (int) irq, io_base, TLan
+DeviceList[dl_ix].deviceName );
+ }
+ }
+
+ // printk( "TLAN: Found %d device(s).\n", TLanDevicesInstalled );
- TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */
- TLan_MiiSendData( base_port, 0x2, 2 ); /* Read ( 10b ) */
- TLan_MiiSendData( base_port, dev, 5 ); /* Device # */
- TLan_MiiSendData( base_port, reg, 5 ); /* Register # */
+ return ( ( TLanDevicesInstalled >= 0 ) ? 0 : -ENODEV );
+} /* init_module */
- TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio); /* Change direction */
- TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Clock through Idle bit */
- TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
- TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Use this to wait 300ns */
-
- nack = TLan_GetBit(TLAN_NET_SIO_MDATA, sio); /* Check for ACK */
- TLan_SetBit(TLAN_NET_SIO_MCLK, sio); /* Finish ACK clock cycle */
- if (nack) { /* No ACK, so fake it */
- for (i = 0; i < 16; i++) {
- TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
- TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
- }
- tmp = 0xffff;
- err = TRUE;
- } else { /* ACKed, so read data */
- for (tmp = 0, i = 0x8000; i; i >>= 1) {
- TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
- if (TLan_GetBit(TLAN_NET_SIO_MDATA, sio))
- tmp |= i;
- TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
- }
- }
- TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Idle cycle */
- TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ /***************************************************************
+ * cleanup_module
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * None
+ *
+ * Goes through the TLanDevices list and frees the device
+ * structs and memory associated with each device (lists
+ * and buffers). It also ureserves the IO port regions
+ * associated with this device.
+ *
+ **************************************************************/
- if ( minten )
- TLan_SetBit(TLAN_NET_SIO_MINTEN, sio);
+extern void cleanup_module(void)
+{
+ struct device *dev;
+ TLanPrivateInfo *priv;
- *val = tmp;
+ while (TLanDevicesInstalled) {
+ dev = TLanDevices;
+ priv = (TLanPrivateInfo *) dev->priv;
+ if ( priv->dmaStorage )
+ kfree( priv->dmaStorage );
+ release_region( dev->base_addr, 0x10 );
+ unregister_netdev( dev );
+ TLanDevices = priv->nextDevice;
+ kfree( dev );
+ TLanDevicesInstalled--;
+ }
+ kfree( TLanPadBuffer );
- sti();
+} /* cleanup_module */
- return err;
- } /* TLan_MiiReadReg */
+#else /* MODULE */
- /*************************************************************************
- * TLan_MiiSendData
+ /***************************************************************
+ * tlan_probe
*
- * Returns: Nothing
- * Parms: base_port The base IO port of the adapter in question.
- * dev The address of the PHY to be queried.
- * data The value to be placed on the MII bus.
- * num_bits The number of bits in data that are to be
- * placed on the MII bus.
+ * Returns:
+ * 0 on success, error code on error
+ * Parms:
+ * dev device struct to use if adapter is
+ * found.
*
- * This function sends on sequence of bits on the MII configuration
- * bus.
+ * The name is lower case to fit in with all the rest of
+ * the netcard_probe names. This function looks for a/
+ * another TLan based adapter, setting it up with the
+ * provided device struct if one is found.
*
- ************************************************************************/
-
- void TLan_MiiSendData( u16 base_port, u32 data, unsigned num_bits )
- {
- u16 sio;
- u32 i;
-
- if ( num_bits == 0 )
- return;
-
- outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR );
- sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
- TLan_SetBit( TLAN_NET_SIO_MTXEN, sio );
-
- for ( i = ( 0x1 << ( num_bits - 1 ) ); i; i >>= 1 ) {
- TLan_ClearBit( TLAN_NET_SIO_MCLK, sio );
- TLan_GetBit( TLAN_NET_SIO_MCLK, sio );
- if ( data & i )
- TLan_SetBit( TLAN_NET_SIO_MDATA, sio );
- else
- TLan_ClearBit( TLAN_NET_SIO_MDATA, sio );
- TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
- TLan_GetBit( TLAN_NET_SIO_MCLK, sio );
+ **************************************************************/
+
+extern int tlan_probe( struct device *dev )
+{
+ static int pad_allocated = 0;
+ int found;
+ TLanPrivateInfo *priv;
+ u8 bus, dfn, irq, rev;
+ u32 io_base, dl_ix;
+
+ found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix );
+ if ( found ) {
+ dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL );
+ if ( dev->priv == NULL ) {
+ printk( "TLAN: Could not allocate memory for device.\n" );
+ }
+ memset( dev->priv, 0, sizeof(TLanPrivateInfo) );
+ priv = (TLanPrivateInfo *) dev->priv;
+
+ dev->name = priv->devName;
+ strcpy( priv->devName, " " );
+
+ dev = init_etherdev( dev, sizeof(TLanPrivateInfo) );
+
+ dev->base_addr = io_base;
+ dev->irq = irq;
+
+ priv->pciBus = bus;
+ priv->pciDeviceFn = dfn;
+ priv->pciRevision = rev;
+ priv->pciEntry = dl_ix;
+
+ if ( ! pad_allocated ) {
+ TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, GFP_KERNEL | GFP_DMA );
+ if ( TLanPadBuffer == NULL ) {
+ printk( "TLAN: Could not allocate memory for pad buffer.\n" );
+ } else {
+ pad_allocated = 1;
+ memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE );
}
+ }
+ printk("TLAN %d.%d: %s irq=%2d io=%04x, %s\n",TLanVersionMajor,
+ TLanVersionMinor,
+ dev->name,
+ (int) irq,
+ io_base,
+ TLanDeviceList[dl_ix].deviceName );
+ TLan_Init( dev );
+ }
+
+ return ( ( found ) ? 0 : -ENODEV );
+
+} /* tlan_probe */
- } /* TLan_MiiSendData */
+#endif /* MODULE */
- /*************************************************************************
- * TLan_MiiSync
- *
- * Returns: Nothing
- * Parms: base_port The base IO port of the adapter in question.
+
+ /***************************************************************
+ * TLan_PciProbe
*
- * This functions syncs all PHYs in terms of the MII configuration bus.
+ * Returns:
+ * 1 if another TLAN card was found, 0 if not.
+ * Parms:
+ * pci_bus The PCI bus the card was found
+ * on.
+ * pci_dfn The PCI whatever the card was
+ * found at.
+ * pci_irq The IRQ of the found adapter.
+ * pci_rev The revision of the adapter.
+ * pci_io_base The first IO port used by the
+ * adapter.
+ * dl_ix The index in the device list
+ * of the adapter.
+ *
+ * This function searches for an adapter with PCI vendor
+ * and device IDs matching those in the TLanDeviceList.
+ * The function 'remembers' the last device it found,
+ * and so finds a new device (if anymore are to be found)
+ * each time the function is called. It then looks up
+ * pertinent PCI info and returns it to the caller.
*
- ************************************************************************/
+ **************************************************************/
+
+int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_
+io_base, u32 *dl_ix )
+{
+ static int dl_index = 0;
+ static int pci_index = 0;
+
+ int not_found;
+ u8 pci_latency;
+ u16 pci_command;
+ int reg;
+
+
+ if ( ! pcibios_present() ) {
+ printk( "TLAN: PCI Bios not present.\n" );
+ return 0;
+ }
+
+ for (; TLanDeviceList[dl_index].vendorId != 0; dl_index++) {
+
+ not_found = pcibios_find_device(
+ TLanDeviceList[dl_index].vendorId,
+ TLanDeviceList[dl_index].deviceId,
+ pci_index,
+ pci_bus,
+ pci_dfn
+ );
+
+ if ( ! not_found ) {
+
+ TLAN_DBG(
+ TLAN_DEBUG_GNRL,
+ "TLAN: found: Vendor Id = 0x%hx, Device Id = 0x%hx\n",
+ TLanDeviceList[dl_index].vendorId,
+ TLanDeviceList[dl_index].deviceId
+ );
+
+ pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_REVISION_ID, pci_rev);
+ pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_INTERRUPT_LINE, pci_irq);
+ pcibios_read_config_word ( *pci_bus, *pci_dfn, PCI_COMMAND, &pci_command);
+ pcibios_read_config_dword( *pci_bus, *pci_dfn, PCI_BASE_ADDRESS_0, pci_io_ba
+se);
+ pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, &pci_laten
+cy);
+
+ if (pci_latency < 0x10) {
+ pcibios_write_config_byte( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, 0xff);
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Setting latency timer to max.\n");
+ }
- void TLan_MiiSync( u16 base_port )
- {
- int i;
- u16 sio;
+ for ( reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg +=4 ) {
+ pcibios_read_config_dword( *pci_bus, *pci_dfn, reg, pci_io_base);
+ if ((pci_command & PCI_COMMAND_IO) && (*pci_io_base & 0x3)) {
+ *pci_io_base &= PCI_BASE_ADDRESS_IO_MASK;
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: IO mapping is available at %x.\n", *pc
+i_io_base);
+ break;
+ } else {
+ *pci_io_base = 0;
+ }
+ }
- outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR );
- sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+ if ( *pci_io_base == 0 )
+ printk("TLAN: IO mapping not available, ignoring device.\n");
- TLan_ClearBit( TLAN_NET_SIO_MTXEN, sio );
- for ( i = 0; i < 32; i++ ) {
- TLan_ClearBit( TLAN_NET_SIO_MCLK, sio );
- TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
+ if (pci_command & PCI_COMMAND_MASTER) {
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Bus mastering is active.\n");
}
- } /* TLan_MiiSync */
+ pci_index++;
+ if ( *pci_io_base ) {
+ *dl_ix = dl_index;
+ return 1;
+ }
+ } else {
+ pci_index = 0;
+ }
+ }
+ return 0;
- /*************************************************************************
- * TLan_MiiWriteReg
- *
- * Returns: Nothing
- * Parms: base_port The base IO port of the adapter in question.
- * dev The address of the PHY to be written to.
- * reg The register whose contents are to be
- * written.
- * val The value to be written to the register.
- *
- * This function uses the TLAN's MII bus to write the contents of a
- * given register on a PHY. It sends the appropriate info and then
- * writes the 16-bit register value from the MII configuration bus
- * via the TLAN SIO register.
- *
- ************************************************************************/
+} /* TLan_PciProbe */
- void TLan_MiiWriteReg(u16 base_port, u16 dev, u16 reg, u16 val)
- {
- u16 sio;
- int minten;
- outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
- sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
- cli();
- TLan_MiiSync( base_port );
+ /***************************************************************
+ * TLan_Init
+ *
+ * Returns:
+ * 0 on success, error code otherwise.
+ * Parms:
+ * dev The structure of the device to be
+ * init'ed.
+ *
+ * This function completes the initialization of the
+ * device structure and driver. It reserves the IO
+ * addresses, allocates memory for the lists and bounce
+ * buffers, retrieves the MAC address from the eeprom
+ * and assignes the device's methods.
+ *
+ **************************************************************/
+
+int TLan_Init( struct device *dev )
+{
+ int dma_size;
+ int err;
+ int i;
+ TLanPrivateInfo *priv;
+
+ priv = (TLanPrivateInfo *) dev->priv;
+
+ err = check_region( dev->base_addr, 0x10 );
+ if ( err ) {
+ printk( "TLAN: %s: Io port region 0x%lx size 0x%x in use.\n",
+ dev->name,
+ dev->base_addr,
+ 0x10 );
+ return -EIO;
+ }
+ request_region( dev->base_addr, 0x10, TLanSignature );
+
+ dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
+ * ( sizeof(TLanList) + TLAN_MAX_FRAME_SIZE );
+ priv->dmaStorage = kmalloc( dma_size, GFP_KERNEL | GFP_DMA );
+ if ( priv->dmaStorage == NULL ) {
+ printk( "TLAN: Could not allocate lists and buffers for %s.\n",
+ dev->name );
+ return -ENOMEM;
+ }
+ memset( priv->dmaStorage, 0, dma_size );
+ priv->rxList = (TLanList *)
+ ( ( ( (u32) priv->dmaStorage ) + 7 ) & 0xFFFFFFF8 );
+ priv->txList = priv->rxList + TLAN_NUM_RX_LISTS;
+ priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS );
+ priv->txBuffer = priv->rxBuffer
+ + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE );
+
+ err = 0;
+ for ( i = 0; i < 6 ; i++ )
+ err |= TLan_EeReadByte( dev->base_addr,
+ (u8) 0x83 + i,
+ (u8 *) &dev->dev_addr[i] );
+ if ( err )
+ printk( "TLAN: %s: Error reading MAC from eeprom: %d\n",
+ dev->name,
+ err );
+ dev->addr_len = 6;
+
+ dev->open = &TLan_Open;
+ dev->hard_start_xmit = &TLan_StartTx;
+ dev->stop = &TLan_Close;
+ dev->get_stats = &TLan_GetStats;
+ dev->set_multicast_list = &TLan_SetMulticastList;
+
+#ifndef MODULE
+
+ aui = dev->mem_start & 0x01;
+ debug = dev->mem_end;
- minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio );
- if ( minten )
- TLan_ClearBit( TLAN_NET_SIO_MINTEN, sio );
+#endif /* MODULE */
- TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */
- TLan_MiiSendData( base_port, 0x1, 2 ); /* Write ( 01b ) */
- TLan_MiiSendData( base_port, dev, 5 ); /* Device # */
- TLan_MiiSendData( base_port, reg, 5 ); /* Register # */
+ return 0;
- TLan_MiiSendData( base_port, 0x2, 2 ); /* Send ACK */
- TLan_MiiSendData( base_port, val, 16 ); /* Send Data */
+} /* TLan_Init */
- TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); /* Idle cycle */
- TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
- if ( minten )
- TLan_SetBit( TLAN_NET_SIO_MINTEN, sio );
- sti();
- } /* TLan_MiiWriteReg */
+ /***************************************************************
+ * TLan_Open
+ *
+ * Returns:
+ * 0 on success, error code otherwise.
+ * Parms:
+ * dev Structure of device to be opened.
+ *
+ * This routine puts the driver and TLAN adapter in a
+ * state where it is ready to send and receive packets.
+ * It allocates the IRQ, resets and brings the adapter
+ * out of reset, and allows interrupts. It also delays
+ * the startup for autonegotiation or sends a Rx GO
+ * command to the adapter, as appropriate.
+ *
+ **************************************************************/
+
+int TLan_Open( struct device *dev )
+{
+ int err;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+ priv->tlanRev = TLan_DioRead8( dev->base_addr, TLAN_DEF_REVISION );
+ err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev
+ );
+ if ( err ) {
+ printk( "TLAN: Cannot open %s because IRQ %d is already in use.\n", dev->name
+, dev->irq );
+ return -EAGAIN;
+ }
+
+ MOD_INC_USE_COUNT;
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ /* NOTE: It might not be necessary to read the stats before a
+ reset if you don't care what the values are.
+ */
+ TLan_ResetLists( dev );
+ TLan_ReadAndClearStats( dev, TLAN_IGNORE );
+ TLan_Reset( dev );
+ TLan_Reset( dev );
+ TLan_SetMac( dev, 0, dev->dev_addr );
+ outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
+ if ( debug >= 1 )
+ outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
+
+ init_timer( &priv->timer );
+ priv->timer.data = (unsigned long) dev;
+ priv->timer.function = &TLan_Timer;
+ if ( priv->phyFlags & TLAN_PHY_AUTONEG ) {
+ priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY;
+ priv->timerSetAt = jiffies;
+ priv->timerType = TLAN_TIMER_LINK;
+ add_timer( &priv->timer );
+ } else {
+ outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
+ outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
+ }
+
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s opened. Revision = %x\n", dev->na
+me, priv->tlanRev );
+
+ return 0;
+
+} /* TLan_Open */
+
+
+
+
+ /***************************************************************
+ * TLan_StartTx
+ *
+ * Returns:
+ * 0 on success, non-zero on failure.
+ * Parms:
+ * skb A pointer to the sk_buff containing the
+ * frame to be sent.
+ * dev The device to send the data on.
+ *
+ * This function adds a frame to the Tx list to be sent
+ * ASAP. First it verifies that the adapter is ready and
+ * there is room in the queue. Then it sets up the next
+ * available list, copies the frame to the corresponding
+ * buffer. If the adapter Tx channel is idle, it gives
+ * the adapter a Tx Go command on the list, otherwise it
+ * sets the forward address of the previous list to point
+ * to this one. Then it frees the sk_buff.
+ *
+ **************************************************************/
+
+int TLan_StartTx( struct sk_buff *skb, struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ TLanList *tail_list;
+ u8 *tail_buffer;
+ int pad;
+
+ if ( ! priv->phyOnline ) {
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s PHY is not ready\n", dev->name );
+ dev_kfree_skb( skb, FREE_WRITE );
+ return 0;
+ }
+
+ tail_list = priv->txList + priv->txTail;
+ if ( tail_list->cStat != TLAN_CSTAT_UNUSED ) {
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s is busy (Head=%d Tail=%d)\n", dev
+->name, priv->txHead, priv->txTail );
+ dev->tbusy = 1;
+ priv->txBusyCount++;
+ return 1;
+ }
+ tail_list->forward = 0;
+ tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE );
+ memcpy( tail_buffer, skb->data, skb->len );
+ pad = TLAN_MIN_FRAME_SIZE - skb->len;
+ if ( pad > 0 ) {
+ tail_list->frameSize = (u16) skb->len + pad;
+ tail_list->buffer[0].count = (u32) skb->len;
+ tail_list->buffer[1].count = TLAN_LAST_BUFFER | (u32) pad;
+ tail_list->buffer[1].address = virt_to_bus( TLanPadBuffer );
+ } else {
+ tail_list->frameSize = (u16) skb->len;
+ tail_list->buffer[0].count = TLAN_LAST_BUFFER | (u32) skb->len;
+ tail_list->buffer[1].count = 0;
+ tail_list->buffer[1].address = 0;
+ }
+ // are we transferring?
+ cli();
+ tail_list->cStat = TLAN_CSTAT_READY;
+ if ( ! priv->txInProgress ) {
+ priv->txInProgress = 1;
+ outw( 0x4, dev->base_addr + TLAN_HOST_INT );
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Starting TX on buffer %d\n", priv->t
+xTail );
+ outl( virt_to_bus( tail_list ), dev->base_addr + TLAN_CH_PARM );
+ outl( TLAN_HC_GO | TLAN_HC_ACK, dev->base_addr + TLAN_HOST_CMD );
+ } else {
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Adding buffer %d to TX channel\n", p
+riv->txTail );
+ if ( priv->txTail == 0 )
+ ( priv->txList + ( TLAN_NUM_TX_LISTS - 1 ) )->forward = virt_to_bus( tail_lis
+t );
+ else
+ ( priv->txList + ( priv->txTail - 1 ) )->forward = virt_to_bus( tail_list );
+ }
+ sti();
+ priv->txTail++;
+ if ( priv->txTail >= TLAN_NUM_TX_LISTS )
+ priv->txTail = 0;
+
+ dev_kfree_skb( skb, FREE_WRITE );
+
+ dev->trans_start = jiffies;
+ return 0;
+
+} /* TLan_StartTx */
+
+
+
+
+ /***************************************************************
+ * TLan_HandleInterrupt
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * irq The line on which the interrupt
+ * occurred.
+ * dev_id A pointer to the device assigned to
+ * this irq line.
+ * regs ???
+ *
+ * This function handles an interrupt generated by its
+ * assigned TLAN adapter. The function deactivates
+ * interrupts on its adapter, records the type of
+ * interrupt, executes the appropriate subhandler, and
+ * acknowdges the interrupt to the adapter (thus
+ * re-enabling adapter interrupts.
+ *
+ **************************************************************/
+void TLan_HandleInterrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ u32 ack;
+ struct device *dev;
+ u32 host_cmd;
+ u16 host_int;
+ int type;
+ dev = (struct device *) dev_id;
+ if ( dev->interrupt )
+ printk( "TLAN: Re-entering interrupt handler for %s: %d.\n" , dev->name, dev
+->interrupt );
+ dev->interrupt++;
-/*****************************************************************************
-******************************************************************************
+ cli();
- ThunderLAN Driver PHY Layer Routines
+ host_int = inw( dev->base_addr + TLAN_HOST_INT );
+ outw( host_int, dev->base_addr + TLAN_HOST_INT ); // Deactivate Ints
- The TLAN chip can drive any number of PHYs (physical devices). Rather
- than having lots of 'if' or '#ifdef' statements, I have created a
- second driver layer for the PHYs. Each PHY can be identified from its
- id in registers 2 and 3, and can be given a Check and Service routine
- that will be called when the adapter is reset and when the adapter
- receives a Network Status interrupt, respectively.
-
-******************************************************************************
-*****************************************************************************/
+ type = ( host_int & TLAN_HI_IT_MASK ) >> 2;
- static int TLan_PhyNop( struct device * );
- static void TLan_PhyPrint( struct device * );
- static void TLan_PhySelect( struct device * );
- static int TLan_PhyInternalCheck( struct device * );
- static int TLan_PhyInternalService( struct device * );
- static int TLan_PhyDp83840aCheck( struct device * );
+ ack = TLanIntVector[type]( dev, host_int );
+ sti();
+ if ( ack ) {
+ host_cmd = TLAN_HC_ACK | ack | ( type << 18 );
+ outl( host_cmd, dev->base_addr + TLAN_HOST_CMD );
+ }
+ dev->interrupt--;
- static TLanPhyIdEntry TLanPhyIdTable[] = {
- { 0x4000, 0x5014, &TLan_PhyInternalCheck, &TLan_PhyInternalService, TLAN_PHY_ACTIVITY },
- { 0x4000, 0x5015, &TLan_PhyInternalCheck, &TLan_PhyInternalService, TLAN_PHY_ACTIVITY },
- { 0x2000, 0x5C01, &TLan_PhyDp83840aCheck, &TLan_PhyNop, TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG },
- { 0x0000, 0x0000, NULL, NULL, 0 }
- };
+} /* TLan_HandleInterrupts */
- /*************************************************************************
- * TLan_PhyPrint
+ /***************************************************************
+ * TLan_Close
+ *
+ * Returns:
+ * An error code.
+ * Parms:
+ * dev The device structure of the device to
+ * close.
*
- * Returns: Nothing
- * Parms: dev A pointer to the device structure of the adapter
- * which the desired PHY is located.
- *
- * This function prints the registers a PHY.
- *
- ************************************************************************/
-
- void TLan_PhyPrint( struct device *dev )
- {
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- u16 i, data0, data1, data2, data3, phy;
- u32 io;
-
- phy = priv->phyAddr;
- io = dev->base_addr;
+ * This function shuts down the adapter. It records any
+ * stats, puts the adapter into reset state, deactivates
+ * its time as needed, and frees the irq it is using.
+ *
+ **************************************************************/
- if ( ( phy > 0 ) || ( phy <= TLAN_PHY_MAX_ADDR ) ) {
- printk( "TLAN: Device %s, PHY 0x%02x.\n", dev->name, phy );
- printk( "TLAN: Off. +0 +1 +2 +3 \n" );
- for ( i = 0; i < 0x20; i+= 4 ) {
- TLan_MiiReadReg( io, phy, i, &data0 );
- TLan_MiiReadReg( io, phy, i + 1, &data1 );
- TLan_MiiReadReg( io, phy, i + 2, &data2 );
- TLan_MiiReadReg( io, phy, i + 3, &data3 );
- printk( "TLAN: 0x%02x 0x%04hx 0x%04hx 0x%04hx 0x%04hx\n", i, data0, data1, data2, data3 );
- }
- } else {
- printk( "TLAN: Device %s, PHY 0x%02x (Invalid).\n", dev->name, phy );
- }
-
- } /* TLan_PhyPrint */
-
-
-
-
- /*************************************************************************
- * TLan_PhySelect
- *
- * Returns: Nothing
- * Parms: dev A pointer to the device structure of the adapter
- * for which the PHY needs determined.
- *
- * This function decides which PHY amoung those attached to the TLAN chip
- * is to be used. The TLAN chip can be attached to multiple PHYs, and
- * the driver needs to decide which one to talk to. Currently this
- * routine picks the PHY with the lowest address as the internal PHY
- * address is 0x1F, the highest possible. This strategy assumes that
- * there can be only one other PHY, and, if it exists, it is the one to
- * be used. If token ring PHYs are ever supported, this routine will
- * become a little more interesting...
- *
- ************************************************************************/
-
- void TLan_PhySelect( struct device *dev )
- {
- int err;
- int phy;
- int entry;
- u16 id_hi;
- u16 id_lo;
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- u16 val;
-
- priv->phyCheck = &TLan_PhyNop; // Make absolutely sure these aren't NULL
- priv->phyService = &TLan_PhyNop;
-
- for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) {
- err = TLan_MiiReadReg( dev->base_addr, phy, 0, &val );
- if ( ! err ) {
- TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_HI, &id_hi );
- TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_LO, &id_lo );
- entry = 0;
- while ( ( TLanPhyIdTable[entry].idHi != 0 ) && ( TLanPhyIdTable[entry].idLo != 0 ) ) {
- if ( ( TLanPhyIdTable[entry].idHi == id_hi ) && ( TLanPhyIdTable[entry].idLo == id_lo ) ) {
- priv->phyAddr = phy;
- priv->phyEntry = entry;
- priv->phyCheck = TLanPhyIdTable[entry].check;
- priv->phyService = TLanPhyIdTable[entry].service;
- priv->phyFlags = TLanPhyIdTable[entry].flags;
- break;
- }
- entry++;
- }
- break;
- }
- }
+int TLan_Close(struct device *dev)
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- } /* TLan_PhySelect */
+ dev->start = 0;
+ dev->tbusy = 1;
+ TLan_ReadAndClearStats( dev, TLAN_RECORD );
+ outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
+ if ( priv->timerSetAt != 0 )
+ del_timer( &priv->timer );
+ free_irq( dev->irq, dev );
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s closed.\n", dev->name );
+ MOD_DEC_USE_COUNT;
+ return 0;
- /*************************************************************************
- * TLan_PhyNop
- *
- * Returns: Nothing
- * Parms: dev A pointer to a device structure.
- *
- * This function does nothing and is meant as a stand-in for when
- * a Check or Service function would be meaningless.
- *
- ************************************************************************/
+} /* TLan_Close */
- int TLan_PhyNop( struct device *dev )
- {
- dev = NULL;
- return 0;
- } /* TLan_PhyNop */
+ /***************************************************************
+ * TLan_GetStats
+ *
+ * Returns:
+ * A pointer to the device's statistics structure.
+ * Parms:
+ * dev The device structure to return the
+ * stats for.
+ *
+ * This function updates the devices statistics by reading
+ * the TLAN chip's onboard registers. Then it returns the
+ * address of the statistics structure.
+ *
+ **************************************************************/
+
+struct net_device_stats *TLan_GetStats( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ int i;
+
+ /* Should only read stats if open ? */
+ TLan_ReadAndClearStats( dev, TLAN_RECORD );
+
+ TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: %s EOC count = %d\n", dev->name, priv-
+>rxEocCount );
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s Busy count = %d\n", dev->name, pri
+v->txBusyCount );
+ if ( debug & TLAN_DEBUG_GNRL ) {
+ TLan_PrintDio( dev->base_addr );
+ TLan_PhyPrint( dev );
+ }
+ if ( debug & TLAN_DEBUG_LIST ) {
+ for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ )
+ TLan_PrintList( priv->rxList + i, "RX", i );
+ for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ )
+ TLan_PrintList( priv->txList + i, "TX", i );
+ }
+
+ return ( &( (TLanPrivateInfo *) dev->priv )->stats );
+} /* TLan_GetStats */
- /*************************************************************************
- * TLan_PhyInternalCheck
- *
- * Returns: Nothing
- * Parms: dev A pointer to a device structure of the adapter
- * holding the PHY to be checked.
- *
- * This function resets the internal PHY on a TLAN chip. See Chap. 7,
- * "Physical Interface (PHY)" of "ThunderLAN Programmer's Guide"
- *
- ************************************************************************/
- int TLan_PhyInternalCheck( struct device *dev )
- {
- u16 gen_ctl;
- u32 io;
- u16 phy;
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- u16 value;
- u8 sio;
- io = dev->base_addr;
- phy = priv->phyAddr;
- TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl );
- if ( gen_ctl & MII_GC_PDOWN ) {
- TLan_MiiSync( io );
- TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE );
- TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK );
- udelay(50000);
- TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK );
- TLan_MiiSync( io );
+ /***************************************************************
+ * TLan_SetMulticastList
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev The device structure to set the
+ * multicast list for.
+ *
+ * This function sets the TLAN adaptor to various receive
+ * modes. If the IFF_PROMISC flag is set, promiscuous
+ * mode is acitviated. Otherwise, promiscuous mode is
+ * turned off. If the IFF_ALLMULTI flag is set, then
+ * the hash table is set to receive all group addresses.
+ * Otherwise, the first three multicast addresses are
+ * stored in AREG_1-3, and the rest are selected via the
+ * hash table, as necessary.
+ *
+ **************************************************************/
+
+void TLan_SetMulticastList( struct device *dev )
+{
+ struct dev_mc_list *dmi = dev->mc_list;
+ u32 hash1 = 0;
+ u32 hash2 = 0;
+ int i;
+ u32 offset;
+ u8 tmp;
+
+ if ( dev->flags & IFF_PROMISC ) {
+ tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD );
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp | TLAN_NET_CMD_CAF );
+ } else {
+ tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD );
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp & ~TLAN_NET_CMD_CAF );
+ if ( dev->flags & IFF_ALLMULTI ) {
+ for ( i = 0; i < 3; i++ )
+ TLan_SetMac( dev, i + 1, NULL );
+ TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, 0xFFFFFFFF );
+ TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, 0xFFFFFFFF );
+ } else {
+ for ( i = 0; i < dev->mc_count; i++ ) {
+ if ( i < 3 ) {
+ TLan_SetMac( dev, i + 1, (char *) &dmi->dmi_addr );
+ } else {
+ offset = TLan_HashFunc( (u8 *) &dmi->dmi_addr );
+ if ( offset < 32 )
+ hash1 |= ( 1 << offset );
+ else
+ hash2 |= ( 1 << ( offset - 32 ) );
+ }
+ dmi = dmi->next;
}
+ for ( ; i < 3; i++ )
+ TLan_SetMac( dev, i + 1, NULL );
+ TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, hash1 );
+ TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, hash2 );
+ }
+ }
- TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
- while ( value & MII_GC_RESET )
- TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
-
- // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX );
- // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX );
- TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 );
+} /* TLan_SetMulticastList */
- udelay(500000);
- TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
- if ( aui )
- value |= TLAN_TC_AUISEL;
- else
- value &= ~TLAN_TC_AUISEL;
- TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
-
- // Read Possible Latched Link Status
- TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
- // Read Real Link Status
- TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
- if ( ( value & MII_GS_LINK ) || aui ) {
- priv->phyOnline = 1;
- TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
- } else {
- priv->phyOnline = 0;
- TLan_DioWrite8( io, TLAN_LED_REG, 0 );
- }
- // Enable Interrupts
- TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
- value |= TLAN_TC_INTEN;
- TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
-
- sio = TLan_DioRead8( io, TLAN_NET_SIO );
- sio |= TLAN_NET_SIO_MINTEN;
- TLan_DioWrite8( io, TLAN_NET_SIO, sio );
-
- return 0;
+/*****************************************************************************
+******************************************************************************
- } /* TLanPhyInternalCheck */
+ ThunderLAN Driver Interrupt Vectors and Table
+ Please see Chap. 4, "Interrupt Handling" of the "ThunderLAN
+ Programmer's Guide" for more informations on handling interrupts
+ generated by TLAN based adapters.
+******************************************************************************
+*****************************************************************************/
- /*************************************************************************
- * TLan_PhyInternalService
- *
- * Returns: Nothing
- * Parms: dev A pointer to a device structure of the adapter
- * holding the PHY to be serviced.
+ /***************************************************************
+ * TLan_HandleInvalid
*
- * This function services an interrupt generated by the internal PHY.
- * It can turn on/off the link LED. See Chap. 7,
- * "Physical Interface (PHY)" of "ThunderLAN Programmer's Guide".
- *
- ************************************************************************/
-
- int TLan_PhyInternalService( struct device *dev )
- {
- u16 tlphy_sts;
- u16 gen_sts;
- u16 an_exp;
- u32 io;
- u16 phy;
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ * Returns:
+ * 0
+ * Parms:
+ * dev Device assigned the IRQ that was
+ * raised.
+ * host_int The contents of the HOST_INT
+ * port.
+ *
+ * This function handles invalid interrupts. This should
+ * never happen unless some other adapter is trying to use
+ * the IRQ line assigned to the device.
+ *
+ **************************************************************/
- io = dev->base_addr;
- phy = priv->phyAddr;
+u32 TLan_HandleInvalid( struct device *dev, u16 host_int )
+{
+ host_int = 0;
+ // printk( "TLAN: Invalid interrupt on %s.\n", dev->name );
+ return 0;
- TLan_MiiReadReg( io, phy, TLAN_TLPHY_STS, &tlphy_sts );
- TLan_MiiReadReg( io, phy, MII_GEN_STS, &gen_sts );
- TLan_MiiReadReg( io, phy, MII_AN_EXP, &an_exp );
- if ( ( gen_sts & MII_GS_LINK ) || aui ) {
- priv->phyOnline = 1;
- TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
- } else {
- priv->phyOnline = 0;
- TLan_DioWrite8( io, TLAN_LED_REG, 0 );
- }
+} /* TLan_HandleInvalid */
- return 0;
- } /* TLan_PhyInternalService */
+ /***************************************************************
+ * TLan_HandleTxEOF
+ *
+ * Returns:
+ * 1
+ * Parms:
+ * dev Device assigned the IRQ that was
+ * raised.
+ * host_int The contents of the HOST_INT
+ * port.
+ *
+ * This function handles Tx EOF interrupts which are raised
+ * by the adapter when it has completed sending the
+ * contents of a buffer. If detemines which list/buffer
+ * was completed and resets it. If the buffer was the last
+ * in the channel (EOC), then the function checks to see if
+ * another buffer is ready to send, and if so, sends a Tx
+ * Go command. Finally, the driver activates/continues the
+ * activity LED.
+ *
+ **************************************************************/
+
+u32 TLan_HandleTxEOF( struct device *dev, u16 host_int )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ int eoc = 0;
+ TLanList *head_list;
+ u32 ack = 1;
+
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOF (Head=%d Tail=%d)\n",
+ priv->txHead, priv->txTail );
+ host_int = 0;
+ head_list = priv->txList + priv->txHead;
+ if ( head_list->cStat & TLAN_CSTAT_EOC )
+ eoc = 1;
+ if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) {
+ printk( "TLAN: Received interrupt for uncompleted TX frame.\n" );
+ }
+ // printk( "Ack %d CSTAT=%hx\n", priv->txHead, head_list->cStat );
+#if LINUX_KERNEL_VERSION > 0x20100
+ priv->stats->tx_bytes += head_list->frameSize;
+#endif
- /*************************************************************************
- * TLan_PhyDp83840aCheck
- *
- * Returns: Nothing
- * Parms: dev A pointer to a device structure of the adapter
- * holding the PHY to be reset.
+ head_list->cStat = TLAN_CSTAT_UNUSED;
+ dev->tbusy = 0;
+ priv->txHead++;
+ if ( priv->txHead >= TLAN_NUM_TX_LISTS )
+ priv->txHead = 0;
+ if ( eoc ) {
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOC (Head=%d Tail=%d)\n"
+, priv->txHead, priv->txTail );
+ head_list = priv->txList + priv->txHead;
+ if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) {
+ outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
+ ack |= TLAN_HC_GO;
+ } else {
+ priv->txInProgress = 0;
+ }
+ }
+ TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
+ if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) {
+ if ( priv->timerSetAt == 0 ) {
+ // printk("TxEOF Starting timer...\n");
+ priv->timerSetAt = jiffies;
+ priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY;
+ priv->timerType = TLAN_TIMER_ACT;
+ add_timer( &priv->timer );
+ } else if ( priv->timerType == TLAN_TIMER_ACT ) {
+ priv->timerSetAt = jiffies;
+ // printk("TxEOF continuing timer...\n");
+ }
+ }
+
+ return ack;
+
+} /* TLan_HandleTxEOF */
+
+
+
+
+ /***************************************************************
+ * TLan_HandleStatOverflow
*
- * This function resets a National Semiconductor DP83840A 10/100 Mb/s
- * PHY device. See National Semiconductor's data sheet for more info.
- * This PHY is used on Compaq Netelligent 10/100 cards.
- *
- ************************************************************************/
+ * Returns:
+ * 1
+ * Parms:
+ * dev Device assigned the IRQ that was
+ * raised.
+ * host_int The contents of the HOST_INT
+ * port.
+ *
+ * This function handles the Statistics Overflow interrupt
+ * which means that one or more of the TLAN statistics
+ * registers has reached 1/2 capacity and needs to be read.
+ *
+ **************************************************************/
- static int TLan_PhyDp83840aCheck( struct device *dev )
- {
- u16 gen_ctl;
- int i;
- u32 io;
- u16 phy;
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- u16 value;
- u8 sio;
+u32 TLan_HandleStatOverflow( struct device *dev, u16 host_int )
+{
+ host_int = 0;
+ TLan_ReadAndClearStats( dev, TLAN_RECORD );
- io = dev->base_addr;
- phy = priv->phyAddr;
+ return 1;
- TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl );
- if ( gen_ctl & MII_GC_PDOWN ) {
- TLan_MiiSync( io );
- TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE );
- TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK );
- for ( i = 0; i < 500000; i++ )
- SLOW_DOWN_IO;
- TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK );
- TLan_MiiSync( io );
- }
+} /* TLan_HandleStatOverflow */
- TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
- while ( value & MII_GC_RESET )
- TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
- // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX );
- // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX );
- TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 );
- TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1000 );
- TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1200 );
- for ( i = 0; i < 50000; i++ )
- SLOW_DOWN_IO;
-/*
- // Read Possible Latched Link Status
- TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
- // Read Real Link Status
- TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
- if ( value & MII_GS_LINK ) {
- priv->phyOnline = 1;
- TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
- } else {
- priv->phyOnline = 0;
- TLan_DioWrite8( io, TLAN_LED_REG, 0 );
- }
+ /***************************************************************
+ * TLan_HandleRxEOF
+ *
+ * Returns:
+ * 1
+ * Parms:
+ * dev Device assigned the IRQ that was
+ * raised.
+ * host_int The contents of the HOST_INT
+ * port.
+ *
+ * This function handles the Rx EOF interrupt which
+ * indicates a frame has been received by the adapter from
+ * the net and the frame has been transferred to memory.
+ * The function determines the bounce buffer the frame has
+ * been loaded into, creates a new sk_buff big enough to
+ * hold the frame, and sends it to protocol stack. It
+ * then resets the used buffer and appends it to the end
+ * of the list. If the frame was the last in the Rx
+ * channel (EOC), the function restarts the receive channel
+ * by sending an Rx Go command to the adapter. Then it
+ * activates/continues the the activity LED.
+ *
+ **************************************************************/
+
+u32 TLan_HandleRxEOF( struct device *dev, u16 host_int )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u32 ack = 1;
+ int eoc = 0;
+ u8 *head_buffer;
+ TLanList *head_list;
+ struct sk_buff *skb;
+ TLanList *tail_list;
+ void *t;
+
+ TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOF (Head=%d Tail=%d)\n",
+priv->rxHead, priv->rxTail );
+ host_int = 0;
+ head_list = priv->rxList + priv->rxHead;
+ tail_list = priv->rxList + priv->rxTail;
+ if ( head_list->cStat & TLAN_CSTAT_EOC )
+ eoc = 1;
+ if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) {
+ printk( "TLAN: Received interrupt for uncompleted RX frame.\n" );
+ } else {
+ skb = dev_alloc_skb( head_list->frameSize + 7 );
+ if ( skb == NULL ) {
+ printk( "TLAN: Couldn't allocate memory for received data.\n" );
+ } else {
+ head_buffer = priv->rxBuffer + ( priv->rxHead * TLAN_MAX_FRAME_SIZE );
+ skb->dev = dev;
+ skb_reserve( skb, 2 );
+ t = (void *) skb_put( skb, head_list->frameSize );
+ // printk( " %hd %p %p\n", head_list->frameSize, skb->data, t );
- // Enable Interrupts
- TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
- value |= TLAN_TC_INTEN;
- TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
-*/
- sio = TLan_DioRead8( dev->base_addr, TLAN_NET_SIO );
- sio &= ~TLAN_NET_SIO_MINTEN;
- TLan_DioWrite8( dev->base_addr, TLAN_NET_SIO, sio );
- priv->phyOnline = 1;
-
- return 0;
+#if LINUX_KERNEL_VERSION > 0x20100
+ priv->stats->rx_bytes += head_list->frameSize;
+#endif
- } /* TLan_PhyDp83840aCheck */
+ memcpy( t, head_buffer, head_list->frameSize );
+ skb->protocol = eth_type_trans( skb, dev );
+ netif_rx( skb );
+ }
+ }
+ head_list->forward = 0;
+ head_list->frameSize = TLAN_MAX_FRAME_SIZE;
+ head_list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
+ tail_list->forward = virt_to_bus( head_list );
+ priv->rxHead++;
+ if ( priv->rxHead >= TLAN_NUM_RX_LISTS )
+ priv->rxHead = 0;
+ priv->rxTail++;
+ if ( priv->rxTail >= TLAN_NUM_RX_LISTS )
+ priv->rxTail = 0;
+ if ( eoc ) {
+ TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOC (Head=%d Tail=%d)\n",
+ priv->rxHead, priv->rxTail );
+ head_list = priv->rxList + priv->rxHead;
+ outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
+ ack |= TLAN_HC_GO | TLAN_HC_RT;
+ priv->rxEocCount++;
+ }
+ TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
+ if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) {
+ if ( priv->timerSetAt == 0 ) {
+ // printk("RxEOF Starting timer...\n");
+ priv->timerSetAt = jiffies;
+ priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY;
+ priv->timerType = TLAN_TIMER_ACT;
+ add_timer( &priv->timer );
+ } else if ( priv->timerType == TLAN_TIMER_ACT ) {
+ // printk("RxEOF tarting continuing timer...\n");
+ priv->timerSetAt = jiffies;
+ }
+ }
+ dev->last_rx = jiffies;
+
+ return ack;
+
+} /* TLan_HandleRxEOF */
+
+
+
+
+ /***************************************************************
+ * TLan_HandleDummy
+ *
+ * Returns:
+ * 1
+ * Parms:
+ * dev Device assigned the IRQ that was
+ * raised.
+ * host_int The contents of the HOST_INT
+ * port.
+ *
+ * This function handles the Dummy interrupt, which is
+ * raised whenever a test interrupt is generated by setting
+ * the Req_Int bit of HOST_CMD to 1.
+ *
+ **************************************************************/
+u32 TLan_HandleDummy( struct device *dev, u16 host_int )
+{
+ host_int = 0;
+ printk( "TLAN: Dummy interrupt on %s.\n", dev->name );
+ return 1;
+} /* TLan_HandleDummy */
-/*****************************************************************************
-******************************************************************************
- ThunderLAN Driver Adapter Related Routines
-******************************************************************************
-*****************************************************************************/
+ /***************************************************************
+ * TLan_HandleTxEOC
+ *
+ * Returns:
+ * 1
+ * Parms:
+ * dev Device assigned the IRQ that was
+ * raised.
+ * host_int The contents of the HOST_INT
+ * port.
+ *
+ * This driver is structured to determine EOC occurances by
+ * reading the CSTAT member of the list structure. Tx EOC
+ * interrupts are disabled via the DIO INTDIS register.
+ * However, TLAN chips before revision 3.0 didn't have this
+ * functionality, so process EOC events if this is the
+ * case.
+ *
+ **************************************************************/
+u32 TLan_HandleTxEOC( struct device *dev, u16 host_int )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ TLanList *head_list;
+ u32 ack = 1;
+ host_int = 0;
+ if ( priv->tlanRev < 0x30 ) {
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOC (Head=%d Tail=%d) --
+ IRQ\n", priv->txHead, priv->txTail );
+ head_list = priv->txList + priv->txHead;
+ if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) {
+ outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
+ ack |= TLAN_HC_GO;
+ } else {
+ priv->txInProgress = 0;
+ }
+ }
- static void TLan_ResetLists( struct device * );
- static void TLan_PrintDio( u16 );
- static void TLan_PrintList( TLanList *, char *, int );
- static void TLan_ReadAndClearStats( struct device *, int );
- static int TLan_Reset( struct device * );
- static void TLan_SetMac( struct device *, int areg, char *mac );
+ return ack;
+} /* TLan_HandleTxEOC */
- /*************************************************************************
- * TLan_ResetLists
- *
- * Returns: Nothing
- * Parms: dev The device structure with the list stuctures to
- * be reset.
- *
- * This routine sets the variables associated with managing the TLAN
- * lists to their initial values.
- *
- ************************************************************************/
-
- void TLan_ResetLists( struct device *dev )
- {
- int i;
- TLanList *list;
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
-
- priv->txHead = 0;
- priv->txTail = 0;
- for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) {
- list = priv->txList + i;
- list->cStat = TLAN_CSTAT_UNUSED;
- list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
- list->buffer[2].count = 0;
- list->buffer[2].address = 0;
- }
- priv->rxHead = 0;
- priv->rxTail = TLAN_NUM_RX_LISTS - 1;
- for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) {
- list = priv->rxList + i;
- list->cStat = TLAN_CSTAT_READY;
- list->frameSize = TLAN_MAX_FRAME_SIZE;
- list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
- list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
- list->buffer[1].count = 0;
- list->buffer[1].address = 0;
- if ( i < TLAN_NUM_RX_LISTS - 1 )
- list->forward = virt_to_bus( list + 1 );
- else
- list->forward = 0;
- }
+ /***************************************************************
+ * TLan_HandleStatusCheck
+ *
+ * Returns:
+ * 0 if Adapter check, 1 if Network Status check.
+ * Parms:
+ * dev Device assigned the IRQ that was
+ * raised.
+ * host_int The contents of the HOST_INT
+ * port.
+ *
+ * This function handles Adapter Check/Network Status
+ * interrupts generated by the adapter. It checks the
+ * vector in the HOST_INT register to determine if it is
+ * an Adapter Check interrupt. If so, it resets the
+ * adapter. Otherwise it clears the status registers
+ * and services the PHY.
+ *
+ **************************************************************/
- } /* TLan_ResetLists */
+u32 TLan_HandleStatusCheck( struct device *dev, u16 host_int )
+{
+ u32 ack;
+ u32 error;
+ u8 net_sts;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+ ack = 1;
+ if ( host_int & TLAN_HI_IV_MASK ) {
+ error = inl( dev->base_addr + TLAN_CH_PARM );
+ printk( "TLAN: Adaptor Check on device %s err = 0x%x\n", dev->name, error );
+ TLan_ReadAndClearStats( dev, TLAN_RECORD );
+ outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
+ TLan_ResetLists( dev );
+ TLan_Reset( dev );
+ dev->tbusy = 0;
+ TLan_SetMac( dev, 0, dev->dev_addr );
+ if ( priv->timerType == 0 ) {
+ if ( priv->phyFlags & TLAN_PHY_AUTONEG ) {
+ priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY;
+ priv->timerSetAt = jiffies;
+ priv->timerType = TLAN_TIMER_LINK;
+ add_timer( &priv->timer );
+ } else {
+ //printk( " RX GO---->\n" );
+ outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
+ outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
+ }
+ }
+ ack = 0;
+ } else {
+ net_sts = TLan_DioRead8( dev->base_addr, TLAN_NET_STS );
+ if ( net_sts )
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_STS, net_sts );
+ if ( net_sts & TLAN_NET_STS_MIRQ ) {
+ (*priv->phyService)( dev );
+ if (debug) {
+ TLan_PhyPrint( dev );
+ }
+ }
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Status Check! %s Net_Sts=%x\n", dev->name,
+(unsigned) net_sts );
+ }
+ return ack;
+} /* TLan_HandleStatusCheck */
- /*************************************************************************
- * TLan_PrintDio
- *
- * Returns: Nothing
- * Parms: io_base Base IO port of the device of which to print
- * DIO registers.
- *
- * This function prints out all the the internal (DIO) registers of a
- * TLAN chip.
- *
- ************************************************************************/
-
- void TLan_PrintDio( u16 io_base )
- {
- u32 data0, data1;
- int i;
-
- printk( "TLAN: Contents of internal registers for io base 0x%04hx.\n", io_base );
- printk( "TLAN: Off. +0 +4\n" );
- for ( i = 0; i < 0x4C; i+= 8 ) {
- data0 = TLan_DioRead32( io_base, i );
- data1 = TLan_DioRead32( io_base, i + 0x4 );
- printk( "TLAN: 0x%02x 0x%08x 0x%08x\n", i, data0, data1 );
- }
- } /* TLan_PrintDio */
+ /***************************************************************
+ * TLan_HandleRxEOC
+ *
+ * Returns:
+ * 1
+ * Parms:
+ * dev Device assigned the IRQ that was
+ * raised.
+ * host_int The contents of the HOST_INT
+ * port.
+ *
+ * This driver is structured to determine EOC occurances by
+ * reading the CSTAT member of the list structure. Rx EOC
+ * interrupts are disabled via the DIO INTDIS register.
+ * However, TLAN chips before revision 3.0 didn't have this
+ * CSTAT member or a INTDIS register, so if this chip is
+ * pre-3.0, process EOC interrupts normally.
+ *
+ **************************************************************/
+u32 TLan_HandleRxEOC( struct device *dev, u16 host_int )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ TLanList *head_list;
+ u32 ack = 1;
+ host_int = 0;
+ if ( priv->tlanRev < 0x30 ) {
+ TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOC (Head=%d Tail=%d) --
+IRQ\n", priv->rxHead, priv->rxTail );
+ head_list = priv->rxList + priv->rxHead;
+ outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
+ ack |= TLAN_HC_GO | TLAN_HC_RT;
+ priv->rxEocCount++;
+ }
- /*************************************************************************
- * TLan_PrintList
- *
- * Returns: Nothing
- * Parms: list A pointer to the TLanList structure to be printed.
- * type A string to designate type of list, "Rx" or "Tx".
- * num The index of the list.
- *
- * This function prints out the contents of the list pointed to by the
- * list parameter.
- *
- ************************************************************************/
-
- void TLan_PrintList( TLanList *list, char *type, int num)
- {
- int i;
-
- printk( "TLAN: %s List %d at 0x%08x\n", type, num, (u32) list );
- printk( "TLAN: Forward = 0x%08x\n", list->forward );
- printk( "TLAN: CSTAT = 0x%04hx\n", list->cStat );
- printk( "TLAN: Frame Size = 0x%04hx\n", list->frameSize );
- // for ( i = 0; i < 10; i++ ) {
- for ( i = 0; i < 2; i++ ) {
- printk( "TLAN: Buffer[%d].count, addr = 0x%08x, 0x%08x\n", i, list->buffer[i].count, list->buffer[i].address );
- }
+ return ack;
- } /* TLan_PrintList */
+} /* TLan_HandleRxEOC */
- /*************************************************************************
- * TLan_ReadAndClearStats
- *
- * Returns: Nothing
- * Parms: dev Pointer to device structure of adapter to which
- * to read stats.
- * record Flag indicating whether to add
- *
- * This functions reads all the internal status registers of the TLAN
- * chip, which clears them as a side effect. It then either adds the
- * values to the device's status struct, or discards them, depending
- * on whether record is TLAN_RECORD (!=0) or TLAN_IGNORE (==0).
- *
- ************************************************************************/
-
- void TLan_ReadAndClearStats( struct device *dev, int record )
- {
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- u32 tx_good, tx_under;
- u32 rx_good, rx_over;
- u32 def_tx, crc, code;
- u32 multi_col, single_col;
- u32 excess_col, late_col, loss;
-
- outw( TLAN_GOOD_TX_FRMS, dev->base_addr + TLAN_DIO_ADR );
- tx_good = inb( dev->base_addr + TLAN_DIO_DATA );
- tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
- tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16;
- tx_under = inb( dev->base_addr + TLAN_DIO_DATA + 3 );
-
- outw( TLAN_GOOD_RX_FRMS, dev->base_addr + TLAN_DIO_ADR );
- rx_good = inb( dev->base_addr + TLAN_DIO_DATA );
- rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
- rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16;
- rx_over = inb( dev->base_addr + TLAN_DIO_DATA + 3 );
-
- outw( TLAN_DEFERRED_TX, dev->base_addr + TLAN_DIO_ADR );
- def_tx = inb( dev->base_addr + TLAN_DIO_DATA );
- def_tx += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
- crc = inb( dev->base_addr + TLAN_DIO_DATA + 2 );
- code = inb( dev->base_addr + TLAN_DIO_DATA + 3 );
-
- outw( TLAN_MULTICOL_FRMS, dev->base_addr + TLAN_DIO_ADR );
- multi_col = inb( dev->base_addr + TLAN_DIO_DATA );
- multi_col += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
- single_col = inb( dev->base_addr + TLAN_DIO_DATA + 2 );
- single_col += inb( dev->base_addr + TLAN_DIO_DATA + 3 ) << 8;
-
- outw( TLAN_EXCESSCOL_FRMS, dev->base_addr + TLAN_DIO_ADR );
- excess_col = inb( dev->base_addr + TLAN_DIO_DATA );
- late_col = inb( dev->base_addr + TLAN_DIO_DATA + 1 );
- loss = inb( dev->base_addr + TLAN_DIO_DATA + 2 );
-
- if ( record ) {
- priv->stats.rx_packets += rx_good;
- priv->stats.rx_errors += rx_over + crc + code;
- priv->stats.tx_packets += tx_good;
- priv->stats.tx_errors += tx_under + loss;
- priv->stats.collisions += multi_col + single_col + excess_col + late_col;
-
- priv->stats.rx_over_errors += rx_over;
- priv->stats.rx_crc_errors += crc;
- priv->stats.rx_frame_errors += code;
-
- priv->stats.tx_aborted_errors += tx_under;
- priv->stats.tx_carrier_errors += loss;
- }
-
- } /* TLan_ReadAndClearStats */
+/*****************************************************************************
+******************************************************************************
+ ThunderLAN Driver Timer Function
+******************************************************************************
+*****************************************************************************/
- /*************************************************************************
- * TLan_Reset
- *
- * Returns: 0
- * Parms: dev Pointer to device structure of adapter to be
- * reset.
- *
- * This function resets the adapter and it's physical device. See
- * Chap. 3, pp. 9-10 of the "ThunderLAN Programmer's Guide" for details.
- * The routine tries to implement what is detailed there, though
- * adjustments have been made.
- *
- ************************************************************************/
-
- int TLan_Reset( struct device *dev )
- {
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- int i;
- u32 data;
- u8 data8;
-
- // 1. Assume Stat registers have been dealt with.
- // 2. Assert reset bit.
- data = inl(dev->base_addr + TLAN_HOST_CMD);
- data |= TLAN_HC_AD_RST;
- outl(data, dev->base_addr + TLAN_HOST_CMD);
- // 3. Turn off interrupts.
- data = inl(dev->base_addr + TLAN_HOST_CMD);
- data |= TLAN_HC_INT_OFF;
- outl(data, dev->base_addr + TLAN_HOST_CMD);
- // 4. Setup AREGs and HASHs.
- for ( i = TLAN_AREG_0; i <= TLAN_HASH_2; i += 4 )
- TLan_DioWrite32( dev->base_addr, (u16) i, 0 );
- // 5. Setup NetConfig register.
- outw(TLAN_NET_CONFIG, dev->base_addr + TLAN_DIO_ADR);
- outw(TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN, dev->base_addr + TLAN_DIO_DATA);
- // 6. Setup BSIZE register.
- // Accept defaults, 0x22, for now.
- // 7. Setup TX commit in Acommit.
- // Allow it to manage itself.
- // 8. Load Ld_Tmr in HOST_CMD.
- // I don't know what this value should be. I'll try 3.
- outl( TLAN_HC_LD_TMR | 0x0, dev->base_addr + TLAN_HOST_CMD );
- // 9. Load Ld_Thr in HOST_CMD.
- // Try 1 for now.
- outl( TLAN_HC_LD_THR | 0x1, dev->base_addr + TLAN_HOST_CMD );
- // 10. Unreset the MII by setting NMRST (in NetSio) to 1.
- outw( TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR );
- TLan_SetBit( TLAN_NET_SIO_NMRST, dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO );
- // 10a. Other.
- // 12. Setup the NetMask register.
- if ( priv->tlanRev >= 0x30 )
- TLan_DioWrite8( dev->base_addr, TLAN_INT_DIS, TLAN_ID_TX_EOC | TLAN_ID_RX_EOC );
- //TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP | TLAN_NET_CMD_DUPLEX | TLAN_NET_CMD_CAF );
- TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP );
- // 11. Initialize PHYs.
- TLan_PhySelect( dev );
- (*priv->phyCheck)( dev );
-
- //data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5 | TLAN_NET_MASK_MASK6 | TLAN_NET_MASK_MASK7;
- data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5 | TLAN_NET_MASK_MASK7;
- TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data8 );
- TLan_DioWrite16( dev->base_addr, TLAN_MAX_RX, 1550 );
- /*
- * 13. Turn on interrupts to host.
- * I don't want any interrupts, yet.
- */
- /*
- data = inl(base_port + TLAN_HOST_CMD);
- data |= TLAN_HC_INT_ON;
- outl(data, base_port + TLAN_HOST_CMD);
- */
-
- return 0;
-
- } /* TLan_Reset */
-
-
-
-
- /*************************************************************************
- * TLan_SetMac
- *
- * Returns: Nothing
- * Parms: dev Pointer to device structure of adapter on which to
- * change the AREG.
- * areg The AREG to set the address in (0 - 3).
- * mac A pointer to an array of chars. Each element
- * stores one byte of the address. IE, it isn't
- * in ascii.
- *
- * This function transfers a MAC address to one of the TLAN AREGs
- * (address registers). The TLAN chip locks the register on writing to
- * offset 0 and unlocks the register after writing to offset 5. If NULL
- * is passed in mac, then the AREG is filled with 0's.
+ /***************************************************************
+ * TLan_Timer
*
- ************************************************************************/
-
- void TLan_SetMac( struct device *dev, int areg, char *mac )
- {
- int i;
-
- areg *= 6;
-
- if ( mac != NULL ) {
- for ( i = 0; i < 6; i++ )
- TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, mac[i] );
+ * Returns:
+ * Nothing
+ * Parms:
+ * data A value given to add timer when
+ * add_timer was called.
+ *
+ * This function handles timed functionality for the
+ * TLAN driver. The two current timer uses are for
+ * delaying for autonegotionation and driving the ACT LED.
+ * - Autonegotiation requires being allowed about
+ * 2 1/2 seconds before attempting to transmit a
+ * packet. It would be a very bad thing to hang
+ * the kernel this long, so the driver doesn't
+ * allow transmission 'til after this time, for
+ * certain PHYs. It would be much nicer if all
+ * PHYs were interrupt-capable like the internal
+ * PHY.
+ * - The ACT LED, which shows adapter activity, is
+ * driven by the driver, and so must be left on
+ * for a short period to power up the LED so it
+ * can be seen. This delay can be changed by
+ * changing the TLAN_TIMER_ACT_DELAY in tlan.h,
+ * if desired. 10 jiffies produces a slightly
+ * sluggish response.
+ *
+ **************************************************************/
+
+void TLan_Timer( unsigned long data )
+{
+ struct device *dev = (struct device *) data;
+ u16 gen_sts;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+ // printk( "TLAN: %s Entered Timer, type = %d\n", dev->name, priv->timerType )
+;
+
+ switch ( priv->timerType ) {
+ case TLAN_TIMER_LINK:
+ TLan_MiiReadReg( dev->base_addr, priv->phyAddr, MII_GEN_STS, &gen_sts );
+ if ( gen_sts & MII_GS_LINK ) {
+ priv->phyOnline = 1;
+ outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
+ outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
+ priv->timerSetAt = 0;
+ priv->timerType = 0;
+ } else {
+ priv->timer.expires = jiffies + ( TLAN_TIMER_LINK_DELAY * 2 );
+ add_timer( &priv->timer );
+ }
+ break;
+ case TLAN_TIMER_ACT:
+ if ( jiffies - priv->timerSetAt >= TLAN_TIMER_ACT_DELAY ) {
+ TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK );
+ priv->timerSetAt = 0;
+ priv->timerType = 0;
} else {
- for ( i = 0; i < 6; i++ )
- TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, 0 );
+ priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY;
+ add_timer( &priv->timer );
}
+ break;
+ default:
+ break;
+ }
- } /* TLan_SetMac */
+} /* TLan_Timer */
@@ -972,711 +1466,814 @@
/*****************************************************************************
******************************************************************************
- ThunderLAN Driver Eeprom routines
-
- The Compaq Netelligent 10 and 10/100 cards use a Microchip 24C02A EEPROM.
- These functions are based on information in Microchip's data sheet. I
- don't know how well this functions will work with other EEPROMs.
+ ThunderLAN Driver Adapter Related Routines
******************************************************************************
*****************************************************************************/
- static void TLan_EeSendStart( u16 );
- static int TLan_EeSendByte( u16, u8, int );
- static void TLan_EeReceiveByte( u16, u8 *, int );
- static int TLan_EeReadByte( u16, u8, u8 * );
-
-
-
- /*************************************************************************
- * TLan_EeSendStart
- *
- * Returns: Nothing
- * Parms: io_base The IO port base address for the TLAN device
- * with the EEPROM to use.
+ /***************************************************************
+ * TLan_ResetLists
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev The device structure with the list
+ * stuctures to be reset.
+ *
+ * This routine sets the variables associated with managing
+ * the TLAN lists to their initial values.
+ *
+ **************************************************************/
+
+void TLan_ResetLists( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ int i;
+ TLanList *list;
+
+ priv->txHead = 0;
+ priv->txTail = 0;
+ for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) {
+ list = priv->txList + i;
+ list->cStat = TLAN_CSTAT_UNUSED;
+ list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_S
+IZE ) );
+ list->buffer[2].count = 0;
+ list->buffer[2].address = 0;
+ }
+
+ priv->rxHead = 0;
+ priv->rxTail = TLAN_NUM_RX_LISTS - 1;
+ for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) {
+ list = priv->rxList + i;
+ list->cStat = TLAN_CSTAT_READY;
+ list->frameSize = TLAN_MAX_FRAME_SIZE;
+ list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
+ list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_S
+IZE ) );
+ list->buffer[1].count = 0;
+ list->buffer[1].address = 0;
+ if ( i < TLAN_NUM_RX_LISTS - 1 )
+ list->forward = virt_to_bus( list + 1 );
+ else
+ list->forward = 0;
+ }
+
+} /* TLan_ResetLists */
+
+
+
+
+ /***************************************************************
+ * TLan_PrintDio
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * io_base Base IO port of the device of
+ * which to print DIO registers.
*
- * This function sends a start cycle to an EEPROM attached to a TLAN
- * chip.
+ * This function prints out all the the internal (DIO)
+ * registers of a TLAN chip.
*
- ************************************************************************/
+ **************************************************************/
- void TLan_EeSendStart( u16 io_base )
- {
- u16 sio;
+void TLan_PrintDio( u16 io_base )
+{
+ u32 data0, data1;
+ int i;
- outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
- sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+ printk( "TLAN: Contents of internal registers for io base 0x%04hx.\n", io_bas
+e );
+ printk( "TLAN: Off. +0 +4\n" );
+ for ( i = 0; i < 0x4C; i+= 8 ) {
+ data0 = TLan_DioRead32( io_base, i );
+ data1 = TLan_DioRead32( io_base, i + 0x4 );
+ printk( "TLAN: 0x%02x 0x%08x 0x%08x\n", i, data0, data1 );
+ }
- TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
- TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
- TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
- //
- // FIXME: Do I really need these SLOW_DOWN_IOs?
- //
- SLOW_DOWN_IO;
- TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );
- SLOW_DOWN_IO;
- TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
- SLOW_DOWN_IO;
+} /* TLan_PrintDio */
- } /* TLan_EeSendStart */
-
- /*************************************************************************
- * TLan_EeSendByte
+ /***************************************************************
+ * TLan_PrintList
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * list A pointer to the TLanList structure to
+ * be printed.
+ * type A string to designate type of list,
+ * "Rx" or "Tx".
+ * num The index of the list.
*
- * Returns: If the correct ack was received, 0, otherwise 1
- * Parms: io_base The IO port base address for the TLAN device
- * with the EEPROM to use.
- * data The 8 bits of information to send to the
- * EEPROM.
- * stop If TLAN_EEPROM_STOP is passed, a stop cycle is
- * sent after the byte is sent after the ack is
- * read.
- *
- * This function sends a byte on the serial EEPROM line, driving the
- * clock to send each bit. The function then reverses transmission
- * direction and reads an acknowledge bit.
- *
- ************************************************************************/
-
- int TLan_EeSendByte( u16 io_base, u8 data, int stop )
- {
- int err;
- u8 place;
- u16 sio;
-
- outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
- sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
-
- // Assume clock is low, tx is enabled;
- for ( place = 0x80; place; place >>= 1 ) {
- if ( place & data )
- TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
- else
- TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );
- TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
- TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
- }
- TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio );
- TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
- err = TLan_GetBit( TLAN_NET_SIO_EDATA, sio );
- TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
- TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
-
- if ( ( ! err ) && stop ) {
- TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is high
- TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
- TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
- }
+ * This function prints out the contents of the list
+ * pointed to by the list parameter.
+ *
+ **************************************************************/
- return ( err );
+void TLan_PrintList( TLanList *list, char *type, int num)
+{
+ int i;
- } /* TLan_EeSendByte */
+ printk( "TLAN: %s List %d at 0x%08x\n", type, num, (u32) list );
+ printk( "TLAN: Forward = 0x%08x\n", list->forward );
+ printk( "TLAN: CSTAT = 0x%04hx\n", list->cStat );
+ printk( "TLAN: Frame Size = 0x%04hx\n", list->frameSize );
+ // for ( i = 0; i < 10; i++ ) {
+ for ( i = 0; i < 2; i++ ) {
+ printk( "TLAN: Buffer[%d].count, addr = 0x%08x, 0x%08x\n", i, list->buffe
+r[i].count, list->buffer[i].address );
+ }
+} /* TLan_PrintList */
- /*************************************************************************
- * TLan_EeReceiveByte
- *
- * Returns: Nothing
- * Parms: io_base The IO port base address for the TLAN device
- * with the EEPROM to use.
- * data An address to a char to hold the data sent
- * from the EEPROM.
- * stop If TLAN_EEPROM_STOP is passed, a stop cycle is
- * sent after the byte is received, and no ack is
- * sent.
- *
- * This function receives 8 bits of data from the EEPROM over the serial
- * link. It then sends and ack bit, or no ack and a stop bit. This
- * function is used to retrieve data after the address of a byte in the
- * EEPROM has been sent.
- *
- ************************************************************************/
-
- void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop )
- {
- u8 place;
- u16 sio;
-
- outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
- sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
- *data = 0;
-
- // Assume clock is low, tx is enabled;
- TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio );
- for ( place = 0x80; place; place >>= 1 ) {
- TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
- if ( TLan_GetBit( TLAN_NET_SIO_EDATA, sio ) )
- *data |= place;
- TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
- }
-
- TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
- if ( ! stop ) {
- TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // Ack = 0
- TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
- TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
- } else {
- TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); // No ack = 1 (?)
- TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
- TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
- TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is high
- TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
- TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
- }
- } /* TLan_EeReceiveByte */
+ /***************************************************************
+ * TLan_ReadAndClearStats
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev Pointer to device structure of adapter
+ * to which to read stats.
+ * record Flag indicating whether to add
+ *
+ * This functions reads all the internal status registers
+ * of the TLAN chip, which clears them as a side effect.
+ * It then either adds the values to the device's status
+ * struct, or discards them, depending on whether record
+ * is TLAN_RECORD (!=0) or TLAN_IGNORE (==0).
+ *
+ **************************************************************/
+
+void TLan_ReadAndClearStats( struct device *dev, int record )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u32 tx_good, tx_under;
+ u32 rx_good, rx_over;
+ u32 def_tx, crc, code;
+ u32 multi_col, single_col;
+ u32 excess_col, late_col, loss;
+
+ outw( TLAN_GOOD_TX_FRMS, dev->base_addr + TLAN_DIO_ADR );
+ tx_good = inb( dev->base_addr + TLAN_DIO_DATA );
+ tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+ tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16;
+ tx_under = inb( dev->base_addr + TLAN_DIO_DATA + 3 );
+
+ outw( TLAN_GOOD_RX_FRMS, dev->base_addr + TLAN_DIO_ADR );
+ rx_good = inb( dev->base_addr + TLAN_DIO_DATA );
+ rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+ rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16;
+ rx_over = inb( dev->base_addr + TLAN_DIO_DATA + 3 );
+
+ outw( TLAN_DEFERRED_TX, dev->base_addr + TLAN_DIO_ADR );
+ def_tx = inb( dev->base_addr + TLAN_DIO_DATA );
+ def_tx += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+ crc = inb( dev->base_addr + TLAN_DIO_DATA + 2 );
+ code = inb( dev->base_addr + TLAN_DIO_DATA + 3 );
+
+ outw( TLAN_MULTICOL_FRMS, dev->base_addr + TLAN_DIO_ADR );
+ multi_col = inb( dev->base_addr + TLAN_DIO_DATA );
+ multi_col += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+ single_col = inb( dev->base_addr + TLAN_DIO_DATA + 2 );
+ single_col += inb( dev->base_addr + TLAN_DIO_DATA + 3 ) << 8;
+
+ outw( TLAN_EXCESSCOL_FRMS, dev->base_addr + TLAN_DIO_ADR );
+ excess_col = inb( dev->base_addr + TLAN_DIO_DATA );
+ late_col = inb( dev->base_addr + TLAN_DIO_DATA + 1 );
+ loss = inb( dev->base_addr + TLAN_DIO_DATA + 2 );
+
+ if ( record ) {
+ priv->stats.rx_packets += rx_good;
+ priv->stats.rx_errors += rx_over + crc + code;
+ priv->stats.tx_packets += tx_good;
+ priv->stats.tx_errors += tx_under + loss;
+ priv->stats.collisions += multi_col + single_col + excess_col + late_col;
+
+ priv->stats.rx_over_errors += rx_over;
+ priv->stats.rx_crc_errors += crc;
+ priv->stats.rx_frame_errors += code;
+
+ priv->stats.tx_aborted_errors += tx_under;
+ priv->stats.tx_carrier_errors += loss;
+ }
+
+} /* TLan_ReadAndClearStats */
- /*************************************************************************
- * TLan_EeReadByte
+ /***************************************************************
+ * TLan_Reset
*
- * Returns: No error = 0, else, the stage at which the error occured.
- * Parms: io_base The IO port base address for the TLAN device
- * with the EEPROM to use.
- * ee_addr The address of the byte in the EEPROM whose
- * contents are to be retrieved.
- * data An address to a char to hold the data obtained
- * from the EEPROM.
+ * Returns:
+ * 0
+ * Parms:
+ * dev Pointer to device structure of adapter
+ * to be reset.
*
- * This function reads a byte of information from an byte cell in the
- * EEPROM.
+ * This function resets the adapter and it's physical
+ * device. See Chap. 3, pp. 9-10 of the "ThunderLAN
+ * Programmer's Guide" for details. The routine tries to
+ * implement what is detailed there, though adjustments
+ * have been made.
*
- ************************************************************************/
+ **************************************************************/
- int TLan_EeReadByte( u16 io_base, u8 ee_addr, u8 *data )
- {
- int err;
+int TLan_Reset( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ int i;
+ u32 addr;
+ u32 data;
+ u8 data8;
- cli();
+// 1. Assert reset bit.
- TLan_EeSendStart( io_base );
- err = TLan_EeSendByte( io_base, 0xA0, TLAN_EEPROM_ACK );
- if (err)
- return 1;
- err = TLan_EeSendByte( io_base, ee_addr, TLAN_EEPROM_ACK );
- if (err)
- return 2;
- TLan_EeSendStart( io_base );
- err = TLan_EeSendByte( io_base, 0xA1, TLAN_EEPROM_ACK );
- if (err)
- return 3;
- TLan_EeReceiveByte( io_base, data, TLAN_EEPROM_STOP );
+ data = inl(dev->base_addr + TLAN_HOST_CMD);
+ data |= TLAN_HC_AD_RST;
+ outl(data, dev->base_addr + TLAN_HOST_CMD);
- sti();
+// 2. Turn off interrupts. ( Probably isn't necessary )
- return 0;
+ data = inl(dev->base_addr + TLAN_HOST_CMD);
+ data |= TLAN_HC_INT_OFF;
+ outl(data, dev->base_addr + TLAN_HOST_CMD);
- } /* TLan_EeReadByte */
+// 3. Clear AREGs and HASHs.
+ for ( i = TLAN_AREG_0; i <= TLAN_HASH_2; i += 4 ) {
+ TLan_DioWrite32( dev->base_addr, (u16) i, 0 );
+ }
+// 4. Setup NetConfig register.
+ data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
+ TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data );
-/*****************************************************************************
-******************************************************************************
+// 5. Load Ld_Tmr and Ld_Thr in HOST_CMD.
- ThunderLAN Driver Interrupt Vectors and Table
+ outl( TLAN_HC_LD_TMR | 0x0, dev->base_addr + TLAN_HOST_CMD );
+ outl( TLAN_HC_LD_THR | 0x1, dev->base_addr + TLAN_HOST_CMD );
- Please see Chap. 4, "Interrupt Handling" of the
- "ThunderLAN Programmer's Guide" for more informations on handling
- interrupts generated by TLAN based adapters.
+// 6. Unreset the MII by setting NMRST (in NetSio) to 1.
-******************************************************************************
-*****************************************************************************/
+ outw( TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR );
+ addr = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO;
+ TLan_SetBit( TLAN_NET_SIO_NMRST, addr );
- static u32 TLan_HandleInvalid( struct device *, u16 );
- static u32 TLan_HandleTxEOF( struct device *, u16 );
- static u32 TLan_HandleStatOverflow( struct device *, u16 );
- static u32 TLan_HandleRxEOF( struct device *, u16 );
- static u32 TLan_HandleDummy( struct device *, u16 );
- static u32 TLan_HandleTxEOC( struct device *, u16 );
- static u32 TLan_HandleStatusCheck( struct device *, u16 );
- static u32 TLan_HandleRxEOC( struct device *, u16 );
+// 7. Setup the remaining registers.
+ if ( priv->tlanRev >= 0x30 ) {
+ data8 = TLAN_ID_TX_EOC | TLAN_ID_RX_EOC;
+ TLan_DioWrite8( dev->base_addr, TLAN_INT_DIS, data8 );
+ }
+ TLan_PhySelect( dev );
+ data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN;
+ if ( priv->phyFlags & TLAN_PHY_BIT_RATE ) {
+ data |= TLAN_NET_CFG_BIT;
+ if ( aui == 1 ) {
+ TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x0a );
+ } else {
+ TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x08 );
+ }
+ }
+ if ( priv->phyFlags & TLAN_PHY_INTERNAL ) {
+ data |= TLAN_NET_CFG_PHY_EN;
+ }
+ TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data );
+ (*priv->phyCheck)( dev );
+ data8 = TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP;
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, data8 );
+ data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5;
+ if ( priv->phyFlags & TLAN_PHY_INTS ) {
+ data8 |= TLAN_NET_MASK_MASK7;
+ }
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data8 );
+ TLan_DioWrite16( dev->base_addr, TLAN_MAX_RX, TLAN_MAX_FRAME_SIZE );
+ if ( priv->phyFlags & TLAN_PHY_UNMANAGED ) {
+ priv->phyOnline = 1;
+ }
+ return 0;
- typedef u32 (TLanIntVectorFunc)( struct device *, u16 );
-
+} /* TLan_Reset */
- /*************************************************************************
- * TLan_HandleInvalid
- *
- * Returns: 0
- * Parms: dev Device assigned the IRQ that was raised.
- * host_int The contents of the HOST_INT port.
- *
- * This function handles invalid interrupts. This should never happen
- * unless some other adapter is trying to use the IRQ line assigned to
- * the device.
+
+ /***************************************************************
+ * TLan_SetMac
*
- ************************************************************************/
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev Pointer to device structure of adapter
+ * on which to change the AREG.
+ * areg The AREG to set the address in (0 - 3).
+ * mac A pointer to an array of chars. Each
+ * element stores one byte of the address.
+ * IE, it isn't in ascii.
+ *
+ * This function transfers a MAC address to one of the
+ * TLAN AREGs (address registers). The TLAN chip locks
+ * the register on writing to offset 0 and unlocks the
+ * register after writing to offset 5. If NULL is passed
+ * in mac, then the AREG is filled with 0's.
+ *
+ **************************************************************/
+
+void TLan_SetMac( struct device *dev, int areg, char *mac )
+{
+ int i;
+
+ areg *= 6;
- u32 TLan_HandleInvalid( struct device *dev, u16 host_int )
- {
- host_int = 0;
- // printk( "TLAN: Invalid interrupt on %s.\n", dev->name );
- return 0;
+ if ( mac != NULL ) {
+ for ( i = 0; i < 6; i++ )
+ TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, mac[i] );
+ } else {
+ for ( i = 0; i < 6; i++ )
+ TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, 0 );
+ }
- } /* TLan_HandleInvalid */
+} /* TLan_SetMac */
- /*************************************************************************
- * TLan_HandleTxEOF
- *
- * Returns: 1
- * Parms: dev Device assigned the IRQ that was raised.
- * host_int The contents of the HOST_INT port.
- *
- * This function handles Tx EOF interrupts which are raised by the
- * adapter when it has completed sending the contents of a buffer. If
- * detemines which list/buffer was completed and resets it. If the
- * buffer was the last in the channel (EOC), then the function checks
- * to see if another buffer is ready to send, and if so, sends a Tx Go
- * command. Finally, the driver activates/continues the activity LED.
- *
- ************************************************************************/
-
- u32 TLan_HandleTxEOF( struct device *dev, u16 host_int )
- {
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- int eoc = 0;
- TLanList *head_list;
- u32 ack = 1;
-
- TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOF (Head=%d Tail=%d)\n", priv->txHead, priv->txTail );
- host_int = 0;
- head_list = priv->txList + priv->txHead;
- if ( head_list->cStat & TLAN_CSTAT_EOC )
- eoc = 1;
- if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) {
- printk( "TLAN: Received interrupt for uncompleted TX frame.\n" );
- }
- // printk( "Ack %d CSTAT=%hx\n", priv->txHead, head_list->cStat );
-#if LINUX_KERNEL_VERSION > 0x20100
- priv->stats->tx_bytes += head_list->frameSize;
-#endif
- head_list->cStat = TLAN_CSTAT_UNUSED;
- dev->tbusy = 0;
- priv->txHead++;
- if ( priv->txHead >= TLAN_NUM_TX_LISTS )
- priv->txHead = 0;
- if ( eoc ) {
- TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOC (Head=%d Tail=%d)\n", priv->txHead, priv->txTail );
- head_list = priv->txList + priv->txHead;
- if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) {
- outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
- ack |= TLAN_HC_GO;
- } else {
- priv->txInProgress = 0;
- }
- }
- TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
- if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) {
- if ( priv->timerSetAt == 0 ) {
- // printk("TxEOF Starting timer...\n");
- priv->timerSetAt = jiffies;
- priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY;
- priv->timerType = TLAN_TIMER_ACT;
- add_timer( &priv->timer );
- } else if ( priv->timerType == TLAN_TIMER_ACT ) {
- priv->timerSetAt = jiffies;
- // printk("TxEOF continuing timer...\n");
- }
- }
+/*****************************************************************************
+******************************************************************************
- return ack;
+ ThunderLAN Driver PHY Layer Routines
- } /* TLan_HandleTxEOF */
+ The TLAN chip can drive any number of PHYs (physical devices). Rather
+ than having lots of 'if' or '#ifdef' statements, I have created a
+ second driver layer for the PHYs. Each PHY can be identified from its
+ id in registers 2 and 3, and can be given a Check and Service routine
+ that will be called when the adapter is reset and when the adapter
+ receives a Network Status interrupt, respectively.
+
+******************************************************************************
+*****************************************************************************/
+static TLanPhyIdEntry TLanPhyIdTable[] = {
+ { 0x4000,
+ 0x5014,
+ &TLan_PhyInternalCheck,
+ &TLan_PhyInternalService,
+ TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL },
+ { 0x4000,
+ 0x5015,
+ &TLan_PhyInternalCheck,
+ &TLan_PhyInternalService,
+ TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL },
+ { 0x4000,
+ 0x5016,
+ &TLan_PhyInternalCheck,
+ &TLan_PhyInternalService,
+ TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL },
+ { 0x2000,
+ 0x5C00,
+ &TLan_PhyDp83840aCheck,
+ &TLan_PhyNop,
+ TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG },
+ { 0x2000,
+ 0x5C01,
+ &TLan_PhyDp83840aCheck,
+ &TLan_PhyNop,
+ TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG },
+ { 0x7810,
+ 0x0000,
+ &TLan_PhyDp83840aCheck,
+ &TLan_PhyNop,
+ TLAN_PHY_AUTONEG },
+ { 0x0000,
+ 0x0000,
+ NULL,
+ NULL,
+ 0
+ }
+ };
- /*************************************************************************
- * TLan_HandleStatOverflow
- *
- * Returns: 1
- * Parms: dev Device assigned the IRQ that was raised.
- * host_int The contents of the HOST_INT port.
+ /*********************************************************************
+ * TLan_PhyPrint
*
- * This function handles the Statistics Overflow interrupt which means
- * that one or more of the TLAN statistics registers has reached 1/2
- * capacity and needs to be read.
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev A pointer to the device structure of the adapter
+ * which the desired PHY is located.
+ *
+ * This function prints the registers a PHY.
*
- ************************************************************************/
-
- u32 TLan_HandleStatOverflow( struct device *dev, u16 host_int )
- {
- host_int = 0;
- TLan_ReadAndClearStats( dev, TLAN_RECORD );
-
- return 1;
+ ********************************************************************/
- } /* TLan_HandleStatOverflow */
+void TLan_PhyPrint( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 i, data0, data1, data2, data3, phy;
+ u32 io;
+ phy = priv->phyAddr;
+ io = dev->base_addr;
+ if ( ( phy > 0 ) && ( phy <= TLAN_PHY_MAX_ADDR ) ) {
+ printk( "TLAN: Device %s, PHY 0x%02x.\n", dev->name, phy );
+ printk( "TLAN: Off. +0 +1 +2 +3 \n" );
+ for ( i = 0; i < 0x20; i+= 4 ) {
+ printk( "TLAN: 0x%02x", i );
+ TLan_MiiReadReg( io, phy, i, &data0 );
+ printk( " 0x%04hx", data0 );
+ TLan_MiiReadReg( io, phy, i + 1, &data1 );
+ printk( " 0x%04hx", data1 );
+ TLan_MiiReadReg( io, phy, i + 2, &data2 );
+ printk( " 0x%04hx", data2 );
+ TLan_MiiReadReg( io, phy, i + 3, &data3 );
+ printk( " 0x%04hx\n", data3 );
+ }
+ } else {
+ printk( "TLAN: Device %s, PHY 0x%02x (Unmanaged/Unknown).\n",
+ dev->name,
+ phy
+ );
+ }
+
+} /* TLan_PhyPrint */
+
+
+
+
+ /*********************************************************************
+ * TLan_PhySelect
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev A pointer to the device structure of the adapter
+ * for which the PHY needs determined.
+ *
+ * This function decides which PHY amoung those attached to the
+ * TLAN chip is to be used. The TLAN chip can be attached to
+ * multiple PHYs, and the driver needs to decide which one to
+ * talk to. Currently this routine picks the PHY with the lowest
+ * address as the internal PHY address is 0x1F, the highest
+ * possible. This strategy assumes that there can be only one
+ * other PHY, and, if it exists, it is the one to be used. If
+ * token ring PHYs are ever supported, this routine will become
+ * a little more interesting...
+ *
+ ********************************************************************/
+
+void TLan_PhySelect( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ int phy;
+ int entry;
+ u16 id_hi[TLAN_PHY_MAX_ADDR + 1];
+ u16 id_lo[TLAN_PHY_MAX_ADDR + 1];
+ u16 hi;
+ u16 lo;
+ u16 vendor;
+ u16 device;
+
+ priv->phyCheck = &TLan_PhyNop; // Make sure these aren't ever NULL
+ priv->phyService = &TLan_PhyNop;
+
+ vendor = TLanDeviceList[priv->pciEntry].vendorId;
+ device = TLanDeviceList[priv->pciEntry].deviceId;
+
+ // This is a bit uglier than I'd like, but the 0xF130 device must
+ // NOT be assigned a valid PHY as it uses an unmanaged, bit-rate
+ // PHY. It is simplest just to use another goto, rather than
+ // nesting the two for loops in the if statement.
+
+ if ( ( vendor == PCI_VENDOR_ID_COMPAQ ) &&
+ ( device == PCI_DEVICE_ID_NETFLEX_3P ) ) {
+ entry = 0;
+ phy = 0;
+ goto FINISH;
+ }
+
+ for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) {
+ hi = lo = 0;
+ TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_HI, &hi );
+ TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_LO, &lo );
+ id_hi[phy] = hi;
+ id_lo[phy] = lo;
+ TLAN_DBG( TLAN_DEBUG_GNRL,
+ "TLAN: Phy %2x, hi = %hx, lo = %hx\n",
+ phy,
+ hi,
+ lo
+ );
+ }
+
+ for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) {
+ if ( ( aui == 1 ) && ( phy != TLAN_PHY_MAX_ADDR ) ) {
+ if ( id_hi[phy] != 0xFFFF ) {
+ TLan_MiiSync(dev->base_addr);
+ TLan_MiiWriteReg(dev->base_addr,
+ phy,
+ MII_GEN_CTL,
+ MII_GC_PDOWN |
+ MII_GC_LOOPBK |
+ MII_GC_ISOLATE );
-
- /*************************************************************************
- * TLan_HandleRxEOF
- *
- * Returns: 1
- * Parms: dev Device assigned the IRQ that was raised.
- * host_int The contents of the HOST_INT port.
- *
- * This function handles the Rx EOF interrupt which indicates a frame
- * has been received by the adapter from the net and the frame has been
- * transferred to memory. The function determines the bounce buffer
- * the frame has been loaded into, creates a new sk_buff big enough to
- * hold the frame, and sends it to protocol stack. It then resets the
- * used buffer and appends it to the end of the list. If the frame was
- * the last in the Rx channel (EOC), the function restarts the receive
- * channel by sending an Rx Go command to the adapter. Then it activates/
- * continues the the activity LED.
- *
- ************************************************************************/
-
- u32 TLan_HandleRxEOF( struct device *dev, u16 host_int )
- {
- u32 ack = 1;
- int eoc = 0;
- u8 *head_buffer;
- TLanList *head_list;
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- struct sk_buff *skb;
- TLanList *tail_list;
- void *t;
-
- TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOF (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail );
- host_int = 0;
- head_list = priv->rxList + priv->rxHead;
- tail_list = priv->rxList + priv->rxTail;
- if ( head_list->cStat & TLAN_CSTAT_EOC )
- eoc = 1;
- if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) {
- printk( "TLAN: Received interrupt for uncompleted RX frame.\n" );
- } else {
- skb = dev_alloc_skb( head_list->frameSize + 7 );
- if ( skb == NULL ) {
- printk( "TLAN: Couldn't allocate memory for received data.\n" );
- } else {
- head_buffer = priv->rxBuffer + ( priv->rxHead * TLAN_MAX_FRAME_SIZE );
- skb->dev = dev;
- skb_reserve( skb, 2 );
- t = (void *) skb_put( skb, head_list->frameSize );
- // printk( " %hd %p %p\n", head_list->frameSize, skb->data, t );
-
-#if LINUX_KERNEL_VERSION > 0x20100
- priv->stats->rx_bytes += head_list->frameSize;
-#endif
-
- memcpy( t, head_buffer, head_list->frameSize );
- skb->protocol = eth_type_trans( skb, dev );
- netif_rx( skb );
- }
}
- head_list->forward = 0;
- head_list->frameSize = TLAN_MAX_FRAME_SIZE;
- head_list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
- tail_list->forward = virt_to_bus( head_list );
- priv->rxHead++;
- if ( priv->rxHead >= TLAN_NUM_RX_LISTS )
- priv->rxHead = 0;
- priv->rxTail++;
- if ( priv->rxTail >= TLAN_NUM_RX_LISTS )
- priv->rxTail = 0;
- if ( eoc ) {
- TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOC (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail );
- head_list = priv->rxList + priv->rxHead;
- outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
- ack |= TLAN_HC_GO | TLAN_HC_RT;
- priv->rxEocCount++;
+ continue;
+ }
+ for ( entry = 0; TLanPhyIdTable[entry].check; entry++) {
+ if ( ( id_hi[phy] == TLanPhyIdTable[entry].idHi ) &&
+ ( id_lo[phy] == TLanPhyIdTable[entry].idLo ) ) {
+ TLAN_DBG( TLAN_DEBUG_GNRL,
+ "TLAN: Selected Phy %hx\n",
+ phy
+ );
+ goto FINISH;
}
- TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
- if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) {
- if ( priv->timerSetAt == 0 ) {
- // printk("RxEOF Starting timer...\n");
- priv->timerSetAt = jiffies;
- priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY;
- priv->timerType = TLAN_TIMER_ACT;
- add_timer( &priv->timer );
- } else if ( priv->timerType == TLAN_TIMER_ACT ) {
- // printk("RxEOF tarting continuing timer...\n");
- priv->timerSetAt = jiffies;
- }
- }
- dev->last_rx = jiffies;
-
- return ack;
-
- } /* TLan_HandleRxEOF */
-
+ }
+ }
+ entry = 0;
+ phy = 0;
+FINISH:
+
+ if ( ( entry == 0 ) && ( phy == 0 ) ) {
+ priv->phyAddr = phy;
+ priv->phyEntry = entry;
+ priv->phyCheck = TLan_PhyNop;
+ priv->phyService = TLan_PhyNop;
+ priv->phyFlags = TLAN_PHY_BIT_RATE |
+ TLAN_PHY_UNMANAGED |
+ TLAN_PHY_ACTIVITY;
+ } else {
+ priv->phyAddr = phy;
+ priv->phyEntry = entry;
+ priv->phyCheck = TLanPhyIdTable[entry].check;
+ priv->phyService = TLanPhyIdTable[entry].service;
+ priv->phyFlags = TLanPhyIdTable[entry].flags;
+ }
+
+} /* TLan_PhySelect */
+
+
+
+
+ /***************************************************************
+ * TLan_PhyNop
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev A pointer to a device structure.
+ *
+ * This function does nothing and is meant as a stand-in
+ * for when a Check or Service function would be
+ * meaningless.
+ *
+ **************************************************************/
+
+int TLan_PhyNop( struct device *dev )
+{
+ dev = NULL;
+ return 0;
+
+} /* TLan_PhyNop */
+
+
+
+
+ /***************************************************************
+ * TLan_PhyInternalCheck
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev A pointer to a device structure of the
+ * adapter holding the PHY to be checked.
+ *
+ * This function resets the internal PHY on a TLAN chip.
+ * See Chap. 7, "Physical Interface (PHY)" of "ThunderLAN
+ * Programmer's Guide"
+ *
+ **************************************************************/
+
+int TLan_PhyInternalCheck( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 gen_ctl;
+ u32 io;
+ u16 phy;
+ u16 value;
+ u8 sio;
+
+ io = dev->base_addr;
+ phy = priv->phyAddr;
+
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl );
+ if ( gen_ctl & MII_GC_PDOWN ) {
+ TLan_MiiSync( io );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_
+ISOLATE );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK );
+ udelay(50000);
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK );
+ TLan_MiiSync( io );
+ }
+
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
+ while ( value & MII_GC_RESET )
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
+
+ // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX );
+ // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 );
+
+ udelay(500000);
+
+ TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
+ if ( aui )
+ value |= TLAN_TC_AUISEL;
+ else
+ value &= ~TLAN_TC_AUISEL;
+ TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
+
+ // Read Possible Latched Link Status
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
+ // Read Real Link Status
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
+ if ( ( value & MII_GS_LINK ) || aui ) {
+ priv->phyOnline = 1;
+ TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
+ } else {
+ priv->phyOnline = 0;
+ TLan_DioWrite8( io, TLAN_LED_REG, 0 );
+ }
- /*************************************************************************
- * TLan_HandleDummy
- *
- * Returns: 1
- * Parms: dev Device assigned the IRQ that was raised.
- * host_int The contents of the HOST_INT port.
- *
- * This function handles the Dummy interrupt, which is raised whenever
- * a test interrupt is generated by setting the Req_Int bit of HOST_CMD
- * to 1.
- *
- ************************************************************************/
+ // Enable Interrupts
+ TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
+ value |= TLAN_TC_INTEN;
+ TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
- u32 TLan_HandleDummy( struct device *dev, u16 host_int )
- {
- host_int = 0;
- printk( "TLAN: Dummy interrupt on %s.\n", dev->name );
- return 1;
+ sio = TLan_DioRead8( io, TLAN_NET_SIO );
+ sio |= TLAN_NET_SIO_MINTEN;
+ TLan_DioWrite8( io, TLAN_NET_SIO, sio );
+
+ return 0;
- } /* TLan_HandleDummy */
+} /* TLanPhyInternalCheck */
- /*************************************************************************
- * TLan_HandleTxEOC
+ /***************************************************************
+ * TLan_PhyInternalService
*
- * Returns: 1
- * Parms: dev Device assigned the IRQ that was raised.
- * host_int The contents of the HOST_INT port.
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev A pointer to a device structure of the
+ * adapter holding the PHY to be serviced.
*
- * This driver is structured to determine EOC occurances by reading the
- * CSTAT member of the list structure. Tx EOC interrupts are disabled
- * via the DIO INTDIS register. However, TLAN chips before revision 3.0
- * didn't have this functionality, so process EOC events if this is the
- * case.
+ * This function services an interrupt generated by the
+ * internal PHY. It can turn on/off the link LED. See
+ * Chap. 7, "Physical Interface (PHY)" of "ThunderLAN
+ * Programmer's Guide".
*
- ************************************************************************/
-
- u32 TLan_HandleTxEOC( struct device *dev, u16 host_int )
- {
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- TLanList *head_list;
- u32 ack = 1;
-
- host_int = 0;
- if ( priv->tlanRev < 0x30 ) {
- TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOC (Head=%d Tail=%d) -- IRQ\n", priv->txHead, priv->txTail );
- head_list = priv->txList + priv->txHead;
- if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) {
- outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
- ack |= TLAN_HC_GO;
- } else {
- priv->txInProgress = 0;
- }
- }
- return ack;
+ **************************************************************/
- } /* TLan_HandleTxEOC */
+int TLan_PhyInternalService( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 tlphy_sts;
+ u16 gen_sts;
+ u16 an_exp;
+ u32 io;
+ u16 phy;
+ io = dev->base_addr;
+ phy = priv->phyAddr;
+ TLan_MiiReadReg( io, phy, TLAN_TLPHY_STS, &tlphy_sts );
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &gen_sts );
+ TLan_MiiReadReg( io, phy, MII_AN_EXP, &an_exp );
+ if ( ( gen_sts & MII_GS_LINK ) || aui ) {
+ priv->phyOnline = 1;
+ TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
+ } else {
+ priv->phyOnline = 0;
+ TLan_DioWrite8( io, TLAN_LED_REG, 0 );
+ }
+ if ( ( tlphy_sts & TLAN_TS_POLOK ) == 0) {
+ u16 value;
+ TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value);
+ value |= TLAN_TC_SWAPOL;
+ TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value);
+ }
- /*************************************************************************
- * TLan_HandleStatusCheck
- *
- * Returns: 0 if Adapter check, 1 if Network Status check.
- * Parms: dev Device assigned the IRQ that was raised.
- * host_int The contents of the HOST_INT port.
- *
- * This function handles Adapter Check/Network Status interrupts
- * generated by the adapter. It checks the vector in the HOST_INT
- * register to determine if it is an Adapter Check interrupt. If so,
- * it resets the adapter. Otherwise it clears the status registers
- * and services the PHY.
- *
- ************************************************************************/
-
- u32 TLan_HandleStatusCheck( struct device *dev, u16 host_int )
- {
- u32 ack;
- u32 error;
- u8 net_sts;
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
-
- ack = 1;
- if ( host_int & TLAN_HI_IV_MASK ) {
- error = inl( dev->base_addr + TLAN_CH_PARM );
- printk( "TLAN: Adaptor Check on device %s err = 0x%x\n", dev->name, error );
- TLan_ReadAndClearStats( dev, TLAN_RECORD );
- outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
- TLan_ResetLists( dev );
- TLan_Reset( dev );
- dev->tbusy = 0;
- TLan_SetMac( dev, 0, dev->dev_addr );
- if ( priv->timerType == 0 ) {
- if ( priv->phyFlags & TLAN_PHY_AUTONEG ) {
- priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY;
- priv->timerSetAt = jiffies;
- priv->timerType = TLAN_TIMER_LINK;
- add_timer( &priv->timer );
- } else {
- //printk( " RX GO---->\n" );
- outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
- outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
- }
- }
- ack = 0;
- } else {
- net_sts = TLan_DioRead8( dev->base_addr, TLAN_NET_STS );
- if ( net_sts )
- TLan_DioWrite8( dev->base_addr, TLAN_NET_STS, net_sts );
- if ( net_sts & TLAN_NET_STS_MIRQ ) {
- (*priv->phyService)( dev );
- if (debug) {
- TLan_PhyPrint( dev );
- }
- }
- TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Status Check! %s Net_Sts=%x\n", dev->name, (unsigned) net_sts );
- }
+ return 0;
- return ack;
+} /* TLan_PhyInternalService */
- } /* TLan_HandleStatusCheck */
-
- /*************************************************************************
- * TLan_HandleRxEOC
+ /***************************************************************
+ * TLan_PhyDp83840aCheck
*
- * Returns: 1
- * Parms: dev Device assigned the IRQ that was raised.
- * host_int The contents of the HOST_INT port.
- *
- * This driver is structured to determine EOC occurances by reading the
- * CSTAT member of the list structure. Rx EOC interrupts are disabled
- * via the DIO INTDIS register. However, TLAN chips before revision 3.0
- * didn't have this CSTAT member or a INTDIS register, so if this chip
- * is pre-3.0, process EOC interrupts normally.
- *
- ************************************************************************/
-
- u32 TLan_HandleRxEOC( struct device *dev, u16 host_int )
- {
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- TLanList *head_list;
- u32 ack = 1;
-
- host_int = 0;
- if ( priv->tlanRev < 0x30 ) {
- TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOC (Head=%d Tail=%d) -- IRQ\n", priv->rxHead, priv->rxTail );
- head_list = priv->rxList + priv->rxHead;
- outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
- ack |= TLAN_HC_GO | TLAN_HC_RT;
- priv->rxEocCount++;
- }
- return ack;
-
- } /* TLan_HandleRxEOC */
-
-
-
- static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = {
- TLan_HandleInvalid,
- TLan_HandleTxEOF,
- TLan_HandleStatOverflow,
- TLan_HandleRxEOF,
- TLan_HandleDummy,
- TLan_HandleTxEOC,
- TLan_HandleStatusCheck,
- TLan_HandleRxEOC
- };
-
-
-
-
-/*****************************************************************************
-******************************************************************************
-
- ThunderLAN Driver Timer Function
-
-******************************************************************************
-*****************************************************************************/
-
- static void TLan_Timer( unsigned long );
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev A pointer to a device structure of the
+ * adapter holding the PHY to be reset.
+ *
+ * This function resets a National Semiconductor DP83840A
+ * 10/100 Mb/s PHY device. See National Semiconductor's
+ * data sheet for more info. This PHY is used on Compaq
+ * Netelligent 10/100 cards.
+ *
+ **************************************************************/
+static int TLan_PhyDp83840aCheck( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 gen_ctl;
+ int i;
+ u32 io;
+ u16 phy;
+ u16 value;
+ u8 sio;
+ io = dev->base_addr;
+ phy = priv->phyAddr;
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl );
+ if ( gen_ctl & MII_GC_PDOWN ) {
+ TLan_MiiSync( io );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_
+ISOLATE );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK );
+ for ( i = 0; i < 500000; i++ )
+ SLOW_DOWN_IO;
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK );
+ TLan_MiiSync( io );
+ }
+
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
+ while ( value & MII_GC_RESET )
+ TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value );
+
+ // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX );
+ // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 );
+ TLan_MiiReadReg( io, phy, MII_AN_ADV, &value );
+ value &= ~0x0140;
+ TLan_MiiWriteReg( io, phy, MII_AN_ADV, value );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1000 );
+ TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1200 );
+
+ for ( i = 0; i < 50000; i++ )
+ SLOW_DOWN_IO;
- /*************************************************************************
- * TLan_Timer
- *
- * Returns: Nothing
- * Parms: data A value given to add timer when add_timer was
- * called.
- *
- * This function handles timed functionality for the TLAN driver. The
- * two current timer uses are for delaying for autonegotionation and
- * driving the ACT LED.
- * - Autonegotiation requires being allowed about 2 1/2 seconds before
- * attempting to transmit a packet. It would be a very bad thing
- * to hang the kernel this long, so the driver doesn't allow
- * transmission 'til after this time, for certain PHYs. It would
- * be much nicer if all PHYs were interrupt-capable like the
- * internal PHY.
- * - The ACT LED, which shows adapter activity, is driven by the driver,
- * and so must be left on for a short period to power up the LED so
- * it can be seen. This delay can be changed by changing the
- * TLAN_TIMER_ACT_DELAY in tlan.h, if desired. 10 jiffies produces a
- * slightly sluggish response.
- *
- ************************************************************************/
-
- void TLan_Timer( unsigned long data )
- {
- struct device *dev = (struct device *) data;
- u16 gen_sts;
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
-
- // printk( "TLAN: %s Entered Timer, type = %d\n", dev->name, priv->timerType );
-
- switch ( priv->timerType ) {
- case TLAN_TIMER_LINK:
- TLan_MiiReadReg( dev->base_addr, priv->phyAddr, MII_GEN_STS, &gen_sts );
- if ( gen_sts & MII_GS_LINK ) {
- priv->phyOnline = 1;
- outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
- outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
- priv->timerSetAt = 0;
- priv->timerType = 0;
- } else {
- priv->timer.expires = jiffies + ( TLAN_TIMER_LINK_DELAY * 2 );
- add_timer( &priv->timer );
- }
- break;
- case TLAN_TIMER_ACT:
- if ( jiffies - priv->timerSetAt >= TLAN_TIMER_ACT_DELAY ) {
- TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK );
- priv->timerSetAt = 0;
- priv->timerType = 0;
- } else {
- priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY;
- add_timer( &priv->timer );
- }
- break;
- default:
- break;
- }
+/*
+ // Read Possible Latched Link Status
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
+ // Read Real Link Status
+ TLan_MiiReadReg( io, phy, MII_GEN_STS, &value );
+ if ( value & MII_GS_LINK ) {
+ priv->phyOnline = 1;
+ TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK );
+ } else {
+ priv->phyOnline = 0;
+ TLan_DioWrite8( io, TLAN_LED_REG, 0 );
+ }
+
+ // Enable Interrupts
+ TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value );
+ value |= TLAN_TC_INTEN;
+ TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value );
+*/
+ sio = TLan_DioRead8( dev->base_addr, TLAN_NET_SIO );
+ sio &= ~TLAN_NET_SIO_MINTEN;
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_SIO, sio );
+// priv->phyOnline = 1;
+
+ return 0;
- } /* TLan_Timer */
+} /* TLan_PhyDp83840aCheck */
@@ -1684,696 +2281,455 @@
/*****************************************************************************
******************************************************************************
- ThunderLAN Driver Primary Functions
+ ThunderLAN Driver MII Routines
- These functions are more or less common to all Linux network drivers.
+ These routines are based on the information in Chap. 2 of the
+ "ThunderLAN Programmer's Guide", pp. 15-24.
******************************************************************************
*****************************************************************************/
- static int TLan_PciProbe( u8 *, u8 *, u8 *, u8 *, u32 *, u32 * );
- static int TLan_Init( struct device * );
- static int TLan_Open(struct device *dev);
- static int TLan_StartTx(struct sk_buff *, struct device *);
- static void TLan_HandleInterrupt(int, void *, struct pt_regs *);
- static int TLan_Close(struct device *);
- static struct net_device_stats *TLan_GetStats( struct device * );
- static void TLan_SetMulticastList( struct device * );
-
-#ifdef MODULE
-
- /*************************************************************************
- * init_module
+ /***************************************************************
+ * TLan_MiiReadReg
*
- * Returns: 0 if module installed ok, non-zero if not.
- * Parms: None
+ * Returns:
+ * 0 if ack received ok
+ * 1 otherwise.
*
- * This function begins the setup of the driver creating a pad buffer,
- * finding all TLAN devices (matching TLanDeviceList entries), and
- * creating and initializing a device structure for each adapter.
+ * Parms:
+ * base_port The base IO port of the adapter in
+ * question.
+ * dev The address of the PHY to be queried.
+ * reg The register whose contents are to be
+ * retreived.
+ * val A pointer to a variable to store the
+ * retrieved value.
*
- ************************************************************************/
+ * This function uses the TLAN's MII bus to retreive the contents
+ * of a given register on a PHY. It sends the appropriate info
+ * and then reads the 16-bit register value from the MII bus via
+ * the TLAN SIO register.
+ *
+ **************************************************************/
- extern int init_module(void)
- {
- int failed, found;
- size_t dev_size;
- struct device *dev;
- TLanPrivateInfo *priv;
- u8 bus, dfn, irq, rev;
- u32 io_base, dl_ix;
+int TLan_MiiReadReg(u16 base_port, u16 dev, u16 reg, u16 *val)
+{
+ u8 nack;
+ u16 sio, tmp;
+ u32 i;
+ int err;
+ int minten;
- printk("TLAN driver, v%d.%d, (C) 1997 Caldera, Inc.\n",
- TLanVersionMajor,
- TLanVersionMinor
- );
+ err = FALSE;
+ outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
- TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, GFP_KERNEL | GFP_DMA );
- if ( TLanPadBuffer == NULL ) {
- printk( "TLAN: Could not allocate memory for pad buffer.\n" );
- return -ENOMEM;
- }
- memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE );
-
- dev_size = sizeof(struct device) + sizeof(TLanPrivateInfo);
- while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix ) ) ) {
- dev = (struct device *) kmalloc( dev_size, GFP_KERNEL );
- if ( dev == NULL ) {
- printk( "TLAN: Could not allocate memory for device.\n" );
- continue;
- }
- memset( dev, 0, dev_size );
+ cli();
- dev->priv = priv = ( (void *) dev ) + sizeof(struct device);
- dev->name = priv->devName;
- strcpy( priv->devName, " " );
- dev->base_addr = io_base;
- dev->irq = irq;
- dev->init = TLan_Init;
+ TLan_MiiSync(base_port);
- priv->pciBus = bus;
- priv->pciDeviceFn = dfn;
- priv->pciRevision = rev;
- priv->pciEntry = dl_ix;
+ minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio );
+ if ( minten )
+ TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio);
- ether_setup( dev );
+ TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */
+ TLan_MiiSendData( base_port, 0x2, 2 ); /* Read ( 10b ) */
+ TLan_MiiSendData( base_port, dev, 5 ); /* Device # */
+ TLan_MiiSendData( base_port, reg, 5 ); /* Register # */
- failed = register_netdev( dev );
- if ( failed ) {
- printk( "TLAN: Could not register network device. Freeing struct.\n" );
- kfree( dev );
- } else {
- priv->nextDevice = TLanDevices;
- TLanDevices = dev;
- TLanDevicesInstalled++;
- printk("TLAN: %s irq=%2d io=%04x, %s\n", dev->name, (int) irq, io_base, TLanDeviceList[dl_ix].deviceName );
- }
- }
-
- // printk( "TLAN: Found %d device(s).\n", TLanDevicesInstalled );
+ TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio); /* Change direction */
- return ( ( TLanDevicesInstalled >= 0 ) ? 0 : -ENODEV );
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Clock Idle bit */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Wait 300ns */
- } /* init_module */
+ nack = TLan_GetBit(TLAN_NET_SIO_MDATA, sio); /* Check for ACK */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio); /* Finish ACK */
+ if (nack) { /* No ACK, so fake it */
+ for (i = 0; i < 16; i++) {
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ }
+ tmp = 0xffff;
+ err = TRUE;
+ } else { /* ACK, so read data */
+ for (tmp = 0, i = 0x8000; i; i >>= 1) {
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
+ if (TLan_GetBit(TLAN_NET_SIO_MDATA, sio))
+ tmp |= i;
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ }
+ }
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Idle cycle */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ if ( minten )
+ TLan_SetBit(TLAN_NET_SIO_MINTEN, sio);
- /*************************************************************************
- * cleanup_module
- *
- * Returns: Nothing
- * Parms: None
- *
- * Goes through the TLanDevices list and frees the device structs and
- * memory associated with each device (lists and buffers). It also
- * ureserves the IO port regions associated with this device.
- *
- ************************************************************************/
-
- extern void cleanup_module(void)
- {
- struct device *dev;
- TLanPrivateInfo *priv;
-
- while (TLanDevicesInstalled) {
- dev = TLanDevices;
- priv = (TLanPrivateInfo *) dev->priv;
- if ( priv->dmaStorage )
- kfree( priv->dmaStorage );
- release_region( dev->base_addr, 0x10 );
- unregister_netdev( dev );
- TLanDevices = priv->nextDevice;
- kfree( dev );
- TLanDevicesInstalled--;
- }
- kfree( TLanPadBuffer );
+ *val = tmp;
- } /* cleanup_module */
+ sti();
+ return err;
-#else /* MODULE */
+} /* TLan_MiiReadReg */
- /*************************************************************************
- * tlan_probe
- *
- * Returns: 0 on success, error code on error
- * Parms: dev device struct to use if adapter is found.
- *
- * The name is lower case to fit in with all the rest of the
- * netcard_probe names. This function looks for a/another TLan based
- * adapter, setting it up with the provided device struct if one is
- * found.
+ /***************************************************************
+ * TLan_MiiSendData
*
- ************************************************************************/
-
- extern int tlan_probe( struct device *dev )
- {
- static int pad_allocated = 0;
- int found;
- TLanPrivateInfo *priv;
- u8 bus, dfn, irq, rev;
- u32 io_base, dl_ix;
-
- found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix );
- if ( found ) {
- dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL );
- if ( dev->priv == NULL ) {
- printk( "TLAN: Could not allocate memory for device.\n" );
- }
- memset( dev->priv, 0, sizeof(TLanPrivateInfo) );
- priv = (TLanPrivateInfo *) dev->priv;
-
- dev->name = priv->devName;
- strcpy( priv->devName, " " );
-
- dev = init_etherdev( dev, sizeof(TLanPrivateInfo) );
-
- dev->base_addr = io_base;
- dev->irq = irq;
-
- priv->pciBus = bus;
- priv->pciDeviceFn = dfn;
- priv->pciRevision = rev;
- priv->pciEntry = dl_ix;
-
- if ( ! pad_allocated ) {
- TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, GFP_KERNEL | GFP_DMA );
- if ( TLanPadBuffer == NULL ) {
- printk( "TLAN: Could not allocate memory for pad buffer.\n" );
- } else {
- pad_allocated = 1;
- memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE );
- }
- }
- printk("TLAN %d.%d: %s irq=%2d io=%04x, %s\n", TLanVersionMajor,
- TLanVersionMinor,
- dev->name,
- (int) irq,
- io_base,
- TLanDeviceList[dl_ix].deviceName );
- TLan_Init( dev );
- }
-
- return ( ( found ) ? 0 : -ENODEV );
-
- } /* tlan_probe */
-
-
-#endif /* MODULE */
-
-
-
-
- /*************************************************************************
- * TLan_PciProbe
+ * Returns:
+ * Nothing
+ * Parms:
+ * base_port The base IO port of the adapter in
+ * question.
+ * dev The address of the PHY to be queried.
+ * data The value to be placed on the MII bus.
+ * num_bits The number of bits in data that are to
+ * be placed on the MII bus.
*
- * Returns: 1 if another TLAN card was found, 0 if not.
- * Parms: pci_bus The PCI bus the card was found on.
- * pci_dfn The PCI whatever the card was found at.
- * pci_irq The IRQ of the found adapter.
- * pci_rev The revision of the adapter.
- * pci_io_base The first IO port used by the adapter.
- * dl_ix The index in the device list of the adapter.
- *
- * This function searches for an adapter with PCI vendor and device
- * IDs matching those in the TLanDeviceList. The function 'remembers'
- * the last device it found, and so finds a new device (if anymore are
- * to be found) each time the function is called. It then looks up
- * pertinent PCI info and returns it to the caller.
+ * This function sends on sequence of bits on the MII
+ * configuration bus.
*
- ************************************************************************/
+ **************************************************************/
- int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_io_base, u32 *dl_ix )
- {
- static int dl_index = 0;
- static int pci_index = 0;
+void TLan_MiiSendData( u16 base_port, u32 data, unsigned num_bits )
+{
+ u16 sio;
+ u32 i;
- int not_found;
- u8 pci_latency;
- u16 pci_command;
+ if ( num_bits == 0 )
+ return;
+ outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR );
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+ TLan_SetBit( TLAN_NET_SIO_MTXEN, sio );
- if ( ! pcibios_present() ) {
- printk( "TLAN: PCI Bios not present.\n" );
- return 0;
- }
-
- for (; TLanDeviceList[dl_index].vendorId != 0; dl_index++) {
- not_found = pcibios_find_device( TLanDeviceList[dl_index].vendorId,
- TLanDeviceList[dl_index].deviceId,
- pci_index,
- pci_bus,
- pci_dfn
- );
- if ( ! not_found ) {
- TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: found: Vendor Id = 0x%hx, Device Id = 0x%hx\n",
- TLanDeviceList[dl_index].vendorId,
- TLanDeviceList[dl_index].deviceId
- );
-
- pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_REVISION_ID, pci_rev);
- pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_INTERRUPT_LINE, pci_irq);
- pcibios_read_config_word ( *pci_bus, *pci_dfn, PCI_COMMAND, &pci_command);
- pcibios_read_config_dword( *pci_bus, *pci_dfn, PCI_BASE_ADDRESS_0, pci_io_base);
- pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, &pci_latency);
-
- if (pci_latency < 0x10) {
- pcibios_write_config_byte( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, 0xff);
- TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Setting latency timer to max.\n");
- }
-
- if ((pci_command & PCI_COMMAND_IO) && (*pci_io_base & 0x3)) {
- *pci_io_base &= PCI_BASE_ADDRESS_IO_MASK;
- TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: IO mapping is available at %x.\n", *pci_io_base);
- } else {
- *pci_io_base = 0;
- printk("TLAN: IO mapping not available, ignoring device.\n");
- }
-
- if (pci_command & PCI_COMMAND_MASTER) {
- TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Bus mastering is active.\n");
- }
-
- pci_index++;
-
- if ( *pci_io_base ) {
- *dl_ix = dl_index;
- return 1;
- }
-
- } else {
- pci_index = 0;
- }
- }
+ for ( i = ( 0x1 << ( num_bits - 1 ) ); i; i >>= 1 ) {
+ TLan_ClearBit( TLAN_NET_SIO_MCLK, sio );
+ TLan_GetBit( TLAN_NET_SIO_MCLK, sio );
+ if ( data & i )
+ TLan_SetBit( TLAN_NET_SIO_MDATA, sio );
+ else
+ TLan_ClearBit( TLAN_NET_SIO_MDATA, sio );
+ TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
+ TLan_GetBit( TLAN_NET_SIO_MCLK, sio );
+ }
- return 0;
+} /* TLan_MiiSendData */
- } /* TLan_PciProbe */
-
- /*************************************************************************
- * TLan_Init
+ /***************************************************************
+ * TLan_MiiSync
*
- * Returns: 0 on success, error code otherwise.
- * Parms: dev The structure of the device to be init'ed.
+ * Returns:
+ * Nothing
+ * Parms:
+ * base_port The base IO port of the adapter in
+ * question.
*
- * This function completes the initialization of the device structure
- * and driver. It reserves the IO addresses, allocates memory for the
- * lists and bounce buffers, retrieves the MAC address from the eeprom
- * and assignes the device's methods.
- *
- ************************************************************************/
-
- int TLan_Init( struct device *dev )
- {
- int dma_size;
- int err;
- int i;
- TLanPrivateInfo *priv;
-
- priv = (TLanPrivateInfo *) dev->priv;
-
- err = check_region( dev->base_addr, 0x10 );
- if ( err ) {
- printk( "TLAN: %s: Io port region 0x%lx size 0x%x in use.\n", dev->name, dev->base_addr, 0x10 );
- return -EIO;
- }
- request_region( dev->base_addr, 0x10, TLanSignature );
-
- dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS ) * ( sizeof(TLanList) + TLAN_MAX_FRAME_SIZE );
- priv->dmaStorage = kmalloc( dma_size, GFP_KERNEL | GFP_DMA );
- if ( priv->dmaStorage == NULL ) {
- printk( "TLAN: Could not allocate lists and buffers for %s.\n", dev->name );
- return -ENOMEM;
- }
- memset( priv->dmaStorage, 0, dma_size );
- priv->rxList = (TLanList *) ( ( ( (u32) priv->dmaStorage ) + 7 ) & 0xFFFFFFF8 );
- priv->txList = priv->rxList + TLAN_NUM_RX_LISTS;
- priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS );
- priv->txBuffer = priv->rxBuffer + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE );
+ * This functions syncs all PHYs in terms of the MII configuration
+ * bus.
+ *
+ **************************************************************/
- err = 0;
- for ( i = 0; i < 6 ; i++ )
- err |= TLan_EeReadByte( dev->base_addr, (u8) 0x83 + i, (u8 *) &dev->dev_addr[i] );
- if ( err )
- printk( "TLAN: %s: Error reading MAC Address from eeprom: %d\n", dev->name, err );
- dev->addr_len = 6;
+void TLan_MiiSync( u16 base_port )
+{
+ int i;
+ u16 sio;
- dev->open = &TLan_Open;
- dev->hard_start_xmit = &TLan_StartTx;
- dev->stop = &TLan_Close;
- dev->get_stats = &TLan_GetStats;
- dev->set_multicast_list = &TLan_SetMulticastList;
+ outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR );
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
- return 0;
+ TLan_ClearBit( TLAN_NET_SIO_MTXEN, sio );
+ for ( i = 0; i < 32; i++ ) {
+ TLan_ClearBit( TLAN_NET_SIO_MCLK, sio );
+ TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
+ }
- } /* TLan_Init */
+} /* TLan_MiiSync */
- /*************************************************************************
- * TLan_Open
+ /***************************************************************
+ * TLan_MiiWriteReg
*
- * Returns: 0 on success, error code otherwise.
- * Parms: dev Structure of device to be opened.
+ * Returns:
+ * Nothing
+ * Parms:
+ * base_port The base IO port of the adapter in
+ * question.
+ * dev The address of the PHY to be written to.
+ * reg The register whose contents are to be
+ * written.
+ * val The value to be written to the register.
*
- * This routine puts the driver and TLAN adapter in a state where it is
- * ready to send and receive packets. It allocates the IRQ, resets and
- * brings the adapter out of reset, and allows interrupts. It also
- * delays the startup for autonegotiation or sends a Rx GO command to
- * the adapter, as appropriate.
+ * This function uses the TLAN's MII bus to write the contents of a
+ * given register on a PHY. It sends the appropriate info and then
+ * writes the 16-bit register value from the MII configuration bus
+ * via the TLAN SIO register.
*
- ************************************************************************/
+ **************************************************************/
- int TLan_Open( struct device *dev )
- {
- int err;
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+void TLan_MiiWriteReg(u16 base_port, u16 dev, u16 reg, u16 val)
+{
+ u16 sio;
+ int minten;
- priv->tlanRev = TLan_DioRead8( dev->base_addr, TLAN_DEF_REVISION );
- err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev );
- if ( err ) {
- printk( "TLAN: Cannot open %s because IRQ %d is already in use.\n", dev->name, dev->irq );
- return -EAGAIN;
- }
+ outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
- MOD_INC_USE_COUNT;
-
- dev->tbusy = 0;
- dev->interrupt = 0;
- dev->start = 1;
-
- /* NOTE: It might not be necessary to read the stats before a
- reset if you don't care what the values are.
- */
- TLan_ResetLists( dev );
- TLan_ReadAndClearStats( dev, TLAN_IGNORE );
- TLan_Reset( dev );
- TLan_Reset( dev );
- TLan_SetMac( dev, 0, dev->dev_addr );
- outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
- if ( debug >= 1 )
- outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
-
- init_timer( &priv->timer );
- priv->timer.data = (unsigned long) dev;
- priv->timer.function = &TLan_Timer;
- if ( priv->phyFlags & TLAN_PHY_AUTONEG ) {
- priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY;
- priv->timerSetAt = jiffies;
- priv->timerType = TLAN_TIMER_LINK;
- add_timer( &priv->timer );
- } else {
- outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
- outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
- }
+ cli();
- TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s opened. Revision = %x\n", dev->name, priv->tlanRev );
+ TLan_MiiSync( base_port );
- return 0;
+ minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio );
+ if ( minten )
+ TLan_ClearBit( TLAN_NET_SIO_MINTEN, sio );
- } /* TLan_Open */
+ TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */
+ TLan_MiiSendData( base_port, 0x1, 2 ); /* Write ( 01b ) */
+ TLan_MiiSendData( base_port, dev, 5 ); /* Device # */
+ TLan_MiiSendData( base_port, reg, 5 ); /* Register # */
+ TLan_MiiSendData( base_port, 0x2, 2 ); /* Send ACK */
+ TLan_MiiSendData( base_port, val, 16 ); /* Send Data */
+ TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); /* Idle cycle */
+ TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
+ if ( minten )
+ TLan_SetBit( TLAN_NET_SIO_MINTEN, sio );
- /*************************************************************************
- * TLan_StartTx
- *
- * Returns: 0 on success, non-zero on failure.
- * Parms: skb A pointer to the sk_buff containing the frame to
- * be sent.
- * dev The device to send the data on.
- *
- * This function adds a frame to the Tx list to be sent ASAP. First it
- * verifies that the adapter is ready and there is room in the queue.
- * Then it sets up the next available list, copies the frame to the
- * corresponding buffer. If the adapter Tx channel is idle, it gives
- * adapter a Tx Go command on the list, otherwise it sets the forward
- * address of the previous list to point to this one. Then it frees
- * the sk_buff.
- *
- ************************************************************************/
-
- int TLan_StartTx( struct sk_buff *skb, struct device *dev )
- {
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- TLanList *tail_list;
- u8 *tail_buffer;
- int pad;
-
- if ( ! priv->phyOnline ) {
- TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s PHY is not ready\n", dev->name );
- dev_kfree_skb( skb, FREE_WRITE );
- return 0;
- }
+ sti();
- tail_list = priv->txList + priv->txTail;
- if ( tail_list->cStat != TLAN_CSTAT_UNUSED ) {
- TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s is busy (Head=%d Tail=%d)\n", dev->name, priv->txHead, priv->txTail );
- dev->tbusy = 1;
- priv->txBusyCount++;
- return 1;
- }
- tail_list->forward = 0;
- tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE );
- memcpy( tail_buffer, skb->data, skb->len );
- pad = TLAN_MIN_FRAME_SIZE - skb->len;
- if ( pad > 0 ) {
- tail_list->frameSize = (u16) skb->len + pad;
- tail_list->buffer[0].count = (u32) skb->len;
- tail_list->buffer[1].count = TLAN_LAST_BUFFER | (u32) pad;
- tail_list->buffer[1].address = virt_to_bus( TLanPadBuffer );
- } else {
- tail_list->frameSize = (u16) skb->len;
- tail_list->buffer[0].count = TLAN_LAST_BUFFER | (u32) skb->len;
- tail_list->buffer[1].count = 0;
- tail_list->buffer[1].address = 0;
- }
- // are we transferring?
- cli();
- tail_list->cStat = TLAN_CSTAT_READY;
- if ( ! priv->txInProgress ) {
- priv->txInProgress = 1;
- outw( 0x4, dev->base_addr + TLAN_HOST_INT );
- TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Starting TX on buffer %d\n", priv->txTail );
- outl( virt_to_bus( tail_list ), dev->base_addr + TLAN_CH_PARM );
- outl( TLAN_HC_GO | TLAN_HC_ACK, dev->base_addr + TLAN_HOST_CMD );
- } else {
- TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Adding buffer %d to TX channel\n", priv->txTail );
- if ( priv->txTail == 0 )
- ( priv->txList + ( TLAN_NUM_TX_LISTS - 1 ) )->forward = virt_to_bus( tail_list );
- else
- ( priv->txList + ( priv->txTail - 1 ) )->forward = virt_to_bus( tail_list );
- }
- sti();
- priv->txTail++;
- if ( priv->txTail >= TLAN_NUM_TX_LISTS )
- priv->txTail = 0;
+} /* TLan_MiiWriteReg */
- dev_kfree_skb( skb, FREE_WRITE );
-
- dev->trans_start = jiffies;
- return 0;
+/*****************************************************************************
+******************************************************************************
- } /* TLan_StartTx */
+ ThunderLAN Driver Eeprom routines
+ The Compaq Netelligent 10 and 10/100 cards use a Microchip 24C02A
+ EEPROM. These functions are based on information in Microchip's
+ data sheet. I don't know how well this functions will work with
+ other EEPROMs.
+******************************************************************************
+*****************************************************************************/
- /*************************************************************************
- * TLan_HandleInterrupt
- *
- * Returns: Nothing
- * Parms: irq The line on which the interrupt occurred.
- * dev_id A pointer to the device assigned to this irq line.
- * regs ???
+ /***************************************************************
+ * TLan_EeSendStart
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * io_base The IO port base address for the
+ * TLAN device with the EEPROM to
+ * use.
*
- * This function handles an interrupt generated by its assigned TLAN
- * adapter. The function deactivates interrupts on its adapter, records
- * the type of interrupt, executes the appropriate subhandler, and
- * acknowdges the interrupt to the adapter (thus re-enabling adapter
- * interrupts.
+ * This function sends a start cycle to an EEPROM attached
+ * to a TLAN chip.
*
- ************************************************************************/
+ **************************************************************/
- void TLan_HandleInterrupt(int irq, void *dev_id, struct pt_regs *regs)
- {
- u32 ack;
- struct device *dev;
- u32 host_cmd;
- u16 host_int;
- int type;
+void TLan_EeSendStart( u16 io_base )
+{
+ u16 sio;
- dev = (struct device *) dev_id;
+ outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
+ sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
- if ( dev->interrupt )
- printk( "TLAN: Re-entering interrupt handler for %s: %d.\n" , dev->name, dev->interrupt );
- dev->interrupt++;
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+ TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
- cli();
+} /* TLan_EeSendStart */
- host_int = inw( dev->base_addr + TLAN_HOST_INT );
- outw( host_int, dev->base_addr + TLAN_HOST_INT ); // Deactivate Ints
- type = ( host_int & TLAN_HI_IT_MASK ) >> 2;
- ack = TLanIntVector[type]( dev, host_int );
- sti();
+ /***************************************************************
+ * TLan_EeSendByte
+ *
+ * Returns:
+ * If the correct ack was received, 0, otherwise 1
+ * Parms: io_base The IO port base address for the
+ * TLAN device with the EEPROM to
+ * use.
+ * data The 8 bits of information to
+ * send to the EEPROM.
+ * stop If TLAN_EEPROM_STOP is passed, a
+ * stop cycle is sent after the
+ * byte is sent after the ack is
+ * read.
+ *
+ * This function sends a byte on the serial EEPROM line,
+ * driving the clock to send each bit. The function then
+ * reverses transmission direction and reads an acknowledge
+ * bit.
+ *
+ **************************************************************/
+
+int TLan_EeSendByte( u16 io_base, u8 data, int stop )
+{
+ int err;
+ u8 place;
+ u16 sio;
+
+ outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
+ sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ // Assume clock is low, tx is enabled;
+ for ( place = 0x80; place != 0; place >>= 1 ) {
+ if ( place & data )
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+ else
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ }
+ TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio );
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ err = TLan_GetBit( TLAN_NET_SIO_EDATA, sio );
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
- if ( ack ) {
- host_cmd = TLAN_HC_ACK | ack | ( type << 18 );
- outl( host_cmd, dev->base_addr + TLAN_HOST_CMD );
- }
+ if ( ( ! err ) && stop ) {
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is h
+igh
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+ }
- dev->interrupt--;
+ return ( err );
- } /* TLan_HandleInterrupts */
+} /* TLan_EeSendByte */
- /*************************************************************************
- * TLan_Close
- *
- * Returns: An error code.
- * Parms: dev The device structure of the device to close.
+ /***************************************************************
+ * TLan_EeReceiveByte
*
- * This function shuts down the adapter. It records any stats, puts
- * the adapter into reset state, deactivates its time as needed, and
- * frees the irq it is using.
+ * Returns:
+ * Nothing
+ * Parms:
+ * io_base The IO port base address for the
+ * TLAN device with the EEPROM to
+ * use.
+ * data An address to a char to hold the
+ * data sent from the EEPROM.
+ * stop If TLAN_EEPROM_STOP is passed, a
+ * stop cycle is sent after the
+ * byte is received, and no ack is
+ * sent.
+ *
+ * This function receives 8 bits of data from the EEPROM
+ * over the serial link. It then sends and ack bit, or no
+ * ack and a stop bit. This function is used to retrieve
+ * data after the address of a byte in the EEPROM has been
+ * sent.
+ *
+ **************************************************************/
+
+void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop )
+{
+ u8 place;
+ u16 sio;
+
+ outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
+ sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+ *data = 0;
+
+ // Assume clock is low, tx is enabled;
+ TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio );
+ for ( place = 0x80; place; place >>= 1 ) {
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ if ( TLan_GetBit( TLAN_NET_SIO_EDATA, sio ) )
+ *data |= place;
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ }
+
+ TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
+ if ( ! stop ) {
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // Ack = 0
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ } else {
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); // No ack = 1 (?)
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is h
+igh
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+ }
+
+} /* TLan_EeReceiveByte */
+
+
+
+
+ /***************************************************************
+ * TLan_EeReadByte
*
- ************************************************************************/
-
- int TLan_Close(struct device *dev)
- {
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
-
- dev->start = 0;
- dev->tbusy = 1;
-
- TLan_ReadAndClearStats( dev, TLAN_RECORD );
- outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
- if ( priv->timerSetAt != 0 )
- del_timer( &priv->timer );
- free_irq( dev->irq, dev );
- TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s closed.\n", dev->name );
-
- MOD_DEC_USE_COUNT;
-
- return 0;
-
- } /* TLan_Close */
-
-
-
-
- /*************************************************************************
- * TLan_GetStats
- *
- * Returns: A pointer to the device's statistics structure.
- * Parms: dev The device structure to return the stats for.
+ * Returns:
+ * No error = 0, else, the stage at which the error
+ * occured.
+ * Parms:
+ * io_base The IO port base address for the
+ * TLAN device with the EEPROM to
+ * use.
+ * ee_addr The address of the byte in the
+ * EEPROM whose contents are to be
+ * retrieved.
+ * data An address to a char to hold the
+ * data obtained from the EEPROM.
*
- * This function updates the devices statistics by reading the TLAN
- * chip's onboard registers. Then it returns the address of the
- * statistics structure.
+ * This function reads a byte of information from an byte
+ * cell in the EEPROM.
*
- ************************************************************************/
+ **************************************************************/
- struct net_device_stats *TLan_GetStats( struct device *dev )
- {
- TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
- int i;
+int TLan_EeReadByte( u16 io_base, u8 ee_addr, u8 *data )
+{
+ int err;
- /* Should only read stats if open ? */
- TLan_ReadAndClearStats( dev, TLAN_RECORD );
+ cli();
- TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: %s EOC count = %d\n", dev->name, priv->rxEocCount );
- TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s Busy count = %d\n", dev->name, priv->txBusyCount );
- if ( debug & TLAN_DEBUG_GNRL ) {
- TLan_PrintDio( dev->base_addr );
- TLan_PhyPrint( dev );
- }
- if ( debug & TLAN_DEBUG_LIST ) {
- for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ )
- TLan_PrintList( priv->rxList + i, "RX", i );
- for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ )
- TLan_PrintList( priv->txList + i, "TX", i );
- }
-
- return ( &( (TLanPrivateInfo *) dev->priv )->stats );
+ TLan_EeSendStart( io_base );
+ err = TLan_EeSendByte( io_base, 0xA0, TLAN_EEPROM_ACK );
+ if (err)
+ return 1;
+ err = TLan_EeSendByte( io_base, ee_addr, TLAN_EEPROM_ACK );
+ if (err)
+ return 2;
+ TLan_EeSendStart( io_base );
+ err = TLan_EeSendByte( io_base, 0xA1, TLAN_EEPROM_ACK );
+ if (err)
+ return 3;
+ TLan_EeReceiveByte( io_base, data, TLAN_EEPROM_STOP );
- } /* TLan_GetStats */
+ sti();
+ return 0;
+} /* TLan_EeReadByte */
- /*************************************************************************
- * TLan_SetMulticastList
- *
- * Returns: Nothing
- * Parms: dev The device structure to set the multicast list for.
- *
- * This function sets the TLAN adaptor to various receive modes. If the
- * IFF_PROMISC flag is set, promiscuous mode is acitviated. Otherwise,
- * promiscuous mode is turned off. If the IFF_ALLMULTI flag is set, then
- * the hash table is set to receive all group addresses. Otherwise, the
- * first three multicast addresses are stored in AREG_1-3, and the rest
- * are selected via the hash table, as necessary.
- *
- ************************************************************************/
-
- void TLan_SetMulticastList( struct device *dev )
- {
- struct dev_mc_list *dmi = dev->mc_list;
- u32 hash1 = 0;
- u32 hash2 = 0;
- int i;
- u32 offset;
- u8 tmp;
-
- if ( dev->flags & IFF_PROMISC ) {
- tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD );
- TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp | TLAN_NET_CMD_CAF );
- } else {
- tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD );
- TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp & ~TLAN_NET_CMD_CAF );
- if ( dev->flags & IFF_ALLMULTI ) {
- for ( i = 0; i < 3; i++ )
- TLan_SetMac( dev, i + 1, NULL );
- TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, 0xFFFFFFFF );
- TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, 0xFFFFFFFF );
- } else {
- for ( i = 0; i < dev->mc_count; i++ ) {
- if ( i < 3 ) {
- TLan_SetMac( dev, i + 1, (char *) &dmi->dmi_addr );
- } else {
- offset = TLan_HashFunc( (u8 *) &dmi->dmi_addr );
- if ( offset < 32 )
- hash1 |= ( 1 << offset );
- else
- hash2 |= ( 1 << ( offset - 32 ) );
- }
- dmi = dmi->next;
- }
- for ( ; i < 3; i++ )
- TLan_SetMac( dev, i + 1, NULL );
- TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, hash1 );
- TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, hash2 );
- }
- }
-
- } /* TLan_SetRxMode */
diff --git a/drivers/net/tlan.h b/drivers/net/tlan.h
index c66e70582..c4f6d6c5e 100644
--- a/drivers/net/tlan.h
+++ b/drivers/net/tlan.h
@@ -1,6 +1,5 @@
#ifndef TLAN_H
#define TLAN_H
-
/********************************************************************
*
* Linux ThunderLAN Driver
@@ -17,6 +16,7 @@
*
********************************************************************/
+
#include <asm/io.h>
#include <asm/types.h>
#include <linux/netdevice.h>
@@ -33,23 +33,23 @@
*
****************************************************************/
- #define FALSE 0
- #define TRUE 1
+#define FALSE 0
+#define TRUE 1
- #define TLAN_MIN_FRAME_SIZE 64
- #define TLAN_MAX_FRAME_SIZE 1600
+#define TLAN_MIN_FRAME_SIZE 64
+#define TLAN_MAX_FRAME_SIZE 1600
- #define TLAN_NUM_RX_LISTS 4
- #define TLAN_NUM_TX_LISTS 8
+#define TLAN_NUM_RX_LISTS 4
+#define TLAN_NUM_TX_LISTS 8
- #define TLAN_IGNORE 0
- #define TLAN_RECORD 1
+#define TLAN_IGNORE 0
+#define TLAN_RECORD 1
- #define TLAN_DBG(lvl, format, args...) if ( debug & lvl ) printk( format, ##args );
- #define TLAN_DEBUG_GNRL 0x0001
- #define TLAN_DEBUG_TX 0x0002
- #define TLAN_DEBUG_RX 0x0004
- #define TLAN_DEBUG_LIST 0x0008
+#define TLAN_DBG(lvl, format, args...) if (debug&lvl) printk( format, ##args );
+#define TLAN_DEBUG_GNRL 0x0001
+#define TLAN_DEBUG_TX 0x0002
+#define TLAN_DEBUG_RX 0x0004
+#define TLAN_DEBUG_LIST 0x0008
@@ -59,22 +59,23 @@
*
****************************************************************/
- /* NOTE: These should be moved to pci.h someday */
- #define PCI_DEVICE_ID_NETELLIGENT_10 0xAE34
- #define PCI_DEVICE_ID_NETELLIGENT_10_100 0xAE32
- #define PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED 0xAE35
- #define PCI_DEVICE_ID_NETFLEX_3P 0xF130
- #define PCI_DEVICE_ID_NETFLEX_3P_BNC 0xF150
- #define PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT 0xAE43
- #define PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL 0xAE40
- #define PCI_DEVICE_ID_DESKPRO_4000_5233MMX 0xB011
+ /* NOTE: These have been moved to pci.h, will use them
+ eventually */
+#define PCI_DEVICE_ID_NETELLIGENT_10 0xAE34
+#define PCI_DEVICE_ID_NETELLIGENT_10_100 0xAE32
+#define PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED 0xAE35
+#define PCI_DEVICE_ID_NETFLEX_3P 0xF130
+#define PCI_DEVICE_ID_NETFLEX_3P_BNC 0xF150
+#define PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT 0xAE43
+#define PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL 0xAE40
+#define PCI_DEVICE_ID_DESKPRO_4000_5233MMX 0xB011
- typedef struct tlan_pci_id {
- u16 vendorId;
- u16 deviceId;
- char *deviceName;
- } TLanPciId;
+typedef struct tlan_pci_id {
+ u16 vendorId;
+ u16 deviceId;
+ char *deviceName;
+} TLanPciId;
@@ -84,32 +85,32 @@
*
****************************************************************/
- #define TLAN_BUFFERS_PER_LIST 10
- #define TLAN_LAST_BUFFER 0x80000000
- #define TLAN_CSTAT_UNUSED 0x8000
- #define TLAN_CSTAT_FRM_CMP 0x4000
- #define TLAN_CSTAT_READY 0x3000
- #define TLAN_CSTAT_EOC 0x0800
- #define TLAN_CSTAT_RX_ERROR 0x0400
- #define TLAN_CSTAT_PASS_CRC 0x0200
- #define TLAN_CSTAT_DP_PR 0x0100
+#define TLAN_BUFFERS_PER_LIST 10
+#define TLAN_LAST_BUFFER 0x80000000
+#define TLAN_CSTAT_UNUSED 0x8000
+#define TLAN_CSTAT_FRM_CMP 0x4000
+#define TLAN_CSTAT_READY 0x3000
+#define TLAN_CSTAT_EOC 0x0800
+#define TLAN_CSTAT_RX_ERROR 0x0400
+#define TLAN_CSTAT_PASS_CRC 0x0200
+#define TLAN_CSTAT_DP_PR 0x0100
- typedef struct tlan_buffer_ref_tag {
- u32 count;
- u32 address;
- } TLanBufferRef;
+typedef struct tlan_buffer_ref_tag {
+ u32 count;
+ u32 address;
+} TLanBufferRef;
- typedef struct tlan_list_tag {
- u32 forward;
- u16 cStat;
- u16 frameSize;
- TLanBufferRef buffer[TLAN_BUFFERS_PER_LIST];
- } TLanList;
+typedef struct tlan_list_tag {
+ u32 forward;
+ u16 cStat;
+ u16 frameSize;
+ TLanBufferRef buffer[TLAN_BUFFERS_PER_LIST];
+} TLanList;
- typedef u8 TLanBuffer[TLAN_MAX_FRAME_SIZE];
+typedef u8 TLanBuffer[TLAN_MAX_FRAME_SIZE];
@@ -119,23 +120,26 @@
*
****************************************************************/
- #define TLAN_PHY_MAX_ADDR 0x1F
- #define TLAN_PHY_INTERNAL 0x1F
+#define TLAN_PHY_MAX_ADDR 0x1F
- #define TLAN_PHY_ACTIVITY 0x00000001
- #define TLAN_PHY_AUTONEG 0x00000002
+#define TLAN_PHY_ACTIVITY 0x00000001
+#define TLAN_PHY_AUTONEG 0x00000002
+#define TLAN_PHY_INTS 0x00000004
+#define TLAN_PHY_BIT_RATE 0x00000008
+#define TLAN_PHY_UNMANAGED 0x00000010
+#define TLAN_PHY_INTERNAL 0x00000020
- typedef int (TLanPhyFunc)( struct device * );
+typedef int (TLanPhyFunc)( struct device * );
- typedef struct tlan_phy_id_entry_tag {
- u16 idHi;
- u16 idLo;
- TLanPhyFunc *check;
- TLanPhyFunc *service;
- u32 flags;
- } TLanPhyIdEntry;
+typedef struct tlan_phy_id_entry_tag {
+ u16 idHi;
+ u16 idLo;
+ TLanPhyFunc *check;
+ TLanPhyFunc *service;
+ u32 flags;
+} TLanPhyIdEntry;
@@ -145,38 +149,38 @@
*
****************************************************************/
- typedef struct tlan_private_tag {
- struct device *nextDevice;
- void *dmaStorage;
- u8 *padBuffer;
- TLanList *rxList;
- u8 *rxBuffer;
- u32 rxHead;
- u32 rxTail;
- u32 rxEocCount;
- TLanList *txList;
- u8 *txBuffer;
- u32 txHead;
- u32 txInProgress;
- u32 txTail;
- u32 txBusyCount;
- u32 phyAddr;
- u32 phyEntry;
- u32 phyOnline;
- u32 phyFlags;
- TLanPhyFunc *phyCheck;
- TLanPhyFunc *phyService;
- u32 timerSetAt;
- u32 timerType;
- struct timer_list timer;
- struct net_device_stats stats;
- u32 pciEntry;
- u8 pciRevision;
- u8 pciBus;
- u8 pciDeviceFn;
- u8 tlanRev;
- char devName[8];
- } TLanPrivateInfo;
+typedef struct tlan_private_tag {
+ struct device *nextDevice;
+ void *dmaStorage;
+ u8 *padBuffer;
+ TLanList *rxList;
+ u8 *rxBuffer;
+ u32 rxHead;
+ u32 rxTail;
+ u32 rxEocCount;
+ TLanList *txList;
+ u8 *txBuffer;
+ u32 txHead;
+ u32 txInProgress;
+ u32 txTail;
+ u32 txBusyCount;
+ u32 phyAddr;
+ u32 phyEntry;
+ u32 phyOnline;
+ u32 phyFlags;
+ TLanPhyFunc *phyCheck;
+ TLanPhyFunc *phyService;
+ u32 timerSetAt;
+ u32 timerType;
+ struct timer_list timer;
+ struct net_device_stats stats;
+ u32 pciEntry;
+ u8 pciRevision;
+ u8 pciBus;
+ u8 pciDeviceFn;
+ u8 tlanRev;
+ char devName[8];
+} TLanPrivateInfo;
@@ -186,11 +190,11 @@
*
****************************************************************/
- #define TLAN_TIMER_LINK 1
- #define TLAN_TIMER_ACT 2
+#define TLAN_TIMER_LINK 1
+#define TLAN_TIMER_ACT 2
- #define TLAN_TIMER_LINK_DELAY 230
- #define TLAN_TIMER_ACT_DELAY 10
+#define TLAN_TIMER_LINK_DELAY 230
+#define TLAN_TIMER_ACT_DELAY 10
@@ -200,8 +204,8 @@
*
****************************************************************/
- #define TLAN_EEPROM_ACK 0
- #define TLAN_EEPROM_STOP 1
+#define TLAN_EEPROM_ACK 0
+#define TLAN_EEPROM_STOP 1
@@ -211,29 +215,29 @@
*
****************************************************************/
- #define TLAN_HOST_CMD 0x00
- #define TLAN_HC_GO 0x80000000
- #define TLAN_HC_STOP 0x40000000
- #define TLAN_HC_ACK 0x20000000
- #define TLAN_HC_CS_MASK 0x1FE00000
- #define TLAN_HC_EOC 0x00100000
- #define TLAN_HC_RT 0x00080000
- #define TLAN_HC_NES 0x00040000
- #define TLAN_HC_AD_RST 0x00008000
- #define TLAN_HC_LD_TMR 0x00004000
- #define TLAN_HC_LD_THR 0x00002000
- #define TLAN_HC_REQ_INT 0x00001000
- #define TLAN_HC_INT_OFF 0x00000800
- #define TLAN_HC_INT_ON 0x00000400
- #define TLAN_HC_AC_MASK 0x000000FF
- #define TLAN_CH_PARM 0x04
- #define TLAN_DIO_ADR 0x08
- #define TLAN_DA_ADR_INC 0x8000
- #define TLAN_DA_RAM_ADR 0x4000
- #define TLAN_HOST_INT 0x0A
- #define TLAN_HI_IV_MASK 0x1FE0
- #define TLAN_HI_IT_MASK 0x001C
- #define TLAN_DIO_DATA 0x0C
+#define TLAN_HOST_CMD 0x00
+#define TLAN_HC_GO 0x80000000
+#define TLAN_HC_STOP 0x40000000
+#define TLAN_HC_ACK 0x20000000
+#define TLAN_HC_CS_MASK 0x1FE00000
+#define TLAN_HC_EOC 0x00100000
+#define TLAN_HC_RT 0x00080000
+#define TLAN_HC_NES 0x00040000
+#define TLAN_HC_AD_RST 0x00008000
+#define TLAN_HC_LD_TMR 0x00004000
+#define TLAN_HC_LD_THR 0x00002000
+#define TLAN_HC_REQ_INT 0x00001000
+#define TLAN_HC_INT_OFF 0x00000800
+#define TLAN_HC_INT_ON 0x00000400
+#define TLAN_HC_AC_MASK 0x000000FF
+#define TLAN_CH_PARM 0x04
+#define TLAN_DIO_ADR 0x08
+#define TLAN_DA_ADR_INC 0x8000
+#define TLAN_DA_RAM_ADR 0x4000
+#define TLAN_HOST_INT 0x0A
+#define TLAN_HI_IV_MASK 0x1FE0
+#define TLAN_HI_IT_MASK 0x001C
+#define TLAN_DIO_DATA 0x0C
/* ThunderLAN Internal Register DIO Offsets */
@@ -479,19 +483,26 @@ inline u32 xor( u32 a, u32 b )
{
return ( ( a && ! b ) || ( ! a && b ) );
}
-#define XOR8( a, b, c, d, e, f, g, h ) xor( a, xor( b, xor( c, xor( d, xor( e, xor( f, xor( g, h ) ) ) ) ) ) )
+#define XOR8( a, b, c, d, e, f, g, h ) xor( a, xor( b, xor( c, xor( d, xor( e, x
+or( f, xor( g, h ) ) ) ) ) ) )
#define DA( a, bit ) ( ( (u8) a[bit/8] ) & ( (u8) ( 1 << bit%8 ) ) )
inline u32 TLan_HashFunc( u8 *a )
{
u32 hash;
- hash = XOR8( DA(a,0), DA(a, 6), DA(a,12), DA(a,18), DA(a,24), DA(a,30), DA(a,36), DA(a,42) );
- hash |= XOR8( DA(a,1), DA(a, 7), DA(a,13), DA(a,19), DA(a,25), DA(a,31), DA(a,37), DA(a,43) ) << 1;
- hash |= XOR8( DA(a,2), DA(a, 8), DA(a,14), DA(a,20), DA(a,26), DA(a,32), DA(a,38), DA(a,44) ) << 2;
- hash |= XOR8( DA(a,3), DA(a, 9), DA(a,15), DA(a,21), DA(a,27), DA(a,33), DA(a,39), DA(a,45) ) << 3;
- hash |= XOR8( DA(a,4), DA(a,10), DA(a,16), DA(a,22), DA(a,28), DA(a,34), DA(a,40), DA(a,46) ) << 4;
- hash |= XOR8( DA(a,5), DA(a,11), DA(a,17), DA(a,23), DA(a,29), DA(a,35), DA(a,41), DA(a,47) ) << 5;
+ hash = XOR8( DA(a,0), DA(a, 6), DA(a,12), DA(a,18), DA(a,24), DA(a,30), DA(a,3
+6), DA(a,42) );
+ hash |= XOR8( DA(a,1), DA(a, 7), DA(a,13), DA(a,19), DA(a,25), DA(a,31), DA(a,3
+7), DA(a,43) ) << 1;
+ hash |= XOR8( DA(a,2), DA(a, 8), DA(a,14), DA(a,20), DA(a,26), DA(a,32), DA(a,3
+8), DA(a,44) ) << 2;
+ hash |= XOR8( DA(a,3), DA(a, 9), DA(a,15), DA(a,21), DA(a,27), DA(a,33), DA(a,3
+9), DA(a,45) ) << 3;
+ hash |= XOR8( DA(a,4), DA(a,10), DA(a,16), DA(a,22), DA(a,28), DA(a,34), DA(a,4
+0), DA(a,46) ) << 4;
+ hash |= XOR8( DA(a,5), DA(a,11), DA(a,17), DA(a,23), DA(a,29), DA(a,35), DA(a,4
+1), DA(a,47) ) << 5;
return hash;
diff --git a/drivers/net/tulip.c b/drivers/net/tulip.c
index 03b7fc10c..d92694ea6 100644
--- a/drivers/net/tulip.c
+++ b/drivers/net/tulip.c
@@ -368,7 +368,7 @@ struct eeprom {
u_char sign[EE_SIGNLEN];
};
-static int read_eeprom(int ioaddr, struct eeprom *eepp);
+static int read_eeprom(unsigned long ioaddr, struct eeprom *eepp);
static int tulip_open(struct device *dev);
static void tulip_init_ring(struct device *dev);
static int tulip_start_xmit(struct sk_buff *skb, struct device *dev);
@@ -462,7 +462,7 @@ static int full_duplex=0;
#define tio_read(port) inl(ioaddr + port)
static void inline
-tio_sia_write(u32 ioaddr, u32 val13, u32 val14, u32 val15)
+tio_sia_write(unsigned long ioaddr, u32 val13, u32 val14, u32 val15)
{
tio_write(0,CSR13);
tio_write(val15,CSR15);
@@ -490,7 +490,7 @@ card_type(struct tulip_private *tp, int device_id, int vendor_id))
}
__initfunc(static int
-read_eeprom(int ioaddr, struct eeprom *eepp))
+read_eeprom(unsigned long ioaddr, struct eeprom *eepp))
{
int i, n;
unsigned short val = 0;
@@ -524,8 +524,8 @@ read_eeprom(int ioaddr, struct eeprom *eepp))
/* Terminate the EEPROM access. */
tio_write(EE_ENB & ~EE_CS, CSR9);
- *p ++ = val;
- *p ++ = val >> 8;
+ *p ++ = le16_to_cpu(val);
+ *p ++ = le16_to_cpu(val) >> 8;
}
/* broken eeprom ? */
p = (u_char *)eepp;
@@ -538,7 +538,7 @@ read_eeprom(int ioaddr, struct eeprom *eepp))
static int
generic21040_fail(struct device *dev)
{
- int ioaddr = dev->base_addr;
+ unsigned long ioaddr = dev->base_addr;
return(tio_read(CSR12) & TSIAS_CONERROR);
}
@@ -546,7 +546,7 @@ generic21040_fail(struct device *dev)
static int
generic21041_fail(struct device *dev)
{
- int ioaddr = dev->base_addr;
+ unsigned long ioaddr = dev->base_addr;
u32 csr12 = tio_read(CSR12);
return((!(csr12 & TSIAS_CONERROR)
@@ -556,7 +556,7 @@ generic21041_fail(struct device *dev)
static void
generic21040_select(struct device *dev)
{
- int ioaddr = dev->base_addr;
+ unsigned long ioaddr = dev->base_addr;
const char *media;
dev->if_port &= 3;
@@ -587,7 +587,7 @@ generic21040_select(struct device *dev)
static void
generic_timer(struct device *dev, u32 count)
{
- int ioaddr = dev->base_addr;
+ unsigned long ioaddr = dev->base_addr;
tio_write(count, CSR11);
while (tio_read(CSR11) & TGEPT_COUNT);
@@ -597,7 +597,7 @@ generic_timer(struct device *dev, u32 count)
static void
generic21041_select(struct device *dev)
{
- int ioaddr = dev->base_addr;
+ unsigned long ioaddr = dev->base_addr;
u32 tsiac = TSIAC_C21041;
u32 tsiax = TSIAX_10TP;
u32 tsiag = TSIAG_10TP;
@@ -627,7 +627,8 @@ generic21041_select(struct device *dev)
static void
auto21140_select(struct device *dev)
{
- int i, ioaddr = dev->base_addr;
+ int i;
+ unsigned long ioaddr = dev->base_addr;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
/* kick port */
@@ -647,7 +648,7 @@ auto21140_select(struct device *dev)
static void
cogent21140_select(struct device *dev)
{
- int ioaddr = dev->base_addr, csr6;
+ unsigned long ioaddr = dev->base_addr, csr6;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
dev->if_port &= 1;
csr6 = tio_read(CSR6) &
@@ -667,7 +668,7 @@ cogent21140_select(struct device *dev)
static void
generic21140_select(struct device *dev)
{
- int ioaddr = dev->base_addr, csr6;
+ unsigned long ioaddr = dev->base_addr, csr6;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
dev->if_port &= 1;
@@ -689,7 +690,7 @@ static int
tulip_open(struct device *dev)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int ioaddr = dev->base_addr;
+ unsigned long ioaddr = dev->base_addr;
int i;
/* Reset the chip, holding bit 0 set at least 10 PCI cycles. */
@@ -713,20 +714,20 @@ tulip_open(struct device *dev)
int *setup_frm = tp->setup_frame, i;
/* You must add the broadcast address when doing perfect filtering! */
- *setup_frm++ = 0xffff;
- *setup_frm++ = 0xffff;
- *setup_frm++ = 0xffff;
+ *setup_frm++ = cpu_to_le32(0xffff);
+ *setup_frm++ = cpu_to_le32(0xffff);
+ *setup_frm++ = cpu_to_le32(0xffff);
/* Fill the rest of the accept table with our physical address. */
for (i = 1; i < 16; i++) {
- *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2];
+ *setup_frm++ = cpu_to_le32(eaddrs[0]);
+ *setup_frm++ = cpu_to_le32(eaddrs[1]);
+ *setup_frm++ = cpu_to_le32(eaddrs[2]);
}
/* Put the setup frame on the Tx list. */
- tp->tx_ring[0].length = 0x08000000 | 192;
- tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame);
- tp->tx_ring[0].buffer2 = 0;
- tp->tx_ring[0].status = TRING_OWN;
+ tp->tx_ring[0].length = cpu_to_le32(0x08000000 | 192);
+ tp->tx_ring[0].buffer1 = cpu_to_le32(virt_to_bus(tp->setup_frame));
+ tp->tx_ring[0].buffer2 = cpu_to_le32(0);
+ tp->tx_ring[0].status = cpu_to_le32(TRING_OWN);
barrier();
tp->cur_tx++, tp->dirty_tx++;
}
@@ -744,7 +745,7 @@ tulip_open(struct device *dev)
tio_write(TPOLL_TRIGGER, CSR1);
sti();
for (i = 0; i < 1000; i++) {
- if (tp->tx_ring[0].status >= 0) {
+ if (((s32)le32_to_cpu(tp->tx_ring[0].status)) >= 0) {
break;
}
udelay(1000);
@@ -788,19 +789,19 @@ tulip_init_ring(struct device *dev)
tp->dirty_rx = tp->dirty_tx = 0;
for (i = 0; i < RX_RING_SIZE; i++) {
- tp->rx_ring[i].status = TRING_OWN;
- tp->rx_ring[i].length = PKT_BUF_SZ;
- tp->rx_ring[i].buffer1 = virt_to_bus(tp->rx_buffs[i]);
- tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]);
+ tp->rx_ring[i].status = cpu_to_le32(TRING_OWN);
+ tp->rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ);
+ tp->rx_ring[i].buffer1 = cpu_to_le32(virt_to_bus(tp->rx_buffs[i]));
+ tp->rx_ring[i].buffer2 = cpu_to_le32(virt_to_bus(&tp->rx_ring[i+1]));
}
/* Mark the last entry as wrapping the ring. */
- tp->rx_ring[i-1].length = PKT_BUF_SZ | 0x02000000;
- tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]);
+ tp->rx_ring[i-1].length = cpu_to_le32(PKT_BUF_SZ | 0x02000000);
+ tp->rx_ring[i-1].buffer2 = cpu_to_le32(virt_to_bus(&tp->rx_ring[0]));
/* The Tx buffer descriptor is filled in as needed, but we
do need to clear the ownership bit. */
for (i = 0; i < TX_RING_SIZE; i++) {
- tp->tx_ring[i].status = 0x00000000;
+ tp->tx_ring[i].status = cpu_to_le32(0x00000000);
}
}
@@ -808,7 +809,7 @@ static int
tulip_start_xmit(struct sk_buff *skb, struct device *dev)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int ioaddr = dev->base_addr;
+ unsigned long ioaddr = dev->base_addr;
int entry, len;
unsigned long daddr;
@@ -827,16 +828,16 @@ tulip_start_xmit(struct sk_buff *skb, struct device *dev)
"SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
dev->name, tio_read(CSR5), tio_read(CSR12),
tio_read(CSR13), tio_read(CSR14), tio_read(CSR15));
-#ifndef __alpha__
+#if !defined(__alpha__) && !defined(__sparc_v9__)
printk(" Rx ring %8.8x: ", (int)tp->rx_ring);
#endif
for (i = 0; i < RX_RING_SIZE; i++)
- printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
-#ifndef __alpha__
+ printk(" %8.8x", (unsigned int)le32_to_cpu(tp->rx_ring[i].status));
+#if !defined(__alpha__) && !defined(__sparc_v9__)
printk("\n Tx ring %8.8x: ", (int)tp->tx_ring);
#endif
for (i = 0; i < TX_RING_SIZE; i++)
- printk(" %8.8x", (unsigned int)tp->tx_ring[i].status);
+ printk(" %8.8x", (unsigned int)le32_to_cpu(tp->tx_ring[i].status));
printk("\n");
tp->stats.tx_errors++;
@@ -853,13 +854,6 @@ tulip_start_xmit(struct sk_buff *skb, struct device *dev)
return(0);
}
- if (skb == NULL || (skb != (struct sk_buff *) -1 && skb->len <= 0)) {
- printk("%s: Obsolete driver layer request made: skbuff==NULL.\n",
- dev->name);
- dev_tint(dev);
- return(0);
- }
-
/* Block a timer-based transmit from overlapping. This could better be
done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
If this ever occurs the queue layer is doing something evil! */
@@ -880,19 +874,22 @@ tulip_start_xmit(struct sk_buff *skb, struct device *dev)
*/
if (skb == (struct sk_buff *) -1) {
daddr = virt_to_bus((char *)tp->setup_frame);
+#ifdef NO_ANK_FIX
len = 192;
+#else
skb = NULL;
+#endif
} else {
daddr = virt_to_bus(skb->data);
len = skb->len;
tp->stats.tx_bytes+=len;
}
tp->tx_skbuff[entry] = skb;
- tp->tx_ring[entry].length = len |
- (entry == TX_RING_SIZE-1 ? 0xe2000000 : 0xe0000000);
- tp->tx_ring[entry].buffer1 = daddr;
- tp->tx_ring[entry].buffer2 = 0;
- tp->tx_ring[entry].status = TRING_OWN; /* Pass ownership to the chip. */
+ tp->tx_ring[entry].length = cpu_to_le32(len |
+ (entry == TX_RING_SIZE-1 ? 0xe2000000 : 0xe0000000));
+ tp->tx_ring[entry].buffer1 = cpu_to_le32(daddr);
+ tp->tx_ring[entry].buffer2 = cpu_to_le32(0);
+ tp->tx_ring[entry].status = cpu_to_le32(TRING_OWN); /* Pass ownership to the chip. */
barrier();
tp->cur_tx++;
@@ -911,7 +908,8 @@ static void tulip_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct device *dev = (struct device *)dev_id;
struct tulip_private *lp;
- int csr5, ioaddr, boguscnt=10;
+ int csr5, boguscnt=10;
+ unsigned long ioaddr;
if (dev == NULL) {
printk ("tulip_interrupt(): irq %d for unknown device.\n", irq);
@@ -940,7 +938,7 @@ static void tulip_interrupt(int irq, void *dev_id, struct pt_regs *regs)
while (dirty_tx < lp->cur_tx) {
int entry = dirty_tx % TX_RING_SIZE;
- int status = lp->tx_ring[entry].status;
+ int status = le32_to_cpu(lp->tx_ring[entry].status);
if (status < 0)
break; /* It still hasn't been Txed */
@@ -1034,8 +1032,8 @@ tulip_rx(struct device *dev)
int i;
/* If we own the next entry, it's a new packet. Send it up. */
- while (lp->rx_ring[entry].status >= 0) {
- int status = lp->rx_ring[entry].status;
+ while (((s32)le32_to_cpu(lp->rx_ring[entry].status)) >= 0) {
+ int status = le32_to_cpu(lp->rx_ring[entry].status);
if ((status & TRING_RxDESCMASK) != TRING_RxDESCMASK) {
printk("%s: Ethernet frame spanned multiple buffers,"
@@ -1050,7 +1048,7 @@ tulip_rx(struct device *dev)
} else {
/* Malloc up new buffer, compatible with net-2e. */
/* Omit the four octet CRC from the length. */
- short pkt_len = (lp->rx_ring[entry].status >> 16) - 4;
+ short pkt_len = (le32_to_cpu(lp->rx_ring[entry].status) >> 16) - 4;
struct sk_buff *skb;
skb = dev_alloc_skb(pkt_len + 2);
@@ -1060,12 +1058,12 @@ tulip_rx(struct device *dev)
/* Check that at least two ring entries are free.
If not, free one and mark stats->rx_dropped++. */
for (i=0; i < RX_RING_SIZE; i++)
- if (lp->rx_ring[(entry+i) % RX_RING_SIZE].status < 0)
+ if (((s32)le32_to_cpu(lp->rx_ring[(entry+i) % RX_RING_SIZE].status)) < 0)
break;
if (i > RX_RING_SIZE -2) {
lp->stats.rx_dropped++;
- lp->rx_ring[entry].status = TRING_OWN;
+ lp->rx_ring[entry].status = cpu_to_le32(TRING_OWN);
lp->cur_rx++;
}
break;
@@ -1081,7 +1079,7 @@ tulip_rx(struct device *dev)
lp->stats.rx_bytes+=skb->len;
}
- lp->rx_ring[entry].status = TRING_OWN;
+ lp->rx_ring[entry].status = cpu_to_le32(TRING_OWN);
entry = (++lp->cur_rx) % RX_RING_SIZE;
}
return(0);
@@ -1090,7 +1088,7 @@ tulip_rx(struct device *dev)
static int
tulip_close(struct device *dev)
{
- int ioaddr = dev->base_addr;
+ unsigned long ioaddr = dev->base_addr;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
dev->start = 0;
@@ -1129,7 +1127,7 @@ tulip_config(struct device *dev, struct ifmap *map)
static struct net_device_stats *tulip_get_stats(struct device *dev)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
- /* short ioaddr = dev->base_addr;*/
+ /* unsigned long ioaddr = dev->base_addr;*/
return(&tp->stats);
}
@@ -1140,7 +1138,7 @@ static struct net_device_stats *tulip_get_stats(struct device *dev)
static void set_multicast_list(struct device *dev)
{
- short ioaddr = dev->base_addr;
+ unsigned long ioaddr = dev->base_addr;
int csr6 = tio_read(CSR6) & ~(TCMOD_MODEMASK|TCMOD_FILTERMASK);
if (dev->flags&IFF_PROMISC)
@@ -1169,20 +1167,20 @@ static void set_multicast_list(struct device *dev)
for (i = 0; i < dev->mc_count; i ++) {
eaddrs=(unsigned short *)dmi->dmi_addr;
dmi=dmi->next;
- *setup_frm++ = *eaddrs++;
- *setup_frm++ = *eaddrs++;
- *setup_frm++ = *eaddrs++;
+ *setup_frm++ = cpu_to_le32(*eaddrs++);
+ *setup_frm++ = cpu_to_le32(*eaddrs++);
+ *setup_frm++ = cpu_to_le32(*eaddrs++);
}
/* Fill the rest of the table with our physical address. */
eaddrs = (unsigned short *)dev->dev_addr;
/* Always accept broadcast packets */
- *setup_frm++ = 0xffff;
- *setup_frm++ = 0xffff;
- *setup_frm++ = 0xffff;
+ *setup_frm++ = cpu_to_le32(0xffff);
+ *setup_frm++ = cpu_to_le32(0xffff);
+ *setup_frm++ = cpu_to_le32(0xffff);
do {
- *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2];
+ *setup_frm++ = cpu_to_le32(eaddrs[0]);
+ *setup_frm++ = cpu_to_le32(eaddrs[1]);
+ *setup_frm++ = cpu_to_le32(eaddrs[2]);
} while (++i < 15);
/* Now add this frame to the Tx list. */
@@ -1191,7 +1189,7 @@ static void set_multicast_list(struct device *dev)
}
__initfunc(int
-tulip_hwinit(struct device *dev, int ioaddr,
+tulip_hwinit(struct device *dev, unsigned long ioaddr,
int irq, int device_id))
{
/* See note below on the Znyx 315 etherarray. */
@@ -1203,7 +1201,7 @@ tulip_hwinit(struct device *dev, int ioaddr,
unsigned short sum, bitsum;
if (check_region(ioaddr, TULIP_TOTAL_SIZE) != 0) {
- printk("tulip_hwinit: region already allocated at %#3x.\n",
+ printk("tulip_hwinit: region already allocated at %#3lx.\n",
ioaddr);
return(-1);
}
@@ -1258,7 +1256,7 @@ tulip_hwinit(struct device *dev, int ioaddr,
}
/* Make certain the data structures are quadword aligned. */
- mesgp += sprintf(mesgp, ") at %#3x, ", ioaddr);
+ mesgp += sprintf(mesgp, ") at %016lx, ", ioaddr);
/* On the Zynx 315 etherarray boards only the first Tulip has an EEPROM.
The addresses of the subsequent ports are derived from the first. */
@@ -1324,8 +1322,15 @@ tulip_hwinit(struct device *dev, int ioaddr,
__initfunc(int tulip_probe(struct device *dev))
{
static struct device *tulip_head=NULL;
- u_char pci_bus, pci_device_fn, pci_latency, pci_irq;
- u_int pci_ioaddr;
+#ifdef __sparc_v9__
+ struct pci_dev *pdev;
+#else
+ u_char btmp;
+ u_int itmp;
+#endif
+ u_char pci_bus, pci_device_fn, pci_latency;
+ u_int pci_irq;
+ unsigned long pci_ioaddr;
u_short pci_command, vendor_id, device_id;
u_int pci_chips[] = {
PCI_DEVICE_ID_DEC_TULIP,
@@ -1336,27 +1341,48 @@ __initfunc(int tulip_probe(struct device *dev))
int num=0, cno;
static int pci_index = 0;
- if (!pcibios_present()) return(-ENODEV);
+ if (!pcibios_present())
+ return(-ENODEV);
for (; pci_index < 0xff; pci_index++) {
if (pcibios_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, pci_index,
&pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL)
break;
+#ifdef __sparc_v9__
+ for(pdev = pci_devices; pdev; pdev = pdev->next)
+ if(pdev->bus->number == pci_bus &&
+ pdev->devfn == pci_device_fn)
+ break;
+ if(!pdev)
+ panic("tulip: Cannot find pci_dev for [%x:%x]\n",
+ pci_bus, pci_device_fn);
+#endif
+
/* get vendor id */
pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID,
&vendor_id);
- /* get IRQ */
- pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_LINE,
- &pci_irq);
-
/* get device id */
pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID,
&device_id);
+#ifndef __sparc_v9__
/* get IO address */
pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0,
- &pci_ioaddr);
+ &itmp);
+ pci_ioaddr = itmp;
+
+ /* get IRQ */
+ pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_LINE,
+ &btmp);
+ pci_irq = btmp;
+#else
+ /* get IO address */
+ pci_ioaddr = pdev->base_address[0];
+
+ /* get IRQ */
+ pci_irq = pdev->irq;
+#endif
/* Remove I/O space marker in bit 0. */
pci_ioaddr &= ~3;
diff --git a/drivers/net/tunnel.c b/drivers/net/tunnel.c
deleted file mode 100644
index dc7c08a07..000000000
--- a/drivers/net/tunnel.c
+++ /dev/null
@@ -1,305 +0,0 @@
-/* tunnel.c: an IP tunnel driver
-
- The purpose of this driver is to provide an IP tunnel through
- which you can tunnel network traffic transparently across subnets.
-
- This was written by looking at Nick Holloway's dummy driver
- Thanks for the great code!
-
- -Sam Lantinga (slouken@cs.ucdavis.edu) 02/01/95
-
- Minor tweaks:
- Cleaned up the code a little and added some pre-1.3.0 tweaks.
- dev->hard_header/hard_header_len changed to use no headers.
- Comments/bracketing tweaked.
- Made the tunnels use dev->name not tunnel: when error reporting.
- Added tx_dropped stat
-
- -Alan Cox (Alan.Cox@linux.org) 21 March 95
-
- Reworked:
- Changed to tunnel to destination gateway in addition to the
- tunnel's pointopoint address
- Almost completely rewritten
- Note: There is currently no firewall or ICMP handling done.
-
- -Sam Lantinga (slouken@cs.ucdavis.edu) 02/13/96
-
-*/
-
-/* Things I wish I had known when writing the tunnel driver:
-
- When the tunnel_xmit() function is called, the skb contains the
- packet to be sent (plus a great deal of extra info), and dev
- contains the tunnel device that _we_ are.
-
- When we are passed a packet, we are expected to fill in the
- source address with our source IP address.
-
- What is the proper way to allocate, copy and free a buffer?
- After you allocate it, it is a "0 length" chunk of memory
- starting at zero. If you want to add headers to the buffer
- later, you'll have to call "skb_reserve(skb, amount)" with
- the amount of memory you want reserved. Then, you call
- "skb_put(skb, amount)" with the amount of space you want in
- the buffer. skb_put() returns a pointer to the top (#0) of
- that buffer. skb->len is set to the amount of space you have
- "allocated" with skb_put(). You can then write up to skb->len
- bytes to that buffer. If you need more, you can call skb_put()
- again with the additional amount of space you need. You can
- find out how much more space you can allocate by calling
- "skb_tailroom(skb)".
- Now, to add header space, call "skb_push(skb, header_len)".
- This creates space at the beginning of the buffer and returns
- a pointer to this new space. If later you need to strip a
- header from a buffer, call "skb_pull(skb, header_len)".
- skb_headroom() will return how much space is left at the top
- of the buffer (before the main data). Remember, this headroom
- space must be reserved before the skb_put() function is called.
-*/
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <net/ip.h>
-#include <linux/if_arp.h>
-#include <linux/init.h>
-
-/*#define TUNNEL_DEBUG*/
-
-/*
- * Our header is a simple IP packet with no options
- */
-
-#define tunnel_hlen sizeof(struct iphdr)
-
-/*
- * Okay, this needs to be high enough that we can fit a "standard"
- * ethernet header and an IP tunnel header into the outgoing packet.
- * [36 bytes]
- */
-
-#define TUNL_HLEN (((ETH_HLEN+15)&~15)+tunnel_hlen)
-
-
-static int tunnel_open(struct device *dev)
-{
- MOD_INC_USE_COUNT;
- return 0;
-}
-
-static int tunnel_close(struct device *dev)
-{
- MOD_DEC_USE_COUNT;
- return 0;
-}
-
-#ifdef TUNNEL_DEBUG
-void print_ip(struct iphdr *ip)
-{
- unsigned char *ipaddr;
-
- printk("IP packet:\n");
- printk("--- header len = %d\n", ip->ihl*4);
- printk("--- ip version: %d\n", ip->version);
- printk("--- ip protocol: %d\n", ip->protocol);
- ipaddr=(unsigned char *)&ip->saddr;
- printk("--- source address: %u.%u.%u.%u\n",
- *ipaddr, *(ipaddr+1), *(ipaddr+2), *(ipaddr+3));
- ipaddr=(unsigned char *)&ip->daddr;
- printk("--- destination address: %u.%u.%u.%u\n",
- *ipaddr, *(ipaddr+1), *(ipaddr+2), *(ipaddr+3));
- printk("--- total packet len: %d\n", ntohs(ip->tot_len));
-}
-#endif
-
-/*
- * This function assumes it is being called from dev_queue_xmit()
- * and that skb is filled properly by that function.
- */
-
-static int tunnel_xmit(struct sk_buff *skb, struct device *dev)
-{
- struct net_device_stats *stats; /* This device's statistics */
- struct rtable *rt; /* Route to the other host */
- struct device *tdev; /* Device to other host */
- struct iphdr *iph; /* Our new IP header */
- int max_headroom; /* The extra header space needed */
-
- stats = (struct net_device_stats *)dev->priv;
-
- /*
- * First things first. Look up the destination address in the
- * routing tables
- */
- iph = skb->nh.iph;
-
- if (ip_route_output(&rt, dev->pa_dstaddr, dev->pa_addr, RT_TOS(iph->tos), NULL)) {
- /* No route to host */
- printk ( KERN_INFO "%s: Can't reach target gateway!\n", dev->name);
- stats->tx_errors++;
- dev_kfree_skb(skb, FREE_WRITE);
- return 0;
- }
- tdev = rt->u.dst.dev;
-
- if (tdev->type == ARPHRD_TUNNEL) {
- /* Tunnel to tunnel? -- I don't think so. */
- printk ( KERN_INFO "%s: Packet targetted at myself!\n" , dev->name);
- ip_rt_put(rt);
- stats->tx_errors++;
- dev_kfree_skb(skb, FREE_WRITE);
- return 0;
- }
-
- skb->h.ipiph = skb->nh.iph;
-
- /*
- * Okay, now see if we can stuff it in the buffer as-is.
- */
- max_headroom = (((tdev->hard_header_len+15)&~15)+tunnel_hlen);
-
- if (skb_headroom(skb) < max_headroom || skb_shared(skb)) {
- struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
- if (!new_skb) {
- ip_rt_put(rt);
- stats->tx_dropped++;
- dev_kfree_skb(skb, FREE_WRITE);
- return 0;
- }
- dev_kfree_skb(skb, FREE_WRITE);
- skb = new_skb;
- }
-
- skb->nh.iph = (struct iphdr *) skb_push(skb, tunnel_hlen);
- dst_release(skb->dst);
- memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
- dst_release(skb->dst);
- skb->dst = &rt->u.dst;
-
- /*
- * Push down and install the IPIP header.
- */
-
- iph = skb->nh.iph;
- iph->version = 4;
- iph->tos = skb->h.ipiph->tos;
- iph->ttl = skb->h.ipiph->ttl;
- iph->frag_off = 0;
- iph->daddr = dev->pa_dstaddr;
- iph->saddr = dev->pa_addr;
- iph->protocol = IPPROTO_IPIP;
- iph->ihl = 5;
- iph->tot_len = htons(skb->len);
- iph->id = htons(ip_id_count++); /* Race condition here? */
- ip_send_check(iph);
-
- stats->tx_bytes+=skb->len;
-
- ip_send(skb);
-
- /* Record statistics and return */
- stats->tx_packets++;
- return 0;
-}
-
-static struct net_device_stats *tunnel_get_stats(struct device *dev)
-{
- return((struct net_device_stats*) dev->priv);
-}
-
-/*
- * Called when a new tunnel device is initialized.
- * The new tunnel device structure is passed to us.
- */
-
-__initfunc(int tunnel_init(struct device *dev))
-{
- /* Oh, just say we're here, in case anyone cares */
- static int tun_msg=0;
- if(!tun_msg)
- {
- printk ( KERN_INFO "tunnel: version v0.2b2\n" );
- tun_msg=1;
- }
-
- /* Add our tunnel functions to the device */
- dev->open = tunnel_open;
- dev->stop = tunnel_close;
- dev->hard_start_xmit = tunnel_xmit;
- dev->get_stats = tunnel_get_stats;
- dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
- if (dev->priv == NULL)
- return -ENOMEM;
- memset(dev->priv, 0, sizeof(struct net_device_stats));
-
- /* Initialize the tunnel device structure */
-
- dev_init_buffers(dev);
-
- dev->hard_header = NULL;
- dev->rebuild_header = NULL;
- dev->set_mac_address = NULL;
- dev->hard_header_cache = NULL;
- dev->header_cache_update= NULL;
-
- dev->type = ARPHRD_TUNNEL;
- dev->hard_header_len = TUNL_HLEN;
- dev->mtu = 1500-tunnel_hlen; /* eth_mtu */
- dev->addr_len = 0; /* Is this only for ARP? */
- dev->tx_queue_len = 2; /* Small queue */
- memset(dev->broadcast,0xFF, ETH_ALEN);
-
- /* New-style flags. */
- dev->flags = IFF_NOARP; /* Don't use ARP on this device */
- /* No broadcasting through a tunnel */
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = 4;
-
- /* We're done. Have I forgotten anything? */
- return 0;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-/* Module specific interface */
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-#ifdef MODULE
-
-
-static char tunnel_name[16];
-
-static struct device dev_tunnel =
-{
- tunnel_name,
- 0, 0, 0, 0,
- 0x0, 0,
- 0, 0, 0, NULL, tunnel_init
- };
-
-int init_module(void)
-{
- /* Find a name for this unit */
- int err=dev_alloc_name(&dev_tunnel, "tunl%d");
- if(err<0)
- return err;
-
-#ifdef TUNNEL_DEBUG
- printk("tunnel: registering device %s\n", dev_tunnel.name);
-#endif
- if (register_netdev(&dev_tunnel) != 0)
- return -EIO;
- return 0;
-}
-
-void cleanup_module(void)
-{
- unregister_netdev(&dev_tunnel);
- kfree_s(dev_tunnel.priv,sizeof(struct net_device_stats));
- dev_tunnel.priv=NULL;
-}
-#endif /* MODULE */
-
diff --git a/drivers/net/wavelan.c b/drivers/net/wavelan.c
index 336283df0..a7a34551d 100644
--- a/drivers/net/wavelan.c
+++ b/drivers/net/wavelan.c
@@ -3666,14 +3666,7 @@ wavelan_interrupt(int irq,
u_short status;
u_short ack_cmd;
- if((dev = (device *) (irq2dev_map[irq])) == (device *) NULL)
- {
-#ifdef DEBUG_INTERRUPT_ERROR
- printk(KERN_WARNING "wavelan_interrupt(): irq %d for unknown device.\n",
- irq);
-#endif
- return;
- }
+ dev = dev_id;
#ifdef DEBUG_INTERRUPT_TRACE
printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name);
@@ -3913,12 +3906,8 @@ wavelan_open(device * dev)
return -ENXIO;
}
- if((irq2dev_map[dev->irq] != (device *) NULL) ||
- /* This is always true, but avoid the false IRQ. */
- ((irq2dev_map[dev->irq] = dev) == (device *) NULL) ||
- (request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN", NULL) != 0))
+ if(request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN", dev) != 0)
{
- irq2dev_map[dev->irq] = (device *) NULL;
#ifdef DEBUG_CONFIG_ERRORS
printk(KERN_WARNING "%s: wavelan_open(): invalid irq\n", dev->name);
#endif
@@ -3933,8 +3922,7 @@ wavelan_open(device * dev)
}
else
{
- free_irq(dev->irq, NULL);
- irq2dev_map[dev->irq] = (device *) NULL;
+ free_irq(dev->irq, dev);
#ifdef DEBUG_CONFIG_ERRORS
printk(KERN_INFO "%s: wavelan_open(): impossible to start the card\n",
dev->name);
@@ -3982,8 +3970,7 @@ wavelan_close(device * dev)
*/
wv_82586_stop(dev);
- free_irq(dev->irq, NULL);
- irq2dev_map[dev->irq] = (device *) NULL;
+ free_irq(dev->irq, dev);
MOD_DEC_USE_COUNT;
diff --git a/drivers/net/wd.c b/drivers/net/wd.c
index 56e8bd5ab..6e0b1a47c 100644
--- a/drivers/net/wd.c
+++ b/drivers/net/wd.c
@@ -254,7 +254,7 @@ __initfunc(int wd_probe1(struct device *dev, int ioaddr))
/* Snarf the interrupt now. There's no point in waiting since we cannot
share and the board will usually be enabled. */
- if (request_irq(dev->irq, ei_interrupt, 0, model_name, NULL)) {
+ if (request_irq(dev->irq, ei_interrupt, 0, model_name, dev)) {
printk (" unable to get IRQ %d.\n", dev->irq);
return EAGAIN;
}
@@ -262,7 +262,7 @@ __initfunc(int wd_probe1(struct device *dev, int ioaddr))
/* Allocate dev->priv and fill in 8390 specific dev fields. */
if (ethdev_init(dev)) {
printk (" unable to get memory for dev->priv.\n");
- free_irq(dev->irq, NULL);
+ free_irq(dev->irq, dev);
return -ENOMEM;
}
@@ -499,8 +499,7 @@ cleanup_module(void)
int ioaddr = dev->base_addr - WD_NIC_OFFSET;
kfree(dev->priv);
dev->priv = NULL;
- free_irq(dev->irq, NULL);
- irq2dev_map[dev->irq] = NULL;
+ free_irq(dev->irq, dev);
release_region(ioaddr, WD_IO_EXTENT);
unregister_netdev(dev);
}
diff --git a/drivers/net/x25_asy.c b/drivers/net/x25_asy.c
index ce4aeb609..275235ea7 100644
--- a/drivers/net/x25_asy.c
+++ b/drivers/net/x25_asy.c
@@ -662,7 +662,12 @@ static void x25_asy_close_tty(struct tty_struct *tty)
if (!sl || sl->magic != X25_ASY_MAGIC)
return;
- (void) dev_close(sl->dev);
+ if (sl->dev->flags & IFF_UP)
+ {
+ dev_lock_wait();
+ (void) dev_close(sl->dev);
+ dev_unlock_list();
+ }
tty->disc_data = 0;
sl->tty = NULL;
@@ -896,11 +901,6 @@ int x25_asy_init(struct device *dev)
/* New-style flags. */
dev->flags = IFF_NOARP;
- dev->family = AF_X25;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = 4;
return 0;
}
diff --git a/drivers/net/zlib.c b/drivers/net/zlib.c
new file mode 100644
index 000000000..a8231b550
--- /dev/null
+++ b/drivers/net/zlib.c
@@ -0,0 +1,5380 @@
+/*
+ * This file is derived from various .h and .c files from the zlib-0.95
+ * distribution by Jean-loup Gailly and Mark Adler, with some additions
+ * by Paul Mackerras to aid in implementing Deflate compression and
+ * decompression for PPP packets. See zlib.h for conditions of
+ * distribution and use.
+ *
+ * Changes that have been made include:
+ * - added Z_PACKET_FLUSH (see zlib.h for details)
+ * - added inflateIncomp and deflateOutputPending
+ * - changed DEBUG_ZLIB to DEBUG_ZLIB
+ * - use ZALLOC_INIT rather than ZALLOC for allocations during initialization
+ * - allow strm->next_out to be NULL, meaning discard the output
+ *
+ * $Id: zlib.c,v 1.2 1997/10/02 17:59:23 davem Exp $
+ */
+
+/*
+ * ==FILEVERSION 970501==
+ *
+ * This marker is used by the Linux installation script to determine
+ * whether an up-to-date version of this file is already installed.
+ */
+
+#define NO_DUMMY_DECL
+#define NO_ZCFUNCS
+#define MY_ZCALLOC
+
+#if defined(__FreeBSD__) && (defined(KERNEL) || defined(_KERNEL))
+#define inflate inflate_ppp /* FreeBSD already has an inflate :-( */
+#endif
+
+
+/* +++ zutil.h */
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995-1996 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* From: zutil.h,v 1.16 1996/07/24 13:41:13 me Exp $ */
+
+#ifndef _Z_UTIL_H
+#define _Z_UTIL_H
+
+#include "zlib.h"
+
+#if defined(KERNEL) || defined(_KERNEL)
+/* Assume this is a *BSD kernel */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/systm.h>
+# define HAVE_MEMCPY
+# define zmemcpy(d, s, n) bcopy((s), (d), (n))
+# define zmemzero bzero
+# define zmemcmp bcmp
+
+#else
+#if defined(__KERNEL__)
+/* Assume this is a Linux kernel */
+#include <linux/string.h>
+#define HAVE_MEMCPY
+#define zmemcpy memcpy
+#define zmemzero(dest, len) memset(dest, 0, len)
+#define zmemcmp memcmp
+
+#else /* not kernel */
+
+#if defined(MSDOS)||defined(VMS)||defined(CRAY)||defined(WIN32)||defined(RISCOS)
+# include <stddef.h>
+# include <errno.h>
+#else
+ extern int errno;
+#endif
+#ifdef STDC
+# include <string.h>
+# include <stdlib.h>
+#endif
+#endif /* __KERNEL__ */
+#endif /* _KERNEL || KERNEL */
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+typedef unsigned char uch;
+typedef uch FAR uchf;
+typedef unsigned short ush;
+typedef ush FAR ushf;
+typedef unsigned long ulg;
+
+extern const char *z_errmsg[10]; /* indexed by 2-zlib_error */
+/* (size given to avoid silly warnings with Visual C++) */
+
+#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
+
+#define ERR_RETURN(strm,err) \
+ return (strm->msg = (char*)ERR_MSG(err), (err))
+/* To be used only when the state is known to be valid */
+
+ /* common constants */
+
+#ifndef DEF_WBITS
+# define DEF_WBITS MAX_WBITS
+#endif
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+
+#if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+#else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
+#endif
+/* default memLevel */
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES 2
+/* The three kinds of block type */
+
+#define MIN_MATCH 3
+#define MAX_MATCH 258
+/* The minimum and maximum match lengths */
+
+#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
+
+ /* target dependencies */
+
+#ifdef MSDOS
+# define OS_CODE 0x00
+# ifdef __TURBOC__
+# include <alloc.h>
+# else /* MSC or DJGPP */
+# include <malloc.h>
+# endif
+#endif
+
+#ifdef OS2
+# define OS_CODE 0x06
+#endif
+
+#ifdef WIN32 /* Window 95 & Windows NT */
+# define OS_CODE 0x0b
+#endif
+
+#if defined(VAXC) || defined(VMS)
+# define OS_CODE 0x02
+# define FOPEN(name, mode) \
+ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
+#endif
+
+#ifdef AMIGA
+# define OS_CODE 0x01
+#endif
+
+#if defined(ATARI) || defined(atarist)
+# define OS_CODE 0x05
+#endif
+
+#ifdef MACOS
+# define OS_CODE 0x07
+#endif
+
+#ifdef __50SERIES /* Prime/PRIMOS */
+# define OS_CODE 0x0F
+#endif
+
+#ifdef TOPS20
+# define OS_CODE 0x0a
+#endif
+
+#if defined(_BEOS_) || defined(RISCOS)
+# define fdopen(fd,mode) NULL /* No fdopen() */
+#endif
+
+ /* Common defaults */
+
+#ifndef OS_CODE
+# define OS_CODE 0x03 /* assume Unix */
+#endif
+
+#ifndef FOPEN
+# define FOPEN(name, mode) fopen((name), (mode))
+#endif
+
+ /* functions */
+
+#ifdef HAVE_STRERROR
+ extern char *strerror OF((int));
+# define zstrerror(errnum) strerror(errnum)
+#else
+# define zstrerror(errnum) ""
+#endif
+
+#if defined(pyr)
+# define NO_MEMCPY
+#endif
+#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(_MSC_VER)
+ /* Use our own functions for small and medium model with MSC <= 5.0.
+ * You may have to use the same strategy for Borland C (untested).
+ */
+# define NO_MEMCPY
+#endif
+#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
+# define HAVE_MEMCPY
+#endif
+#ifdef HAVE_MEMCPY
+# ifdef SMALL_MEDIUM /* MSDOS small or medium model */
+# define zmemcpy _fmemcpy
+# define zmemcmp _fmemcmp
+# define zmemzero(dest, len) _fmemset(dest, 0, len)
+# else
+# define zmemcpy memcpy
+# define zmemcmp memcmp
+# define zmemzero(dest, len) memset(dest, 0, len)
+# endif
+#else
+ extern void zmemcpy OF((Bytef* dest, Bytef* source, uInt len));
+ extern int zmemcmp OF((Bytef* s1, Bytef* s2, uInt len));
+ extern void zmemzero OF((Bytef* dest, uInt len));
+#endif
+
+/* Diagnostic functions */
+#ifdef DEBUG_ZLIB
+# include <stdio.h>
+# ifndef verbose
+# define verbose 0
+# endif
+ extern void z_error OF((char *m));
+# define Assert(cond,msg) {if(!(cond)) z_error(msg);}
+# define Trace(x) fprintf x
+# define Tracev(x) {if (verbose) fprintf x ;}
+# define Tracevv(x) {if (verbose>1) fprintf x ;}
+# define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
+# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
+#else
+# define Assert(cond,msg)
+# define Trace(x)
+# define Tracev(x)
+# define Tracevv(x)
+# define Tracec(c,x)
+# define Tracecv(c,x)
+#endif
+
+
+typedef uLong (*check_func) OF((uLong check, const Bytef *buf, uInt len));
+
+voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size));
+void zcfree OF((voidpf opaque, voidpf ptr));
+
+#define ZALLOC(strm, items, size) \
+ (*((strm)->zalloc))((strm)->opaque, (items), (size))
+#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
+#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
+
+#endif /* _Z_UTIL_H */
+/* --- zutil.h */
+
+/* +++ deflate.h */
+/* deflate.h -- internal compression state
+ * Copyright (C) 1995-1996 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* From: deflate.h,v 1.10 1996/07/02 12:41:00 me Exp $ */
+
+#ifndef _DEFLATE_H
+#define _DEFLATE_H
+
+/* #include "zutil.h" */
+
+/* ===========================================================================
+ * Internal compression state.
+ */
+
+#define LENGTH_CODES 29
+/* number of length codes, not counting the special END_BLOCK code */
+
+#define LITERALS 256
+/* number of literal bytes 0..255 */
+
+#define L_CODES (LITERALS+1+LENGTH_CODES)
+/* number of Literal or Length codes, including the END_BLOCK code */
+
+#define D_CODES 30
+/* number of distance codes */
+
+#define BL_CODES 19
+/* number of codes used to transfer the bit lengths */
+
+#define HEAP_SIZE (2*L_CODES+1)
+/* maximum heap size */
+
+#define MAX_BITS 15
+/* All codes must not exceed MAX_BITS bits */
+
+#define INIT_STATE 42
+#define BUSY_STATE 113
+#define FINISH_STATE 666
+/* Stream status */
+
+
+/* Data structure describing a single value and its code string. */
+typedef struct ct_data_s {
+ union {
+ ush freq; /* frequency count */
+ ush code; /* bit string */
+ } fc;
+ union {
+ ush dad; /* father node in Huffman tree */
+ ush len; /* length of bit string */
+ } dl;
+} FAR ct_data;
+
+#define Freq fc.freq
+#define Code fc.code
+#define Dad dl.dad
+#define Len dl.len
+
+typedef struct static_tree_desc_s static_tree_desc;
+
+typedef struct tree_desc_s {
+ ct_data *dyn_tree; /* the dynamic tree */
+ int max_code; /* largest code with non zero frequency */
+ static_tree_desc *stat_desc; /* the corresponding static tree */
+} FAR tree_desc;
+
+typedef ush Pos;
+typedef Pos FAR Posf;
+typedef unsigned IPos;
+
+/* A Pos is an index in the character window. We use short instead of int to
+ * save space in the various tables. IPos is used only for parameter passing.
+ */
+
+typedef struct deflate_state {
+ z_streamp strm; /* pointer back to this zlib stream */
+ int status; /* as the name implies */
+ Bytef *pending_buf; /* output still pending */
+ ulg pending_buf_size; /* size of pending_buf */
+ Bytef *pending_out; /* next pending byte to output to the stream */
+ int pending; /* nb of bytes in the pending buffer */
+ int noheader; /* suppress zlib header and adler32 */
+ Byte data_type; /* UNKNOWN, BINARY or ASCII */
+ Byte method; /* STORED (for zip only) or DEFLATED */
+ int last_flush; /* value of flush param for previous deflate call */
+
+ /* used by deflate.c: */
+
+ uInt w_size; /* LZ77 window size (32K by default) */
+ uInt w_bits; /* log2(w_size) (8..16) */
+ uInt w_mask; /* w_size - 1 */
+
+ Bytef *window;
+ /* Sliding window. Input bytes are read into the second half of the window,
+ * and move to the first half later to keep a dictionary of at least wSize
+ * bytes. With this organization, matches are limited to a distance of
+ * wSize-MAX_MATCH bytes, but this ensures that IO is always
+ * performed with a length multiple of the block size. Also, it limits
+ * the window size to 64K, which is quite useful on MSDOS.
+ * To do: use the user input buffer as sliding window.
+ */
+
+ ulg window_size;
+ /* Actual size of window: 2*wSize, except when the user input buffer
+ * is directly used as sliding window.
+ */
+
+ Posf *prev;
+ /* Link to older string with same hash index. To limit the size of this
+ * array to 64K, this link is maintained only for the last 32K strings.
+ * An index in this array is thus a window index modulo 32K.
+ */
+
+ Posf *head; /* Heads of the hash chains or NIL. */
+
+ uInt ins_h; /* hash index of string to be inserted */
+ uInt hash_size; /* number of elements in hash table */
+ uInt hash_bits; /* log2(hash_size) */
+ uInt hash_mask; /* hash_size-1 */
+
+ uInt hash_shift;
+ /* Number of bits by which ins_h must be shifted at each input
+ * step. It must be such that after MIN_MATCH steps, the oldest
+ * byte no longer takes part in the hash key, that is:
+ * hash_shift * MIN_MATCH >= hash_bits
+ */
+
+ long block_start;
+ /* Window position at the beginning of the current output block. Gets
+ * negative when the window is moved backwards.
+ */
+
+ uInt match_length; /* length of best match */
+ IPos prev_match; /* previous match */
+ int match_available; /* set if previous match exists */
+ uInt strstart; /* start of string to insert */
+ uInt match_start; /* start of matching string */
+ uInt lookahead; /* number of valid bytes ahead in window */
+
+ uInt prev_length;
+ /* Length of the best match at previous step. Matches not greater than this
+ * are discarded. This is used in the lazy match evaluation.
+ */
+
+ uInt max_chain_length;
+ /* To speed up deflation, hash chains are never searched beyond this
+ * length. A higher limit improves compression ratio but degrades the
+ * speed.
+ */
+
+ uInt max_lazy_match;
+ /* Attempt to find a better match only when the current match is strictly
+ * smaller than this value. This mechanism is used only for compression
+ * levels >= 4.
+ */
+# define max_insert_length max_lazy_match
+ /* Insert new strings in the hash table only if the match length is not
+ * greater than this length. This saves time but degrades compression.
+ * max_insert_length is used only for compression levels <= 3.
+ */
+
+ int level; /* compression level (1..9) */
+ int strategy; /* favor or force Huffman coding*/
+
+ uInt good_match;
+ /* Use a faster search when the previous match is longer than this */
+
+ int nice_match; /* Stop searching when current match exceeds this */
+
+ /* used by trees.c: */
+ /* Didn't use ct_data typedef below to supress compiler warning */
+ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
+ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
+ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
+
+ struct tree_desc_s l_desc; /* desc. for literal tree */
+ struct tree_desc_s d_desc; /* desc. for distance tree */
+ struct tree_desc_s bl_desc; /* desc. for bit length tree */
+
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */
+ int heap_len; /* number of elements in the heap */
+ int heap_max; /* element of largest frequency */
+ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+ * The same heap array is used to build all trees.
+ */
+
+ uch depth[2*L_CODES+1];
+ /* Depth of each subtree used as tie breaker for trees of equal frequency
+ */
+
+ uchf *l_buf; /* buffer for literals or lengths */
+
+ uInt lit_bufsize;
+ /* Size of match buffer for literals/lengths. There are 4 reasons for
+ * limiting lit_bufsize to 64K:
+ * - frequencies can be kept in 16 bit counters
+ * - if compression is not successful for the first block, all input
+ * data is still in the window so we can still emit a stored block even
+ * when input comes from standard input. (This can also be done for
+ * all blocks if lit_bufsize is not greater than 32K.)
+ * - if compression is not successful for a file smaller than 64K, we can
+ * even emit a stored file instead of a stored block (saving 5 bytes).
+ * This is applicable only for zip (not gzip or zlib).
+ * - creating new Huffman trees less frequently may not provide fast
+ * adaptation to changes in the input data statistics. (Take for
+ * example a binary file with poorly compressible code followed by
+ * a highly compressible string table.) Smaller buffer sizes give
+ * fast adaptation but have of course the overhead of transmitting
+ * trees more frequently.
+ * - I can't count above 4
+ */
+
+ uInt last_lit; /* running index in l_buf */
+
+ ushf *d_buf;
+ /* Buffer for distances. To simplify the code, d_buf and l_buf have
+ * the same number of elements. To use different lengths, an extra flag
+ * array would be necessary.
+ */
+
+ ulg opt_len; /* bit length of current block with optimal trees */
+ ulg static_len; /* bit length of current block with static trees */
+ ulg compressed_len; /* total bit length of compressed file */
+ uInt matches; /* number of string matches in current block */
+ int last_eob_len; /* bit length of EOB code for last block */
+
+#ifdef DEBUG_ZLIB
+ ulg bits_sent; /* bit length of the compressed data */
+#endif
+
+ ush bi_buf;
+ /* Output buffer. bits are inserted starting at the bottom (least
+ * significant bits).
+ */
+ int bi_valid;
+ /* Number of valid bits in bi_buf. All bits above the last valid bit
+ * are always zero.
+ */
+
+} FAR deflate_state;
+
+/* Output a byte on the stream.
+ * IN assertion: there is enough room in pending_buf.
+ */
+#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
+
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+ /* in trees.c */
+void _tr_init OF((deflate_state *s));
+int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));
+ulg _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len,
+ int eof));
+void _tr_align OF((deflate_state *s));
+void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len,
+ int eof));
+void _tr_stored_type_only OF((deflate_state *));
+
+#endif
+/* --- deflate.h */
+
+/* +++ deflate.c */
+/* deflate.c -- compress data using the deflation algorithm
+ * Copyright (C) 1995-1996 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process depends on being able to identify portions
+ * of the input text which are identical to earlier input (within a
+ * sliding window trailing behind the input currently being processed).
+ *
+ * The most straightforward technique turns out to be the fastest for
+ * most input files: try all possible matches and select the longest.
+ * The key feature of this algorithm is that insertions into the string
+ * dictionary are very simple and thus fast, and deletions are avoided
+ * completely. Insertions are performed at each input character, whereas
+ * string matches are performed only when the previous match ends. So it
+ * is preferable to spend more time in matches to allow very fast string
+ * insertions and avoid deletions. The matching algorithm for small
+ * strings is inspired from that of Rabin & Karp. A brute force approach
+ * is used to find longer strings when a small match has been found.
+ * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
+ * (by Leonid Broukhis).
+ * A previous version of this file used a more sophisticated algorithm
+ * (by Fiala and Greene) which is guaranteed to run in linear amortized
+ * time, but has a larger average cost, uses more memory and is patented.
+ * However the F&G algorithm may be faster for some highly redundant
+ * files if the parameter max_chain_length (described below) is too large.
+ *
+ * ACKNOWLEDGEMENTS
+ *
+ * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
+ * I found it in 'freeze' written by Leonid Broukhis.
+ * Thanks to many people for bug reports and testing.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
+ * Available in ftp://ds.internic.net/rfc/rfc1951.txt
+ *
+ * A description of the Rabin and Karp algorithm is given in the book
+ * "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
+ *
+ * Fiala,E.R., and Greene,D.H.
+ * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
+ *
+ */
+
+/* From: deflate.c,v 1.15 1996/07/24 13:40:58 me Exp $ */
+
+/* #include "deflate.h" */
+
+char deflate_copyright[] = " deflate 1.0.4 Copyright 1995-1996 Jean-loup Gailly ";
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+
+/* ===========================================================================
+ * Function prototypes.
+ */
+typedef enum {
+ need_more, /* block not completed, need more input or more output */
+ block_done, /* block flush performed */
+ finish_started, /* finish started, need only more output at next deflate */
+ finish_done /* finish done, accept no more input or output */
+} block_state;
+
+typedef block_state (*compress_func) OF((deflate_state *s, int flush));
+/* Compression function. Returns the block state after the call. */
+
+local void fill_window OF((deflate_state *s));
+local block_state deflate_stored OF((deflate_state *s, int flush));
+local block_state deflate_fast OF((deflate_state *s, int flush));
+local block_state deflate_slow OF((deflate_state *s, int flush));
+local void lm_init OF((deflate_state *s));
+local void putShortMSB OF((deflate_state *s, uInt b));
+local void flush_pending OF((z_streamp strm));
+local int read_buf OF((z_streamp strm, charf *buf, unsigned size));
+#ifdef ASMV
+ void match_init OF((void)); /* asm code initialization */
+ uInt longest_match OF((deflate_state *s, IPos cur_match));
+#else
+local uInt longest_match OF((deflate_state *s, IPos cur_match));
+#endif
+
+#ifdef DEBUG_ZLIB
+local void check_match OF((deflate_state *s, IPos start, IPos match,
+ int length));
+#endif
+
+/* ===========================================================================
+ * Local data
+ */
+
+#define NIL 0
+/* Tail of hash chains */
+
+#ifndef TOO_FAR
+# define TOO_FAR 4096
+#endif
+/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+/* Values for max_lazy_match, good_match and max_chain_length, depending on
+ * the desired pack level (0..9). The values given below have been tuned to
+ * exclude worst case performance for pathological files. Better values may be
+ * found for specific files.
+ */
+typedef struct config_s {
+ ush good_length; /* reduce lazy search above this match length */
+ ush max_lazy; /* do not perform lazy search above this match length */
+ ush nice_length; /* quit search above this match length */
+ ush max_chain;
+ compress_func func;
+} config;
+
+local config configuration_table[10] = {
+/* good lazy nice chain */
+/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */
+/* 1 */ {4, 4, 8, 4, deflate_fast}, /* maximum speed, no lazy matches */
+/* 2 */ {4, 5, 16, 8, deflate_fast},
+/* 3 */ {4, 6, 32, 32, deflate_fast},
+
+/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */
+/* 5 */ {8, 16, 32, 32, deflate_slow},
+/* 6 */ {8, 16, 128, 128, deflate_slow},
+/* 7 */ {8, 32, 128, 256, deflate_slow},
+/* 8 */ {32, 128, 258, 1024, deflate_slow},
+/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* maximum compression */
+
+/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
+ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
+ * meaning.
+ */
+
+#define EQUAL 0
+/* result of memcmp for equal strings */
+
+#ifndef NO_DUMMY_DECL
+struct static_tree_desc_s {int dummy;}; /* for buggy compilers */
+#endif
+
+/* ===========================================================================
+ * Update a hash value with the given input byte
+ * IN assertion: all calls to to UPDATE_HASH are made with consecutive
+ * input characters, so that a running hash key can be computed from the
+ * previous key instead of complete recalculation each time.
+ */
+#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)
+
+
+/* ===========================================================================
+ * Insert string str in the dictionary and set match_head to the previous head
+ * of the hash chain (the most recent string with same hash key). Return
+ * the previous length of the hash chain.
+ * IN assertion: all calls to to INSERT_STRING are made with consecutive
+ * input characters and the first MIN_MATCH bytes of str are valid
+ * (except for the last MIN_MATCH-1 bytes of the input file).
+ */
+#define INSERT_STRING(s, str, match_head) \
+ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+ s->prev[(str) & s->w_mask] = match_head = s->head[s->ins_h], \
+ s->head[s->ins_h] = (Pos)(str))
+
+/* ===========================================================================
+ * Initialize the hash table (avoiding 64K overflow for 16 bit systems).
+ * prev[] will be initialized on the fly.
+ */
+#define CLEAR_HASH(s) \
+ s->head[s->hash_size-1] = NIL; \
+ zmemzero((charf *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));
+
+/* ========================================================================= */
+int deflateInit_(strm, level, version, stream_size)
+ z_streamp strm;
+ int level;
+ const char *version;
+ int stream_size;
+{
+ return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY, version, stream_size);
+ /* To do: ignore strm->next_in if we use it as window */
+}
+
+/* ========================================================================= */
+int deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
+ version, stream_size)
+ z_streamp strm;
+ int level;
+ int method;
+ int windowBits;
+ int memLevel;
+ int strategy;
+ const char *version;
+ int stream_size;
+{
+ deflate_state *s;
+ int noheader = 0;
+ static char* my_version = ZLIB_VERSION;
+
+ ushf *overlay;
+ /* We overlay pending_buf and d_buf+l_buf. This works since the average
+ * output size for (length,distance) codes is <= 24 bits.
+ */
+
+ if (version == Z_NULL || version[0] != my_version[0] ||
+ stream_size != sizeof(z_stream)) {
+ return Z_VERSION_ERROR;
+ }
+ if (strm == Z_NULL) return Z_STREAM_ERROR;
+
+ strm->msg = Z_NULL;
+#ifndef NO_ZCFUNCS
+ if (strm->zalloc == Z_NULL) {
+ strm->zalloc = zcalloc;
+ strm->opaque = (voidpf)0;
+ }
+ if (strm->zfree == Z_NULL) strm->zfree = zcfree;
+#endif
+
+ if (level == Z_DEFAULT_COMPRESSION) level = 6;
+
+ if (windowBits < 0) { /* undocumented feature: suppress zlib header */
+ noheader = 1;
+ windowBits = -windowBits;
+ }
+ if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED ||
+ windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
+ strategy < 0 || strategy > Z_HUFFMAN_ONLY) {
+ return Z_STREAM_ERROR;
+ }
+ s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
+ if (s == Z_NULL) return Z_MEM_ERROR;
+ strm->state = (struct internal_state FAR *)s;
+ s->strm = strm;
+
+ s->noheader = noheader;
+ s->w_bits = windowBits;
+ s->w_size = 1 << s->w_bits;
+ s->w_mask = s->w_size - 1;
+
+ s->hash_bits = memLevel + 7;
+ s->hash_size = 1 << s->hash_bits;
+ s->hash_mask = s->hash_size - 1;
+ s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
+
+ s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
+ s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos));
+ s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos));
+
+ s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
+
+ overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
+ s->pending_buf = (uchf *) overlay;
+ s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L);
+
+ if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
+ s->pending_buf == Z_NULL) {
+ strm->msg = (char*)ERR_MSG(Z_MEM_ERROR);
+ deflateEnd (strm);
+ return Z_MEM_ERROR;
+ }
+ s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
+ s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
+
+ s->level = level;
+ s->strategy = strategy;
+ s->method = (Byte)method;
+
+ return deflateReset(strm);
+}
+
+/* ========================================================================= */
+int deflateSetDictionary (strm, dictionary, dictLength)
+ z_streamp strm;
+ const Bytef *dictionary;
+ uInt dictLength;
+{
+ deflate_state *s;
+ uInt length = dictLength;
+ uInt n;
+ IPos hash_head = 0;
+
+ if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL)
+ return Z_STREAM_ERROR;
+
+ s = (deflate_state *) strm->state;
+ if (s->status != INIT_STATE) return Z_STREAM_ERROR;
+
+ strm->adler = adler32(strm->adler, dictionary, dictLength);
+
+ if (length < MIN_MATCH) return Z_OK;
+ if (length > MAX_DIST(s)) {
+ length = MAX_DIST(s);
+#ifndef USE_DICT_HEAD
+ dictionary += dictLength - length; /* use the tail of the dictionary */
+#endif
+ }
+ zmemcpy((charf *)s->window, dictionary, length);
+ s->strstart = length;
+ s->block_start = (long)length;
+
+ /* Insert all strings in the hash table (except for the last two bytes).
+ * s->lookahead stays null, so s->ins_h will be recomputed at the next
+ * call of fill_window.
+ */
+ s->ins_h = s->window[0];
+ UPDATE_HASH(s, s->ins_h, s->window[1]);
+ for (n = 0; n <= length - MIN_MATCH; n++) {
+ INSERT_STRING(s, n, hash_head);
+ }
+ if (hash_head) hash_head = 0; /* to make compiler happy */
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int deflateReset (strm)
+ z_streamp strm;
+{
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL ||
+ strm->zalloc == Z_NULL || strm->zfree == Z_NULL) return Z_STREAM_ERROR;
+
+ strm->total_in = strm->total_out = 0;
+ strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */
+ strm->data_type = Z_UNKNOWN;
+
+ s = (deflate_state *)strm->state;
+ s->pending = 0;
+ s->pending_out = s->pending_buf;
+
+ if (s->noheader < 0) {
+ s->noheader = 0; /* was set to -1 by deflate(..., Z_FINISH); */
+ }
+ s->status = s->noheader ? BUSY_STATE : INIT_STATE;
+ strm->adler = 1;
+ s->last_flush = Z_NO_FLUSH;
+
+ _tr_init(s);
+ lm_init(s);
+
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int deflateParams(strm, level, strategy)
+ z_streamp strm;
+ int level;
+ int strategy;
+{
+ deflate_state *s;
+ compress_func func;
+ int err = Z_OK;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ s = (deflate_state *) strm->state;
+
+ if (level == Z_DEFAULT_COMPRESSION) {
+ level = 6;
+ }
+ if (level < 0 || level > 9 || strategy < 0 || strategy > Z_HUFFMAN_ONLY) {
+ return Z_STREAM_ERROR;
+ }
+ func = configuration_table[s->level].func;
+
+ if (func != configuration_table[level].func && strm->total_in != 0) {
+ /* Flush the last buffer: */
+ err = deflate(strm, Z_PARTIAL_FLUSH);
+ }
+ if (s->level != level) {
+ s->level = level;
+ s->max_lazy_match = configuration_table[level].max_lazy;
+ s->good_match = configuration_table[level].good_length;
+ s->nice_match = configuration_table[level].nice_length;
+ s->max_chain_length = configuration_table[level].max_chain;
+ }
+ s->strategy = strategy;
+ return err;
+}
+
+/* =========================================================================
+ * Put a short in the pending buffer. The 16-bit value is put in MSB order.
+ * IN assertion: the stream state is correct and there is enough room in
+ * pending_buf.
+ */
+local void putShortMSB (s, b)
+ deflate_state *s;
+ uInt b;
+{
+ put_byte(s, (Byte)(b >> 8));
+ put_byte(s, (Byte)(b & 0xff));
+}
+
+/* =========================================================================
+ * Flush as much pending output as possible. All deflate() output goes
+ * through this function so some applications may wish to modify it
+ * to avoid allocating a large strm->next_out buffer and copying into it.
+ * (See also read_buf()).
+ */
+local void flush_pending(strm)
+ z_streamp strm;
+{
+ deflate_state *s = (deflate_state *) strm->state;
+ unsigned len = s->pending;
+
+ if (len > strm->avail_out) len = strm->avail_out;
+ if (len == 0) return;
+
+ if (strm->next_out != NULL) {
+ zmemcpy(strm->next_out, s->pending_out, len);
+ strm->next_out += len;
+ }
+ s->pending_out += len;
+ strm->total_out += len;
+ strm->avail_out -= len;
+ s->pending -= len;
+ if (s->pending == 0) {
+ s->pending_out = s->pending_buf;
+ }
+}
+
+/* ========================================================================= */
+int deflate (strm, flush)
+ z_streamp strm;
+ int flush;
+{
+ int old_flush; /* value of flush param for previous deflate call */
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL ||
+ flush > Z_FINISH || flush < 0) {
+ return Z_STREAM_ERROR;
+ }
+ s = (deflate_state *) strm->state;
+
+ if ((strm->next_in == Z_NULL && strm->avail_in != 0) ||
+ (s->status == FINISH_STATE && flush != Z_FINISH)) {
+ ERR_RETURN(strm, Z_STREAM_ERROR);
+ }
+ if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);
+
+ s->strm = strm; /* just in case */
+ old_flush = s->last_flush;
+ s->last_flush = flush;
+
+ /* Write the zlib header */
+ if (s->status == INIT_STATE) {
+
+ uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
+ uInt level_flags = (s->level-1) >> 1;
+
+ if (level_flags > 3) level_flags = 3;
+ header |= (level_flags << 6);
+ if (s->strstart != 0) header |= PRESET_DICT;
+ header += 31 - (header % 31);
+
+ s->status = BUSY_STATE;
+ putShortMSB(s, header);
+
+ /* Save the adler32 of the preset dictionary: */
+ if (s->strstart != 0) {
+ putShortMSB(s, (uInt)(strm->adler >> 16));
+ putShortMSB(s, (uInt)(strm->adler & 0xffff));
+ }
+ strm->adler = 1L;
+ }
+
+ /* Flush as much pending output as possible */
+ if (s->pending != 0) {
+ flush_pending(strm);
+ if (strm->avail_out == 0) {
+ /* Since avail_out is 0, deflate will be called again with
+ * more output space, but possibly with both pending and
+ * avail_in equal to zero. There won't be anything to do,
+ * but this is not an error situation so make sure we
+ * return OK instead of BUF_ERROR at next call of deflate:
+ */
+ s->last_flush = -1;
+ return Z_OK;
+ }
+
+ /* Make sure there is something to do and avoid duplicate consecutive
+ * flushes. For repeated and useless calls with Z_FINISH, we keep
+ * returning Z_STREAM_END instead of Z_BUFF_ERROR.
+ */
+ } else if (strm->avail_in == 0 && flush <= old_flush &&
+ flush != Z_FINISH) {
+ ERR_RETURN(strm, Z_BUF_ERROR);
+ }
+
+ /* User must not provide more input after the first FINISH: */
+ if (s->status == FINISH_STATE && strm->avail_in != 0) {
+ ERR_RETURN(strm, Z_BUF_ERROR);
+ }
+
+ /* Start a new block or continue the current one.
+ */
+ if (strm->avail_in != 0 || s->lookahead != 0 ||
+ (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) {
+ block_state bstate;
+
+ bstate = (*(configuration_table[s->level].func))(s, flush);
+
+ if (bstate == finish_started || bstate == finish_done) {
+ s->status = FINISH_STATE;
+ }
+ if (bstate == need_more || bstate == finish_started) {
+ if (strm->avail_out == 0) {
+ s->last_flush = -1; /* avoid BUF_ERROR next call, see above */
+ }
+ return Z_OK;
+ /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
+ * of deflate should use the same flush parameter to make sure
+ * that the flush is complete. So we don't have to output an
+ * empty block here, this will be done at next call. This also
+ * ensures that for a very small output buffer, we emit at most
+ * one empty block.
+ */
+ }
+ if (bstate == block_done) {
+ if (flush == Z_PARTIAL_FLUSH) {
+ _tr_align(s);
+ } else if (flush == Z_PACKET_FLUSH) {
+ /* Output just the 3-bit `stored' block type value,
+ but not a zero length. */
+ _tr_stored_type_only(s);
+ } else { /* FULL_FLUSH or SYNC_FLUSH */
+ _tr_stored_block(s, (char*)0, 0L, 0);
+ /* For a full flush, this empty block will be recognized
+ * as a special marker by inflate_sync().
+ */
+ if (flush == Z_FULL_FLUSH) {
+ CLEAR_HASH(s); /* forget history */
+ }
+ }
+ flush_pending(strm);
+ if (strm->avail_out == 0) {
+ s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */
+ return Z_OK;
+ }
+ }
+ }
+ Assert(strm->avail_out > 0, "bug2");
+
+ if (flush != Z_FINISH) return Z_OK;
+ if (s->noheader) return Z_STREAM_END;
+
+ /* Write the zlib trailer (adler32) */
+ putShortMSB(s, (uInt)(strm->adler >> 16));
+ putShortMSB(s, (uInt)(strm->adler & 0xffff));
+ flush_pending(strm);
+ /* If avail_out is zero, the application will call deflate again
+ * to flush the rest.
+ */
+ s->noheader = -1; /* write the trailer only once! */
+ return s->pending != 0 ? Z_OK : Z_STREAM_END;
+}
+
+/* ========================================================================= */
+int deflateEnd (strm)
+ z_streamp strm;
+{
+ int status;
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ s = (deflate_state *) strm->state;
+
+ status = s->status;
+ if (status != INIT_STATE && status != BUSY_STATE &&
+ status != FINISH_STATE) {
+ return Z_STREAM_ERROR;
+ }
+
+ /* Deallocate in reverse order of allocations: */
+ TRY_FREE(strm, s->pending_buf);
+ TRY_FREE(strm, s->head);
+ TRY_FREE(strm, s->prev);
+ TRY_FREE(strm, s->window);
+
+ ZFREE(strm, s);
+ strm->state = Z_NULL;
+
+ return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK;
+}
+
+/* =========================================================================
+ * Copy the source state to the destination state.
+ */
+int deflateCopy (dest, source)
+ z_streamp dest;
+ z_streamp source;
+{
+ deflate_state *ds;
+ deflate_state *ss;
+ ushf *overlay;
+
+ if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ ss = (deflate_state *) source->state;
+
+ *dest = *source;
+
+ ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state));
+ if (ds == Z_NULL) return Z_MEM_ERROR;
+ dest->state = (struct internal_state FAR *) ds;
+ *ds = *ss;
+ ds->strm = dest;
+
+ ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
+ ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos));
+ ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos));
+ overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2);
+ ds->pending_buf = (uchf *) overlay;
+
+ if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||
+ ds->pending_buf == Z_NULL) {
+ deflateEnd (dest);
+ return Z_MEM_ERROR;
+ }
+ /* ??? following zmemcpy doesn't work for 16-bit MSDOS */
+ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));
+ zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos));
+ zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos));
+ zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
+
+ ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
+ ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush);
+ ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize;
+
+ ds->l_desc.dyn_tree = ds->dyn_ltree;
+ ds->d_desc.dyn_tree = ds->dyn_dtree;
+ ds->bl_desc.dyn_tree = ds->bl_tree;
+
+ return Z_OK;
+}
+
+/* ===========================================================================
+ * Return the number of bytes of output which are immediately available
+ * for output from the decompressor.
+ */
+int deflateOutputPending (strm)
+ z_streamp strm;
+{
+ if (strm == Z_NULL || strm->state == Z_NULL) return 0;
+
+ return ((deflate_state *)(strm->state))->pending;
+}
+
+/* ===========================================================================
+ * Read a new buffer from the current input stream, update the adler32
+ * and total number of bytes read. All deflate() input goes through
+ * this function so some applications may wish to modify it to avoid
+ * allocating a large strm->next_in buffer and copying from it.
+ * (See also flush_pending()).
+ */
+local int read_buf(strm, buf, size)
+ z_streamp strm;
+ charf *buf;
+ unsigned size;
+{
+ unsigned len = strm->avail_in;
+
+ if (len > size) len = size;
+ if (len == 0) return 0;
+
+ strm->avail_in -= len;
+
+ if (!((deflate_state *)(strm->state))->noheader) {
+ strm->adler = adler32(strm->adler, strm->next_in, len);
+ }
+ zmemcpy(buf, strm->next_in, len);
+ strm->next_in += len;
+ strm->total_in += len;
+
+ return (int)len;
+}
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new zlib stream
+ */
+local void lm_init (s)
+ deflate_state *s;
+{
+ s->window_size = (ulg)2L*s->w_size;
+
+ CLEAR_HASH(s);
+
+ /* Set the default configuration parameters:
+ */
+ s->max_lazy_match = configuration_table[s->level].max_lazy;
+ s->good_match = configuration_table[s->level].good_length;
+ s->nice_match = configuration_table[s->level].nice_length;
+ s->max_chain_length = configuration_table[s->level].max_chain;
+
+ s->strstart = 0;
+ s->block_start = 0L;
+ s->lookahead = 0;
+ s->match_length = s->prev_length = MIN_MATCH-1;
+ s->match_available = 0;
+ s->ins_h = 0;
+#ifdef ASMV
+ match_init(); /* initialize the asm code */
+#endif
+}
+
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the head of the hash chain for the current
+ * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
+ * OUT assertion: the match length is not greater than s->lookahead.
+ */
+#ifndef ASMV
+/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
+ * match.S. The code will be functionally equivalent.
+ */
+local uInt longest_match(s, cur_match)
+ deflate_state *s;
+ IPos cur_match; /* current match */
+{
+ unsigned chain_length = s->max_chain_length;/* max hash chain length */
+ register Bytef *scan = s->window + s->strstart; /* current string */
+ register Bytef *match; /* matched string */
+ register int len; /* length of current match */
+ int best_len = s->prev_length; /* best match length so far */
+ int nice_match = s->nice_match; /* stop if match long enough */
+ IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
+ s->strstart - (IPos)MAX_DIST(s) : NIL;
+ /* Stop when cur_match becomes <= limit. To simplify the code,
+ * we prevent matches with the string of window index 0.
+ */
+ Posf *prev = s->prev;
+ uInt wmask = s->w_mask;
+
+#ifdef UNALIGNED_OK
+ /* Compare two bytes at a time. Note: this is not always beneficial.
+ * Try with and without -DUNALIGNED_OK to check.
+ */
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
+ register ush scan_start = *(ushf*)scan;
+ register ush scan_end = *(ushf*)(scan+best_len-1);
+#else
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+ register Byte scan_end1 = scan[best_len-1];
+ register Byte scan_end = scan[best_len];
+#endif
+
+ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+ /* Do not waste too much time if we already have a good match: */
+ if (s->prev_length >= s->good_match) {
+ chain_length >>= 2;
+ }
+ /* Do not look for matches beyond the end of the input. This is necessary
+ * to make deflate deterministic.
+ */
+ if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
+
+ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+ do {
+ Assert(cur_match < s->strstart, "no future");
+ match = s->window + cur_match;
+
+ /* Skip to next match if the match length cannot increase
+ * or if the match length is less than 2:
+ */
+#if (defined(UNALIGNED_OK) && MAX_MATCH == 258)
+ /* This code assumes sizeof(unsigned short) == 2. Do not use
+ * UNALIGNED_OK if your compiler uses a different size.
+ */
+ if (*(ushf*)(match+best_len-1) != scan_end ||
+ *(ushf*)match != scan_start) continue;
+
+ /* It is not necessary to compare scan[2] and match[2] since they are
+ * always equal when the other bytes match, given that the hash keys
+ * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
+ * strstart+3, +5, ... up to strstart+257. We check for insufficient
+ * lookahead only every 4th comparison; the 128th check will be made
+ * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
+ * necessary to put more guard bytes at the end of the window, or
+ * to check more often for insufficient lookahead.
+ */
+ Assert(scan[2] == match[2], "scan[2]?");
+ scan++, match++;
+ do {
+ } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ scan < strend);
+ /* The funny "do {}" generates better code on most compilers */
+
+ /* Here, scan <= window+strstart+257 */
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+ if (*scan == *match) scan++;
+
+ len = (MAX_MATCH - 1) - (int)(strend-scan);
+ scan = strend - (MAX_MATCH-1);
+
+#else /* UNALIGNED_OK */
+
+ if (match[best_len] != scan_end ||
+ match[best_len-1] != scan_end1 ||
+ *match != *scan ||
+ *++match != scan[1]) continue;
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2, match++;
+ Assert(*scan == *match, "match[2]?");
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do {
+ } while (*++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ scan < strend);
+
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+ len = MAX_MATCH - (int)(strend - scan);
+ scan = strend - MAX_MATCH;
+
+#endif /* UNALIGNED_OK */
+
+ if (len > best_len) {
+ s->match_start = cur_match;
+ best_len = len;
+ if (len >= nice_match) break;
+#ifdef UNALIGNED_OK
+ scan_end = *(ushf*)(scan+best_len-1);
+#else
+ scan_end1 = scan[best_len-1];
+ scan_end = scan[best_len];
+#endif
+ }
+ } while ((cur_match = prev[cur_match & wmask]) > limit
+ && --chain_length != 0);
+
+ if ((uInt)best_len <= s->lookahead) return best_len;
+ return s->lookahead;
+}
+#endif /* ASMV */
+
+#ifdef DEBUG_ZLIB
+/* ===========================================================================
+ * Check that the match at match_start is indeed a match.
+ */
+local void check_match(s, start, match, length)
+ deflate_state *s;
+ IPos start, match;
+ int length;
+{
+ /* check that the match is indeed a match */
+ if (zmemcmp((charf *)s->window + match,
+ (charf *)s->window + start, length) != EQUAL) {
+ fprintf(stderr, " start %u, match %u, length %d\n",
+ start, match, length);
+ do {
+ fprintf(stderr, "%c%c", s->window[match++], s->window[start++]);
+ } while (--length != 0);
+ z_error("invalid match");
+ }
+ if (z_verbose > 1) {
+ fprintf(stderr,"\\[%d,%d]", start-match, length);
+ do { putc(s->window[start++], stderr); } while (--length != 0);
+ }
+}
+#else
+# define check_match(s, start, match, length)
+#endif
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead.
+ *
+ * IN assertion: lookahead < MIN_LOOKAHEAD
+ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
+ * At least one byte has been read, or avail_in == 0; reads are
+ * performed for at least two bytes (required for the zip translate_eol
+ * option -- not supported here).
+ */
+local void fill_window(s)
+ deflate_state *s;
+{
+ register unsigned n, m;
+ register Posf *p;
+ unsigned more; /* Amount of free space at the end of the window. */
+ uInt wsize = s->w_size;
+
+ do {
+ more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
+
+ /* Deal with !@#$% 64K limit: */
+ if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
+ more = wsize;
+
+ } else if (more == (unsigned)(-1)) {
+ /* Very unlikely, but possible on 16 bit machine if strstart == 0
+ * and lookahead == 1 (input done one byte at time)
+ */
+ more--;
+
+ /* If the window is almost full and there is insufficient lookahead,
+ * move the upper half to the lower one to make room in the upper half.
+ */
+ } else if (s->strstart >= wsize+MAX_DIST(s)) {
+
+ zmemcpy((charf *)s->window, (charf *)s->window+wsize,
+ (unsigned)wsize);
+ s->match_start -= wsize;
+ s->strstart -= wsize; /* we now have strstart >= MAX_DIST */
+ s->block_start -= (long) wsize;
+
+ /* Slide the hash table (could be avoided with 32 bit values
+ at the expense of memory usage). We slide even when level == 0
+ to keep the hash table consistent if we switch back to level > 0
+ later. (Using level 0 permanently is not an optimal usage of
+ zlib, so we don't care about this pathological case.)
+ */
+ n = s->hash_size;
+ p = &s->head[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ } while (--n);
+
+ n = wsize;
+ p = &s->prev[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ /* If n is not on any hash chain, prev[n] is garbage but
+ * its value will never be used.
+ */
+ } while (--n);
+ more += wsize;
+ }
+ if (s->strm->avail_in == 0) return;
+
+ /* If there was no sliding:
+ * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
+ * more == window_size - lookahead - strstart
+ * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
+ * => more >= window_size - 2*WSIZE + 2
+ * In the BIG_MEM or MMAP case (not yet supported),
+ * window_size == input_size + MIN_LOOKAHEAD &&
+ * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
+ * Otherwise, window_size == 2*WSIZE so more >= 2.
+ * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
+ */
+ Assert(more >= 2, "more < 2");
+
+ n = read_buf(s->strm, (charf *)s->window + s->strstart + s->lookahead,
+ more);
+ s->lookahead += n;
+
+ /* Initialize the hash value now that we have some input: */
+ if (s->lookahead >= MIN_MATCH) {
+ s->ins_h = s->window[s->strstart];
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ }
+ /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
+ * but this is not important since only literal bytes will be emitted.
+ */
+
+ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
+}
+
+/* ===========================================================================
+ * Flush the current block, with given end-of-file flag.
+ * IN assertion: strstart is set to the end of the current match.
+ */
+#define FLUSH_BLOCK_ONLY(s, eof) { \
+ _tr_flush_block(s, (s->block_start >= 0L ? \
+ (charf *)&s->window[(unsigned)s->block_start] : \
+ (charf *)Z_NULL), \
+ (ulg)((long)s->strstart - s->block_start), \
+ (eof)); \
+ s->block_start = s->strstart; \
+ flush_pending(s->strm); \
+ Tracev((stderr,"[FLUSH]")); \
+}
+
+/* Same but force premature exit if necessary. */
+#define FLUSH_BLOCK(s, eof) { \
+ FLUSH_BLOCK_ONLY(s, eof); \
+ if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \
+}
+
+/* ===========================================================================
+ * Copy without compression as much as possible from the input stream, return
+ * the current block state.
+ * This function does not insert new strings in the dictionary since
+ * uncompressible data is probably not useful. This function is used
+ * only for the level=0 compression option.
+ * NOTE: this function should be optimized to avoid extra copying from
+ * window to pending_buf.
+ */
+local block_state deflate_stored(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
+ * to pending_buf_size, and each stored block has a 5 byte header:
+ */
+ ulg max_block_size = 0xffff;
+ ulg max_start;
+
+ if (max_block_size > s->pending_buf_size - 5) {
+ max_block_size = s->pending_buf_size - 5;
+ }
+
+ /* Copy as much as possible from input to output: */
+ for (;;) {
+ /* Fill the window as much as possible: */
+ if (s->lookahead <= 1) {
+
+ Assert(s->strstart < s->w_size+MAX_DIST(s) ||
+ s->block_start >= (long)s->w_size, "slide too late");
+
+ fill_window(s);
+ if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more;
+
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+ Assert(s->block_start >= 0L, "block gone");
+
+ s->strstart += s->lookahead;
+ s->lookahead = 0;
+
+ /* Emit a stored block if pending_buf will be full: */
+ max_start = s->block_start + max_block_size;
+ if (s->strstart == 0 || (ulg)s->strstart >= max_start) {
+ /* strstart == 0 is possible when wraparound on 16-bit machine */
+ s->lookahead = (uInt)(s->strstart - max_start);
+ s->strstart = (uInt)max_start;
+ FLUSH_BLOCK(s, 0);
+ }
+ /* Flush if we may have to slide, otherwise block_start may become
+ * negative and the data will be gone:
+ */
+ if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) {
+ FLUSH_BLOCK(s, 0);
+ }
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+
+/* ===========================================================================
+ * Compress as much as possible from the input stream, return the current
+ * block state.
+ * This function does not perform lazy evaluation of matches and inserts
+ * new strings in the dictionary only for unmatched strings or for short
+ * matches. It is used only for the fast compression options.
+ */
+local block_state deflate_fast(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head = NIL; /* head of the hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+ return need_more;
+ }
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ * At this point we have always match_length < MIN_MATCH
+ */
+ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ if (s->strategy != Z_HUFFMAN_ONLY) {
+ s->match_length = longest_match (s, hash_head);
+ }
+ /* longest_match() sets match_start */
+ }
+ if (s->match_length >= MIN_MATCH) {
+ check_match(s, s->strstart, s->match_start, s->match_length);
+
+ bflush = _tr_tally(s, s->strstart - s->match_start,
+ s->match_length - MIN_MATCH);
+
+ s->lookahead -= s->match_length;
+
+ /* Insert new strings in the hash table only if the match length
+ * is not too large. This saves time but degrades compression.
+ */
+ if (s->match_length <= s->max_insert_length &&
+ s->lookahead >= MIN_MATCH) {
+ s->match_length--; /* string at strstart already in hash table */
+ do {
+ s->strstart++;
+ INSERT_STRING(s, s->strstart, hash_head);
+ /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+ * always MIN_MATCH bytes ahead.
+ */
+ } while (--s->match_length != 0);
+ s->strstart++;
+ } else {
+ s->strstart += s->match_length;
+ s->match_length = 0;
+ s->ins_h = s->window[s->strstart];
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
+ * matter since it will be recomputed at next deflate call.
+ */
+ }
+ } else {
+ /* No match, output a literal byte */
+ Tracevv((stderr,"%c", s->window[s->strstart]));
+ bflush = _tr_tally (s, 0, s->window[s->strstart]);
+ s->lookahead--;
+ s->strstart++;
+ }
+ if (bflush) FLUSH_BLOCK(s, 0);
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+
+/* ===========================================================================
+ * Same as above, but achieves better compression. We use a lazy
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ */
+local block_state deflate_slow(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head = NIL; /* head of hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ /* Process the input block. */
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+ return need_more;
+ }
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ */
+ s->prev_length = s->match_length, s->prev_match = s->match_start;
+ s->match_length = MIN_MATCH-1;
+
+ if (hash_head != NIL && s->prev_length < s->max_lazy_match &&
+ s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ if (s->strategy != Z_HUFFMAN_ONLY) {
+ s->match_length = longest_match (s, hash_head);
+ }
+ /* longest_match() sets match_start */
+
+ if (s->match_length <= 5 && (s->strategy == Z_FILTERED ||
+ (s->match_length == MIN_MATCH &&
+ s->strstart - s->match_start > TOO_FAR))) {
+
+ /* If prev_match is also MIN_MATCH, match_start is garbage
+ * but we will ignore the current match anyway.
+ */
+ s->match_length = MIN_MATCH-1;
+ }
+ }
+ /* If there was a match at the previous step and the current
+ * match is not better, output the previous match:
+ */
+ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {
+ uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
+ /* Do not insert strings in hash table beyond this. */
+
+ check_match(s, s->strstart-1, s->prev_match, s->prev_length);
+
+ bflush = _tr_tally(s, s->strstart -1 - s->prev_match,
+ s->prev_length - MIN_MATCH);
+
+ /* Insert in hash table all strings up to the end of the match.
+ * strstart-1 and strstart are already inserted. If there is not
+ * enough lookahead, the last two strings are not inserted in
+ * the hash table.
+ */
+ s->lookahead -= s->prev_length-1;
+ s->prev_length -= 2;
+ do {
+ if (++s->strstart <= max_insert) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+ } while (--s->prev_length != 0);
+ s->match_available = 0;
+ s->match_length = MIN_MATCH-1;
+ s->strstart++;
+
+ if (bflush) FLUSH_BLOCK(s, 0);
+
+ } else if (s->match_available) {
+ /* If there was no match at the previous position, output a
+ * single literal. If there was a match but the current match
+ * is longer, truncate the previous match to a single literal.
+ */
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ if (_tr_tally (s, 0, s->window[s->strstart-1])) {
+ FLUSH_BLOCK_ONLY(s, 0);
+ }
+ s->strstart++;
+ s->lookahead--;
+ if (s->strm->avail_out == 0) return need_more;
+ } else {
+ /* There is no previous match to compare with, wait for
+ * the next step to decide.
+ */
+ s->match_available = 1;
+ s->strstart++;
+ s->lookahead--;
+ }
+ }
+ Assert (flush != Z_NO_FLUSH, "no flush?");
+ if (s->match_available) {
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ _tr_tally (s, 0, s->window[s->strstart-1]);
+ s->match_available = 0;
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+/* --- deflate.c */
+
+/* +++ trees.c */
+/* trees.c -- output deflated data using Huffman coding
+ * Copyright (C) 1995-1996 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process uses several Huffman trees. The more
+ * common source values are represented by shorter bit sequences.
+ *
+ * Each code tree is stored in a compressed form which is itself
+ * a Huffman encoding of the lengths of all the code strings (in
+ * ascending order by source values). The actual code strings are
+ * reconstructed from the lengths in the inflate process, as described
+ * in the deflate specification.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
+ * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
+ *
+ * Storer, James A.
+ * Data Compression: Methods and Theory, pp. 49-50.
+ * Computer Science Press, 1988. ISBN 0-7167-8156-5.
+ *
+ * Sedgewick, R.
+ * Algorithms, p290.
+ * Addison-Wesley, 1983. ISBN 0-201-06672-6.
+ */
+
+/* From: trees.c,v 1.11 1996/07/24 13:41:06 me Exp $ */
+
+/* #include "deflate.h" */
+
+#ifdef DEBUG_ZLIB
+# include <ctype.h>
+#endif
+
+/* ===========================================================================
+ * Constants
+ */
+
+#define MAX_BL_BITS 7
+/* Bit length codes must not exceed MAX_BL_BITS bits */
+
+#define END_BLOCK 256
+/* end of block literal code */
+
+#define REP_3_6 16
+/* repeat previous bit length 3-6 times (2 bits of repeat count) */
+
+#define REPZ_3_10 17
+/* repeat a zero length 3-10 times (3 bits of repeat count) */
+
+#define REPZ_11_138 18
+/* repeat a zero length 11-138 times (7 bits of repeat count) */
+
+local int extra_lbits[LENGTH_CODES] /* extra bits for each length code */
+ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};
+
+local int extra_dbits[D_CODES] /* extra bits for each distance code */
+ = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
+
+local int extra_blbits[BL_CODES]/* extra bits for each bit length code */
+ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
+
+local uch bl_order[BL_CODES]
+ = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
+/* The lengths of the bit length codes are sent in order of decreasing
+ * probability, to avoid transmitting the lengths for unused bit length codes.
+ */
+
+#define Buf_size (8 * 2*sizeof(char))
+/* Number of bits used within bi_buf. (bi_buf might be implemented on
+ * more than 16 bits on some systems.)
+ */
+
+/* ===========================================================================
+ * Local data. These are initialized only once.
+ */
+
+local ct_data static_ltree[L_CODES+2];
+/* The static literal tree. Since the bit lengths are imposed, there is no
+ * need for the L_CODES extra codes used during heap construction. However
+ * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
+ * below).
+ */
+
+local ct_data static_dtree[D_CODES];
+/* The static distance tree. (Actually a trivial tree since all codes use
+ * 5 bits.)
+ */
+
+local uch dist_code[512];
+/* distance codes. The first 256 values correspond to the distances
+ * 3 .. 258, the last 256 values correspond to the top 8 bits of
+ * the 15 bit distances.
+ */
+
+local uch length_code[MAX_MATCH-MIN_MATCH+1];
+/* length code for each normalized match length (0 == MIN_MATCH) */
+
+local int base_length[LENGTH_CODES];
+/* First normalized length for each code (0 = MIN_MATCH) */
+
+local int base_dist[D_CODES];
+/* First normalized distance for each code (0 = distance of 1) */
+
+struct static_tree_desc_s {
+ ct_data *static_tree; /* static tree or NULL */
+ intf *extra_bits; /* extra bits for each code or NULL */
+ int extra_base; /* base index for extra_bits */
+ int elems; /* max number of elements in the tree */
+ int max_length; /* max bit length for the codes */
+};
+
+local static_tree_desc static_l_desc =
+{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
+
+local static_tree_desc static_d_desc =
+{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS};
+
+local static_tree_desc static_bl_desc =
+{(ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS};
+
+/* ===========================================================================
+ * Local (static) routines in this file.
+ */
+
+local void tr_static_init OF((void));
+local void init_block OF((deflate_state *s));
+local void pqdownheap OF((deflate_state *s, ct_data *tree, int k));
+local void gen_bitlen OF((deflate_state *s, tree_desc *desc));
+local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count));
+local void build_tree OF((deflate_state *s, tree_desc *desc));
+local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local void send_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local int build_bl_tree OF((deflate_state *s));
+local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
+ int blcodes));
+local void compress_block OF((deflate_state *s, ct_data *ltree,
+ ct_data *dtree));
+local void set_data_type OF((deflate_state *s));
+local unsigned bi_reverse OF((unsigned value, int length));
+local void bi_windup OF((deflate_state *s));
+local void bi_flush OF((deflate_state *s));
+local void copy_block OF((deflate_state *s, charf *buf, unsigned len,
+ int header));
+
+#ifndef DEBUG_ZLIB
+# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)
+ /* Send a code of the given tree. c and tree must not have side effects */
+
+#else /* DEBUG_ZLIB */
+# define send_code(s, c, tree) \
+ { if (verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \
+ send_bits(s, tree[c].Code, tree[c].Len); }
+#endif
+
+#define d_code(dist) \
+ ((dist) < 256 ? dist_code[dist] : dist_code[256+((dist)>>7)])
+/* Mapping from a distance to a distance code. dist is the distance - 1 and
+ * must not have side effects. dist_code[256] and dist_code[257] are never
+ * used.
+ */
+
+/* ===========================================================================
+ * Output a short LSB first on the stream.
+ * IN assertion: there is enough room in pendingBuf.
+ */
+#define put_short(s, w) { \
+ put_byte(s, (uch)((w) & 0xff)); \
+ put_byte(s, (uch)((ush)(w) >> 8)); \
+}
+
+/* ===========================================================================
+ * Send a value on a given number of bits.
+ * IN assertion: length <= 16 and value fits in length bits.
+ */
+#ifdef DEBUG_ZLIB
+local void send_bits OF((deflate_state *s, int value, int length));
+
+local void send_bits(s, value, length)
+ deflate_state *s;
+ int value; /* value to send */
+ int length; /* number of bits */
+{
+ Tracevv((stderr," l %2d v %4x ", length, value));
+ Assert(length > 0 && length <= 15, "invalid length");
+ s->bits_sent += (ulg)length;
+
+ /* If not enough room in bi_buf, use (valid) bits from bi_buf and
+ * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
+ * unused bits in value.
+ */
+ if (s->bi_valid > (int)Buf_size - length) {
+ s->bi_buf |= (value << s->bi_valid);
+ put_short(s, s->bi_buf);
+ s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);
+ s->bi_valid += length - Buf_size;
+ } else {
+ s->bi_buf |= value << s->bi_valid;
+ s->bi_valid += length;
+ }
+}
+#else /* !DEBUG_ZLIB */
+
+#define send_bits(s, value, length) \
+{ int len = length;\
+ if (s->bi_valid > (int)Buf_size - len) {\
+ int val = value;\
+ s->bi_buf |= (val << s->bi_valid);\
+ put_short(s, s->bi_buf);\
+ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\
+ s->bi_valid += len - Buf_size;\
+ } else {\
+ s->bi_buf |= (value) << s->bi_valid;\
+ s->bi_valid += len;\
+ }\
+}
+#endif /* DEBUG_ZLIB */
+
+
+#define MAX(a,b) (a >= b ? a : b)
+/* the arguments must not have side effects */
+
+/* ===========================================================================
+ * Initialize the various 'constant' tables. In a multi-threaded environment,
+ * this function may be called by two threads concurrently, but this is
+ * harmless since both invocations do exactly the same thing.
+ */
+local void tr_static_init()
+{
+ static int static_init_done = 0;
+ int n; /* iterates over tree elements */
+ int bits; /* bit counter */
+ int length; /* length value */
+ int code; /* code value */
+ int dist; /* distance index */
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ if (static_init_done) return;
+
+ /* Initialize the mapping length (0..255) -> length code (0..28) */
+ length = 0;
+ for (code = 0; code < LENGTH_CODES-1; code++) {
+ base_length[code] = length;
+ for (n = 0; n < (1<<extra_lbits[code]); n++) {
+ length_code[length++] = (uch)code;
+ }
+ }
+ Assert (length == 256, "tr_static_init: length != 256");
+ /* Note that the length 255 (match length 258) can be represented
+ * in two different ways: code 284 + 5 bits or code 285, so we
+ * overwrite length_code[255] to use the best encoding:
+ */
+ length_code[length-1] = (uch)code;
+
+ /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+ dist = 0;
+ for (code = 0 ; code < 16; code++) {
+ base_dist[code] = dist;
+ for (n = 0; n < (1<<extra_dbits[code]); n++) {
+ dist_code[dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "tr_static_init: dist != 256");
+ dist >>= 7; /* from now on, all distances are divided by 128 */
+ for ( ; code < D_CODES; code++) {
+ base_dist[code] = dist << 7;
+ for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
+ dist_code[256 + dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "tr_static_init: 256+dist != 512");
+
+ /* Construct the codes of the static literal tree */
+ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
+ n = 0;
+ while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;
+ while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;
+ while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;
+ while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;
+ /* Codes 286 and 287 do not exist, but we must include them in the
+ * tree construction to get a canonical Huffman tree (longest code
+ * all ones)
+ */
+ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);
+
+ /* The static distance tree is trivial: */
+ for (n = 0; n < D_CODES; n++) {
+ static_dtree[n].Len = 5;
+ static_dtree[n].Code = bi_reverse((unsigned)n, 5);
+ }
+ static_init_done = 1;
+}
+
+/* ===========================================================================
+ * Initialize the tree data structures for a new zlib stream.
+ */
+void _tr_init(s)
+ deflate_state *s;
+{
+ tr_static_init();
+
+ s->compressed_len = 0L;
+
+ s->l_desc.dyn_tree = s->dyn_ltree;
+ s->l_desc.stat_desc = &static_l_desc;
+
+ s->d_desc.dyn_tree = s->dyn_dtree;
+ s->d_desc.stat_desc = &static_d_desc;
+
+ s->bl_desc.dyn_tree = s->bl_tree;
+ s->bl_desc.stat_desc = &static_bl_desc;
+
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ s->last_eob_len = 8; /* enough lookahead for inflate */
+#ifdef DEBUG_ZLIB
+ s->bits_sent = 0L;
+#endif
+
+ /* Initialize the first block of the first file: */
+ init_block(s);
+}
+
+/* ===========================================================================
+ * Initialize a new block.
+ */
+local void init_block(s)
+ deflate_state *s;
+{
+ int n; /* iterates over tree elements */
+
+ /* Initialize the trees. */
+ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0;
+ for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0;
+ for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
+
+ s->dyn_ltree[END_BLOCK].Freq = 1;
+ s->opt_len = s->static_len = 0L;
+ s->last_lit = s->matches = 0;
+}
+
+#define SMALLEST 1
+/* Index within the heap array of least frequent node in the Huffman tree */
+
+
+/* ===========================================================================
+ * Remove the smallest element from the heap and recreate the heap with
+ * one less element. Updates heap and heap_len.
+ */
+#define pqremove(s, tree, top) \
+{\
+ top = s->heap[SMALLEST]; \
+ s->heap[SMALLEST] = s->heap[s->heap_len--]; \
+ pqdownheap(s, tree, SMALLEST); \
+}
+
+/* ===========================================================================
+ * Compares to subtrees, using the tree depth as tie breaker when
+ * the subtrees have equal frequency. This minimizes the worst case length.
+ */
+#define smaller(tree, n, m, depth) \
+ (tree[n].Freq < tree[m].Freq || \
+ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
+
+/* ===========================================================================
+ * Restore the heap property by moving down the tree starting at node k,
+ * exchanging a node with the smallest of its two sons if necessary, stopping
+ * when the heap property is re-established (each father smaller than its
+ * two sons).
+ */
+local void pqdownheap(s, tree, k)
+ deflate_state *s;
+ ct_data *tree; /* the tree to restore */
+ int k; /* node to move down */
+{
+ int v = s->heap[k];
+ int j = k << 1; /* left son of k */
+ while (j <= s->heap_len) {
+ /* Set j to the smallest of the two sons: */
+ if (j < s->heap_len &&
+ smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
+ j++;
+ }
+ /* Exit if v is smaller than both sons */
+ if (smaller(tree, v, s->heap[j], s->depth)) break;
+
+ /* Exchange v with the smallest son */
+ s->heap[k] = s->heap[j]; k = j;
+
+ /* And continue down the tree, setting j to the left son of k */
+ j <<= 1;
+ }
+ s->heap[k] = v;
+}
+
+/* ===========================================================================
+ * Compute the optimal bit lengths for a tree and update the total bit length
+ * for the current block.
+ * IN assertion: the fields freq and dad are set, heap[heap_max] and
+ * above are the tree nodes sorted by increasing frequency.
+ * OUT assertions: the field len is set to the optimal bit length, the
+ * array bl_count contains the frequencies for each bit length.
+ * The length opt_len is updated; static_len is also updated if stree is
+ * not null.
+ */
+local void gen_bitlen(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ int max_code = desc->max_code;
+ ct_data *stree = desc->stat_desc->static_tree;
+ intf *extra = desc->stat_desc->extra_bits;
+ int base = desc->stat_desc->extra_base;
+ int max_length = desc->stat_desc->max_length;
+ int h; /* heap index */
+ int n, m; /* iterate over the tree elements */
+ int bits; /* bit length */
+ int xbits; /* extra bits */
+ ush f; /* frequency */
+ int overflow = 0; /* number of elements with bit length too large */
+
+ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;
+
+ /* In a first pass, compute the optimal bit lengths (which may
+ * overflow in the case of the bit length tree).
+ */
+ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
+
+ for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
+ n = s->heap[h];
+ bits = tree[tree[n].Dad].Len + 1;
+ if (bits > max_length) bits = max_length, overflow++;
+ tree[n].Len = (ush)bits;
+ /* We overwrite tree[n].Dad which is no longer needed */
+
+ if (n > max_code) continue; /* not a leaf node */
+
+ s->bl_count[bits]++;
+ xbits = 0;
+ if (n >= base) xbits = extra[n-base];
+ f = tree[n].Freq;
+ s->opt_len += (ulg)f * (bits + xbits);
+ if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits);
+ }
+ if (overflow == 0) return;
+
+ Trace((stderr,"\nbit length overflow\n"));
+ /* This happens for example on obj2 and pic of the Calgary corpus */
+
+ /* Find the first bit length which could increase: */
+ do {
+ bits = max_length-1;
+ while (s->bl_count[bits] == 0) bits--;
+ s->bl_count[bits]--; /* move one leaf down the tree */
+ s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
+ s->bl_count[max_length]--;
+ /* The brother of the overflow item also moves one step up,
+ * but this does not affect bl_count[max_length]
+ */
+ overflow -= 2;
+ } while (overflow > 0);
+
+ /* Now recompute all bit lengths, scanning in increasing frequency.
+ * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+ * lengths instead of fixing only the wrong ones. This idea is taken
+ * from 'ar' written by Haruhiko Okumura.)
+ */
+ for (bits = max_length; bits != 0; bits--) {
+ n = s->bl_count[bits];
+ while (n != 0) {
+ m = s->heap[--h];
+ if (m > max_code) continue;
+ if (tree[m].Len != (unsigned) bits) {
+ Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
+ s->opt_len += ((long)bits - (long)tree[m].Len)
+ *(long)tree[m].Freq;
+ tree[m].Len = (ush)bits;
+ }
+ n--;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ * zero code length.
+ */
+local void gen_codes (tree, max_code, bl_count)
+ ct_data *tree; /* the tree to decorate */
+ int max_code; /* largest code with non zero frequency */
+ ushf *bl_count; /* number of codes at each bit length */
+{
+ ush next_code[MAX_BITS+1]; /* next code value for each bit length */
+ ush code = 0; /* running code value */
+ int bits; /* bit index */
+ int n; /* code index */
+
+ /* The distribution counts are first used to generate the code values
+ * without bit reversal.
+ */
+ for (bits = 1; bits <= MAX_BITS; bits++) {
+ next_code[bits] = code = (code + bl_count[bits-1]) << 1;
+ }
+ /* Check that the bit counts in bl_count are consistent. The last code
+ * must be all ones.
+ */
+ Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
+ "inconsistent bit counts");
+ Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
+
+ for (n = 0; n <= max_code; n++) {
+ int len = tree[n].Len;
+ if (len == 0) continue;
+ /* Now reverse the bits */
+ tree[n].Code = bi_reverse(next_code[len]++, len);
+
+ Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
+ n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
+ }
+}
+
+/* ===========================================================================
+ * Construct one Huffman tree and assigns the code bit strings and lengths.
+ * Update the total bit length for the current block.
+ * IN assertion: the field freq is set for all tree elements.
+ * OUT assertions: the fields len and code are set to the optimal bit length
+ * and corresponding code. The length opt_len is updated; static_len is
+ * also updated if stree is not null. The field max_code is set.
+ */
+local void build_tree(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ ct_data *stree = desc->stat_desc->static_tree;
+ int elems = desc->stat_desc->elems;
+ int n, m; /* iterate over heap elements */
+ int max_code = -1; /* largest code with non zero frequency */
+ int node; /* new node being created */
+
+ /* Construct the initial heap, with least frequent element in
+ * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+ * heap[0] is not used.
+ */
+ s->heap_len = 0, s->heap_max = HEAP_SIZE;
+
+ for (n = 0; n < elems; n++) {
+ if (tree[n].Freq != 0) {
+ s->heap[++(s->heap_len)] = max_code = n;
+ s->depth[n] = 0;
+ } else {
+ tree[n].Len = 0;
+ }
+ }
+
+ /* The pkzip format requires that at least one distance code exists,
+ * and that at least one bit should be sent even if there is only one
+ * possible code. So to avoid special checks later on we force at least
+ * two codes of non zero frequency.
+ */
+ while (s->heap_len < 2) {
+ node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
+ tree[node].Freq = 1;
+ s->depth[node] = 0;
+ s->opt_len--; if (stree) s->static_len -= stree[node].Len;
+ /* node is 0 or 1 so it does not have extra bits */
+ }
+ desc->max_code = max_code;
+
+ /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+ * establish sub-heaps of increasing lengths:
+ */
+ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
+
+ /* Construct the Huffman tree by repeatedly combining the least two
+ * frequent nodes.
+ */
+ node = elems; /* next internal node of the tree */
+ do {
+ pqremove(s, tree, n); /* n = node of least frequency */
+ m = s->heap[SMALLEST]; /* m = node of next least frequency */
+
+ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
+ s->heap[--(s->heap_max)] = m;
+
+ /* Create a new node father of n and m */
+ tree[node].Freq = tree[n].Freq + tree[m].Freq;
+ s->depth[node] = (uch) (MAX(s->depth[n], s->depth[m]) + 1);
+ tree[n].Dad = tree[m].Dad = (ush)node;
+#ifdef DUMP_BL_TREE
+ if (tree == s->bl_tree) {
+ fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
+ node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
+ }
+#endif
+ /* and insert the new node in the heap */
+ s->heap[SMALLEST] = node++;
+ pqdownheap(s, tree, SMALLEST);
+
+ } while (s->heap_len >= 2);
+
+ s->heap[--(s->heap_max)] = s->heap[SMALLEST];
+
+ /* At this point, the fields freq and dad are set. We can now
+ * generate the bit lengths.
+ */
+ gen_bitlen(s, (tree_desc *)desc);
+
+ /* The field len is now set, we can generate the bit codes */
+ gen_codes ((ct_data *)tree, max_code, s->bl_count);
+}
+
+/* ===========================================================================
+ * Scan a literal or distance tree to determine the frequencies of the codes
+ * in the bit length tree.
+ */
+local void scan_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ if (nextlen == 0) max_count = 138, min_count = 3;
+ tree[max_code+1].Len = (ush)0xffff; /* guard */
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ s->bl_tree[curlen].Freq += count;
+ } else if (curlen != 0) {
+ if (curlen != prevlen) s->bl_tree[curlen].Freq++;
+ s->bl_tree[REP_3_6].Freq++;
+ } else if (count <= 10) {
+ s->bl_tree[REPZ_3_10].Freq++;
+ } else {
+ s->bl_tree[REPZ_11_138].Freq++;
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Send a literal or distance tree in compressed form, using the codes in
+ * bl_tree.
+ */
+local void send_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ /* tree[max_code+1].Len = -1; */ /* guard already set */
+ if (nextlen == 0) max_count = 138, min_count = 3;
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ do { send_code(s, curlen, s->bl_tree); } while (--count != 0);
+
+ } else if (curlen != 0) {
+ if (curlen != prevlen) {
+ send_code(s, curlen, s->bl_tree); count--;
+ }
+ Assert(count >= 3 && count <= 6, " 3_6?");
+ send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
+
+ } else if (count <= 10) {
+ send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
+
+ } else {
+ send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+local int build_bl_tree(s)
+ deflate_state *s;
+{
+ int max_blindex; /* index of last bit length code of non zero freq */
+
+ /* Determine the bit length frequencies for literal and distance trees */
+ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);
+ scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);
+
+ /* Build the bit length tree: */
+ build_tree(s, (tree_desc *)(&(s->bl_desc)));
+ /* opt_len now includes the length of the tree representations, except
+ * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+ */
+
+ /* Determine the number of bit length codes to send. The pkzip format
+ * requires that at least 4 bit length codes be sent. (appnote.txt says
+ * 3 but the actual value used is 4.)
+ */
+ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
+ if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
+ }
+ /* Update opt_len to include the bit length tree and counts */
+ s->opt_len += 3*(max_blindex+1) + 5+5+4;
+ Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
+ s->opt_len, s->static_len));
+
+ return max_blindex;
+}
+
+/* ===========================================================================
+ * Send the header for a block using dynamic Huffman trees: the counts, the
+ * lengths of the bit length codes, the literal tree and the distance tree.
+ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+ */
+local void send_all_trees(s, lcodes, dcodes, blcodes)
+ deflate_state *s;
+ int lcodes, dcodes, blcodes; /* number of codes for each tree */
+{
+ int rank; /* index in bl_order */
+
+ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
+ Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
+ "too many codes");
+ Tracev((stderr, "\nbl counts: "));
+ send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
+ send_bits(s, dcodes-1, 5);
+ send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */
+ for (rank = 0; rank < blcodes; rank++) {
+ Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
+ send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
+ }
+ Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
+ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
+ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
+}
+
+/* ===========================================================================
+ * Send a stored block
+ */
+void _tr_stored_block(s, buf, stored_len, eof)
+ deflate_state *s;
+ charf *buf; /* input block */
+ ulg stored_len; /* length of input block */
+ int eof; /* true if this is the last block for a file */
+{
+ send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */
+ s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L;
+ s->compressed_len += (stored_len + 4) << 3;
+
+ copy_block(s, buf, (unsigned)stored_len, 1); /* with header */
+}
+
+/* Send just the `stored block' type code without any length bytes or data.
+ */
+void _tr_stored_type_only(s)
+ deflate_state *s;
+{
+ send_bits(s, (STORED_BLOCK << 1), 3);
+ bi_windup(s);
+ s->compressed_len = (s->compressed_len + 3) & ~7L;
+}
+
+
+/* ===========================================================================
+ * Send one empty static block to give enough lookahead for inflate.
+ * This takes 10 bits, of which 7 may remain in the bit buffer.
+ * The current inflate code requires 9 bits of lookahead. If the
+ * last two codes for the previous block (real code plus EOB) were coded
+ * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode
+ * the last real code. In this case we send two empty static blocks instead
+ * of one. (There are no problems if the previous block is stored or fixed.)
+ * To simplify the code, we assume the worst case of last real code encoded
+ * on one bit only.
+ */
+void _tr_align(s)
+ deflate_state *s;
+{
+ send_bits(s, STATIC_TREES<<1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+ s->compressed_len += 10L; /* 3 for block type, 7 for EOB */
+ bi_flush(s);
+ /* Of the 10 bits for the empty block, we have already sent
+ * (10 - bi_valid) bits. The lookahead for the last real code (before
+ * the EOB of the previous block) was thus at least one plus the length
+ * of the EOB plus what we have just sent of the empty static block.
+ */
+ if (1 + s->last_eob_len + 10 - s->bi_valid < 9) {
+ send_bits(s, STATIC_TREES<<1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+ s->compressed_len += 10L;
+ bi_flush(s);
+ }
+ s->last_eob_len = 7;
+}
+
+/* ===========================================================================
+ * Determine the best encoding for the current block: dynamic trees, static
+ * trees or store, and output the encoded block to the zip file. This function
+ * returns the total compressed length for the file so far.
+ */
+ulg _tr_flush_block(s, buf, stored_len, eof)
+ deflate_state *s;
+ charf *buf; /* input block, or NULL if too old */
+ ulg stored_len; /* length of input block */
+ int eof; /* true if this is the last block for a file */
+{
+ ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
+ int max_blindex = 0; /* index of last bit length code of non zero freq */
+
+ /* Build the Huffman trees unless a stored block is forced */
+ if (s->level > 0) {
+
+ /* Check if the file is ascii or binary */
+ if (s->data_type == Z_UNKNOWN) set_data_type(s);
+
+ /* Construct the literal and distance trees */
+ build_tree(s, (tree_desc *)(&(s->l_desc)));
+ Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+
+ build_tree(s, (tree_desc *)(&(s->d_desc)));
+ Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+ /* At this point, opt_len and static_len are the total bit lengths of
+ * the compressed block data, excluding the tree representations.
+ */
+
+ /* Build the bit length tree for the above two trees, and get the index
+ * in bl_order of the last bit length code to send.
+ */
+ max_blindex = build_bl_tree(s);
+
+ /* Determine the best encoding. Compute first the block length in bytes*/
+ opt_lenb = (s->opt_len+3+7)>>3;
+ static_lenb = (s->static_len+3+7)>>3;
+
+ Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
+ opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
+ s->last_lit));
+
+ if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
+
+ } else {
+ Assert(buf != (char*)0, "lost buf");
+ opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
+ }
+
+ /* If compression failed and this is the first and last block,
+ * and if the .zip file can be seeked (to rewrite the local header),
+ * the whole file is transformed into a stored file:
+ */
+#ifdef STORED_FILE_OK
+# ifdef FORCE_STORED_FILE
+ if (eof && s->compressed_len == 0L) { /* force stored file */
+# else
+ if (stored_len <= opt_lenb && eof && s->compressed_len==0L && seekable()) {
+# endif
+ /* Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: */
+ if (buf == (charf*)0) error ("block vanished");
+
+ copy_block(s, buf, (unsigned)stored_len, 0); /* without header */
+ s->compressed_len = stored_len << 3;
+ s->method = STORED;
+ } else
+#endif /* STORED_FILE_OK */
+
+#ifdef FORCE_STORED
+ if (buf != (char*)0) { /* force stored block */
+#else
+ if (stored_len+4 <= opt_lenb && buf != (char*)0) {
+ /* 4: two words for the lengths */
+#endif
+ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+ * Otherwise we can't have processed more than WSIZE input bytes since
+ * the last block flush, because compression would have been
+ * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+ * transform a block into a stored block.
+ */
+ _tr_stored_block(s, buf, stored_len, eof);
+
+#ifdef FORCE_STATIC
+ } else if (static_lenb >= 0) { /* force static trees */
+#else
+ } else if (static_lenb == opt_lenb) {
+#endif
+ send_bits(s, (STATIC_TREES<<1)+eof, 3);
+ compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree);
+ s->compressed_len += 3 + s->static_len;
+ } else {
+ send_bits(s, (DYN_TREES<<1)+eof, 3);
+ send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
+ max_blindex+1);
+ compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree);
+ s->compressed_len += 3 + s->opt_len;
+ }
+ Assert (s->compressed_len == s->bits_sent, "bad compressed size");
+ init_block(s);
+
+ if (eof) {
+ bi_windup(s);
+ s->compressed_len += 7; /* align on byte boundary */
+ }
+ Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
+ s->compressed_len-7*eof));
+
+ return s->compressed_len >> 3;
+}
+
+/* ===========================================================================
+ * Save the match info and tally the frequency counts. Return true if
+ * the current block must be flushed.
+ */
+int _tr_tally (s, dist, lc)
+ deflate_state *s;
+ unsigned dist; /* distance of matched string */
+ unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
+{
+ s->d_buf[s->last_lit] = (ush)dist;
+ s->l_buf[s->last_lit++] = (uch)lc;
+ if (dist == 0) {
+ /* lc is the unmatched char */
+ s->dyn_ltree[lc].Freq++;
+ } else {
+ s->matches++;
+ /* Here, lc is the match length - MIN_MATCH */
+ dist--; /* dist = match distance - 1 */
+ Assert((ush)dist < (ush)MAX_DIST(s) &&
+ (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
+ (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match");
+
+ s->dyn_ltree[length_code[lc]+LITERALS+1].Freq++;
+ s->dyn_dtree[d_code(dist)].Freq++;
+ }
+
+ /* Try to guess if it is profitable to stop the current block here */
+ if (s->level > 2 && (s->last_lit & 0xfff) == 0) {
+ /* Compute an upper bound for the compressed length */
+ ulg out_length = (ulg)s->last_lit*8L;
+ ulg in_length = (ulg)((long)s->strstart - s->block_start);
+ int dcode;
+ for (dcode = 0; dcode < D_CODES; dcode++) {
+ out_length += (ulg)s->dyn_dtree[dcode].Freq *
+ (5L+extra_dbits[dcode]);
+ }
+ out_length >>= 3;
+ Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
+ s->last_lit, in_length, out_length,
+ 100L - out_length*100L/in_length));
+ if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
+ }
+ return (s->last_lit == s->lit_bufsize-1);
+ /* We avoid equality with lit_bufsize because of wraparound at 64K
+ * on 16 bit machines and because stored blocks are restricted to
+ * 64K-1 bytes.
+ */
+}
+
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+local void compress_block(s, ltree, dtree)
+ deflate_state *s;
+ ct_data *ltree; /* literal tree */
+ ct_data *dtree; /* distance tree */
+{
+ unsigned dist; /* distance of matched string */
+ int lc; /* match length or unmatched char (if dist == 0) */
+ unsigned lx = 0; /* running index in l_buf */
+ unsigned code; /* the code to send */
+ int extra; /* number of extra bits to send */
+
+ if (s->last_lit != 0) do {
+ dist = s->d_buf[lx];
+ lc = s->l_buf[lx++];
+ if (dist == 0) {
+ send_code(s, lc, ltree); /* send a literal byte */
+ Tracecv(isgraph(lc), (stderr," '%c' ", lc));
+ } else {
+ /* Here, lc is the match length - MIN_MATCH */
+ code = length_code[lc];
+ send_code(s, code+LITERALS+1, ltree); /* send the length code */
+ extra = extra_lbits[code];
+ if (extra != 0) {
+ lc -= base_length[code];
+ send_bits(s, lc, extra); /* send the extra length bits */
+ }
+ dist--; /* dist is now the match distance - 1 */
+ code = d_code(dist);
+ Assert (code < D_CODES, "bad d_code");
+
+ send_code(s, code, dtree); /* send the distance code */
+ extra = extra_dbits[code];
+ if (extra != 0) {
+ dist -= base_dist[code];
+ send_bits(s, dist, extra); /* send the extra distance bits */
+ }
+ } /* literal or match pair ? */
+
+ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
+ Assert(s->pending < s->lit_bufsize + 2*lx, "pendingBuf overflow");
+
+ } while (lx < s->last_lit);
+
+ send_code(s, END_BLOCK, ltree);
+ s->last_eob_len = ltree[END_BLOCK].Len;
+}
+
+/* ===========================================================================
+ * Set the data type to ASCII or BINARY, using a crude approximation:
+ * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise.
+ * IN assertion: the fields freq of dyn_ltree are set and the total of all
+ * frequencies does not exceed 64K (to fit in an int on 16 bit machines).
+ */
+local void set_data_type(s)
+ deflate_state *s;
+{
+ int n = 0;
+ unsigned ascii_freq = 0;
+ unsigned bin_freq = 0;
+ while (n < 7) bin_freq += s->dyn_ltree[n++].Freq;
+ while (n < 128) ascii_freq += s->dyn_ltree[n++].Freq;
+ while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq;
+ s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII);
+}
+
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+local unsigned bi_reverse(code, len)
+ unsigned code; /* the value to invert */
+ int len; /* its bit length */
+{
+ register unsigned res = 0;
+ do {
+ res |= code & 1;
+ code >>= 1, res <<= 1;
+ } while (--len > 0);
+ return res >> 1;
+}
+
+/* ===========================================================================
+ * Flush the bit buffer, keeping at most 7 bits in it.
+ */
+local void bi_flush(s)
+ deflate_state *s;
+{
+ if (s->bi_valid == 16) {
+ put_short(s, s->bi_buf);
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ } else if (s->bi_valid >= 8) {
+ put_byte(s, (Byte)s->bi_buf);
+ s->bi_buf >>= 8;
+ s->bi_valid -= 8;
+ }
+}
+
+/* ===========================================================================
+ * Flush the bit buffer and align the output on a byte boundary
+ */
+local void bi_windup(s)
+ deflate_state *s;
+{
+ if (s->bi_valid > 8) {
+ put_short(s, s->bi_buf);
+ } else if (s->bi_valid > 0) {
+ put_byte(s, (Byte)s->bi_buf);
+ }
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+#ifdef DEBUG_ZLIB
+ s->bits_sent = (s->bits_sent+7) & ~7;
+#endif
+}
+
+/* ===========================================================================
+ * Copy a stored block, storing first the length and its
+ * one's complement if requested.
+ */
+local void copy_block(s, buf, len, header)
+ deflate_state *s;
+ charf *buf; /* the input data */
+ unsigned len; /* its length */
+ int header; /* true if block header must be written */
+{
+ bi_windup(s); /* align on byte boundary */
+ s->last_eob_len = 8; /* enough lookahead for inflate */
+
+ if (header) {
+ put_short(s, (ush)len);
+ put_short(s, (ush)~len);
+#ifdef DEBUG_ZLIB
+ s->bits_sent += 2*16;
+#endif
+ }
+#ifdef DEBUG_ZLIB
+ s->bits_sent += (ulg)len<<3;
+#endif
+ /* bundle up the put_byte(s, *buf++) calls */
+ zmemcpy(&s->pending_buf[s->pending], buf, len);
+ s->pending += len;
+}
+/* --- trees.c */
+
+/* +++ inflate.c */
+/* inflate.c -- zlib interface to inflate modules
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* #include "zutil.h" */
+
+/* +++ infblock.h */
+/* infblock.h -- header to use infblock.c
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+struct inflate_blocks_state;
+typedef struct inflate_blocks_state FAR inflate_blocks_statef;
+
+extern inflate_blocks_statef * inflate_blocks_new OF((
+ z_streamp z,
+ check_func c, /* check function */
+ uInt w)); /* window size */
+
+extern int inflate_blocks OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ int)); /* initial return code */
+
+extern void inflate_blocks_reset OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ uLongf *)); /* check value on output */
+
+extern int inflate_blocks_free OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ uLongf *)); /* check value on output */
+
+extern void inflate_set_dictionary OF((
+ inflate_blocks_statef *s,
+ const Bytef *d, /* dictionary */
+ uInt n)); /* dictionary length */
+
+extern int inflate_addhistory OF((
+ inflate_blocks_statef *,
+ z_streamp));
+
+extern int inflate_packet_flush OF((
+ inflate_blocks_statef *));
+/* --- infblock.h */
+
+#ifndef NO_DUMMY_DECL
+struct inflate_blocks_state {int dummy;}; /* for buggy compilers */
+#endif
+
+/* inflate private state */
+struct internal_state {
+
+ /* mode */
+ enum {
+ METHOD, /* waiting for method byte */
+ FLAG, /* waiting for flag byte */
+ DICT4, /* four dictionary check bytes to go */
+ DICT3, /* three dictionary check bytes to go */
+ DICT2, /* two dictionary check bytes to go */
+ DICT1, /* one dictionary check byte to go */
+ DICT0, /* waiting for inflateSetDictionary */
+ BLOCKS, /* decompressing blocks */
+ CHECK4, /* four check bytes to go */
+ CHECK3, /* three check bytes to go */
+ CHECK2, /* two check bytes to go */
+ CHECK1, /* one check byte to go */
+ DONE, /* finished check, done */
+ BAD} /* got an error--stay here */
+ mode; /* current inflate mode */
+
+ /* mode dependent information */
+ union {
+ uInt method; /* if FLAGS, method byte */
+ struct {
+ uLong was; /* computed check value */
+ uLong need; /* stream check value */
+ } check; /* if CHECK, check values to compare */
+ uInt marker; /* if BAD, inflateSync's marker bytes count */
+ } sub; /* submode */
+
+ /* mode independent information */
+ int nowrap; /* flag for no wrapper */
+ uInt wbits; /* log2(window size) (8..15, defaults to 15) */
+ inflate_blocks_statef
+ *blocks; /* current inflate_blocks state */
+
+};
+
+
+int inflateReset(z)
+z_streamp z;
+{
+ uLong c;
+
+ if (z == Z_NULL || z->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ z->total_in = z->total_out = 0;
+ z->msg = Z_NULL;
+ z->state->mode = z->state->nowrap ? BLOCKS : METHOD;
+ inflate_blocks_reset(z->state->blocks, z, &c);
+ Trace((stderr, "inflate: reset\n"));
+ return Z_OK;
+}
+
+
+int inflateEnd(z)
+z_streamp z;
+{
+ uLong c;
+
+ if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL)
+ return Z_STREAM_ERROR;
+ if (z->state->blocks != Z_NULL)
+ inflate_blocks_free(z->state->blocks, z, &c);
+ ZFREE(z, z->state);
+ z->state = Z_NULL;
+ Trace((stderr, "inflate: end\n"));
+ return Z_OK;
+}
+
+
+int inflateInit2_(z, w, version, stream_size)
+z_streamp z;
+int w;
+const char *version;
+int stream_size;
+{
+ if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+ stream_size != sizeof(z_stream))
+ return Z_VERSION_ERROR;
+
+ /* initialize state */
+ if (z == Z_NULL)
+ return Z_STREAM_ERROR;
+ z->msg = Z_NULL;
+#ifndef NO_ZCFUNCS
+ if (z->zalloc == Z_NULL)
+ {
+ z->zalloc = zcalloc;
+ z->opaque = (voidpf)0;
+ }
+ if (z->zfree == Z_NULL) z->zfree = zcfree;
+#endif
+ if ((z->state = (struct internal_state FAR *)
+ ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL)
+ return Z_MEM_ERROR;
+ z->state->blocks = Z_NULL;
+
+ /* handle undocumented nowrap option (no zlib header or check) */
+ z->state->nowrap = 0;
+ if (w < 0)
+ {
+ w = - w;
+ z->state->nowrap = 1;
+ }
+
+ /* set window size */
+ if (w < 8 || w > 15)
+ {
+ inflateEnd(z);
+ return Z_STREAM_ERROR;
+ }
+ z->state->wbits = (uInt)w;
+
+ /* create inflate_blocks state */
+ if ((z->state->blocks =
+ inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w))
+ == Z_NULL)
+ {
+ inflateEnd(z);
+ return Z_MEM_ERROR;
+ }
+ Trace((stderr, "inflate: allocated\n"));
+
+ /* reset state */
+ inflateReset(z);
+ return Z_OK;
+}
+
+
+int inflateInit_(z, version, stream_size)
+z_streamp z;
+const char *version;
+int stream_size;
+{
+ return inflateInit2_(z, DEF_WBITS, version, stream_size);
+}
+
+
+#define NEEDBYTE {if(z->avail_in==0)goto empty;r=Z_OK;}
+#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++)
+
+int inflate(z, f)
+z_streamp z;
+int f;
+{
+ int r;
+ uInt b;
+
+ if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL || f < 0)
+ return Z_STREAM_ERROR;
+ r = Z_BUF_ERROR;
+ while (1) switch (z->state->mode)
+ {
+ case METHOD:
+ NEEDBYTE
+ if (((z->state->sub.method = NEXTBYTE) & 0xf) != Z_DEFLATED)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"unknown compression method";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ if ((z->state->sub.method >> 4) + 8 > z->state->wbits)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"invalid window size";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ z->state->mode = FLAG;
+ case FLAG:
+ NEEDBYTE
+ b = NEXTBYTE;
+ if (((z->state->sub.method << 8) + b) % 31)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"incorrect header check";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ Trace((stderr, "inflate: zlib header ok\n"));
+ if (!(b & PRESET_DICT))
+ {
+ z->state->mode = BLOCKS;
+ break;
+ }
+ z->state->mode = DICT4;
+ case DICT4:
+ NEEDBYTE
+ z->state->sub.check.need = (uLong)NEXTBYTE << 24;
+ z->state->mode = DICT3;
+ case DICT3:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 16;
+ z->state->mode = DICT2;
+ case DICT2:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 8;
+ z->state->mode = DICT1;
+ case DICT1:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE;
+ z->adler = z->state->sub.check.need;
+ z->state->mode = DICT0;
+ return Z_NEED_DICT;
+ case DICT0:
+ z->state->mode = BAD;
+ z->msg = (char*)"need dictionary";
+ z->state->sub.marker = 0; /* can try inflateSync */
+ return Z_STREAM_ERROR;
+ case BLOCKS:
+ r = inflate_blocks(z->state->blocks, z, r);
+ if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0)
+ r = inflate_packet_flush(z->state->blocks);
+ if (r == Z_DATA_ERROR)
+ {
+ z->state->mode = BAD;
+ z->state->sub.marker = 0; /* can try inflateSync */
+ break;
+ }
+ if (r != Z_STREAM_END)
+ return r;
+ r = Z_OK;
+ inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was);
+ if (z->state->nowrap)
+ {
+ z->state->mode = DONE;
+ break;
+ }
+ z->state->mode = CHECK4;
+ case CHECK4:
+ NEEDBYTE
+ z->state->sub.check.need = (uLong)NEXTBYTE << 24;
+ z->state->mode = CHECK3;
+ case CHECK3:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 16;
+ z->state->mode = CHECK2;
+ case CHECK2:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 8;
+ z->state->mode = CHECK1;
+ case CHECK1:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE;
+
+ if (z->state->sub.check.was != z->state->sub.check.need)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"incorrect data check";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ Trace((stderr, "inflate: zlib check ok\n"));
+ z->state->mode = DONE;
+ case DONE:
+ return Z_STREAM_END;
+ case BAD:
+ return Z_DATA_ERROR;
+ default:
+ return Z_STREAM_ERROR;
+ }
+
+ empty:
+ if (f != Z_PACKET_FLUSH)
+ return r;
+ z->state->mode = BAD;
+ z->msg = (char *)"need more for packet flush";
+ z->state->sub.marker = 0; /* can try inflateSync */
+ return Z_DATA_ERROR;
+}
+
+
+int inflateSetDictionary(z, dictionary, dictLength)
+z_streamp z;
+const Bytef *dictionary;
+uInt dictLength;
+{
+ uInt length = dictLength;
+
+ if (z == Z_NULL || z->state == Z_NULL || z->state->mode != DICT0)
+ return Z_STREAM_ERROR;
+
+ if (adler32(1L, dictionary, dictLength) != z->adler) return Z_DATA_ERROR;
+ z->adler = 1L;
+
+ if (length >= ((uInt)1<<z->state->wbits))
+ {
+ length = (1<<z->state->wbits)-1;
+ dictionary += dictLength - length;
+ }
+ inflate_set_dictionary(z->state->blocks, dictionary, length);
+ z->state->mode = BLOCKS;
+ return Z_OK;
+}
+
+/*
+ * This subroutine adds the data at next_in/avail_in to the output history
+ * without performing any output. The output buffer must be "caught up";
+ * i.e. no pending output (hence s->read equals s->write), and the state must
+ * be BLOCKS (i.e. we should be willing to see the start of a series of
+ * BLOCKS). On exit, the output will also be caught up, and the checksum
+ * will have been updated if need be.
+ */
+
+int inflateIncomp(z)
+z_stream *z;
+{
+ if (z->state->mode != BLOCKS)
+ return Z_DATA_ERROR;
+ return inflate_addhistory(z->state->blocks, z);
+}
+
+
+int inflateSync(z)
+z_streamp z;
+{
+ uInt n; /* number of bytes to look at */
+ Bytef *p; /* pointer to bytes */
+ uInt m; /* number of marker bytes found in a row */
+ uLong r, w; /* temporaries to save total_in and total_out */
+
+ /* set up */
+ if (z == Z_NULL || z->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ if (z->state->mode != BAD)
+ {
+ z->state->mode = BAD;
+ z->state->sub.marker = 0;
+ }
+ if ((n = z->avail_in) == 0)
+ return Z_BUF_ERROR;
+ p = z->next_in;
+ m = z->state->sub.marker;
+
+ /* search */
+ while (n && m < 4)
+ {
+ if (*p == (Byte)(m < 2 ? 0 : 0xff))
+ m++;
+ else if (*p)
+ m = 0;
+ else
+ m = 4 - m;
+ p++, n--;
+ }
+
+ /* restore */
+ z->total_in += p - z->next_in;
+ z->next_in = p;
+ z->avail_in = n;
+ z->state->sub.marker = m;
+
+ /* return no joy or set up to restart on a new block */
+ if (m != 4)
+ return Z_DATA_ERROR;
+ r = z->total_in; w = z->total_out;
+ inflateReset(z);
+ z->total_in = r; z->total_out = w;
+ z->state->mode = BLOCKS;
+ return Z_OK;
+}
+
+#undef NEEDBYTE
+#undef NEXTBYTE
+/* --- inflate.c */
+
+/* +++ infblock.c */
+/* infblock.c -- interpret and process block types to last block
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* #include "zutil.h" */
+/* #include "infblock.h" */
+
+/* +++ inftrees.h */
+/* inftrees.h -- header to use inftrees.c
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* Huffman code lookup table entry--this entry is four bytes for machines
+ that have 16-bit pointers (e.g. PC's in the small or medium model). */
+
+typedef struct inflate_huft_s FAR inflate_huft;
+
+struct inflate_huft_s {
+ union {
+ struct {
+ Byte Exop; /* number of extra bits or operation */
+ Byte Bits; /* number of bits in this code or subcode */
+ } what;
+ Bytef *pad; /* pad structure to a power of 2 (4 bytes for */
+ } word; /* 16-bit, 8 bytes for 32-bit machines) */
+ union {
+ uInt Base; /* literal, length base, or distance base */
+ inflate_huft *Next; /* pointer to next level of table */
+ } more;
+};
+
+#ifdef DEBUG_ZLIB
+ extern uInt inflate_hufts;
+#endif
+
+extern int inflate_trees_bits OF((
+ uIntf *, /* 19 code lengths */
+ uIntf *, /* bits tree desired/actual depth */
+ inflate_huft * FAR *, /* bits tree result */
+ z_streamp )); /* for zalloc, zfree functions */
+
+extern int inflate_trees_dynamic OF((
+ uInt, /* number of literal/length codes */
+ uInt, /* number of distance codes */
+ uIntf *, /* that many (total) code lengths */
+ uIntf *, /* literal desired/actual bit depth */
+ uIntf *, /* distance desired/actual bit depth */
+ inflate_huft * FAR *, /* literal/length tree result */
+ inflate_huft * FAR *, /* distance tree result */
+ z_streamp )); /* for zalloc, zfree functions */
+
+extern int inflate_trees_fixed OF((
+ uIntf *, /* literal desired/actual bit depth */
+ uIntf *, /* distance desired/actual bit depth */
+ inflate_huft * FAR *, /* literal/length tree result */
+ inflate_huft * FAR *)); /* distance tree result */
+
+extern int inflate_trees_free OF((
+ inflate_huft *, /* tables to free */
+ z_streamp )); /* for zfree function */
+
+/* --- inftrees.h */
+
+/* +++ infcodes.h */
+/* infcodes.h -- header to use infcodes.c
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+struct inflate_codes_state;
+typedef struct inflate_codes_state FAR inflate_codes_statef;
+
+extern inflate_codes_statef *inflate_codes_new OF((
+ uInt, uInt,
+ inflate_huft *, inflate_huft *,
+ z_streamp ));
+
+extern int inflate_codes OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ int));
+
+extern void inflate_codes_free OF((
+ inflate_codes_statef *,
+ z_streamp ));
+
+/* --- infcodes.h */
+
+/* +++ infutil.h */
+/* infutil.h -- types and macros common to blocks and codes
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+#ifndef _INFUTIL_H
+#define _INFUTIL_H
+
+typedef enum {
+ TYPE, /* get type bits (3, including end bit) */
+ LENS, /* get lengths for stored */
+ STORED, /* processing stored block */
+ TABLE, /* get table lengths */
+ BTREE, /* get bit lengths tree for a dynamic block */
+ DTREE, /* get length, distance trees for a dynamic block */
+ CODES, /* processing fixed or dynamic block */
+ DRY, /* output remaining window bytes */
+ DONEB, /* finished last block, done */
+ BADB} /* got a data error--stuck here */
+inflate_block_mode;
+
+/* inflate blocks semi-private state */
+struct inflate_blocks_state {
+
+ /* mode */
+ inflate_block_mode mode; /* current inflate_block mode */
+
+ /* mode dependent information */
+ union {
+ uInt left; /* if STORED, bytes left to copy */
+ struct {
+ uInt table; /* table lengths (14 bits) */
+ uInt index; /* index into blens (or border) */
+ uIntf *blens; /* bit lengths of codes */
+ uInt bb; /* bit length tree depth */
+ inflate_huft *tb; /* bit length decoding tree */
+ } trees; /* if DTREE, decoding info for trees */
+ struct {
+ inflate_huft *tl;
+ inflate_huft *td; /* trees to free */
+ inflate_codes_statef
+ *codes;
+ } decode; /* if CODES, current state */
+ } sub; /* submode */
+ uInt last; /* true if this block is the last block */
+
+ /* mode independent information */
+ uInt bitk; /* bits in bit buffer */
+ uLong bitb; /* bit buffer */
+ Bytef *window; /* sliding window */
+ Bytef *end; /* one byte after sliding window */
+ Bytef *read; /* window read pointer */
+ Bytef *write; /* window write pointer */
+ check_func checkfn; /* check function */
+ uLong check; /* check on output */
+
+};
+
+
+/* defines for inflate input/output */
+/* update pointers and return */
+#define UPDBITS {s->bitb=b;s->bitk=k;}
+#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;}
+#define UPDOUT {s->write=q;}
+#define UPDATE {UPDBITS UPDIN UPDOUT}
+#define LEAVE {UPDATE return inflate_flush(s,z,r);}
+/* get bytes and bits */
+#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;}
+#define NEEDBYTE {if(n)r=Z_OK;else LEAVE}
+#define NEXTBYTE (n--,*p++)
+#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}}
+#define DUMPBITS(j) {b>>=(j);k-=(j);}
+/* output bytes */
+#define WAVAIL (uInt)(q<s->read?s->read-q-1:s->end-q)
+#define LOADOUT {q=s->write;m=(uInt)WAVAIL;}
+#define WWRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}}
+#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT}
+#define NEEDOUT {if(m==0){WWRAP if(m==0){FLUSH WWRAP if(m==0) LEAVE}}r=Z_OK;}
+#define OUTBYTE(a) {*q++=(Byte)(a);m--;}
+/* load local pointers */
+#define LOAD {LOADIN LOADOUT}
+
+/* masks for lower bits (size given to avoid silly warnings with Visual C++) */
+extern uInt inflate_mask[17];
+
+/* copy as much as possible from the sliding window to the output area */
+extern int inflate_flush OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ int));
+
+#ifndef NO_DUMMY_DECL
+struct internal_state {int dummy;}; /* for buggy compilers */
+#endif
+
+#endif
+/* --- infutil.h */
+
+#ifndef NO_DUMMY_DECL
+struct inflate_codes_state {int dummy;}; /* for buggy compilers */
+#endif
+
+/* Table for deflate from PKZIP's appnote.txt. */
+local const uInt border[] = { /* Order of the bit length code lengths */
+ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+/*
+ Notes beyond the 1.93a appnote.txt:
+
+ 1. Distance pointers never point before the beginning of the output
+ stream.
+ 2. Distance pointers can point back across blocks, up to 32k away.
+ 3. There is an implied maximum of 7 bits for the bit length table and
+ 15 bits for the actual data.
+ 4. If only one code exists, then it is encoded using one bit. (Zero
+ would be more efficient, but perhaps a little confusing.) If two
+ codes exist, they are coded using one bit each (0 and 1).
+ 5. There is no way of sending zero distance codes--a dummy must be
+ sent if there are none. (History: a pre 2.0 version of PKZIP would
+ store blocks with no distance codes, but this was discovered to be
+ too harsh a criterion.) Valid only for 1.93a. 2.04c does allow
+ zero distance codes, which is sent as one code of zero bits in
+ length.
+ 6. There are up to 286 literal/length codes. Code 256 represents the
+ end-of-block. Note however that the static length tree defines
+ 288 codes just to fill out the Huffman codes. Codes 286 and 287
+ cannot be used though, since there is no length base or extra bits
+ defined for them. Similarily, there are up to 30 distance codes.
+ However, static trees define 32 codes (all 5 bits) to fill out the
+ Huffman codes, but the last two had better not show up in the data.
+ 7. Unzip can check dynamic Huffman blocks for complete code sets.
+ The exception is that a single code would not be complete (see #4).
+ 8. The five bits following the block type is really the number of
+ literal codes sent minus 257.
+ 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits
+ (1+6+6). Therefore, to output three times the length, you output
+ three codes (1+1+1), whereas to output four times the same length,
+ you only need two codes (1+3). Hmm.
+ 10. In the tree reconstruction algorithm, Code = Code + Increment
+ only if BitLength(i) is not zero. (Pretty obvious.)
+ 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19)
+ 12. Note: length code 284 can represent 227-258, but length code 285
+ really is 258. The last length deserves its own, short code
+ since it gets used a lot in very redundant files. The length
+ 258 is special since 258 - 3 (the min match length) is 255.
+ 13. The literal/length and distance code bit lengths are read as a
+ single stream of lengths. It is possible (and advantageous) for
+ a repeat code (16, 17, or 18) to go across the boundary between
+ the two sets of lengths.
+ */
+
+
+void inflate_blocks_reset(s, z, c)
+inflate_blocks_statef *s;
+z_streamp z;
+uLongf *c;
+{
+ if (s->checkfn != Z_NULL)
+ *c = s->check;
+ if (s->mode == BTREE || s->mode == DTREE)
+ ZFREE(z, s->sub.trees.blens);
+ if (s->mode == CODES)
+ {
+ inflate_codes_free(s->sub.decode.codes, z);
+ inflate_trees_free(s->sub.decode.td, z);
+ inflate_trees_free(s->sub.decode.tl, z);
+ }
+ s->mode = TYPE;
+ s->bitk = 0;
+ s->bitb = 0;
+ s->read = s->write = s->window;
+ if (s->checkfn != Z_NULL)
+ z->adler = s->check = (*s->checkfn)(0L, Z_NULL, 0);
+ Trace((stderr, "inflate: blocks reset\n"));
+}
+
+
+inflate_blocks_statef *inflate_blocks_new(z, c, w)
+z_streamp z;
+check_func c;
+uInt w;
+{
+ inflate_blocks_statef *s;
+
+ if ((s = (inflate_blocks_statef *)ZALLOC
+ (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL)
+ return s;
+ if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL)
+ {
+ ZFREE(z, s);
+ return Z_NULL;
+ }
+ s->end = s->window + w;
+ s->checkfn = c;
+ s->mode = TYPE;
+ Trace((stderr, "inflate: blocks allocated\n"));
+ inflate_blocks_reset(s, z, &s->check);
+ return s;
+}
+
+
+#ifdef DEBUG_ZLIB
+ extern uInt inflate_hufts;
+#endif
+int inflate_blocks(s, z, r)
+inflate_blocks_statef *s;
+z_streamp z;
+int r;
+{
+ uInt t; /* temporary storage */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+
+ /* copy input/output information to locals (UPDATE macro restores) */
+ LOAD
+
+ /* process input based on current state */
+ while (1) switch (s->mode)
+ {
+ case TYPE:
+ NEEDBITS(3)
+ t = (uInt)b & 7;
+ s->last = t & 1;
+ switch (t >> 1)
+ {
+ case 0: /* stored */
+ Trace((stderr, "inflate: stored block%s\n",
+ s->last ? " (last)" : ""));
+ DUMPBITS(3)
+ t = k & 7; /* go to byte boundary */
+ DUMPBITS(t)
+ s->mode = LENS; /* get length of stored block */
+ break;
+ case 1: /* fixed */
+ Trace((stderr, "inflate: fixed codes block%s\n",
+ s->last ? " (last)" : ""));
+ {
+ uInt bl, bd;
+ inflate_huft *tl, *td;
+
+ inflate_trees_fixed(&bl, &bd, &tl, &td);
+ s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z);
+ if (s->sub.decode.codes == Z_NULL)
+ {
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ s->sub.decode.tl = Z_NULL; /* don't try to free these */
+ s->sub.decode.td = Z_NULL;
+ }
+ DUMPBITS(3)
+ s->mode = CODES;
+ break;
+ case 2: /* dynamic */
+ Trace((stderr, "inflate: dynamic codes block%s\n",
+ s->last ? " (last)" : ""));
+ DUMPBITS(3)
+ s->mode = TABLE;
+ break;
+ case 3: /* illegal */
+ DUMPBITS(3)
+ s->mode = BADB;
+ z->msg = (char*)"invalid block type";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ break;
+ case LENS:
+ NEEDBITS(32)
+ if ((((~b) >> 16) & 0xffff) != (b & 0xffff))
+ {
+ s->mode = BADB;
+ z->msg = (char*)"invalid stored block lengths";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ s->sub.left = (uInt)b & 0xffff;
+ b = k = 0; /* dump bits */
+ Tracev((stderr, "inflate: stored length %u\n", s->sub.left));
+ s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE);
+ break;
+ case STORED:
+ if (n == 0)
+ LEAVE
+ NEEDOUT
+ t = s->sub.left;
+ if (t > n) t = n;
+ if (t > m) t = m;
+ zmemcpy(q, p, t);
+ p += t; n -= t;
+ q += t; m -= t;
+ if ((s->sub.left -= t) != 0)
+ break;
+ Tracev((stderr, "inflate: stored end, %lu total out\n",
+ z->total_out + (q >= s->read ? q - s->read :
+ (s->end - s->read) + (q - s->window))));
+ s->mode = s->last ? DRY : TYPE;
+ break;
+ case TABLE:
+ NEEDBITS(14)
+ s->sub.trees.table = t = (uInt)b & 0x3fff;
+#ifndef PKZIP_BUG_WORKAROUND
+ if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
+ {
+ s->mode = BADB;
+ z->msg = (char*)"too many length or distance symbols";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+#endif
+ t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f);
+ if (t < 19)
+ t = 19;
+ if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL)
+ {
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ DUMPBITS(14)
+ s->sub.trees.index = 0;
+ Tracev((stderr, "inflate: table sizes ok\n"));
+ s->mode = BTREE;
+ case BTREE:
+ while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10))
+ {
+ NEEDBITS(3)
+ s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7;
+ DUMPBITS(3)
+ }
+ while (s->sub.trees.index < 19)
+ s->sub.trees.blens[border[s->sub.trees.index++]] = 0;
+ s->sub.trees.bb = 7;
+ t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb,
+ &s->sub.trees.tb, z);
+ if (t != Z_OK)
+ {
+ ZFREE(z, s->sub.trees.blens);
+ r = t;
+ if (r == Z_DATA_ERROR)
+ s->mode = BADB;
+ LEAVE
+ }
+ s->sub.trees.index = 0;
+ Tracev((stderr, "inflate: bits tree ok\n"));
+ s->mode = DTREE;
+ case DTREE:
+ while (t = s->sub.trees.table,
+ s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))
+ {
+ inflate_huft *h;
+ uInt i, j, c;
+
+ t = s->sub.trees.bb;
+ NEEDBITS(t)
+ h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]);
+ t = h->word.what.Bits;
+ c = h->more.Base;
+ if (c < 16)
+ {
+ DUMPBITS(t)
+ s->sub.trees.blens[s->sub.trees.index++] = c;
+ }
+ else /* c == 16..18 */
+ {
+ i = c == 18 ? 7 : c - 14;
+ j = c == 18 ? 11 : 3;
+ NEEDBITS(t + i)
+ DUMPBITS(t)
+ j += (uInt)b & inflate_mask[i];
+ DUMPBITS(i)
+ i = s->sub.trees.index;
+ t = s->sub.trees.table;
+ if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) ||
+ (c == 16 && i < 1))
+ {
+ inflate_trees_free(s->sub.trees.tb, z);
+ ZFREE(z, s->sub.trees.blens);
+ s->mode = BADB;
+ z->msg = (char*)"invalid bit length repeat";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ c = c == 16 ? s->sub.trees.blens[i - 1] : 0;
+ do {
+ s->sub.trees.blens[i++] = c;
+ } while (--j);
+ s->sub.trees.index = i;
+ }
+ }
+ inflate_trees_free(s->sub.trees.tb, z);
+ s->sub.trees.tb = Z_NULL;
+ {
+ uInt bl, bd;
+ inflate_huft *tl, *td;
+ inflate_codes_statef *c;
+
+ bl = 9; /* must be <= 9 for lookahead assumptions */
+ bd = 6; /* must be <= 9 for lookahead assumptions */
+ t = s->sub.trees.table;
+#ifdef DEBUG_ZLIB
+ inflate_hufts = 0;
+#endif
+ t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f),
+ s->sub.trees.blens, &bl, &bd, &tl, &td, z);
+ ZFREE(z, s->sub.trees.blens);
+ if (t != Z_OK)
+ {
+ if (t == (uInt)Z_DATA_ERROR)
+ s->mode = BADB;
+ r = t;
+ LEAVE
+ }
+ Tracev((stderr, "inflate: trees ok, %d * %d bytes used\n",
+ inflate_hufts, sizeof(inflate_huft)));
+ if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL)
+ {
+ inflate_trees_free(td, z);
+ inflate_trees_free(tl, z);
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ s->sub.decode.codes = c;
+ s->sub.decode.tl = tl;
+ s->sub.decode.td = td;
+ }
+ s->mode = CODES;
+ case CODES:
+ UPDATE
+ if ((r = inflate_codes(s, z, r)) != Z_STREAM_END)
+ return inflate_flush(s, z, r);
+ r = Z_OK;
+ inflate_codes_free(s->sub.decode.codes, z);
+ inflate_trees_free(s->sub.decode.td, z);
+ inflate_trees_free(s->sub.decode.tl, z);
+ LOAD
+ Tracev((stderr, "inflate: codes end, %lu total out\n",
+ z->total_out + (q >= s->read ? q - s->read :
+ (s->end - s->read) + (q - s->window))));
+ if (!s->last)
+ {
+ s->mode = TYPE;
+ break;
+ }
+ if (k > 7) /* return unused byte, if any */
+ {
+ Assert(k < 16, "inflate_codes grabbed too many bytes")
+ k -= 8;
+ n++;
+ p--; /* can always return one */
+ }
+ s->mode = DRY;
+ case DRY:
+ FLUSH
+ if (s->read != s->write)
+ LEAVE
+ s->mode = DONEB;
+ case DONEB:
+ r = Z_STREAM_END;
+ LEAVE
+ case BADB:
+ r = Z_DATA_ERROR;
+ LEAVE
+ default:
+ r = Z_STREAM_ERROR;
+ LEAVE
+ }
+}
+
+
+int inflate_blocks_free(s, z, c)
+inflate_blocks_statef *s;
+z_streamp z;
+uLongf *c;
+{
+ inflate_blocks_reset(s, z, c);
+ ZFREE(z, s->window);
+ ZFREE(z, s);
+ Trace((stderr, "inflate: blocks freed\n"));
+ return Z_OK;
+}
+
+
+void inflate_set_dictionary(s, d, n)
+inflate_blocks_statef *s;
+const Bytef *d;
+uInt n;
+{
+ zmemcpy((charf *)s->window, d, n);
+ s->read = s->write = s->window + n;
+}
+
+/*
+ * This subroutine adds the data at next_in/avail_in to the output history
+ * without performing any output. The output buffer must be "caught up";
+ * i.e. no pending output (hence s->read equals s->write), and the state must
+ * be BLOCKS (i.e. we should be willing to see the start of a series of
+ * BLOCKS). On exit, the output will also be caught up, and the checksum
+ * will have been updated if need be.
+ */
+int inflate_addhistory(s, z)
+inflate_blocks_statef *s;
+z_stream *z;
+{
+ uLong b; /* bit buffer */ /* NOT USED HERE */
+ uInt k; /* bits in bit buffer */ /* NOT USED HERE */
+ uInt t; /* temporary storage */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+
+ if (s->read != s->write)
+ return Z_STREAM_ERROR;
+ if (s->mode != TYPE)
+ return Z_DATA_ERROR;
+
+ /* we're ready to rock */
+ LOAD
+ /* while there is input ready, copy to output buffer, moving
+ * pointers as needed.
+ */
+ while (n) {
+ t = n; /* how many to do */
+ /* is there room until end of buffer? */
+ if (t > m) t = m;
+ /* update check information */
+ if (s->checkfn != Z_NULL)
+ s->check = (*s->checkfn)(s->check, q, t);
+ zmemcpy(q, p, t);
+ q += t;
+ p += t;
+ n -= t;
+ z->total_out += t;
+ s->read = q; /* drag read pointer forward */
+/* WWRAP */ /* expand WWRAP macro by hand to handle s->read */
+ if (q == s->end) {
+ s->read = q = s->window;
+ m = WAVAIL;
+ }
+ }
+ UPDATE
+ return Z_OK;
+}
+
+
+/*
+ * At the end of a Deflate-compressed PPP packet, we expect to have seen
+ * a `stored' block type value but not the (zero) length bytes.
+ */
+int inflate_packet_flush(s)
+ inflate_blocks_statef *s;
+{
+ if (s->mode != LENS)
+ return Z_DATA_ERROR;
+ s->mode = TYPE;
+ return Z_OK;
+}
+/* --- infblock.c */
+
+/* +++ inftrees.c */
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* #include "zutil.h" */
+/* #include "inftrees.h" */
+
+char inflate_copyright[] = " inflate 1.0.4 Copyright 1995-1996 Mark Adler ";
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+
+#ifndef NO_DUMMY_DECL
+struct internal_state {int dummy;}; /* for buggy compilers */
+#endif
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+
+local int huft_build OF((
+ uIntf *, /* code lengths in bits */
+ uInt, /* number of codes */
+ uInt, /* number of "simple" codes */
+ const uIntf *, /* list of base values for non-simple codes */
+ const uIntf *, /* list of extra bits for non-simple codes */
+ inflate_huft * FAR*,/* result: starting table */
+ uIntf *, /* maximum lookup bits (returns actual) */
+ z_streamp )); /* for zalloc function */
+
+local voidpf falloc OF((
+ voidpf, /* opaque pointer (not used) */
+ uInt, /* number of items */
+ uInt)); /* size of item */
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+local const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+ /* see note #13 above about 258 */
+local const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */
+local const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577};
+local const uInt cpdext[30] = { /* Extra bits for distance codes */
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+ 12, 12, 13, 13};
+
+/*
+ Huffman code decoding is performed using a multi-level table lookup.
+ The fastest way to decode is to simply build a lookup table whose
+ size is determined by the longest code. However, the time it takes
+ to build this table can also be a factor if the data being decoded
+ is not very long. The most common codes are necessarily the
+ shortest codes, so those codes dominate the decoding time, and hence
+ the speed. The idea is you can have a shorter table that decodes the
+ shorter, more probable codes, and then point to subsidiary tables for
+ the longer codes. The time it costs to decode the longer codes is
+ then traded against the time it takes to make longer tables.
+
+ This results of this trade are in the variables lbits and dbits
+ below. lbits is the number of bits the first level table for literal/
+ length codes can decode in one step, and dbits is the same thing for
+ the distance codes. Subsequent tables are also less than or equal to
+ those sizes. These values may be adjusted either when all of the
+ codes are shorter than that, in which case the longest code length in
+ bits is used, or when the shortest code is *longer* than the requested
+ table size, in which case the length of the shortest code in bits is
+ used.
+
+ There are two different values for the two tables, since they code a
+ different number of possibilities each. The literal/length table
+ codes 286 possible values, or in a flat code, a little over eight
+ bits. The distance table codes 30 possible values, or a little less
+ than five bits, flat. The optimum values for speed end up being
+ about one bit more than those, so lbits is 8+1 and dbits is 5+1.
+ The optimum values may differ though from machine to machine, and
+ possibly even between compilers. Your mileage may vary.
+ */
+
+
+/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */
+#define BMAX 15 /* maximum bit length of any code */
+#define N_MAX 288 /* maximum number of codes in any set */
+
+#ifdef DEBUG_ZLIB
+ uInt inflate_hufts;
+#endif
+
+local int huft_build(b, n, s, d, e, t, m, zs)
+uIntf *b; /* code lengths in bits (all assumed <= BMAX) */
+uInt n; /* number of codes (assumed <= N_MAX) */
+uInt s; /* number of simple-valued codes (0..s-1) */
+const uIntf *d; /* list of base values for non-simple codes */
+const uIntf *e; /* list of extra bits for non-simple codes */
+inflate_huft * FAR *t; /* result: starting table */
+uIntf *m; /* maximum lookup bits, returns actual */
+z_streamp zs; /* for zalloc function */
+/* Given a list of code lengths and a maximum table size, make a set of
+ tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR
+ if the given code set is incomplete (the tables are still built in this
+ case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of
+ lengths), or Z_MEM_ERROR if not enough memory. */
+{
+
+ uInt a; /* counter for codes of length k */
+ uInt c[BMAX+1]; /* bit length count table */
+ uInt f; /* i repeats in table every f entries */
+ int g; /* maximum code length */
+ int h; /* table level */
+ register uInt i; /* counter, current code */
+ register uInt j; /* counter */
+ register int k; /* number of bits in current code */
+ int l; /* bits per table (returned in m) */
+ register uIntf *p; /* pointer into c[], b[], or v[] */
+ inflate_huft *q; /* points to current table */
+ struct inflate_huft_s r; /* table entry for structure assignment */
+ inflate_huft *u[BMAX]; /* table stack */
+ uInt v[N_MAX]; /* values in order of bit length */
+ register int w; /* bits before this table == (l * h) */
+ uInt x[BMAX+1]; /* bit offsets, then code stack */
+ uIntf *xp; /* pointer into x */
+ int y; /* number of dummy codes added */
+ uInt z; /* number of entries in current table */
+
+
+ /* Generate counts for each bit length */
+ p = c;
+#define C0 *p++ = 0;
+#define C2 C0 C0 C0 C0
+#define C4 C2 C2 C2 C2
+ C4 /* clear c[]--assume BMAX+1 is 16 */
+ p = b; i = n;
+ do {
+ c[*p++]++; /* assume all entries <= BMAX */
+ } while (--i);
+ if (c[0] == n) /* null input--all zero length codes */
+ {
+ *t = (inflate_huft *)Z_NULL;
+ *m = 0;
+ return Z_OK;
+ }
+
+
+ /* Find minimum and maximum length, bound *m by those */
+ l = *m;
+ for (j = 1; j <= BMAX; j++)
+ if (c[j])
+ break;
+ k = j; /* minimum code length */
+ if ((uInt)l < j)
+ l = j;
+ for (i = BMAX; i; i--)
+ if (c[i])
+ break;
+ g = i; /* maximum code length */
+ if ((uInt)l > i)
+ l = i;
+ *m = l;
+
+
+ /* Adjust last length count to fill out codes, if needed */
+ for (y = 1 << j; j < i; j++, y <<= 1)
+ if ((y -= c[j]) < 0)
+ return Z_DATA_ERROR;
+ if ((y -= c[i]) < 0)
+ return Z_DATA_ERROR;
+ c[i] += y;
+
+
+ /* Generate starting offsets into the value table for each length */
+ x[1] = j = 0;
+ p = c + 1; xp = x + 2;
+ while (--i) { /* note that i == g from above */
+ *xp++ = (j += *p++);
+ }
+
+
+ /* Make a table of values in order of bit lengths */
+ p = b; i = 0;
+ do {
+ if ((j = *p++) != 0)
+ v[x[j]++] = i;
+ } while (++i < n);
+ n = x[g]; /* set n to length of v */
+
+
+ /* Generate the Huffman codes and for each, make the table entries */
+ x[0] = i = 0; /* first Huffman code is zero */
+ p = v; /* grab values in bit order */
+ h = -1; /* no tables yet--level -1 */
+ w = -l; /* bits decoded == (l * h) */
+ u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */
+ q = (inflate_huft *)Z_NULL; /* ditto */
+ z = 0; /* ditto */
+
+ /* go through the bit lengths (k already is bits in shortest code) */
+ for (; k <= g; k++)
+ {
+ a = c[k];
+ while (a--)
+ {
+ /* here i is the Huffman code of length k bits for value *p */
+ /* make tables up to required level */
+ while (k > w + l)
+ {
+ h++;
+ w += l; /* previous table always l bits */
+
+ /* compute minimum size table less than or equal to l bits */
+ z = g - w;
+ z = z > (uInt)l ? l : z; /* table size upper limit */
+ if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */
+ { /* too few codes for k-w bit table */
+ f -= a + 1; /* deduct codes from patterns left */
+ xp = c + k;
+ if (j < z)
+ while (++j < z) /* try smaller tables up to z bits */
+ {
+ if ((f <<= 1) <= *++xp)
+ break; /* enough codes to use up j bits */
+ f -= *xp; /* else deduct codes from patterns */
+ }
+ }
+ z = 1 << j; /* table entries for j-bit table */
+
+ /* allocate and link in new table */
+ if ((q = (inflate_huft *)ZALLOC
+ (zs,z + 1,sizeof(inflate_huft))) == Z_NULL)
+ {
+ if (h)
+ inflate_trees_free(u[0], zs);
+ return Z_MEM_ERROR; /* not enough memory */
+ }
+#ifdef DEBUG_ZLIB
+ inflate_hufts += z + 1;
+#endif
+ *t = q + 1; /* link to list for huft_free() */
+ *(t = &(q->next)) = Z_NULL;
+ u[h] = ++q; /* table starts after link */
+
+ /* connect to last table, if there is one */
+ if (h)
+ {
+ x[h] = i; /* save pattern for backing up */
+ r.bits = (Byte)l; /* bits to dump before this table */
+ r.exop = (Byte)j; /* bits in this table */
+ r.next = q; /* pointer to this table */
+ j = i >> (w - l); /* (get around Turbo C bug) */
+ u[h-1][j] = r; /* connect to last table */
+ }
+ }
+
+ /* set up table entry in r */
+ r.bits = (Byte)(k - w);
+ if (p >= v + n)
+ r.exop = 128 + 64; /* out of values--invalid code */
+ else if (*p < s)
+ {
+ r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */
+ r.base = *p++; /* simple code is just the value */
+ }
+ else
+ {
+ r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */
+ r.base = d[*p++ - s];
+ }
+
+ /* fill code-like entries with r */
+ f = 1 << (k - w);
+ for (j = i >> w; j < z; j += f)
+ q[j] = r;
+
+ /* backwards increment the k-bit code i */
+ for (j = 1 << (k - 1); i & j; j >>= 1)
+ i ^= j;
+ i ^= j;
+
+ /* backup over finished tables */
+ while ((i & ((1 << w) - 1)) != x[h])
+ {
+ h--; /* don't need to update q */
+ w -= l;
+ }
+ }
+ }
+
+
+ /* Return Z_BUF_ERROR if we were given an incomplete table */
+ return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
+}
+
+
+int inflate_trees_bits(c, bb, tb, z)
+uIntf *c; /* 19 code lengths */
+uIntf *bb; /* bits tree desired/actual depth */
+inflate_huft * FAR *tb; /* bits tree result */
+z_streamp z; /* for zfree function */
+{
+ int r;
+
+ r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, tb, bb, z);
+ if (r == Z_DATA_ERROR)
+ z->msg = (char*)"oversubscribed dynamic bit lengths tree";
+ else if (r == Z_BUF_ERROR || *bb == 0)
+ {
+ inflate_trees_free(*tb, z);
+ z->msg = (char*)"incomplete dynamic bit lengths tree";
+ r = Z_DATA_ERROR;
+ }
+ return r;
+}
+
+
+int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, z)
+uInt nl; /* number of literal/length codes */
+uInt nd; /* number of distance codes */
+uIntf *c; /* that many (total) code lengths */
+uIntf *bl; /* literal desired/actual bit depth */
+uIntf *bd; /* distance desired/actual bit depth */
+inflate_huft * FAR *tl; /* literal/length tree result */
+inflate_huft * FAR *td; /* distance tree result */
+z_streamp z; /* for zfree function */
+{
+ int r;
+
+ /* build literal/length tree */
+ r = huft_build(c, nl, 257, cplens, cplext, tl, bl, z);
+ if (r != Z_OK || *bl == 0)
+ {
+ if (r == Z_DATA_ERROR)
+ z->msg = (char*)"oversubscribed literal/length tree";
+ else if (r != Z_MEM_ERROR)
+ {
+ inflate_trees_free(*tl, z);
+ z->msg = (char*)"incomplete literal/length tree";
+ r = Z_DATA_ERROR;
+ }
+ return r;
+ }
+
+ /* build distance tree */
+ r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, z);
+ if (r != Z_OK || (*bd == 0 && nl > 257))
+ {
+ if (r == Z_DATA_ERROR)
+ z->msg = (char*)"oversubscribed distance tree";
+ else if (r == Z_BUF_ERROR) {
+#ifdef PKZIP_BUG_WORKAROUND
+ r = Z_OK;
+ }
+#else
+ inflate_trees_free(*td, z);
+ z->msg = (char*)"incomplete distance tree";
+ r = Z_DATA_ERROR;
+ }
+ else if (r != Z_MEM_ERROR)
+ {
+ z->msg = (char*)"empty distance tree with lengths";
+ r = Z_DATA_ERROR;
+ }
+ inflate_trees_free(*tl, z);
+ return r;
+#endif
+ }
+
+ /* done */
+ return Z_OK;
+}
+
+
+/* build fixed tables only once--keep them here */
+local int fixed_built = 0;
+#define FIXEDH 530 /* number of hufts used by fixed tables */
+local inflate_huft fixed_mem[FIXEDH];
+local uInt fixed_bl;
+local uInt fixed_bd;
+local inflate_huft *fixed_tl;
+local inflate_huft *fixed_td;
+
+
+local voidpf falloc(q, n, s)
+voidpf q; /* opaque pointer */
+uInt n; /* number of items */
+uInt s; /* size of item */
+{
+ Assert(s == sizeof(inflate_huft) && n <= *(intf *)q,
+ "inflate_trees falloc overflow");
+ *(intf *)q -= n+s-s; /* s-s to avoid warning */
+ return (voidpf)(fixed_mem + *(intf *)q);
+}
+
+
+int inflate_trees_fixed(bl, bd, tl, td)
+uIntf *bl; /* literal desired/actual bit depth */
+uIntf *bd; /* distance desired/actual bit depth */
+inflate_huft * FAR *tl; /* literal/length tree result */
+inflate_huft * FAR *td; /* distance tree result */
+{
+ /* build fixed tables if not already (multiple overlapped executions ok) */
+ if (!fixed_built)
+ {
+ int k; /* temporary variable */
+ unsigned c[288]; /* length list for huft_build */
+ z_stream z; /* for falloc function */
+ int f = FIXEDH; /* number of hufts left in fixed_mem */
+
+ /* set up fake z_stream for memory routines */
+ z.zalloc = falloc;
+ z.zfree = Z_NULL;
+ z.opaque = (voidpf)&f;
+
+ /* literal table */
+ for (k = 0; k < 144; k++)
+ c[k] = 8;
+ for (; k < 256; k++)
+ c[k] = 9;
+ for (; k < 280; k++)
+ c[k] = 7;
+ for (; k < 288; k++)
+ c[k] = 8;
+ fixed_bl = 7;
+ huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, &z);
+
+ /* distance table */
+ for (k = 0; k < 30; k++)
+ c[k] = 5;
+ fixed_bd = 5;
+ huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, &z);
+
+ /* done */
+ Assert(f == 0, "invalid build of fixed tables");
+ fixed_built = 1;
+ }
+ *bl = fixed_bl;
+ *bd = fixed_bd;
+ *tl = fixed_tl;
+ *td = fixed_td;
+ return Z_OK;
+}
+
+
+int inflate_trees_free(t, z)
+inflate_huft *t; /* table to free */
+z_streamp z; /* for zfree function */
+/* Free the malloc'ed tables built by huft_build(), which makes a linked
+ list of the tables it made, with the links in a dummy first entry of
+ each table. */
+{
+ register inflate_huft *p, *q, *r;
+
+ /* Reverse linked list */
+ p = Z_NULL;
+ q = t;
+ while (q != Z_NULL)
+ {
+ r = (q - 1)->next;
+ (q - 1)->next = p;
+ p = q;
+ q = r;
+ }
+ /* Go through linked list, freeing from the malloced (t[-1]) address. */
+ while (p != Z_NULL)
+ {
+ q = (--p)->next;
+ ZFREE(z,p);
+ p = q;
+ }
+ return Z_OK;
+}
+/* --- inftrees.c */
+
+/* +++ infcodes.c */
+/* infcodes.c -- process literals and length/distance pairs
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* #include "zutil.h" */
+/* #include "inftrees.h" */
+/* #include "infblock.h" */
+/* #include "infcodes.h" */
+/* #include "infutil.h" */
+
+/* +++ inffast.h */
+/* inffast.h -- header to use inffast.c
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+extern int inflate_fast OF((
+ uInt,
+ uInt,
+ inflate_huft *,
+ inflate_huft *,
+ inflate_blocks_statef *,
+ z_streamp ));
+/* --- inffast.h */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* inflate codes private state */
+struct inflate_codes_state {
+
+ /* mode */
+ enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+ START, /* x: set up for LEN */
+ LEN, /* i: get length/literal/eob next */
+ LENEXT, /* i: getting length extra (have base) */
+ DIST, /* i: get distance next */
+ DISTEXT, /* i: getting distance extra */
+ COPY, /* o: copying bytes in window, waiting for space */
+ LIT, /* o: got literal, waiting for output space */
+ WASH, /* o: got eob, possibly still output waiting */
+ END, /* x: got eob and all data flushed */
+ BADCODE} /* x: got error */
+ mode; /* current inflate_codes mode */
+
+ /* mode dependent information */
+ uInt len;
+ union {
+ struct {
+ inflate_huft *tree; /* pointer into tree */
+ uInt need; /* bits needed */
+ } code; /* if LEN or DIST, where in tree */
+ uInt lit; /* if LIT, literal */
+ struct {
+ uInt get; /* bits to get for extra */
+ uInt dist; /* distance back to copy from */
+ } copy; /* if EXT or COPY, where and how much */
+ } sub; /* submode */
+
+ /* mode independent information */
+ Byte lbits; /* ltree bits decoded per branch */
+ Byte dbits; /* dtree bits decoder per branch */
+ inflate_huft *ltree; /* literal/length/eob tree */
+ inflate_huft *dtree; /* distance tree */
+
+};
+
+
+inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z)
+uInt bl, bd;
+inflate_huft *tl;
+inflate_huft *td; /* need separate declaration for Borland C++ */
+z_streamp z;
+{
+ inflate_codes_statef *c;
+
+ if ((c = (inflate_codes_statef *)
+ ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL)
+ {
+ c->mode = START;
+ c->lbits = (Byte)bl;
+ c->dbits = (Byte)bd;
+ c->ltree = tl;
+ c->dtree = td;
+ Tracev((stderr, "inflate: codes new\n"));
+ }
+ return c;
+}
+
+
+int inflate_codes(s, z, r)
+inflate_blocks_statef *s;
+z_streamp z;
+int r;
+{
+ uInt j; /* temporary storage */
+ inflate_huft *t; /* temporary pointer */
+ uInt e; /* extra bits or operation */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+ Bytef *f; /* pointer to copy strings from */
+ inflate_codes_statef *c = s->sub.decode.codes; /* codes state */
+
+ /* copy input/output information to locals (UPDATE macro restores) */
+ LOAD
+
+ /* process input and output based on current state */
+ while (1) switch (c->mode)
+ { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+ case START: /* x: set up for LEN */
+#ifndef SLOW
+ if (m >= 258 && n >= 10)
+ {
+ UPDATE
+ r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z);
+ LOAD
+ if (r != Z_OK)
+ {
+ c->mode = r == Z_STREAM_END ? WASH : BADCODE;
+ break;
+ }
+ }
+#endif /* !SLOW */
+ c->sub.code.need = c->lbits;
+ c->sub.code.tree = c->ltree;
+ c->mode = LEN;
+ case LEN: /* i: get length/literal/eob next */
+ j = c->sub.code.need;
+ NEEDBITS(j)
+ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+ DUMPBITS(t->bits)
+ e = (uInt)(t->exop);
+ if (e == 0) /* literal */
+ {
+ c->sub.lit = t->base;
+ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", t->base));
+ c->mode = LIT;
+ break;
+ }
+ if (e & 16) /* length */
+ {
+ c->sub.copy.get = e & 15;
+ c->len = t->base;
+ c->mode = LENEXT;
+ break;
+ }
+ if ((e & 64) == 0) /* next table */
+ {
+ c->sub.code.need = e;
+ c->sub.code.tree = t->next;
+ break;
+ }
+ if (e & 32) /* end of block */
+ {
+ Tracevv((stderr, "inflate: end of block\n"));
+ c->mode = WASH;
+ break;
+ }
+ c->mode = BADCODE; /* invalid code */
+ z->msg = (char*)"invalid literal/length code";
+ r = Z_DATA_ERROR;
+ LEAVE
+ case LENEXT: /* i: getting length extra (have base) */
+ j = c->sub.copy.get;
+ NEEDBITS(j)
+ c->len += (uInt)b & inflate_mask[j];
+ DUMPBITS(j)
+ c->sub.code.need = c->dbits;
+ c->sub.code.tree = c->dtree;
+ Tracevv((stderr, "inflate: length %u\n", c->len));
+ c->mode = DIST;
+ case DIST: /* i: get distance next */
+ j = c->sub.code.need;
+ NEEDBITS(j)
+ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+ DUMPBITS(t->bits)
+ e = (uInt)(t->exop);
+ if (e & 16) /* distance */
+ {
+ c->sub.copy.get = e & 15;
+ c->sub.copy.dist = t->base;
+ c->mode = DISTEXT;
+ break;
+ }
+ if ((e & 64) == 0) /* next table */
+ {
+ c->sub.code.need = e;
+ c->sub.code.tree = t->next;
+ break;
+ }
+ c->mode = BADCODE; /* invalid code */
+ z->msg = (char*)"invalid distance code";
+ r = Z_DATA_ERROR;
+ LEAVE
+ case DISTEXT: /* i: getting distance extra */
+ j = c->sub.copy.get;
+ NEEDBITS(j)
+ c->sub.copy.dist += (uInt)b & inflate_mask[j];
+ DUMPBITS(j)
+ Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist));
+ c->mode = COPY;
+ case COPY: /* o: copying bytes in window, waiting for space */
+#ifndef __TURBOC__ /* Turbo C bug for following expression */
+ f = (uInt)(q - s->window) < c->sub.copy.dist ?
+ s->end - (c->sub.copy.dist - (q - s->window)) :
+ q - c->sub.copy.dist;
+#else
+ f = q - c->sub.copy.dist;
+ if ((uInt)(q - s->window) < c->sub.copy.dist)
+ f = s->end - (c->sub.copy.dist - (uInt)(q - s->window));
+#endif
+ while (c->len)
+ {
+ NEEDOUT
+ OUTBYTE(*f++)
+ if (f == s->end)
+ f = s->window;
+ c->len--;
+ }
+ c->mode = START;
+ break;
+ case LIT: /* o: got literal, waiting for output space */
+ NEEDOUT
+ OUTBYTE(c->sub.lit)
+ c->mode = START;
+ break;
+ case WASH: /* o: got eob, possibly more output */
+ FLUSH
+ if (s->read != s->write)
+ LEAVE
+ c->mode = END;
+ case END:
+ r = Z_STREAM_END;
+ LEAVE
+ case BADCODE: /* x: got error */
+ r = Z_DATA_ERROR;
+ LEAVE
+ default:
+ r = Z_STREAM_ERROR;
+ LEAVE
+ }
+}
+
+
+void inflate_codes_free(c, z)
+inflate_codes_statef *c;
+z_streamp z;
+{
+ ZFREE(z, c);
+ Tracev((stderr, "inflate: codes free\n"));
+}
+/* --- infcodes.c */
+
+/* +++ infutil.c */
+/* inflate_util.c -- data and routines common to blocks and codes
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* #include "zutil.h" */
+/* #include "infblock.h" */
+/* #include "inftrees.h" */
+/* #include "infcodes.h" */
+/* #include "infutil.h" */
+
+#ifndef NO_DUMMY_DECL
+struct inflate_codes_state {int dummy;}; /* for buggy compilers */
+#endif
+
+/* And'ing with mask[n] masks the lower n bits */
+uInt inflate_mask[17] = {
+ 0x0000,
+ 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+ 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+
+/* copy as much as possible from the sliding window to the output area */
+int inflate_flush(s, z, r)
+inflate_blocks_statef *s;
+z_streamp z;
+int r;
+{
+ uInt n;
+ Bytef *p;
+ Bytef *q;
+
+ /* local copies of source and destination pointers */
+ p = z->next_out;
+ q = s->read;
+
+ /* compute number of bytes to copy as far as end of window */
+ n = (uInt)((q <= s->write ? s->write : s->end) - q);
+ if (n > z->avail_out) n = z->avail_out;
+ if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+ /* update counters */
+ z->avail_out -= n;
+ z->total_out += n;
+
+ /* update check information */
+ if (s->checkfn != Z_NULL)
+ z->adler = s->check = (*s->checkfn)(s->check, q, n);
+
+ /* copy as far as end of window */
+ if (p != NULL) {
+ zmemcpy(p, q, n);
+ p += n;
+ }
+ q += n;
+
+ /* see if more to copy at beginning of window */
+ if (q == s->end)
+ {
+ /* wrap pointers */
+ q = s->window;
+ if (s->write == s->end)
+ s->write = s->window;
+
+ /* compute bytes to copy */
+ n = (uInt)(s->write - q);
+ if (n > z->avail_out) n = z->avail_out;
+ if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+ /* update counters */
+ z->avail_out -= n;
+ z->total_out += n;
+
+ /* update check information */
+ if (s->checkfn != Z_NULL)
+ z->adler = s->check = (*s->checkfn)(s->check, q, n);
+
+ /* copy */
+ if (p != NULL) {
+ zmemcpy(p, q, n);
+ p += n;
+ }
+ q += n;
+ }
+
+ /* update pointers */
+ z->next_out = p;
+ s->read = q;
+
+ /* done */
+ return r;
+}
+/* --- infutil.c */
+
+/* +++ inffast.c */
+/* inffast.c -- process literals and length/distance pairs fast
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* #include "zutil.h" */
+/* #include "inftrees.h" */
+/* #include "infblock.h" */
+/* #include "infcodes.h" */
+/* #include "infutil.h" */
+/* #include "inffast.h" */
+
+#ifndef NO_DUMMY_DECL
+struct inflate_codes_state {int dummy;}; /* for buggy compilers */
+#endif
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* macros for bit input with no checking and for returning unused bytes */
+#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<<k;k+=8;}}
+#define UNGRAB {n+=(c=k>>3);p-=c;k&=7;}
+
+/* Called with number of bytes left to write in window at least 258
+ (the maximum string length) and number of input bytes available
+ at least ten. The ten bytes are six bytes for the longest length/
+ distance pair plus four bytes for overloading the bit buffer. */
+
+int inflate_fast(bl, bd, tl, td, s, z)
+uInt bl, bd;
+inflate_huft *tl;
+inflate_huft *td; /* need separate declaration for Borland C++ */
+inflate_blocks_statef *s;
+z_streamp z;
+{
+ inflate_huft *t; /* temporary pointer */
+ uInt e; /* extra bits or operation */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+ uInt ml; /* mask for literal/length tree */
+ uInt md; /* mask for distance tree */
+ uInt c; /* bytes to copy */
+ uInt d; /* distance back to copy from */
+ Bytef *r; /* copy source pointer */
+
+ /* load input, output, bit values */
+ LOAD
+
+ /* initialize masks */
+ ml = inflate_mask[bl];
+ md = inflate_mask[bd];
+
+ /* do until not enough input or output space for fast loop */
+ do { /* assume called with m >= 258 && n >= 10 */
+ /* get literal/length code */
+ GRABBITS(20) /* max bits for literal/length code */
+ if ((e = (t = tl + ((uInt)b & ml))->exop) == 0)
+ {
+ DUMPBITS(t->bits)
+ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+ "inflate: * literal '%c'\n" :
+ "inflate: * literal 0x%02x\n", t->base));
+ *q++ = (Byte)t->base;
+ m--;
+ continue;
+ }
+ do {
+ DUMPBITS(t->bits)
+ if (e & 16)
+ {
+ /* get extra bits for length */
+ e &= 15;
+ c = t->base + ((uInt)b & inflate_mask[e]);
+ DUMPBITS(e)
+ Tracevv((stderr, "inflate: * length %u\n", c));
+
+ /* decode distance base of block to copy */
+ GRABBITS(15); /* max bits for distance code */
+ e = (t = td + ((uInt)b & md))->exop;
+ do {
+ DUMPBITS(t->bits)
+ if (e & 16)
+ {
+ /* get extra bits to add to distance base */
+ e &= 15;
+ GRABBITS(e) /* get extra bits (up to 13) */
+ d = t->base + ((uInt)b & inflate_mask[e]);
+ DUMPBITS(e)
+ Tracevv((stderr, "inflate: * distance %u\n", d));
+
+ /* do the copy */
+ m -= c;
+ if ((uInt)(q - s->window) >= d) /* offset before dest */
+ { /* just copy */
+ r = q - d;
+ *q++ = *r++; c--; /* minimum count is three, */
+ *q++ = *r++; c--; /* so unroll loop a little */
+ }
+ else /* else offset after destination */
+ {
+ e = d - (uInt)(q - s->window); /* bytes from offset to end */
+ r = s->end - e; /* pointer to offset */
+ if (c > e) /* if source crosses, */
+ {
+ c -= e; /* copy to end of window */
+ do {
+ *q++ = *r++;
+ } while (--e);
+ r = s->window; /* copy rest from start of window */
+ }
+ }
+ do { /* copy all or what's left */
+ *q++ = *r++;
+ } while (--c);
+ break;
+ }
+ else if ((e & 64) == 0)
+ e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop;
+ else
+ {
+ z->msg = (char*)"invalid distance code";
+ UNGRAB
+ UPDATE
+ return Z_DATA_ERROR;
+ }
+ } while (1);
+ break;
+ }
+ if ((e & 64) == 0)
+ {
+ if ((e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop) == 0)
+ {
+ DUMPBITS(t->bits)
+ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+ "inflate: * literal '%c'\n" :
+ "inflate: * literal 0x%02x\n", t->base));
+ *q++ = (Byte)t->base;
+ m--;
+ break;
+ }
+ }
+ else if (e & 32)
+ {
+ Tracevv((stderr, "inflate: * end of block\n"));
+ UNGRAB
+ UPDATE
+ return Z_STREAM_END;
+ }
+ else
+ {
+ z->msg = (char*)"invalid literal/length code";
+ UNGRAB
+ UPDATE
+ return Z_DATA_ERROR;
+ }
+ } while (1);
+ } while (m >= 258 && n >= 10);
+
+ /* not enough input or output--restore pointers and return */
+ UNGRAB
+ UPDATE
+ return Z_OK;
+}
+/* --- inffast.c */
+
+/* +++ zutil.c */
+/* zutil.c -- target dependent utility functions for the compression library
+ * Copyright (C) 1995-1996 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* From: zutil.c,v 1.17 1996/07/24 13:41:12 me Exp $ */
+
+#ifdef DEBUG_ZLIB
+#include <stdio.h>
+#endif
+
+/* #include "zutil.h" */
+
+#ifndef NO_DUMMY_DECL
+struct internal_state {int dummy;}; /* for buggy compilers */
+#endif
+
+#ifndef STDC
+extern void exit OF((int));
+#endif
+
+const char *z_errmsg[10] = {
+"need dictionary", /* Z_NEED_DICT 2 */
+"stream end", /* Z_STREAM_END 1 */
+"", /* Z_OK 0 */
+"file error", /* Z_ERRNO (-1) */
+"stream error", /* Z_STREAM_ERROR (-2) */
+"data error", /* Z_DATA_ERROR (-3) */
+"insufficient memory", /* Z_MEM_ERROR (-4) */
+"buffer error", /* Z_BUF_ERROR (-5) */
+"incompatible version",/* Z_VERSION_ERROR (-6) */
+""};
+
+
+const char *zlibVersion()
+{
+ return ZLIB_VERSION;
+}
+
+#ifdef DEBUG_ZLIB
+void z_error (m)
+ char *m;
+{
+ fprintf(stderr, "%s\n", m);
+ exit(1);
+}
+#endif
+
+#ifndef HAVE_MEMCPY
+
+void zmemcpy(dest, source, len)
+ Bytef* dest;
+ Bytef* source;
+ uInt len;
+{
+ if (len == 0) return;
+ do {
+ *dest++ = *source++; /* ??? to be unrolled */
+ } while (--len != 0);
+}
+
+int zmemcmp(s1, s2, len)
+ Bytef* s1;
+ Bytef* s2;
+ uInt len;
+{
+ uInt j;
+
+ for (j = 0; j < len; j++) {
+ if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1;
+ }
+ return 0;
+}
+
+void zmemzero(dest, len)
+ Bytef* dest;
+ uInt len;
+{
+ if (len == 0) return;
+ do {
+ *dest++ = 0; /* ??? to be unrolled */
+ } while (--len != 0);
+}
+#endif
+
+#ifdef __TURBOC__
+#if (defined( __BORLANDC__) || !defined(SMALL_MEDIUM)) && !defined(__32BIT__)
+/* Small and medium model in Turbo C are for now limited to near allocation
+ * with reduced MAX_WBITS and MAX_MEM_LEVEL
+ */
+# define MY_ZCALLOC
+
+/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
+ * and farmalloc(64K) returns a pointer with an offset of 8, so we
+ * must fix the pointer. Warning: the pointer must be put back to its
+ * original form in order to free it, use zcfree().
+ */
+
+#define MAX_PTR 10
+/* 10*64K = 640K */
+
+local int next_ptr = 0;
+
+typedef struct ptr_table_s {
+ voidpf org_ptr;
+ voidpf new_ptr;
+} ptr_table;
+
+local ptr_table table[MAX_PTR];
+/* This table is used to remember the original form of pointers
+ * to large buffers (64K). Such pointers are normalized with a zero offset.
+ * Since MSDOS is not a preemptive multitasking OS, this table is not
+ * protected from concurrent access. This hack doesn't work anyway on
+ * a protected system like OS/2. Use Microsoft C instead.
+ */
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+ voidpf buf = opaque; /* just to make some compilers happy */
+ ulg bsize = (ulg)items*size;
+
+ /* If we allocate less than 65520 bytes, we assume that farmalloc
+ * will return a usable pointer which doesn't have to be normalized.
+ */
+ if (bsize < 65520L) {
+ buf = farmalloc(bsize);
+ if (*(ush*)&buf != 0) return buf;
+ } else {
+ buf = farmalloc(bsize + 16L);
+ }
+ if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
+ table[next_ptr].org_ptr = buf;
+
+ /* Normalize the pointer to seg:0 */
+ *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4;
+ *(ush*)&buf = 0;
+ table[next_ptr++].new_ptr = buf;
+ return buf;
+}
+
+void zcfree (voidpf opaque, voidpf ptr)
+{
+ int n;
+ if (*(ush*)&ptr != 0) { /* object < 64K */
+ farfree(ptr);
+ return;
+ }
+ /* Find the original pointer */
+ for (n = 0; n < next_ptr; n++) {
+ if (ptr != table[n].new_ptr) continue;
+
+ farfree(table[n].org_ptr);
+ while (++n < next_ptr) {
+ table[n-1] = table[n];
+ }
+ next_ptr--;
+ return;
+ }
+ ptr = opaque; /* just to make some compilers happy */
+ Assert(0, "zcfree: ptr not found");
+}
+#endif
+#endif /* __TURBOC__ */
+
+
+#if defined(M_I86) && !defined(__32BIT__)
+/* Microsoft C in 16-bit mode */
+
+# define MY_ZCALLOC
+
+#if (!defined(_MSC_VER) || (_MSC_VER < 600))
+# define _halloc halloc
+# define _hfree hfree
+#endif
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+ if (opaque) opaque = 0; /* to make compiler happy */
+ return _halloc((long)items, size);
+}
+
+void zcfree (voidpf opaque, voidpf ptr)
+{
+ if (opaque) opaque = 0; /* to make compiler happy */
+ _hfree(ptr);
+}
+
+#endif /* MSC */
+
+
+#ifndef MY_ZCALLOC /* Any system without a special alloc function */
+
+#ifndef STDC
+extern voidp calloc OF((uInt items, uInt size));
+extern void free OF((voidpf ptr));
+#endif
+
+voidpf zcalloc (opaque, items, size)
+ voidpf opaque;
+ unsigned items;
+ unsigned size;
+{
+ if (opaque) items += size - size; /* make compiler happy */
+ return (voidpf)calloc(items, size);
+}
+
+void zcfree (opaque, ptr)
+ voidpf opaque;
+ voidpf ptr;
+{
+ free(ptr);
+ if (opaque) return; /* make compiler happy */
+}
+
+#endif /* MY_ZCALLOC */
+/* --- zutil.c */
+
+/* +++ adler32.c */
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995-1996 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* From: adler32.c,v 1.10 1996/05/22 11:52:18 me Exp $ */
+
+/* #include "zlib.h" */
+
+#define BASE 65521L /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf,i) {s1 += buf[i]; s2 += s1;}
+#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1);
+#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2);
+#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
+#define DO16(buf) DO8(buf,0); DO8(buf,8);
+
+/* ========================================================================= */
+uLong adler32(adler, buf, len)
+ uLong adler;
+ const Bytef *buf;
+ uInt len;
+{
+ unsigned long s1 = adler & 0xffff;
+ unsigned long s2 = (adler >> 16) & 0xffff;
+ int k;
+
+ if (buf == Z_NULL) return 1L;
+
+ while (len > 0) {
+ k = len < NMAX ? len : NMAX;
+ len -= k;
+ while (k >= 16) {
+ DO16(buf);
+ buf += 16;
+ k -= 16;
+ }
+ if (k != 0) do {
+ s1 += *buf++;
+ s2 += s1;
+ } while (--k);
+ s1 %= BASE;
+ s2 %= BASE;
+ }
+ return (s2 << 16) | s1;
+}
+/* --- adler32.c */
diff --git a/drivers/net/zlib.h b/drivers/net/zlib.h
new file mode 100644
index 000000000..f2e00c2db
--- /dev/null
+++ b/drivers/net/zlib.h
@@ -0,0 +1,1010 @@
+/* $Id: zlib.h,v 1.1 1997/10/02 02:35:37 paulus Exp $ */
+
+/*
+ * This file is derived from zlib.h and zconf.h from the zlib-0.95
+ * distribution by Jean-loup Gailly and Mark Adler, with some additions
+ * by Paul Mackerras to aid in implementing Deflate compression and
+ * decompression for PPP packets.
+ */
+
+/*
+ * ==FILEVERSION 970501==
+ *
+ * This marker is used by the Linux installation script to determine
+ * whether an up-to-date version of this file is already installed.
+ */
+
+
+/* +++ zlib.h */
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+ version 1.0.4, Jul 24th, 1996.
+
+ Copyright (C) 1995-1996 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ gzip@prep.ai.mit.edu madler@alumni.caltech.edu
+
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt
+ (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
+*/
+
+#ifndef _ZLIB_H
+#define _ZLIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* +++ zconf.h */
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-1996 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* From: zconf.h,v 1.20 1996/07/02 15:09:28 me Exp $ */
+
+#ifndef _ZCONF_H
+#define _ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ */
+#ifdef Z_PREFIX
+# define deflateInit_ z_deflateInit_
+# define deflate z_deflate
+# define deflateEnd z_deflateEnd
+# define inflateInit_ z_inflateInit_
+# define inflate z_inflate
+# define inflateEnd z_inflateEnd
+# define deflateInit2_ z_deflateInit2_
+# define deflateSetDictionary z_deflateSetDictionary
+# define deflateCopy z_deflateCopy
+# define deflateReset z_deflateReset
+# define deflateParams z_deflateParams
+# define inflateInit2_ z_inflateInit2_
+# define inflateSetDictionary z_inflateSetDictionary
+# define inflateSync z_inflateSync
+# define inflateReset z_inflateReset
+# define compress z_compress
+# define uncompress z_uncompress
+# define adler32 z_adler32
+# define crc32 z_crc32
+# define get_crc_table z_get_crc_table
+
+# define Byte z_Byte
+# define uInt z_uInt
+# define uLong z_uLong
+# define Bytef z_Bytef
+# define charf z_charf
+# define intf z_intf
+# define uIntf z_uIntf
+# define uLongf z_uLongf
+# define voidpf z_voidpf
+# define voidp z_voidp
+#endif
+
+#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
+# define WIN32
+#endif
+#if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386)
+# ifndef __32BIT__
+# define __32BIT__
+# endif
+#endif
+#if defined(__MSDOS__) && !defined(MSDOS)
+# define MSDOS
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#if defined(MSDOS) && !defined(__32BIT__)
+# define MAXSEG_64K
+#endif
+#ifdef MSDOS
+# define UNALIGNED_OK
+#endif
+
+#if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32)) && !defined(STDC)
+# define STDC
+#endif
+#if (defined(__STDC__) || defined(__cplusplus)) && !defined(STDC)
+# define STDC
+#endif
+
+#ifndef STDC
+# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+# define const
+# endif
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__)
+# define NO_DUMMY_DECL
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+# ifdef MAXSEG_64K
+# define MAX_MEM_LEVEL 8
+# else
+# define MAX_MEM_LEVEL 9
+# endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2 */
+#ifndef MAX_WBITS
+# define MAX_WBITS 15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+ 1 << (windowBits+2) + 1 << (memLevel+9)
+ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+ The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+ /* Type declarations */
+
+#ifndef OF /* function prototypes */
+# ifdef STDC
+# define OF(args) args
+# else
+# define OF(args) ()
+# endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__)
+ /* MSC small or medium model */
+# define SMALL_MEDIUM
+# ifdef _MSC_VER
+# define FAR __far
+# else
+# define FAR far
+# endif
+#endif
+#if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__))
+# ifndef __32BIT__
+# define SMALL_MEDIUM
+# define FAR __far
+# endif
+#endif
+#ifndef FAR
+# define FAR
+#endif
+
+typedef unsigned char Byte; /* 8 bits */
+typedef unsigned int uInt; /* 16 bits or more */
+typedef unsigned long uLong; /* 32 bits or more */
+
+#if defined(__BORLANDC__) && defined(SMALL_MEDIUM)
+ /* Borland C/C++ ignores FAR inside typedef */
+# define Bytef Byte FAR
+#else
+ typedef Byte FAR Bytef;
+#endif
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+ typedef void FAR *voidpf;
+ typedef void *voidp;
+#else
+ typedef Byte FAR *voidpf;
+ typedef Byte *voidp;
+#endif
+
+
+/* Compile with -DZLIB_DLL for Windows DLL support */
+#if (defined(_WINDOWS) || defined(WINDOWS)) && defined(ZLIB_DLL)
+# include <windows.h>
+# define EXPORT WINAPI
+#else
+# define EXPORT
+#endif
+
+#endif /* _ZCONF_H */
+/* --- zconf.h */
+
+#define ZLIB_VERSION "1.0.4P"
+
+/*
+ The 'zlib' compression library provides in-memory compression and
+ decompression functions, including integrity checks of the uncompressed
+ data. This version of the library supports only one compression method
+ (deflation) but other algorithms may be added later and will have the same
+ stream interface.
+
+ For compression the application must provide the output buffer and
+ may optionally provide the input buffer for optimization. For decompression,
+ the application must provide the input buffer and may optionally provide
+ the output buffer for optimization.
+
+ Compression can be done in a single step if the buffers are large
+ enough (for example if an input file is mmap'ed), or can be done by
+ repeated calls of the compression function. In the latter case, the
+ application must provide more input and/or consume the output
+ (providing more output space) before each call.
+
+ The library does not install any signal handler. It is recommended to
+ add at least a handler for SIGSEGV when decompressing; the library checks
+ the consistency of the input data whenever possible but may go nuts
+ for some forms of corrupted input.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void (*free_func) OF((voidpf opaque, voidpf address));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+ Bytef *next_in; /* next input byte */
+ uInt avail_in; /* number of bytes available at next_in */
+ uLong total_in; /* total nb of input bytes read so far */
+
+ Bytef *next_out; /* next output byte should be put there */
+ uInt avail_out; /* remaining free space at next_out */
+ uLong total_out; /* total nb of bytes output so far */
+
+ char *msg; /* last error message, NULL if no error */
+ struct internal_state FAR *state; /* not visible by applications */
+
+ alloc_func zalloc; /* used to allocate the internal state */
+ free_func zfree; /* used to free the internal state */
+ voidpf opaque; /* private data object passed to zalloc and zfree */
+
+ int data_type; /* best guess about the data type: ascii or binary */
+ uLong adler; /* adler32 value of the uncompressed data */
+ uLong reserved; /* reserved for future use */
+} z_stream;
+
+typedef z_stream FAR *z_streamp;
+
+/*
+ The application must update next_in and avail_in when avail_in has
+ dropped to zero. It must update next_out and avail_out when avail_out
+ has dropped to zero. The application must initialize zalloc, zfree and
+ opaque before calling the init function. All other fields are set by the
+ compression library and must not be updated by the application.
+
+ The opaque value provided by the application will be passed as the first
+ parameter for calls of zalloc and zfree. This can be useful for custom
+ memory management. The compression library attaches no meaning to the
+ opaque value.
+
+ zalloc must return Z_NULL if there is not enough memory for the object.
+ On 16-bit systems, the functions zalloc and zfree must be able to allocate
+ exactly 65536 bytes, but will not be required to allocate more than this
+ if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+ pointers returned by zalloc for objects of exactly 65536 bytes *must*
+ have their offset normalized to zero. The default allocation function
+ provided by this library ensures this (see zutil.c). To reduce memory
+ requirements and avoid any allocation of 64K objects, at the expense of
+ compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+
+ The fields total_in and total_out can be used for statistics or
+ progress reports. After compression, total_in holds the total size of
+ the uncompressed data and may be saved for use in the decompressor
+ (particularly if the decompressor wants to decompress everything in
+ a single step).
+*/
+
+ /* constants */
+
+#define Z_NO_FLUSH 0
+#define Z_PARTIAL_FLUSH 1
+#define Z_PACKET_FLUSH 2
+#define Z_SYNC_FLUSH 3
+#define Z_FULL_FLUSH 4
+#define Z_FINISH 5
+/* Allowed flush values; see deflate() below for details */
+
+#define Z_OK 0
+#define Z_STREAM_END 1
+#define Z_NEED_DICT 2
+#define Z_ERRNO (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR (-3)
+#define Z_MEM_ERROR (-4)
+#define Z_BUF_ERROR (-5)
+#define Z_VERSION_ERROR (-6)
+/* Return codes for the compression/decompression functions. Negative
+ * values are errors, positive values are used for special but normal events.
+ */
+
+#define Z_NO_COMPRESSION 0
+#define Z_BEST_SPEED 1
+#define Z_BEST_COMPRESSION 9
+#define Z_DEFAULT_COMPRESSION (-1)
+/* compression levels */
+
+#define Z_FILTERED 1
+#define Z_HUFFMAN_ONLY 2
+#define Z_DEFAULT_STRATEGY 0
+/* compression strategy; see deflateInit2() below for details */
+
+#define Z_BINARY 0
+#define Z_ASCII 1
+#define Z_UNKNOWN 2
+/* Possible values of the data_type field */
+
+#define Z_DEFLATED 8
+/* The deflate compression method (the only one supported in this version) */
+
+#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
+
+#define zlib_version zlibVersion()
+/* for compatibility with versions < 1.0.2 */
+
+ /* basic functions */
+
+extern const char * EXPORT zlibVersion OF((void));
+/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
+ If the first character differs, the library code actually used is
+ not compatible with the zlib.h header file used by the application.
+ This check is automatically made by deflateInit and inflateInit.
+ */
+
+/*
+extern int EXPORT deflateInit OF((z_streamp strm, int level));
+
+ Initializes the internal stream state for compression. The fields
+ zalloc, zfree and opaque must be initialized before by the caller.
+ If zalloc and zfree are set to Z_NULL, deflateInit updates them to
+ use default allocation functions.
+
+ The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
+ 1 gives best speed, 9 gives best compression, 0 gives no compression at
+ all (the input data is simply copied a block at a time).
+ Z_DEFAULT_COMPRESSION requests a default compromise between speed and
+ compression (currently equivalent to level 6).
+
+ deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if level is not a valid compression level,
+ Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
+ with the version assumed by the caller (ZLIB_VERSION).
+ msg is set to null if there is no error message. deflateInit does not
+ perform any compression: this will be done by deflate().
+*/
+
+
+extern int EXPORT deflate OF((z_streamp strm, int flush));
+/*
+ Performs one or both of the following actions:
+
+ - Compress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in and avail_in are updated and
+ processing will resume at this point for the next call of deflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. This action is forced if the parameter flush is non zero.
+ Forcing flush frequently degrades the compression ratio, so this parameter
+ should be set only when necessary (in interactive applications).
+ Some output may be provided even if flush is not set.
+
+ Before the call of deflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating avail_in or avail_out accordingly; avail_out
+ should never be zero before the call. The application can consume the
+ compressed output when it wants, for example when the output buffer is full
+ (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK
+ and with zero avail_out, it must be called again after making room in the
+ output buffer because there might be more output pending.
+
+ If the parameter flush is set to Z_PARTIAL_FLUSH, the current compression
+ block is terminated and flushed to the output buffer so that the
+ decompressor can get all input data available so far. For method 9, a future
+ variant on method 8, the current block will be flushed but not terminated.
+ Z_SYNC_FLUSH has the same effect as partial flush except that the compressed
+ output is byte aligned (the compressor can clear its internal bit buffer)
+ and the current block is always terminated; this can be useful if the
+ compressor has to be restarted from scratch after an interruption (in which
+ case the internal state of the compressor may be lost).
+ If flush is set to Z_FULL_FLUSH, the compression block is terminated, a
+ special marker is output and the compression dictionary is discarded; this
+ is useful to allow the decompressor to synchronize if one compressed block
+ has been damaged (see inflateSync below). Flushing degrades compression and
+ so should be used only when necessary. Using Z_FULL_FLUSH too often can
+ seriously degrade the compression. If deflate returns with avail_out == 0,
+ this function must be called again with the same value of the flush
+ parameter and more output space (updated avail_out), until the flush is
+ complete (deflate returns with non-zero avail_out).
+
+ If the parameter flush is set to Z_PACKET_FLUSH, the compression
+ block is terminated, and a zero-length stored block is output,
+ omitting the length bytes (the effect of this is that the 3-bit type
+ code 000 for a stored block is output, and the output is then
+ byte-aligned). This is designed for use at the end of a PPP packet.
+
+ If the parameter flush is set to Z_FINISH, pending input is processed,
+ pending output is flushed and deflate returns with Z_STREAM_END if there
+ was enough output space; if deflate returns with Z_OK, this function must be
+ called again with Z_FINISH and more output space (updated avail_out) but no
+ more input data, until it returns with Z_STREAM_END or an error. After
+ deflate has returned Z_STREAM_END, the only possible operations on the
+ stream are deflateReset or deflateEnd.
+
+ Z_FINISH can be used immediately after deflateInit if all the compression
+ is to be done in a single step. In this case, avail_out must be at least
+ 0.1% larger than avail_in plus 12 bytes. If deflate does not return
+ Z_STREAM_END, then it must be called again as described above.
+
+ deflate() may update data_type if it can make a good guess about
+ the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered
+ binary. This field is only for information purposes and does not affect
+ the compression algorithm in any manner.
+
+ deflate() returns Z_OK if some progress has been made (more input
+ processed or more output produced), Z_STREAM_END if all input has been
+ consumed and all output has been produced (only when flush is set to
+ Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+ if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible.
+*/
+
+
+extern int EXPORT deflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+ stream state was inconsistent, Z_DATA_ERROR if the stream was freed
+ prematurely (some input or output was discarded). In the error case,
+ msg may be set but then points to a static string (which must not be
+ deallocated).
+*/
+
+
+/*
+extern int EXPORT inflateInit OF((z_streamp strm));
+
+ Initializes the internal stream state for decompression. The fields
+ zalloc, zfree and opaque must be initialized before by the caller. If
+ zalloc and zfree are set to Z_NULL, inflateInit updates them to use default
+ allocation functions.
+
+ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_VERSION_ERROR if the zlib library version is incompatible
+ with the version assumed by the caller. msg is set to null if there is no
+ error message. inflateInit does not perform any decompression: this will be
+ done by inflate().
+*/
+
+
+extern int EXPORT inflate OF((z_streamp strm, int flush));
+/*
+ Performs one or both of the following actions:
+
+ - Decompress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in is updated and processing
+ will resume at this point for the next call of inflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. inflate() provides as much output as possible, until there
+ is no more input data or no more space in the output buffer (see below
+ about the flush parameter).
+
+ Before the call of inflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating the next_* and avail_* values accordingly.
+ The application can consume the uncompressed output when it wants, for
+ example when the output buffer is full (avail_out == 0), or after each
+ call of inflate(). If inflate returns Z_OK and with zero avail_out, it
+ must be called again after making room in the output buffer because there
+ might be more output pending.
+
+ If the parameter flush is set to Z_PARTIAL_FLUSH or Z_PACKET_FLUSH,
+ inflate flushes as much output as possible to the output buffer. The
+ flushing behavior of inflate is not specified for values of the flush
+ parameter other than Z_PARTIAL_FLUSH, Z_PACKET_FLUSH or Z_FINISH, but the
+ current implementation actually flushes as much output as possible
+ anyway. For Z_PACKET_FLUSH, inflate checks that once all the input data
+ has been consumed, it is expecting to see the length field of a stored
+ block; if not, it returns Z_DATA_ERROR.
+
+ inflate() should normally be called until it returns Z_STREAM_END or an
+ error. However if all decompression is to be performed in a single step
+ (a single call of inflate), the parameter flush should be set to
+ Z_FINISH. In this case all pending input is processed and all pending
+ output is flushed; avail_out must be large enough to hold all the
+ uncompressed data. (The size of the uncompressed data may have been saved
+ by the compressor for this purpose.) The next operation on this stream must
+ be inflateEnd to deallocate the decompression state. The use of Z_FINISH
+ is never required, but can be used to inform inflate that a faster routine
+ may be used for the single inflate() call.
+
+ inflate() returns Z_OK if some progress has been made (more input
+ processed or more output produced), Z_STREAM_END if the end of the
+ compressed data has been reached and all uncompressed output has been
+ produced, Z_NEED_DICT if a preset dictionary is needed at this point (see
+ inflateSetDictionary below), Z_DATA_ERROR if the input data was corrupted,
+ Z_STREAM_ERROR if the stream structure was inconsistent (for example if
+ next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory,
+ Z_BUF_ERROR if no progress is possible or if there was not enough room in
+ the output buffer when Z_FINISH is used. In the Z_DATA_ERROR case, the
+ application may then call inflateSync to look for a good compression block.
+ In the Z_NEED_DICT case, strm->adler is set to the Adler32 value of the
+ dictionary chosen by the compressor.
+*/
+
+
+extern int EXPORT inflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+ was inconsistent. In the error case, msg may be set but then points to a
+ static string (which must not be deallocated).
+*/
+
+ /* Advanced functions */
+
+/*
+ The following functions are needed only in some special applications.
+*/
+
+/*
+extern int EXPORT deflateInit2 OF((z_streamp strm,
+ int level,
+ int method,
+ int windowBits,
+ int memLevel,
+ int strategy));
+
+ This is another version of deflateInit with more compression options. The
+ fields next_in, zalloc, zfree and opaque must be initialized before by
+ the caller.
+
+ The method parameter is the compression method. It must be Z_DEFLATED in
+ this version of the library. (Method 9 will allow a 64K history buffer and
+ partial block flushes.)
+
+ The windowBits parameter is the base two logarithm of the window size
+ (the size of the history buffer). It should be in the range 8..15 for this
+ version of the library (the value 16 will be allowed for method 9). Larger
+ values of this parameter result in better compression at the expense of
+ memory usage. The default value is 15 if deflateInit is used instead.
+
+ The memLevel parameter specifies how much memory should be allocated
+ for the internal compression state. memLevel=1 uses minimum memory but
+ is slow and reduces compression ratio; memLevel=9 uses maximum memory
+ for optimal speed. The default value is 8. See zconf.h for total memory
+ usage as a function of windowBits and memLevel.
+
+ The strategy parameter is used to tune the compression algorithm. Use the
+ value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
+ filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no
+ string match). Filtered data consists mostly of small values with a
+ somewhat random distribution. In this case, the compression algorithm is
+ tuned to compress them better. The effect of Z_FILTERED is to force more
+ Huffman coding and less string matching; it is somewhat intermediate
+ between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects
+ the compression ratio but not the correctness of the compressed output even
+ if it is not set appropriately.
+
+ If next_in is not null, the library will use this buffer to hold also
+ some history information; the buffer must either hold the entire input
+ data, or have at least 1<<(windowBits+1) bytes and be writable. If next_in
+ is null, the library will allocate its own history buffer (and leave next_in
+ null). next_out need not be provided here but must be provided by the
+ application for the next call of deflate().
+
+ If the history buffer is provided by the application, next_in must
+ must never be changed by the application since the compressor maintains
+ information inside this buffer from call to call; the application
+ must provide more input only by increasing avail_in. next_in is always
+ reset by the library in this case.
+
+ deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was
+ not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as
+ an invalid method). msg is set to null if there is no error message.
+ deflateInit2 does not perform any compression: this will be done by
+ deflate().
+*/
+
+extern int EXPORT deflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the compression dictionary (history buffer) from the given
+ byte sequence without producing any compressed output. This function must
+ be called immediately after deflateInit or deflateInit2, before any call
+ of deflate. The compressor and decompressor must use exactly the same
+ dictionary (see inflateSetDictionary).
+ The dictionary should consist of strings (byte sequences) that are likely
+ to be encountered later in the data to be compressed, with the most commonly
+ used strings preferably put towards the end of the dictionary. Using a
+ dictionary is most useful when the data to be compressed is short and
+ can be predicted with good accuracy; the data can then be compressed better
+ than with the default empty dictionary. In this version of the library,
+ only the last 32K bytes of the dictionary are used.
+ Upon return of this function, strm->adler is set to the Adler32 value
+ of the dictionary; the decompressor may later use this value to determine
+ which dictionary has been used by the compressor. (The Adler32 value
+ applies to the whole dictionary even if only a subset of the dictionary is
+ actually used by the compressor.)
+
+ deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state
+ is inconsistent (for example if deflate has already been called for this
+ stream). deflateSetDictionary does not perform any compression: this will
+ be done by deflate().
+*/
+
+extern int EXPORT deflateCopy OF((z_streamp dest,
+ z_streamp source));
+/*
+ Sets the destination stream as a complete copy of the source stream. If
+ the source stream is using an application-supplied history buffer, a new
+ buffer is allocated for the destination stream. The compressed output
+ buffer is always application-supplied. It's the responsibility of the
+ application to provide the correct values of next_out and avail_out for the
+ next call of deflate.
+
+ This function can be useful when several compression strategies will be
+ tried, for example when there are several ways of pre-processing the input
+ data with a filter. The streams that will be discarded should then be freed
+ by calling deflateEnd. Note that deflateCopy duplicates the internal
+ compression state which can be quite large, so this strategy is slow and
+ can consume lots of memory.
+
+ deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+extern int EXPORT deflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to deflateEnd followed by deflateInit,
+ but does not free and reallocate all the internal compression state.
+ The stream will keep the same compression level and any other attributes
+ that may have been set by deflateInit2.
+
+ deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+extern int EXPORT deflateParams OF((z_streamp strm, int level, int strategy));
+/*
+ Dynamically update the compression level and compression strategy.
+ This can be used to switch between compression and straight copy of
+ the input data, or to switch to a different kind of input data requiring
+ a different strategy. If the compression level is changed, the input
+ available so far is compressed with the old level (and may be flushed);
+ the new level will take effect only at the next call of deflate().
+
+ Before the call of deflateParams, the stream state must be set as for
+ a call of deflate(), since the currently available input may have to
+ be compressed and flushed. In particular, strm->avail_out must be non-zero.
+
+ deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
+ stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR
+ if strm->avail_out was zero.
+*/
+
+extern int EXPORT deflateOutputPending OF((z_streamp strm));
+/*
+ Returns the number of bytes of output which are immediately
+ available from the compressor (i.e. without any further input
+ or flush).
+*/
+
+/*
+extern int EXPORT inflateInit2 OF((z_streamp strm,
+ int windowBits));
+
+ This is another version of inflateInit with more compression options. The
+ fields next_out, zalloc, zfree and opaque must be initialized before by
+ the caller.
+
+ The windowBits parameter is the base two logarithm of the maximum window
+ size (the size of the history buffer). It should be in the range 8..15 for
+ this version of the library (the value 16 will be allowed soon). The
+ default value is 15 if inflateInit is used instead. If a compressed stream
+ with a larger window size is given as input, inflate() will return with
+ the error code Z_DATA_ERROR instead of trying to allocate a larger window.
+
+ If next_out is not null, the library will use this buffer for the history
+ buffer; the buffer must either be large enough to hold the entire output
+ data, or have at least 1<<windowBits bytes. If next_out is null, the
+ library will allocate its own buffer (and leave next_out null). next_in
+ need not be provided here but must be provided by the application for the
+ next call of inflate().
+
+ If the history buffer is provided by the application, next_out must
+ never be changed by the application since the decompressor maintains
+ history information inside this buffer from call to call; the application
+ can only reset next_out to the beginning of the history buffer when
+ avail_out is zero and all output has been consumed.
+
+ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was
+ not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as
+ windowBits < 8). msg is set to null if there is no error message.
+ inflateInit2 does not perform any decompression: this will be done by
+ inflate().
+*/
+
+extern int EXPORT inflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the decompression dictionary (history buffer) from the given
+ uncompressed byte sequence. This function must be called immediately after
+ a call of inflate if this call returned Z_NEED_DICT. The dictionary chosen
+ by the compressor can be determined from the Adler32 value returned by this
+ call of inflate. The compressor and decompressor must use exactly the same
+ dictionary (see deflateSetDictionary).
+
+ inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state is
+ inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
+ expected one (incorrect Adler32 value). inflateSetDictionary does not
+ perform any decompression: this will be done by subsequent calls of
+ inflate().
+*/
+
+extern int EXPORT inflateSync OF((z_streamp strm));
+/*
+ Skips invalid compressed data until the special marker (see deflate()
+ above) can be found, or until all available input is skipped. No output
+ is provided.
+
+ inflateSync returns Z_OK if the special marker has been found, Z_BUF_ERROR
+ if no more input was provided, Z_DATA_ERROR if no marker has been found,
+ or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
+ case, the application may save the current current value of total_in which
+ indicates where valid compressed data was found. In the error case, the
+ application may repeatedly call inflateSync, providing more input each time,
+ until success or end of the input data.
+*/
+
+extern int EXPORT inflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to inflateEnd followed by inflateInit,
+ but does not free and reallocate all the internal decompression state.
+ The stream will keep attributes that may have been set by inflateInit2.
+
+ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+extern int inflateIncomp OF((z_stream *strm));
+/*
+ This function adds the data at next_in (avail_in bytes) to the output
+ history without performing any output. There must be no pending output,
+ and the decompressor must be expecting to see the start of a block.
+ Calling this function is equivalent to decompressing a stored block
+ containing the data at next_in (except that the data is not output).
+*/
+
+ /* utility functions */
+
+/*
+ The following utility functions are implemented on top of the
+ basic stream-oriented functions. To simplify the interface, some
+ default options are assumed (compression level, window size,
+ standard memory allocation functions). The source code of these
+ utility functions can easily be modified if you need special options.
+*/
+
+extern int EXPORT compress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Compresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be at least 0.1% larger than
+ sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the
+ compressed buffer.
+ This function can be used to compress a whole file at once if the
+ input file is mmap'ed.
+ compress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer.
+*/
+
+extern int EXPORT uncompress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Decompresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be large enough to hold the
+ entire uncompressed data. (The size of the uncompressed data must have
+ been saved previously by the compressor and transmitted to the decompressor
+ by some mechanism outside the scope of this compression library.)
+ Upon exit, destLen is the actual size of the compressed buffer.
+ This function can be used to decompress a whole file at once if the
+ input file is mmap'ed.
+
+ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer, or Z_DATA_ERROR if the input data was corrupted.
+*/
+
+
+typedef voidp gzFile;
+
+extern gzFile EXPORT gzopen OF((const char *path, const char *mode));
+/*
+ Opens a gzip (.gz) file for reading or writing. The mode parameter
+ is as in fopen ("rb" or "wb") but can also include a compression level
+ ("wb9"). gzopen can be used to read a file which is not in gzip format;
+ in this case gzread will directly read from the file without decompression.
+ gzopen returns NULL if the file could not be opened or if there was
+ insufficient memory to allocate the (de)compression state; errno
+ can be checked to distinguish the two cases (if errno is zero, the
+ zlib error is Z_MEM_ERROR).
+*/
+
+extern gzFile EXPORT gzdopen OF((int fd, const char *mode));
+/*
+ gzdopen() associates a gzFile with the file descriptor fd. File
+ descriptors are obtained from calls like open, dup, creat, pipe or
+ fileno (in the file has been previously opened with fopen).
+ The mode parameter is as in gzopen.
+ The next call of gzclose on the returned gzFile will also close the
+ file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
+ descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode).
+ gzdopen returns NULL if there was insufficient memory to allocate
+ the (de)compression state.
+*/
+
+extern int EXPORT gzread OF((gzFile file, voidp buf, unsigned len));
+/*
+ Reads the given number of uncompressed bytes from the compressed file.
+ If the input file was not in gzip format, gzread copies the given number
+ of bytes into the buffer.
+ gzread returns the number of uncompressed bytes actually read (0 for
+ end of file, -1 for error). */
+
+extern int EXPORT gzwrite OF((gzFile file, const voidp buf, unsigned len));
+/*
+ Writes the given number of uncompressed bytes into the compressed file.
+ gzwrite returns the number of uncompressed bytes actually written
+ (0 in case of error).
+*/
+
+extern int EXPORT gzflush OF((gzFile file, int flush));
+/*
+ Flushes all pending output into the compressed file. The parameter
+ flush is as in the deflate() function. The return value is the zlib
+ error number (see function gzerror below). gzflush returns Z_OK if
+ the flush parameter is Z_FINISH and all output could be flushed.
+ gzflush should be called only when strictly necessary because it can
+ degrade compression.
+*/
+
+extern int EXPORT gzclose OF((gzFile file));
+/*
+ Flushes all pending output if necessary, closes the compressed file
+ and deallocates all the (de)compression state. The return value is the zlib
+ error number (see function gzerror below).
+*/
+
+extern const char * EXPORT gzerror OF((gzFile file, int *errnum));
+/*
+ Returns the error message for the last error which occurred on the
+ given compressed file. errnum is set to zlib error number. If an
+ error occurred in the file system and not in the compression library,
+ errnum is set to Z_ERRNO and the application may consult errno
+ to get the exact error code.
+*/
+
+ /* checksum functions */
+
+/*
+ These functions are not related to compression but are exported
+ anyway because they might be useful in applications using the
+ compression library.
+*/
+
+extern uLong EXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
+
+/*
+ Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+ return the updated checksum. If buf is NULL, this function returns
+ the required initial value for the checksum.
+ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+ much faster. Usage example:
+
+ uLong adler = adler32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ adler = adler32(adler, buffer, length);
+ }
+ if (adler != original_adler) error();
+*/
+
+extern uLong EXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
+/*
+ Update a running crc with the bytes buf[0..len-1] and return the updated
+ crc. If buf is NULL, this function returns the required initial value
+ for the crc. Pre- and post-conditioning (one's complement) is performed
+ within this function so it shouldn't be done by the application.
+ Usage example:
+
+ uLong crc = crc32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ crc = crc32(crc, buffer, length);
+ }
+ if (crc != original_crc) error();
+*/
+
+
+ /* various hacks, don't look :) */
+
+/* deflateInit and inflateInit are macros to allow checking the zlib version
+ * and the compiler's view of z_stream:
+ */
+extern int EXPORT deflateInit_ OF((z_streamp strm, int level,
+ const char *version, int stream_size));
+extern int EXPORT inflateInit_ OF((z_streamp strm,
+ const char *version, int stream_size));
+extern int EXPORT deflateInit2_ OF((z_streamp strm, int level, int method,
+ int windowBits, int memLevel, int strategy,
+ const char *version, int stream_size));
+extern int EXPORT inflateInit2_ OF((z_streamp strm, int windowBits,
+ const char *version, int stream_size));
+#define deflateInit(strm, level) \
+ deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit(strm) \
+ inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream))
+#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+ (strategy), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit2(strm, windowBits) \
+ inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
+
+#if !defined(_Z_UTIL_H) && !defined(NO_DUMMY_DECL)
+ struct internal_state {int dummy;}; /* hack for buggy compilers */
+#endif
+
+uLongf *get_crc_table OF((void)); /* can be used by asm versions of crc32() */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZLIB_H */
+/* --- zlib.h */
diff --git a/drivers/net/znet.c b/drivers/net/znet.c
index cb53120a2..8c11e3d9f 100644
--- a/drivers/net/znet.c
+++ b/drivers/net/znet.c
@@ -247,13 +247,12 @@ __initfunc(int znet_probe(struct device *dev))
zn.tx_dma = netinfo->dma2;
/* These should never fail. You can't add devices to a sealed box! */
- if (request_irq(dev->irq, &znet_interrupt, 0, "ZNet", NULL)
+ if (request_irq(dev->irq, &znet_interrupt, 0, "ZNet", dev)
|| request_dma(zn.rx_dma,"ZNet rx")
|| request_dma(zn.tx_dma,"ZNet tx")) {
printk(KERN_WARNING "%s: Not opened -- resource busy?!?\n", dev->name);
return EBUSY;
}
- irq2dev_map[dev->irq] = dev;
/* Allocate buffer memory. We can cross a 128K boundary, so we
must be careful about the allocation. It's easiest to waste 8K. */
@@ -403,7 +402,7 @@ static int znet_send_packet(struct sk_buff *skb, struct device *dev)
/* The ZNET interrupt handler. */
static void znet_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
- struct device *dev = irq2dev_map[irq];
+ struct device *dev = dev_id;
int ioaddr;
int boguscnt = 20;
@@ -602,7 +601,7 @@ static int znet_close(struct device *dev)
disable_dma(zn.rx_dma);
disable_dma(zn.tx_dma);
- free_irq(dev->irq, NULL);
+ free_irq(dev->irq, dev);
if (znet_debug > 1)
printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 4a8b247ca..a6966ad95 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -42,10 +42,15 @@ struct pci_dev *pci_devices = 0;
*/
struct pci_dev_info dev_info[] = {
DEVICE( COMPAQ, COMPAQ_1280, "QVision 1280/p"),
- DEVICE( COMPAQ, COMPAQ_NETELL100,"Netelligent 10/100"),
- DEVICE( COMPAQ, COMPAQ_NETELL10,"Netelligent 10"),
- DEVICE( COMPAQ, COMPAQ_NETFLEX3,"NetFlex 3"),
+ DEVICE( COMPAQ, COMPAQ_SMART2P, "Smart-2/P RAID Controller"),
+ DEVICE( COMPAQ, COMPAQ_NETEL100,"Netelligent 10/100"),
+ DEVICE( COMPAQ, COMPAQ_NETEL10, "Netelligent 10"),
+ DEVICE( COMPAQ, COMPAQ_NETFLEX3I,"NetFlex 3"),
+ DEVICE( COMPAQ, COMPAQ_NETEL100D,"Netelligent 10/100 Dual"),
+ DEVICE( COMPAQ, COMPAQ_NETEL100PI,"Netelligent 10/100 ProLiant"),
+ DEVICE( COMPAQ, COMPAQ_NETEL100I,"Netelligent 10/100 Integrated"),
DEVICE( COMPAQ, COMPAQ_THUNDER, "ThunderLAN"),
+ DEVICE( COMPAQ, COMPAQ_NETFLEX3B,"NetFlex 3 BNC"),
DEVICE( NCR, NCR_53C810, "53c810"),
DEVICE( NCR, NCR_53C820, "53c820"),
DEVICE( NCR, NCR_53C825, "53c825"),
@@ -58,7 +63,9 @@ struct pci_dev_info dev_info[] = {
DEVICE( ATI, ATI_68800, "68800AX"),
DEVICE( ATI, ATI_215CT222, "215CT222"),
DEVICE( ATI, ATI_210888CX, "210888CX"),
+ DEVICE( ATI, ATI_215GP, "Mach64 GP (Rage Pro)"),
DEVICE( ATI, ATI_215GT, "Mach64 GT (Rage II)"),
+ DEVICE( ATI, ATI_215GTB, "Mach64 GT (Rage II)"),
DEVICE( ATI, ATI_210888GX, "210888GX"),
DEVICE( ATI, ATI_264VT, "Mach64 VT"),
DEVICE( VLSI, VLSI_82C592, "82C592-FC1"),
@@ -134,7 +141,7 @@ struct pci_dev_info dev_info[] = {
DEVICE( SI, SI_5571, "5571"),
DEVICE( SI, SI_7001, "7001"),
DEVICE( HP, HP_J2585A, "J2585A"),
- DEVICE( HP, HP_J2585B, "J2585B"),
+ DEVICE( HP, HP_J2585B, "J2585B (Lassen)"),
DEVICE( PCTECH, PCTECH_RZ1000, "RZ1000 (buggy)"),
DEVICE( PCTECH, PCTECH_RZ1001, "RZ1001 (buggy?)"),
DEVICE( DPT, DPT, "SmartCache/Raid"),
@@ -155,10 +162,8 @@ struct pci_dev_info dev_info[] = {
DEVICE( MOTOROLA, MOTOROLA_MPC105,"MPC105 Eagle"),
DEVICE( MOTOROLA, MOTOROLA_MPC106,"MPC106 Grackle"),
DEVICE( MOTOROLA, MOTOROLA_RAVEN, "Raven"),
+ DEVICE( PROMISE, PROMISE_IDE_UDMA,"IDE Ultra DMA/33"),
DEVICE( PROMISE, PROMISE_5300, "DC5030"),
- DEVICE( APPLE, APPLE_BANDIT, "Bandit"),
- DEVICE( APPLE, APPLE_GC, "Grand Central"),
- DEVICE( APPLE, APPLE_HYDRA, "Hydra"),
DEVICE( N9, N9_I128, "Imagine 128"),
DEVICE( N9, N9_I128_2, "Imagine 128v2"),
DEVICE( UMC, UMC_UM8673F, "UM8673F"),
@@ -171,6 +176,10 @@ struct pci_dev_info dev_info[] = {
DEVICE( UMC, UMC_UM8886N, "UM8886N"),
DEVICE( UMC, UMC_UM8891N, "UM8891N"),
DEVICE( X, X_AGX016, "ITT AGX016"),
+ DEVICE( PICOP, PICOP_PT86C52X, "PT86C52x Vesuvius"),
+ DEVICE( APPLE, APPLE_BANDIT, "Bandit"),
+ DEVICE( APPLE, APPLE_GC, "Grand Central"),
+ DEVICE( APPLE, APPLE_HYDRA, "Hydra"),
DEVICE( NEXGEN, NEXGEN_82C501, "82C501"),
DEVICE( QLOGIC, QLOGIC_ISP1020, "ISP1020"),
DEVICE( QLOGIC, QLOGIC_ISP1022, "ISP1022"),
@@ -191,7 +200,7 @@ struct pci_dev_info dev_info[] = {
DEVICE( CMD, CMD_670, "670"),
DEVICE( VISION, VISION_QD8500, "QD-8500"),
DEVICE( VISION, VISION_QD8580, "QD-8580"),
- DEVICE( BROOKTREE, BT848, "Brooktree 848"),
+ DEVICE( BROOKTREE, BROOKTREE_848, "Bt848"),
DEVICE( SIERRA, SIERRA_STB, "STB Horizon 64"),
DEVICE( ACC, ACC_2056, "2056"),
DEVICE( WINBOND, WINBOND_83769, "W83769F"),
@@ -205,6 +214,7 @@ struct pci_dev_info dev_info[] = {
DEVICE( 3COM, 3COM_3C900TPO, "3C900 10bTPO"),
DEVICE( 3COM, 3COM_3C900COMBO,"3C900 10b Combo"),
DEVICE( 3COM, 3COM_3C905TX, "3C905 100bTX"),
+ DEVICE( SMC, SMC_EPIC100, "9432 TX"),
DEVICE( AL, AL_M1445, "M1445"),
DEVICE( AL, AL_M1449, "M1449"),
DEVICE( AL, AL_M1451, "M1451"),
@@ -212,9 +222,16 @@ struct pci_dev_info dev_info[] = {
DEVICE( AL, AL_M1489, "M1489"),
DEVICE( AL, AL_M1511, "M1511"),
DEVICE( AL, AL_M1513, "M1513"),
+ DEVICE( AL, AL_M1521, "M1521"),
+ DEVICE( AL, AL_M1523, "M1523"),
+ DEVICE( AL, AL_M1531, "M1531 Aladdin IV"),
+ DEVICE( AL, AL_M1533, "M1533 Aladdin IV"),
DEVICE( AL, AL_M4803, "M4803"),
+ DEVICE( AL, AL_M5219, "M5219"),
+ DEVICE( AL, AL_M5229, "M5229 TXpro"),
DEVICE( NEOMAGIC, NEOMAGIC_MAGICGRAPH_NM2070, "Magicgraph NM2070"),
DEVICE( NEOMAGIC, NEOMAGIC_MAGICGRAPH_128V, "MagicGraph 128V"),
+ DEVICE( NEOMAGIC, NEOMAGIC_MAGICGRAPH_128ZV, "MagicGraph 128ZV"),
DEVICE( ASP, ASP_ABP940, "ABP940"),
DEVICE( ASP, ASP_ABP940U, "ABP940U"),
DEVICE( CERN, CERN_SPSB_PMC, "STAR/RD24 SCI-PCI (PMC)"),
@@ -232,12 +249,16 @@ struct pci_dev_info dev_info[] = {
DEVICE( INIT, INIT_320P, "320 P"),
DEVICE( VIA, VIA_82C505, "VT 82C505"),
DEVICE( VIA, VIA_82C561, "VT 82C561"),
- DEVICE( VIA, VIA_82C586_1, "VT 82C586 Apollo VP-1"),
+ DEVICE( VIA, VIA_82C586_1, "VT 82C586 Apollo IDE"),
DEVICE( VIA, VIA_82C576, "VT 82C576 3V"),
- DEVICE( VIA, VIA_82C585, "VT 82C585VP Apollo VP-1"),
- DEVICE( VIA, VIA_82C586, "VT 82C586 Apollo VP-1"),
- DEVICE( VIA, VIA_82C416, "VT 82C416MV"),
+ DEVICE( VIA, VIA_82C585, "VT 82C585 Apollo VP1/VPX"),
+ DEVICE( VIA, VIA_82C586_0, "VT 82C586 Apollo ISA"),
+ DEVICE( VIA, VIA_82C595, "VT 82C595 Apollo VP2"),
DEVICE( VIA, VIA_82C926, "VT 82C926 Amazon"),
+ DEVICE( VIA, VIA_82C416, "VT 82C416MV"),
+ DEVICE( VIA, VIA_82C595_97, "VT 82C595 Apollo VP2/97"),
+ DEVICE( VIA, VIA_82C586_2, "VT 82C586 Apollo USB"),
+ DEVICE( VIA, VIA_82C586_3, "VT 82C586B Apollo ACPI"),
DEVICE( VORTEX, VORTEX_GDT60x0, "GDT 60x0"),
DEVICE( VORTEX, VORTEX_GDT6000B,"GDT 6000b"),
DEVICE( VORTEX, VORTEX_GDT6x10, "GDT 6110/6510"),
@@ -252,6 +273,24 @@ struct pci_dev_info dev_info[] = {
DEVICE( VORTEX, VORTEX_GDT6x25, "GDT 6125/6525"),
DEVICE( VORTEX, VORTEX_GDT6535, "GDT 6535"),
DEVICE( VORTEX, VORTEX_GDT6555, "GDT 6555"),
+ DEVICE( VORTEX, VORTEX_GDT6x17RP,"GDT 6117RP/6517RP"),
+ DEVICE( VORTEX, VORTEX_GDT6x27RP,"GDT 6127RP/6527RP"),
+ DEVICE( VORTEX, VORTEX_GDT6537RP,"GDT 6537RP"),
+ DEVICE( VORTEX, VORTEX_GDT6557RP,"GDT 6557RP"),
+ DEVICE( VORTEX, VORTEX_GDT6x11RP,"GDT 6111RP/6511RP"),
+ DEVICE( VORTEX, VORTEX_GDT6x21RP,"GDT 6121RP/6521RP"),
+ DEVICE( VORTEX, VORTEX_GDT6x17RP1,"GDT 6117RP1/6517RP1"),
+ DEVICE( VORTEX, VORTEX_GDT6x27RP1,"GDT 6127RP1/6527RP1"),
+ DEVICE( VORTEX, VORTEX_GDT6537RP1,"GDT 6537RP1"),
+ DEVICE( VORTEX, VORTEX_GDT6557RP1,"GDT 6557RP1"),
+ DEVICE( VORTEX, VORTEX_GDT6x11RP1,"GDT 6111RP1/6511RP1"),
+ DEVICE( VORTEX, VORTEX_GDT6x21RP1,"GDT 6121RP1/6521RP1"),
+ DEVICE( VORTEX, VORTEX_GDT6x17RP2,"GDT 6117RP2/6517RP2"),
+ DEVICE( VORTEX, VORTEX_GDT6x27RP2,"GDT 6127RP2/6527RP2"),
+ DEVICE( VORTEX, VORTEX_GDT6537RP2,"GDT 6537RP2"),
+ DEVICE( VORTEX, VORTEX_GDT6557RP2,"GDT 6557RP2"),
+ DEVICE( VORTEX, VORTEX_GDT6x11RP2,"GDT 6111RP2/6511RP2"),
+ DEVICE( VORTEX, VORTEX_GDT6x21RP2,"GDT 6121RP2/6521RP2"),
DEVICE( EF, EF_ATM_FPGA, "155P-MF1 (FPGA)"),
DEVICE( EF, EF_ATM_ASIC, "155P-MF1 (ASIC)"),
DEVICE( FORE, FORE_PCA200PC, "PCA-200PC"),
@@ -261,6 +300,7 @@ struct pci_dev_info dev_info[] = {
DEVICE( PLX, PLX_9060, "PCI9060 i960 bridge"),
DEVICE( ALLIANCE, ALLIANCE_PROMOTIO, "Promotion-6410"),
DEVICE( ALLIANCE, ALLIANCE_PROVIDEO, "Provideo"),
+ DEVICE( ALLIANCE, ALLIANCE_AT24, "AT24"),
DEVICE( VMIC, VMIC_VME, "VMIVME-7587"),
DEVICE( DIGI, DIGI_RIGHTSWITCH, "RightSwitch SE-6"),
DEVICE( MUTECH, MUTECH_MV1000, "MV-1000"),
@@ -278,6 +318,7 @@ struct pci_dev_info dev_info[] = {
DEVICE( IKON, IKON_10117, "10117 Greensheet"),
DEVICE( ZORAN, ZORAN_36057, "ZR36057"),
DEVICE( ZORAN, ZORAN_36120, "ZR36120"),
+ DEVICE( COMPEX, COMPEX_ENET100VG4, "Readylink ENET100-VG4"),
DEVICE( COMPEX, COMPEX_RL2000, "ReadyLink 2000"),
DEVICE( RP, RP8OCTA, "RocketPort 8 Oct"),
DEVICE( RP, RP8INTF, "RocketPort 8 Intf"),
@@ -294,6 +335,9 @@ struct pci_dev_info dev_info[] = {
DEVICE( OPTIBASE, OPTIBASE_VPLEX, "VideoPlex"),
DEVICE( OPTIBASE, OPTIBASE_VPLEXCC,"VideoPlex CC"),
DEVICE( OPTIBASE, OPTIBASE_VQUEST,"VideoQuest"),
+ DEVICE( ENSONIQ, ENSONIQ_AUDIOPCI,"AudioPCI"),
+ DEVICE( PICTUREL, PICTUREL_PCIVST,"PCIVST"),
+ DEVICE( NVIDIA, NVIDIA_RIVA128, "Riva 128"),
DEVICE( SYMPHONY, SYMPHONY_101, "82C101"),
DEVICE( TEKRAM, TEKRAM_DC290, "DC-290"),
DEVICE( 3DLABS, 3DLABS_300SX, "GLINT 300SX"),
@@ -302,6 +346,8 @@ struct pci_dev_info dev_info[] = {
DEVICE( 3DLABS, 3DLABS_PERMEDIA,"PERMEDIA"),
DEVICE( AVANCE, AVANCE_ALG2064, "ALG2064i"),
DEVICE( AVANCE, AVANCE_2302, "ALG-2302"),
+ DEVICE( NETVIN, NETVIN_NV5000SC,"NV5000"),
+ DEVICE( S3, S3_PLATO_PXS, "PLATO/PX (system)"),
DEVICE( S3, S3_ViRGE, "ViRGE"),
DEVICE( S3, S3_TRIO, "Trio32/Trio64"),
DEVICE( S3, S3_AURORA64VP, "Aurora64V+"),
@@ -314,9 +360,10 @@ struct pci_dev_info dev_info[] = {
DEVICE( S3, S3_964_1, "Vision 964-P"),
DEVICE( S3, S3_964_2, "Vision 964-P"),
DEVICE( S3, S3_968, "Vision 968"),
- DEVICE( S3, S3_TRIO64V2, "Trio64V2"),
- DEVICE( S3, S3_PLATO_PXG, "Plato"),
- DEVICE( S3, S3_ViRGE_DXGX, "ViRGE/DX"),
+ DEVICE( S3, S3_TRIO64V2, "Trio64V2/DX or /GX"),
+ DEVICE( S3, S3_PLATO_PXG, "PLATO/PX (graphics)"),
+ DEVICE( S3, S3_ViRGE_DXGX, "ViRGE/DX or /GX"),
+ DEVICE( S3, S3_ViRGE_GX2, "ViRGE/GX2"),
DEVICE( INTEL, INTEL_82375, "82375EB"),
BRIDGE( INTEL, INTEL_82424, "82424ZX Saturn", 0x00),
DEVICE( INTEL, INTEL_82378, "82378IB"),
@@ -340,12 +387,12 @@ struct pci_dev_info dev_info[] = {
DEVICE( INTEL, INTEL_82371SB_2,"82371SB Natoma/Triton II PIIX3"),
DEVICE( INTEL, INTEL_82437VX, "82437VX Triton II"),
DEVICE( INTEL, INTEL_82439TX, "82439TX"),
- DEVICE( INTEL, INTEL_82371AB_0,"82371AB PIIX4"),
- DEVICE( INTEL, INTEL_82371AB, "82371AB 430TX PIIX4"),
- DEVICE( INTEL, INTEL_82371AB_2,"82371AB PIIX4"),
- DEVICE( INTEL, INTEL_82371AB_3,"82371AB PIIX4 Power Management"),
+ DEVICE( INTEL, INTEL_82371AB_0,"82371AB PIIX4 ISA"),
+ DEVICE( INTEL, INTEL_82371AB, "82371AB PIIX4 IDE"),
+ DEVICE( INTEL, INTEL_82371AB_2,"82371AB PIIX4 USB"),
+ DEVICE( INTEL, INTEL_82371AB_3,"82371AB PIIX4 ACPI"),
DEVICE( INTEL, INTEL_P6, "Orion P6"),
- DEVICE( INTEL, INTEL_P6_2, "82450GX Orion P6"),
+ DEVICE( INTEL, INTEL_82450GX, "82450GX Orion P6"),
DEVICE( KTI, KTI_ET32P2, "ET32P2"),
DEVICE( ADAPTEC, ADAPTEC_7850, "AIC-7850"),
DEVICE( ADAPTEC, ADAPTEC_7855, "AIC-7855"),
@@ -578,6 +625,7 @@ const char *pci_strvendor(unsigned int vendor)
case PCI_VENDOR_ID_MATROX: return "Matrox";
case PCI_VENDOR_ID_CT: return "Chips & Technologies";
case PCI_VENDOR_ID_MIRO: return "Miro";
+ case PCI_VENDOR_ID_NEC: return "NEC";
case PCI_VENDOR_ID_FD: return "Future Domain";
case PCI_VENDOR_ID_SI: return "Silicon Integrated Systems";
case PCI_VENDOR_ID_HP: return "Hewlett Packard";
@@ -591,10 +639,11 @@ const char *pci_strvendor(unsigned int vendor)
case PCI_VENDOR_ID_WINBOND2: return "Winbond";
case PCI_VENDOR_ID_MOTOROLA: return "Motorola";
case PCI_VENDOR_ID_PROMISE: return "Promise Technology";
- case PCI_VENDOR_ID_APPLE: return "Apple";
case PCI_VENDOR_ID_N9: return "Number Nine";
case PCI_VENDOR_ID_UMC: return "UMC";
case PCI_VENDOR_ID_X: return "X TECHNOLOGY";
+ case PCI_VENDOR_ID_PICOP: return "PicoPower";
+ case PCI_VENDOR_ID_APPLE: return "Apple";
case PCI_VENDOR_ID_NEXGEN: return "Nexgen";
case PCI_VENDOR_ID_QLOGIC: return "Q Logic";
case PCI_VENDOR_ID_LEADTEK: return "Leadtek Research";
@@ -651,10 +700,14 @@ const char *pci_strvendor(unsigned int vendor)
case PCI_VENDOR_ID_3DFX: return "3Dfx";
case PCI_VENDOR_ID_SIGMADES: return "Sigma Designs";
case PCI_VENDOR_ID_OPTIBASE: return "Optibase";
+ case PCI_VENDOR_ID_ENSONIQ: return "Ensoniq";
+ case PCI_VENDOR_ID_PICTUREL: return "Picture Elements";
+ case PCI_VENDOR_ID_NVIDIA: return "NVidia/SGS Thomson";
case PCI_VENDOR_ID_SYMPHONY: return "Symphony";
case PCI_VENDOR_ID_TEKRAM: return "Tekram";
case PCI_VENDOR_ID_3DLABS: return "3Dlabs";
case PCI_VENDOR_ID_AVANCE: return "Avance";
+ case PCI_VENDOR_ID_NETVIN: return "NetVin";
case PCI_VENDOR_ID_S3: return "S3 Inc.";
case PCI_VENDOR_ID_INTEL: return "Intel";
case PCI_VENDOR_ID_KTI: return "KTI";
@@ -693,10 +746,10 @@ const char *pcibios_strerror(int error)
case PCIBIOS_BAD_REGISTER_NUMBER:
return "BAD_REGISTER_NUMBER";
- case PCIBIOS_SET_FAILED:
+ case PCIBIOS_SET_FAILED:
return "SET_FAILED";
- case PCIBIOS_BUFFER_TOO_SMALL:
+ case PCIBIOS_BUFFER_TOO_SMALL:
return "BUFFER_TOO_SMALL";
default:
diff --git a/drivers/sbus/audio/audio.c b/drivers/sbus/audio/audio.c
index ded93c8e6..ae77b1455 100644
--- a/drivers/sbus/audio/audio.c
+++ b/drivers/sbus/audio/audio.c
@@ -183,7 +183,7 @@ static int sparcaudio_write(struct inode * inode, struct file * file,
/* Check to make sure that an output buffer is available. */
if (driver->output_count == driver->num_output_buffers) {
interruptible_sleep_on(&driver->output_write_wait);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return bytes_written > 0 ? bytes_written : -EINTR;
}
@@ -226,7 +226,7 @@ static int sparcaudio_ioctl(struct inode * inode, struct file * file,
case AUDIO_DRAIN:
if (driver->output_count > 0) {
interruptible_sleep_on(&driver->output_drain_wait);
- retval = (current->signal & ~current->blocked) ? -EINTR : 0;
+ retval = signal_pending(current) ? -EINTR : 0;
}
break;
@@ -270,7 +270,7 @@ static int sparcaudio_open(struct inode * inode, struct file * file)
return -EBUSY;
interruptible_sleep_on(&driver->open_wait);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -EINTR;
goto retry_open;
}
@@ -279,7 +279,7 @@ static int sparcaudio_open(struct inode * inode, struct file * file)
return -EBUSY;
interruptible_sleep_on(&driver->open_wait);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -EINTR;
goto retry_open;
}
diff --git a/drivers/sbus/char/pcikbd.c b/drivers/sbus/char/pcikbd.c
index 86f527027..8d90f85d6 100644
--- a/drivers/sbus/char/pcikbd.c
+++ b/drivers/sbus/char/pcikbd.c
@@ -740,7 +740,7 @@ static long read_aux(struct inode * inode, struct file * file,
add_wait_queue(&queue->proc_list, &wait);
repeat:
current->state = TASK_INTERRUPTIBLE;
- if (queue_empty() && !(current->signal & ~current->blocked)) {
+ if (queue_empty() && !signal_pending(current)) {
schedule();
goto repeat;
}
@@ -757,7 +757,7 @@ repeat:
inode->i_atime = CURRENT_TIME;
return count-i;
}
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -ERESTARTSYS;
return 0;
}
diff --git a/drivers/sbus/char/sab82532.c b/drivers/sbus/char/sab82532.c
index 775fbbe5a..2134d3d75 100644
--- a/drivers/sbus/char/sab82532.c
+++ b/drivers/sbus/char/sab82532.c
@@ -1247,11 +1247,11 @@ static int sab82532_ioctl(struct tty_struct *tty, struct file * file,
if (retval)
return retval;
tty_wait_until_sent(tty, 0);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -EINTR;
if (!arg) {
send_break(info, HZ/4); /* 1/4 second */
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -EINTR;
}
return 0;
@@ -1260,10 +1260,10 @@ static int sab82532_ioctl(struct tty_struct *tty, struct file * file,
if (retval)
return retval;
tty_wait_until_sent(tty, 0);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -EINTR;
send_break(info, arg ? arg*(HZ/10) : HZ/4);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -EINTR;
return 0;
case TIOCSBRK:
@@ -1325,7 +1325,7 @@ static int sab82532_ioctl(struct tty_struct *tty, struct file * file,
while (1) {
interruptible_sleep_on(&info->delta_msr_wait);
/* see if a signal did it */
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -ERESTARTSYS;
cli();
cnow = info->icount; /* atomic copy */
@@ -1699,7 +1699,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
!(info->flags & ASYNC_CLOSING) &&
(do_clocal || !(info->regs->r.vstr & SAB82532_VSTR_CD)))
break;
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
diff --git a/drivers/sbus/char/sunkbd.c b/drivers/sbus/char/sunkbd.c
index dd4d933d9..1340e3f7c 100644
--- a/drivers/sbus/char/sunkbd.c
+++ b/drivers/sbus/char/sunkbd.c
@@ -1256,7 +1256,7 @@ kbd_read (struct inode *inode, struct file *f, char *buffer, unsigned long count
if (f->f_flags & O_NONBLOCK)
return -EWOULDBLOCK;
add_wait_queue (&kbd_wait, &wait);
- while (kbd_head == kbd_tail && !(current->signal & ~current->blocked)){
+ while (kbd_head == kbd_tail && !signal_pending(current)) {
current->state = TASK_INTERRUPTIBLE;
schedule ();
}
diff --git a/drivers/sbus/char/sunmouse.c b/drivers/sbus/char/sunmouse.c
index 9f5bbb5b6..2f8f8b3c9 100644
--- a/drivers/sbus/char/sunmouse.c
+++ b/drivers/sbus/char/sunmouse.c
@@ -367,7 +367,7 @@ sun_mouse_read(struct inode *inode, struct file *file, char *buffer,
if (file->f_flags & O_NONBLOCK)
return -EWOULDBLOCK;
add_wait_queue (&sunmouse.proc_list, &wait);
- while (queue_empty () && !(current->signal & ~current->blocked)){
+ while (queue_empty () && !signal_pending(current)) {
current->state = TASK_INTERRUPTIBLE;
schedule ();
}
@@ -414,7 +414,7 @@ sun_mouse_read(struct inode *inode, struct file *file, char *buffer,
return count-c;
}
/* Only called if nothing was sent */
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -ERESTARTSYS;
return 0;
}
diff --git a/drivers/sbus/char/zs.c b/drivers/sbus/char/zs.c
index 3d6759a7c..8875f7dc7 100644
--- a/drivers/sbus/char/zs.c
+++ b/drivers/sbus/char/zs.c
@@ -1775,7 +1775,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
!(info->flags & ZILOG_CLOSING) &&
(do_clocal || (DCD & r0)))
break;
- if (current->signal & ~current->blocked) {
+ if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
diff --git a/drivers/scsi/BusLogic.h b/drivers/scsi/BusLogic.h
index 515e84b64..87e44e758 100644
--- a/drivers/scsi/BusLogic.h
+++ b/drivers/scsi/BusLogic.h
@@ -1529,7 +1529,12 @@ static inline void BusLogic_Delay(int Seconds)
unsigned long ProcessorFlags;
save_flags(ProcessorFlags);
sti();
- while (--Seconds >= 0) udelay(1000000);
+ while (--Seconds >= 0) {
+ int i = 1000;
+ do {
+ udelay(1000);
+ } while (--i);
+ }
restore_flags(ProcessorFlags);
}
diff --git a/drivers/scsi/ChangeLog.ncr53c8xx b/drivers/scsi/ChangeLog.ncr53c8xx
index ecfd09676..3a110ce40 100644
--- a/drivers/scsi/ChangeLog.ncr53c8xx
+++ b/drivers/scsi/ChangeLog.ncr53c8xx
@@ -1,4 +1,24 @@
-Thu Aug 23 23:43 1997 Gerard Roudier (groudier@club-internet.fr)
+Sat Sep 20 21:00 1997 Gerard Roudier (groudier@club-internet.fr)
+ * revision 2.5c
+ - Several PCI configuration registers fix-ups for powerpc.
+ (Patch sent by Cort).
+
+Thu Aug 28 10:00 1997 Gerard Roudier (groudier@club-internet.fr)
+ * revision 2.5b
+ - Add 'ncr53c8xx' char pointer variable. This variable allows to
+ pass a boot command to the driver when it is loaded as a module.
+ Option separator is ' ' instead of ','. Example:
+ insmod <mod_path>/ncr53c8xx.o ncr53c8xx='verb:2 sync:0 specf:n'
+ - Always use 'driver_setup.settle_delay' for internal resets.
+ 2 seconds hardcoded is sometimes too short. Suggested by Richard W.
+ This delay may be shortenned in order to avoid spurious timeouts.
+ - Fix release module stuff that failed for more than 1 controller.
+ - For linux versions > 1.3.70, trust the 'dev_id' parameter passed
+ to the interrupt handler (dev_id = struct ncb *).
+ - Fix up in 'ncr_log_hard_error()' when the DSP points outside scripts.
+ Suggested by Stefan Esser.
+
+Tue Aug 23 23:43 1997 Gerard Roudier (groudier@club-internet.fr)
* revision 2.5a
- Update Configure.help for inclusion in linux-2.1.51/2/3
- Use BASE_2 address from PCI config space instead of some
diff --git a/drivers/scsi/Config.in b/drivers/scsi/Config.in
index de7a65d6c..3d3ee5695 100644
--- a/drivers/scsi/Config.in
+++ b/drivers/scsi/Config.in
@@ -49,6 +49,7 @@ dep_tristate 'EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) supp
int ' maximum number of queued commands' CONFIG_SCSI_EATA_MAX_TAGS 16
fi
dep_tristate 'Future Domain 16xx SCSI/AHA 2920 support' CONFIG_SCSI_FUTURE_DOMAIN $CONFIG_SCSI
+dep_tristate 'GDT SCSI Disk Array Controller support' CONFIG_SCSI_GDTH $CONFIG_SCSI
dep_tristate 'Generic NCR5380/53c400 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 $CONFIG_SCSI
if [ "$CONFIG_SCSI_GENERIC_NCR5380" != "n" ]; then
bool ' Enable NCR53c400 extensions' CONFIG_SCSI_GENERIC_NCR53C400
@@ -82,16 +83,21 @@ if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_NCR53C7xx" != "y" ]; then
fi
fi
if [ "$CONFIG_MCA" = "y" ]; then
- dep_tristate 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA $CONFIG_SCSI
+ dep_tristate 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA $CONFIG_SCSI
+ if [ "$CONFIG_SCSI_IBMMCA" != "n" ]; then
+ bool ' reset SCSI-devices while booting' SCSI_IBMMCA_DEV_RESET
+ fi
fi
if [ "$CONFIG_PARPORT" != "n" ]; then
dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI $CONFIG_PARPORT
if [ "$CONFIG_SCSI_PPA" != "n" ]; then
int ' Pedantic EPP-checking' CONFIG_SCSI_PPA_HAVE_PEDANTIC 2 0 3
- int ' EPP timeout' CONFIG_SCSI_PPA_EPP_TIME 128
fi
fi
dep_tristate 'PAS16 SCSI support' CONFIG_SCSI_PAS16 $CONFIG_SCSI
+dep_tristate 'PCI2000 support' CONFIG_SCSI_PCI2000 $CONFIG_SCSI
+dep_tristate 'PCI2220i support' CONFIG_SCSI_PCI2220I $CONFIG_SCSI
+dep_tristate 'PSI240i support' CONFIG_SCSI_PSI240I $CONFIG_SCSI
dep_tristate 'Qlogic FAS SCSI support' CONFIG_SCSI_QLOGIC_FAS $CONFIG_SCSI
if [ "$CONFIG_PCI" = "y" ]; then
dep_tristate 'Qlogic ISP SCSI support' CONFIG_SCSI_QLOGIC_ISP $CONFIG_SCSI
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 9fb808309..eefb74fe4 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -15,6 +15,7 @@ MOD_LIST_NAME := SCSI_MODULES
SCSI_SRCS = $(wildcard $(L_OBJS:%.o=%.c))
AHA152X = -DDEBUG_AHA152X -DAUTOCONF
+GDTH = #-DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS
.SUFFIXES:
.SUFFIXES: .c .o .h .a
@@ -67,12 +68,8 @@ else
endif
endif
-ifeq ($(CONFIG_BLK_DEV_SR_VENDOR),y)
-SR_VENDOR = sr_vendor.o
-endif
-
ifeq ($(CONFIG_BLK_DEV_SR),y)
-L_OBJS += sr.o sr_ioctl.o $(SR_VENDOR)
+L_OBJS += sr.o sr_ioctl.o sr_vendor.o
else
ifeq ($(CONFIG_BLK_DEV_SR),m)
M_OBJS += sr_mod.o
@@ -95,6 +92,30 @@ else
endif
endif
+ifeq ($(CONFIG_SCSI_PCI2000),y)
+L_OBJS += pci2000.o
+else
+ ifeq ($(CONFIG_SCSI_PCI2000),m)
+ M_OBJS += pci2000.o
+ endif
+endif
+
+ifeq ($(CONFIG_SCSI_PCI2220I),y)
+L_OBJS += pci2220i.o
+else
+ ifeq ($(CONFIG_SCSI_PCI2220I),m)
+ M_OBJS += pci2220i.o
+ endif
+endif
+
+ifeq ($(CONFIG_SCSI_PSI240I),y)
+L_OBJS += psi240i.o
+else
+ ifeq ($(CONFIG_SCSI_PSI240I),m)
+ M_OBJS += psi240i.o
+ endif
+endif
+
ifeq ($(CONFIG_A4000T_SCSI),y)
L_OBJS += amiga7xx.o 53c7xx.o
else
@@ -297,6 +318,14 @@ else
endif
endif
+ifeq ($(CONFIG_SCSI_GDTH),y)
+L_OBJS += gdth.o
+else
+ ifeq ($(CONFIG_SCSI_GDTH),m)
+ M_OBJS += gdth.o
+ endif
+endif
+
ifeq ($(CONFIG_SCSI_DEBUG),y)
L_OBJS += scsi_debug.o
else
@@ -445,6 +474,9 @@ BusLogic.o: BusLogic.c FlashPoint.c
aha152x.o: aha152x.c
$(CC) $(CFLAGS) $(AHA152X) -c aha152x.c
+gdth.o: gdth.c gdth.h gdth_proc.c gdth_proc.h
+ $(CC) $(CFLAGS) $(GDTH) -c gdth.c
+
aic7xxx.o: aic7xxx.c aic7xxx_seq.h aic7xxx_reg.h
$(CC) $(CFLAGS) -c -o $@ aic7xxx.c
@@ -469,8 +501,8 @@ scsi_mod.o: $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o \
scsicam.o scsi_proc.o
$(LD) $(LD_RFLAG) -r -o $@ $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o scsicam.o scsi_proc.o
-sr_mod.o: sr.o sr_ioctl.o $(SR_VENDOR)
- $(LD) $(LD_RFLAG) -r -o $@ sr.o sr_ioctl.o $(SR_VENDOR)
+sr_mod.o: sr.o sr_ioctl.o sr_vendor.o
+ $(LD) $(LD_RFLAG) -r -o $@ sr.o sr_ioctl.o sr_vendor.o
sd_mod.o: sd.o sd_ioctl.o
$(LD) $(LD_RFLAG) -r -o $@ sd.o sd_ioctl.o
diff --git a/drivers/scsi/fdomain.c b/drivers/scsi/fdomain.c
index 43599d806..2f59d69f3 100644
--- a/drivers/scsi/fdomain.c
+++ b/drivers/scsi/fdomain.c
@@ -470,6 +470,7 @@ struct signature {
{ "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 },
diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c
new file mode 100644
index 000000000..77a9abacd
--- /dev/null
+++ b/drivers/scsi/gdth.c
@@ -0,0 +1,3387 @@
+/************************************************************************
+ * GDT ISA/EISA/PCI Disk Array Controller driver for Linux *
+ * *
+ * gdth.c *
+ * Copyright (C) 1995-97 ICP vortex Computersysteme GmbH, Achim Leubner *
+ * *
+ * <achim@vortex.de> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published *
+ * by the Free Software Foundation; either version 2 of the License, *
+ * or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this kernel; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ * *
+ * Tested with Linux 1.2.13, ..., 2.1.61 *
+ * *
+ * $Log: gdth.c,v $
+ * 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(), readb(), writeb(), ...
+ * register_reboot_notifier() to get a notify on shutdown 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
+ *
+ *
+ * $Id: gdth.c,v 1.10 1997/10/31 12:29:57 achim Exp $
+ ************************************************************************/
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/in.h>
+#include <linux/proc_fs.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#if LINUX_VERSION_CODE >= 0x020100
+#include <linux/reboot.h>
+#endif
+
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#if LINUX_VERSION_CODE >= 0x010300
+#include <linux/blk.h>
+#else
+#include "../block/blk.h"
+#endif
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+
+#include "gdth.h"
+
+#if LINUX_VERSION_CODE >= 0x010346
+static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs);
+#else
+static void gdth_interrupt(int irq,struct pt_regs *regs);
+#endif
+static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp);
+static int gdth_async_event(int hanum,int service);
+
+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,unchar b);
+static gdth_evt_str *gdth_store_event(ushort source, ushort idx,
+ gdth_evt_data *evt);
+static int gdth_read_event(int handle, gdth_evt_str *estr);
+static void gdth_readapp_event(unchar application, gdth_evt_str *estr);
+static void gdth_clear_events(void);
+
+static void gdth_copy_internal_data(Scsi_Cmnd *scp,char *buffer,ushort count);
+static int gdth_internal_cache_cmd(int hanum,Scsi_Cmnd *scp,
+ unchar b,ulong *flags);
+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(ulong bios_adr);
+static int gdth_search_pci(ushort device_id,ushort index,gdth_pci_str *pcistr);
+static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha);
+static int gdth_init_isa(ulong 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,ulong time);
+static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong p1,
+ ulong p2,ulong p3);
+static int gdth_search_drives(int hanum);
+
+static void *gdth_mmap(ulong paddr, ulong size);
+static void gdth_munmap(void *addr);
+
+static const char *gdth_ctr_name(int hanum);
+#if LINUX_VERSION_CODE >= 0x020100
+static int gdth_halt(struct notifier_block *nb, ulong event, void *buf);
+#else
+void gdth_halt(void);
+#endif
+
+#ifdef DEBUG_GDTH
+static unchar DebugState = DEBUG_GDTH;
+extern int sys_syslog(int,char*,int);
+#define LOGEN sys_syslog(7,NULL,0);
+#define WAITSEC(a) {ulong idx; for(idx=0;idx<a*1000L;++idx) udelay(1000);}
+
+#ifdef SLOWMOTION_GDTH
+#define SLOWM WAITSEC(2)
+#undef INIT_RETRIES
+#undef INIT_TIMEOUT
+#undef POLL_TIMEOUT
+#define INIT_RETRIES 15
+#define INIT_TIMEOUT 150
+#define POLL_TIMEOUT 150
+#else
+#define SLOWM
+#endif
+
+#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; SLOWM}}
+#define TRACE2(a) {if (DebugState==1 || DebugState==2) {ser_printk a; SLOWM}}
+#define TRACE3(a) {if (DebugState!=0) {ser_printk a; SLOWM}}
+
+#else /* !__SERIAL__ */
+#define TRACE(a) {if (DebugState==1) {LOGEN;printk a; SLOWM}}
+#define TRACE2(a) {if (DebugState==1 || DebugState==2) {LOGEN;printk a; SLOWM}}
+#define TRACE3(a) {if (DebugState!=0) {LOGEN;printk a; SLOWM}}
+#endif
+
+#else /* !DEBUG */
+#define TRACE(a)
+#define TRACE2(a)
+#define TRACE3(a)
+#endif
+
+#ifdef GDTH_STATISTICS
+static ulong max_rq=0, max_index=0, max_sg=0;
+static ulong act_ints=0, act_ios=0, act_stats=0, act_rq=0;
+#define GDTH_TIMER 31 /* see linux/timer.h ! */
+#endif
+
+#define PTR2USHORT(a) (ushort)(ulong)(a)
+#define JIFFYWAIT(a) {ulong gdtjf;gdtjf=jiffies+(a);while(gdtjf>jiffies);}
+#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 DMADATA(a) (&((gdth_ext_str *)((a)->hostdata))->dmaext)
+
+
+#if LINUX_VERSION_CODE < 0x010300
+static void *gdth_mmap(ulong paddr, ulong size)
+{
+ if (paddr >= high_memory)
+ return NULL;
+ else
+ return (void *)paddr;
+}
+static void gdth_munmap(void *addr)
+{
+}
+inline ulong virt_to_phys(volatile void *addr)
+{
+ return (ulong)addr;
+}
+inline void *phys_to_virt(ulong addr)
+{
+ return (void *)addr;
+}
+#define virt_to_bus virt_to_phys
+#define bus_to_virt phys_to_virt
+#define readb(addr) (*(volatile unchar *)(addr))
+#define readw(addr) (*(volatile ushort *)(addr))
+#define readl(addr) (*(volatile ulong *)(addr))
+#define writeb(b,addr) (*(volatile unchar *)(addr) = (b))
+#define writew(b,addr) (*(volatile ushort *)(addr) = (b))
+#define writel(b,addr) (*(volatile ulong *)(addr) = (b))
+#define memset_io(a,b,c) memset((void *)(a),(b),(c))
+#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c))
+#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c))
+
+#elif LINUX_VERSION_CODE < 0x020100
+static int remapped = FALSE;
+static void *gdth_mmap(ulong paddr, ulong size)
+{
+ if ( paddr >= high_memory) {
+ remapped = TRUE;
+ return vremap(paddr, size);
+ } else {
+ return (void *)paddr;
+ }
+}
+static void gdth_munmap(void *addr)
+{
+ if (remapped)
+ vfree(addr);
+ remapped = FALSE;
+}
+#else
+static void *gdth_mmap(ulong paddr, ulong size)
+{
+ return ioremap(paddr, size);
+}
+static void gdth_munmap(void *addr)
+{
+ return iounmap(addr);
+}
+#endif
+
+
+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 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 char *gdth_ioctl_tab[4][MAXHA]; /* ioctl buffer */
+static gdth_evt_str ebuffer[MAX_EVENTS]; /* event buffer */
+static int elastidx;
+static int eoldidx;
+
+static struct {
+ Scsi_Cmnd *cmnd; /* pending request */
+ ushort service; /* service */
+} gdth_cmd_tab[GDTH_MAXCMDS][MAXHA]; /* table of pend. requests */
+
+#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,DIN,DUN,DUN,DIN,DIN,DIN,
+ DIN,DIN,DIN,DNO,DIN,DNO,DNO,DIN,DIN,DIN,DIN,DIN,DIN,DIN,DIN,DIN,
+ DIN,DIN,DIN,DIN,DIN,DNO,DUN,DNO,DNO,DNO,DUN,DNO,DIN,DIN,DUN,DUN,
+ DUN,DUN,DUN,DUN,DUN,DIN,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,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,DNO,DNO,DUN,DIN,DNO,DIN,DUN,DNO,DUN,DIN,DIN,
+ DIN,DIN,DIN,DNO,DUN,DIN,DIN,DIN,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,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 params: gdth=<IRQ>
+ *
+ * Where: <IRQ> is any of the valid IRQs for EISA controllers (10,11,12,14)
+ * Sets the IRQ of the GDT3000/3020 EISA controller to this value,
+ * if the IRQ can not automat. detect (controller BIOS disabled)
+ * See gdth_init_eisa()
+ *
+ * You can use the command line gdth=0 to disable the driver
+ */
+static unchar irqs[MAXHA] = {0xff};
+static unchar disable_gdth_scan = FALSE;
+
+/* /proc support */
+#if LINUX_VERSION_CODE >= 0x010300
+#include <linux/stat.h>
+struct proc_dir_entry proc_scsi_gdth = {
+ PROC_SCSI_GDTH, 4, "gdth",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+#include "gdth_proc.h"
+#include "gdth_proc.c"
+#endif
+
+#if LINUX_VERSION_CODE >= 0x020100
+/* notifier block to get a notify on system shutdown/halt/reboot */
+static struct notifier_block gdth_notifier = {
+ gdth_halt, NULL, 0
+};
+#endif
+
+/* controller search and initialization functions */
+
+static int gdth_search_eisa(ushort eisa_adr)
+{
+ ulong 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 gdth_search_isa(ulong bios_adr)
+{
+ void *addr;
+ ulong id;
+
+ TRACE(("gdth_search_isa() bios adr. %lx\n",bios_adr));
+ if ((addr = gdth_mmap(bios_adr+BIOS_ID_OFFS, sizeof(ulong))) != NULL) {
+ id = readl(addr);
+ gdth_munmap(addr);
+ if (id == GDT2_ID) /* GDT2000 */
+ return 1;
+ }
+ return 0;
+}
+
+
+static int gdth_search_pci(ushort device_id,ushort index,gdth_pci_str *pcistr)
+{
+ int error;
+ ulong base0,base1,base2;
+
+ TRACE(("gdth_search_pci() device_id %d, index %d\n",
+ device_id,index));
+
+ if (!pcibios_present())
+ return 0;
+
+ if (pcibios_find_device(PCI_VENDOR_ID_VORTEX,device_id,index,
+ &pcistr->bus,&pcistr->device_fn))
+ return 0;
+
+ /* GDT PCI controller found, now read resources from config space */
+#if LINUX_VERSION_CODE >= 0x010300
+#define GDTH_BASEP (int *)
+#else
+#define GDTH_BASEP
+#endif
+ if ((error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+ PCI_BASE_ADDRESS_0,
+ GDTH_BASEP&base0)) ||
+ (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+ PCI_BASE_ADDRESS_1,
+ GDTH_BASEP&base1)) ||
+ (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+ PCI_BASE_ADDRESS_2,
+ GDTH_BASEP&base2)) ||
+ (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn,
+ PCI_ROM_ADDRESS,
+ GDTH_BASEP&pcistr->bios)) ||
+ (error = pcibios_read_config_byte(pcistr->bus,pcistr->device_fn,
+ PCI_INTERRUPT_LINE,&pcistr->irq))) {
+ printk("GDT-PCI: error %s reading configuration space",
+ pcibios_strerror(error));
+ return -1;
+ }
+
+ pcistr->device_id = device_id;
+ if (device_id <= PCI_DEVICE_ID_VORTEX_GDT6000B || /* GDT6000 or GDT6000B */
+ device_id >= PCI_DEVICE_ID_VORTEX_GDT6x17RP) { /* MPR */
+ if ((base0 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY)
+ return -1;
+ pcistr->dpmem = base0 & PCI_BASE_ADDRESS_MEM_MASK;
+ } else { /* GDT6110, GDT6120, .. */
+ if ((base0 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY ||
+ (base2 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY ||
+ (base1 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_IO)
+ return -1;
+ pcistr->dpmem = base2 & PCI_BASE_ADDRESS_MEM_MASK;
+ pcistr->io_mm = base0 & PCI_BASE_ADDRESS_MEM_MASK;
+ pcistr->io = base1 & PCI_BASE_ADDRESS_IO_MASK;
+ }
+ return 1;
+}
+
+
+static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha)
+{
+ ulong 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;
+ JIFFYWAIT(2);
+ while (inb(eisa_adr+EDOORREG) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-EISA: Initialization error (DEINIT failed)\n");
+ return 0;
+ }
+ udelay(1000);
+ TRACE2(("wait for DEINIT: retries=%ld\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 = (ulong)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->type = GDT_EISA;
+ ha->stype = id;
+ outl(1,eisa_adr+MAILBOXREG+8);
+ outb(0xfe,eisa_adr+LDOORREG);
+ retries = INIT_RETRIES;
+ JIFFYWAIT(2);
+ while (inb(eisa_adr+EDOORREG) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-EISA: Initialization error (get IRQ failed)\n");
+ return 0;
+ }
+ udelay(1000);
+ }
+ 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, check IRQ table from cmd line !\n"));
+ for (i=0,irq_found=FALSE; i<MAXHA && irqs[i]!=0xff; ++i) {
+ if (irqs[i]!=0) {
+ irq_found=TRUE;
+ break;
+ }
+ }
+ if (irq_found) {
+ ha->irq = irqs[i];
+ irqs[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->type = GDT_EISA;
+ ha->stype= id;
+ }
+ return 1;
+}
+
+
+static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha)
+{
+ register gdt2_dpram_str *dp2_ptr;
+ int i;
+ unchar irq_drq,prot_ver;
+ ulong retries;
+
+ TRACE(("gdth_init_isa() bios adr. %lx\n",bios_adr));
+
+ ha->brd = gdth_mmap(bios_adr, sizeof(gdt2_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-ISA: Initialization error (DPMEM remap error)\n");
+ return 0;
+ }
+ dp2_ptr = (gdt2_dpram_str *)ha->brd;
+ writeb(1, &dp2_ptr->io.memlock); /* switch off write protection */
+ /* reset interface area */
+ memset_io((char *)&dp2_ptr->u,0,sizeof(dp2_ptr->u));
+
+ /* disable board interrupts, read DRQ and IRQ */
+ writeb(0xff, &dp2_ptr->io.irqdel);
+ writeb(0x00, &dp2_ptr->io.irqen);
+ writeb(0x00, &dp2_ptr->u.ic.S_Status);
+ writeb(0x00, &dp2_ptr->u.ic.Cmd_Index);
+
+ irq_drq = 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 = 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 */
+ writel(bios_adr, &dp2_ptr->u.ic.S_Info[0]);
+ writeb(0xff, &dp2_ptr->u.ic.S_Cmd_Indx);
+ writeb(0, &dp2_ptr->io.event);
+ retries = INIT_RETRIES;
+ JIFFYWAIT(2);
+ while (readb(&dp2_ptr->u.ic.S_Status) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-ISA: Initialization error (DEINIT failed)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ udelay(1000);
+ }
+ prot_ver = (unchar)readl(&dp2_ptr->u.ic.S_Info[0]);
+ writeb(0, &dp2_ptr->u.ic.Status);
+ writeb(0xff, &dp2_ptr->io.irqdel);
+ if (prot_ver != PROTOCOL_VERSION) {
+ printk("GDT-ISA: Illegal protocol version\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ 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 */
+ writel(0x00, &dp2_ptr->u.ic.S_Info[0]);
+ writel(0x00, &dp2_ptr->u.ic.S_Info[1]);
+ writel(0x01, &dp2_ptr->u.ic.S_Info[2]);
+ writel(0x00, &dp2_ptr->u.ic.S_Info[3]);
+ writeb(0xfe, &dp2_ptr->u.ic.S_Cmd_Indx);
+ writeb(0, &dp2_ptr->io.event);
+ retries = INIT_RETRIES;
+ JIFFYWAIT(2);
+ while (readb(&dp2_ptr->u.ic.S_Status) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-ISA: Initialization error\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ udelay(1000);
+ }
+ writeb(0, &dp2_ptr->u.ic.Status);
+ writeb(0xff, &dp2_ptr->io.irqdel);
+ return 1;
+}
+
+
+static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha)
+{
+ register gdt6_dpram_str *dp6_ptr;
+ register gdt6c_dpram_str *dp6c_ptr;
+ register gdt6m_dpram_str *dp6m_ptr;
+ ulong retries;
+ unchar prot_ver;
+
+ TRACE(("gdth_init_pci()\n"));
+
+ ha->brd_phys = (pcistr->bus << 8) | (pcistr->device_fn & 0xf8);
+ ha->stype = (ulong)pcistr->device_id;
+ ha->irq = pcistr->irq;
+
+ if (ha->stype <= PCI_DEVICE_ID_VORTEX_GDT6000B) { /* GDT6000 or GDT6000B */
+ TRACE2(("init_pci() dpmem %lx irq %d\n",pcistr->dpmem,ha->irq));
+ ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+ return 0;
+ }
+ dp6_ptr = (gdt6_dpram_str *)ha->brd;
+ /* reset interface area */
+ memset_io((char *)&dp6_ptr->u,0,sizeof(dp6_ptr->u));
+ if (readl(&dp6_ptr->u) != 0) {
+ printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ /* disable board interrupts, deinit services */
+ writeb(0xff, &dp6_ptr->io.irqdel);
+ writeb(0x00, &dp6_ptr->io.irqen);;
+ writeb(0x00, &dp6_ptr->u.ic.S_Status);
+ writeb(0x00, &dp6_ptr->u.ic.Cmd_Index);
+
+ writel(pcistr->dpmem, &dp6_ptr->u.ic.S_Info[0]);
+ writeb(0xff, &dp6_ptr->u.ic.S_Cmd_Indx);
+ writeb(0, &dp6_ptr->io.event);
+ retries = INIT_RETRIES;
+ JIFFYWAIT(2);
+ while (readb(&dp6_ptr->u.ic.S_Status) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ udelay(1000);
+ }
+ prot_ver = (unchar)readl(&dp6_ptr->u.ic.S_Info[0]);
+ writeb(0, &dp6_ptr->u.ic.S_Status);
+ writeb(0xff, &dp6_ptr->io.irqdel);
+ if (prot_ver != PROTOCOL_VERSION) {
+ printk("GDT-PCI: Illegal protocol version\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ ha->type = GDT_PCI;
+ ha->ic_all_size = sizeof(dp6_ptr->u);
+
+ /* special command to controller BIOS */
+ writel(0x00, &dp6_ptr->u.ic.S_Info[0]);
+ writel(0x00, &dp6_ptr->u.ic.S_Info[1]);
+ writel(0x01, &dp6_ptr->u.ic.S_Info[2]);
+ writel(0x00, &dp6_ptr->u.ic.S_Info[3]);
+ writeb(0xfe, &dp6_ptr->u.ic.S_Cmd_Indx);
+ writeb(0, &dp6_ptr->io.event);
+ retries = INIT_RETRIES;
+ JIFFYWAIT(2);
+ while (readb(&dp6_ptr->u.ic.S_Status) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ udelay(1000);
+ }
+ writeb(0, &dp6_ptr->u.ic.S_Status);
+ writeb(0xff, &dp6_ptr->io.irqdel);
+
+ } else if (ha->stype <= PCI_DEVICE_ID_VORTEX_GDT6555) { /* GDT6110, GDT6120, .. */
+ ha->plx = (gdt6c_plx_regs *)pcistr->io;
+ TRACE2(("init_pci_new() dpmem %lx io %lx irq %d\n",
+ pcistr->dpmem,(ulong)ha->plx,ha->irq));
+ ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6c_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ dp6c_ptr = (gdt6c_dpram_str *)ha->brd;
+ /* reset interface area */
+ memset_io((char *)&dp6c_ptr->u,0,sizeof(dp6c_ptr->u));
+ if (readl(&dp6c_ptr->u) != 0) {
+ printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ /* disable board interrupts, deinit services */
+ outb(0x00,PTR2USHORT(&ha->plx->control1));
+ outb(0xff,PTR2USHORT(&ha->plx->edoor_reg));
+
+ writeb(0x00, &dp6c_ptr->u.ic.S_Status);
+ writeb(0x00, &dp6c_ptr->u.ic.Cmd_Index);
+
+ writel(pcistr->dpmem, &dp6c_ptr->u.ic.S_Info[0]);
+ writeb(0xff, &dp6c_ptr->u.ic.S_Cmd_Indx);
+
+ outb(1,PTR2USHORT(&ha->plx->ldoor_reg));
+
+ retries = INIT_RETRIES;
+ JIFFYWAIT(2);
+ while (readb(&dp6c_ptr->u.ic.S_Status) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ udelay(1000);
+ }
+ prot_ver = (unchar)readl(&dp6c_ptr->u.ic.S_Info[0]);
+ writeb(0, &dp6c_ptr->u.ic.Status);
+ if (prot_ver != PROTOCOL_VERSION) {
+ printk("GDT-PCI: Illegal protocol version\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ ha->type = GDT_PCINEW;
+ ha->ic_all_size = sizeof(dp6c_ptr->u);
+
+ /* special command to controller BIOS */
+ writel(0x00, &dp6c_ptr->u.ic.S_Info[0]);
+ writel(0x00, &dp6c_ptr->u.ic.S_Info[1]);
+ writel(0x01, &dp6c_ptr->u.ic.S_Info[2]);
+ writel(0x00, &dp6c_ptr->u.ic.S_Info[3]);
+ writeb(0xfe, &dp6c_ptr->u.ic.S_Cmd_Indx);
+
+ outb(1,PTR2USHORT(&ha->plx->ldoor_reg));
+
+ retries = INIT_RETRIES;
+ JIFFYWAIT(2);
+ while (readb(&dp6c_ptr->u.ic.S_Status) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ udelay(1000);
+ }
+ writeb(0, &dp6c_ptr->u.ic.S_Status);
+
+ } else { /* MPR */
+ TRACE2(("init_pci_mpr() dpmem %lx irq %d\n",pcistr->dpmem,ha->irq));
+ ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6m_dpram_str));
+ if (ha->brd == NULL) {
+ printk("GDT-PCI: Initialization error (DPMEM remap error)\n");
+ return 0;
+ }
+
+ dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
+ /* reset interface area */
+ memset_io((char *)&dp6m_ptr->u,0,sizeof(dp6m_ptr->u));
+ if (readl(&dp6m_ptr->u) != 0) {
+ printk("GDT-PCI: Initialization error (DPMEM write error)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ /* disable board interrupts, deinit services */
+ writeb(readb(&dp6m_ptr->i960r.edoor_en_reg) | 4,
+ &dp6m_ptr->i960r.edoor_en_reg);
+ writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+ writeb(0x00, &dp6m_ptr->u.ic.S_Status);
+ writeb(0x00, &dp6m_ptr->u.ic.Cmd_Index);
+
+ writel(pcistr->dpmem, &dp6m_ptr->u.ic.S_Info[0]);
+ writeb(0xff, &dp6m_ptr->u.ic.S_Cmd_Indx);
+ writeb(1, &dp6m_ptr->i960r.ldoor_reg);
+ retries = INIT_RETRIES;
+ JIFFYWAIT(2);
+ while (readb(&dp6m_ptr->u.ic.S_Status) != 0xff) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error (DEINIT failed)\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ udelay(1000);
+ }
+ prot_ver = (unchar)readl(&dp6m_ptr->u.ic.S_Info[0]);
+ writeb(0, &dp6m_ptr->u.ic.S_Status);
+ if (prot_ver != PROTOCOL_VERSION) {
+ printk("GDT-PCI: Illegal protocol version\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+
+ ha->type = GDT_PCIMPR;
+ ha->ic_all_size = sizeof(dp6m_ptr->u);
+
+ /* special command to controller BIOS */
+ writel(0x00, &dp6m_ptr->u.ic.S_Info[0]);
+ writel(0x00, &dp6m_ptr->u.ic.S_Info[1]);
+ writel(0x01, &dp6m_ptr->u.ic.S_Info[2]);
+ writel(0x00, &dp6m_ptr->u.ic.S_Info[3]);
+ writeb(0xfe, &dp6m_ptr->u.ic.S_Cmd_Indx);
+ writeb(1, &dp6m_ptr->i960r.ldoor_reg);
+ retries = INIT_RETRIES;
+ JIFFYWAIT(2);
+ while (readb(&dp6m_ptr->u.ic.S_Status) != 0xfe) {
+ if (--retries == 0) {
+ printk("GDT-PCI: Initialization error\n");
+ gdth_munmap(ha->brd);
+ return 0;
+ }
+ udelay(1000);
+ }
+ writeb(0, &dp6m_ptr->u.ic.S_Status);
+ }
+
+ return 1;
+}
+
+
+/* controller protocol functions */
+
+static void gdth_enable_int(int hanum)
+{
+ gdth_ha_str *ha;
+ ulong flags;
+ gdt2_dpram_str *dp2_ptr;
+ gdt6_dpram_str *dp6_ptr;
+ gdt6m_dpram_str *dp6m_ptr;
+
+ TRACE(("gdth_enable_int() hanum %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ save_flags(flags);
+ cli();
+
+ 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 = (gdt2_dpram_str *)ha->brd;
+ writeb(1, &dp2_ptr->io.irqdel);
+ writeb(0, &dp2_ptr->u.ic.Cmd_Index);
+ writeb(1, &dp2_ptr->io.irqen);
+ } else if (ha->type == GDT_PCI) {
+ dp6_ptr = (gdt6_dpram_str *)ha->brd;
+ writeb(1, &dp6_ptr->io.irqdel);
+ writeb(0, &dp6_ptr->u.ic.Cmd_Index);
+ 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 = (gdt6m_dpram_str *)ha->brd;
+ writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+ writeb(readb(&dp6m_ptr->i960r.edoor_en_reg) & ~4,
+ &dp6m_ptr->i960r.edoor_en_reg);
+ }
+ restore_flags(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 = readb(&((gdt2_dpram_str *)ha->brd)->u.ic.Cmd_Index);
+ else if (ha->type == GDT_PCI)
+ *pIStatus = readb(&((gdt6_dpram_str *)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 = readb(&((gdt6m_dpram_str *)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)readb(&((gdt2_dpram_str *)ha->brd)->u.ic.Sema0);
+ else if (ha->type == GDT_PCI)
+ gdtsema0 = (int)readb(&((gdt6_dpram_str *)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)readb(&((gdt6m_dpram_str *)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 (gdth_cmd_tab[i][hanum].cmnd == UNUSED_CMND) {
+ gdth_cmd_tab[i][hanum].cmnd = ha->pccb->RequestBuffer;
+ gdth_cmd_tab[i][hanum].service = ha->pccb->Service;
+ ha->pccb->CommandIndex = (ulong)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)
+ writeb(1, &((gdt2_dpram_str *)ha->brd)->u.ic.Sema0);
+ else if (ha->type == GDT_PCI)
+ writeb(1, &((gdt6_dpram_str *)ha->brd)->u.ic.Sema0);
+ else if (ha->type == GDT_PCINEW)
+ outb(1, PTR2USHORT(&ha->plx->sema0_reg));
+ else if (ha->type == GDT_PCIMPR)
+ writeb(1, &((gdt6m_dpram_str *)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 *dp6m_ptr;
+ register gdt6c_dpram_str *dp6c_ptr;
+ gdt6_dpram_str *dp6_ptr;
+ gdt2_dpram_str *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 = (gdt2_dpram_str *)ha->brd;
+ writew(dp_offset + DPMEM_COMMAND_OFFSET,
+ &dp2_ptr->u.ic.comm_queue[cmd_no].offset);
+ 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 = (gdt6_dpram_str *)ha->brd;
+ writew(dp_offset + DPMEM_COMMAND_OFFSET,
+ &dp6_ptr->u.ic.comm_queue[cmd_no].offset);
+ 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 = (gdt6c_dpram_str *)ha->brd;
+ writew(dp_offset + DPMEM_COMMAND_OFFSET,
+ &dp6c_ptr->u.ic.comm_queue[cmd_no].offset);
+ 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 = (gdt6m_dpram_str *)ha->brd;
+ writew(dp_offset + DPMEM_COMMAND_OFFSET,
+ &dp6m_ptr->u.ic.comm_queue[cmd_no].offset);
+ 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;
+
+#ifdef GDTH_STATISTICS
+ ulong i,j;
+ for (i=0,j=0; j<GDTH_MAXCMDS; ++j) {
+ if (gdth_cmd_tab[j][hanum].cmnd != UNUSED_CMND)
+ ++i;
+ }
+ if (max_index < i) {
+ max_index = i;
+ TRACE3(("GDT: max_index = %d\n",(ushort)i));
+ }
+#endif
+
+ TRACE(("gdth_release_event() hanum %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ if (ha->pccb->OpCode == GDT_INIT)
+ ha->pccb->Service |= 0x80;
+
+ if (ha->type == GDT_EISA) {
+ outb(ha->pccb->Service, ha->bmic + LDOORREG);
+ if (ha->pccb->OpCode == GDT_INIT) /* store DMA buffer */
+ outl((ulong)ha->pccb, ha->bmic + MAILBOXREG);
+ } else if (ha->type == GDT_ISA)
+ writeb(0, &((gdt2_dpram_str *)ha->brd)->io.event);
+ else if (ha->type == GDT_PCI)
+ writeb(0, &((gdt6_dpram_str *)ha->brd)->io.event);
+ else if (ha->type == GDT_PCINEW)
+ outb(1, PTR2USHORT(&ha->plx->ldoor_reg));
+ else if (ha->type == GDT_PCIMPR)
+ writeb(1, &((gdt6m_dpram_str *)ha->brd)->i960r.ldoor_reg);
+}
+
+
+static int gdth_wait(int hanum,int index,ulong time)
+{
+ gdth_ha_str *ha;
+ int answer_found = FALSE;
+
+ TRACE(("gdth_wait() hanum %d index %d time %ld\n",hanum,index,time));
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ if (index == 0)
+ return 1; /* no wait required */
+
+ gdth_from_wait = TRUE;
+ do {
+#if LINUX_VERSION_CODE >= 0x010346
+ gdth_interrupt((int)ha->irq,NULL,NULL);
+#else
+ gdth_interrupt((int)ha->irq,NULL);
+#endif
+ if (wait_hanum==hanum && wait_index==index) {
+ answer_found = TRUE;
+ break;
+ }
+ udelay(1000);
+ } while (--time);
+ gdth_from_wait = FALSE;
+
+ while (gdth_test_busy(hanum))
+ udelay(1);
+
+ return (answer_found);
+}
+
+
+static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong p1,
+ ulong p2,ulong 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 = p2;
+ cmd_ptr->u.ioctl.param_size = (ushort)p3;
+ cmd_ptr->u.ioctl.p_param = virt_to_bus(ha->pscratch);
+ } else {
+ cmd_ptr->u.cache.DeviceNo = (ushort)p1;
+ cmd_ptr->u.cache.BlockNo = p2;
+ }
+ } else if (service == SCSIRAWSERVICE) {
+ 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 = 0;
+ }
+ ha->cmd_len = sizeof(gdth_cmd_str);
+ ha->cmd_offs_dpmem = 0;
+ ha->cmd_cnt = 0;
+ gdth_copy_command(hanum);
+ gdth_release_event(hanum);
+ JIFFYWAIT(2);
+ 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;
+ udelay(1000);
+ }
+
+ return (ha->status != S_OK ? 0:1);
+}
+
+
+/* search for devices */
+
+static int gdth_search_drives(int hanum)
+{
+ register gdth_ha_str *ha;
+ ushort cdev_cnt,i;
+ unchar b,t,pos_found;
+ ulong drv_cyls, drv_hds, drv_secs;
+ ulong bus_no;
+ gdth_getch_str *chn;
+
+ TRACE(("gdth_search_drives() hanum %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ /* initialize controller services, at first: screen service */
+ if (!gdth_internal_cmd(hanum,SCREENSERVICE,GDT_INIT,0,0,0)) {
+ printk("GDT: Initialization error screen service (code %d)\n",
+ ha->status);
+ return 0;
+ }
+ TRACE2(("gdth_search_drives(): SCREENSERVICE initialized\n"));
+
+ /* initialize cache service */
+ if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) {
+ printk("GDT: Initialization error cache service (code %d)\n",
+ ha->status);
+ return 0;
+ }
+ TRACE2(("gdth_search_drives(): CACHESERVICE initialized\n"));
+ cdev_cnt = (ushort)ha->info;
+
+ /* mount all cache devices */
+ gdth_internal_cmd(hanum,CACHESERVICE,GDT_MOUNT,0xffff,1,0);
+ TRACE2(("gdth_search_drives(): mountall CACHESERVICE OK\n"));
+
+ /* initialize cache service after mountall */
+ if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) {
+ printk("GDT: Initialization error cache service (code %d)\n",
+ ha->status);
+ return 0;
+ }
+ TRACE2(("gdth_search_drives() CACHES. init. after mountall\n"));
+ cdev_cnt = (ushort)ha->info;
+
+ /* detect number of SCSI buses */
+ chn = (gdth_getch_str *)DMADATA(gdth_ctr_tab[hanum]);
+ 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: Error detecting SCSI channel count (0x%x)\n",
+ ha->status);
+ return 0;
+ }
+ break;
+ }
+ if (chn->siop_id < MAXID)
+ ha->id[bus_no][chn->siop_id].type = SIOP_DTYP;
+ }
+ ha->bus_cnt = (unchar)bus_no;
+ TRACE2(("gdth_search_drives() %d SCSI 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: Initialization error cache service (code %d)\n",
+ ha->status);
+ return 0;
+ }
+ ha->cpar = ((gdth_cinfo_str *)DMADATA(gdth_ctr_tab[hanum]))->cpar;
+ TRACE2(("gdth_search_drives() cinfo: vs %lx 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));
+
+ /* initialize raw service */
+ if (!gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_INIT,0,0,0)) {
+ printk("GDT: Initialization error raw service (code %d)\n",
+ ha->status);
+ return 0;
+ }
+ TRACE2(("gdth_search_drives(): RAWSERVICE initialized\n"));
+
+ /* set/get features raw service (scatter/gather) */
+ ha->raw_feat = 0;
+ 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 %ld\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. %ld\n",
+ ha->info));
+ ha->cache_feat = (ushort)ha->info;
+ }
+ }
+
+ /* scanning for raw devices */
+ for (b=0; b<ha->bus_cnt; ++b) {
+ for (t=0; t<MAXID; ++t) {
+ TRACE(("gdth_search_drives() rawd. bus %d id %d\n",b,t));
+ if (ha->id[b][t].type != SIOP_DTYP &&
+ gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_INFO,0,b,t)) {
+ ha->id[b][t].type = RAW_DTYP;
+ }
+ }
+ }
+
+ /* scanning for cache devices */
+ for (i=0; i<cdev_cnt && i<MAX_HDRIVES; ++i) {
+ TRACE(("gdth_search_drives() cachedev. %d\n",i));
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_INFO,i,0,0)) {
+ /* dynamic relation between host drive number and Bus/ID */
+ /* search free position */
+ pos_found = FALSE;
+ for (b=0,t=0; b<ha->bus_cnt; ++b) {
+ for (t=0; t<MAXID; ++t) {
+ if (ha->id[b][t].type == EMPTY_DTYP) {
+ pos_found = TRUE;
+ break;
+ }
+ }
+ if (pos_found)
+ break;
+ }
+ TRACE(("gdth_search_dr() drive %d free pos at bus/id %d/%d\n",
+ i,b,t));
+
+ ha->id[b][t].type = CACHE_DTYP;
+ ha->id[b][t].devtype = 0;
+ ha->id[b][t].size = ha->info;
+ ha->id[b][t].hostdrive = i;
+
+ /* evaluate mapping (sectors per head, heads per cylinder) */
+ ha->id[b][t].size &= ~SECS32;
+ if (ha->info2 == 0) {
+ drv_cyls = ha->id[b][t].size /HEADS/SECS;
+ if (drv_cyls <= MAXCYLS) {
+ drv_hds = HEADS;
+ drv_secs= SECS;
+ } else { /* too high for 64*32 */
+ drv_cyls = ha->id[b][t].size /MEDHEADS/MEDSECS;
+ if (drv_cyls <= MAXCYLS) {
+ drv_hds = MEDHEADS;
+ drv_secs= MEDSECS;
+ } else { /* too high for 127*63 */
+ drv_cyls = ha->id[b][t].size /BIGHEADS/BIGSECS;
+ drv_hds = BIGHEADS;
+ drv_secs= BIGSECS;
+ }
+ }
+ } else {
+ drv_hds = ha->info2 & 0xff;
+ drv_secs = (ha->info2 >> 8) & 0xff;
+ drv_cyls = ha->id[b][t].size /drv_hds/drv_secs;
+ }
+ ha->id[b][t].heads = (unchar)drv_hds;
+ ha->id[b][t].secs = (unchar)drv_secs;
+ /* round size */
+ ha->id[b][t].size = drv_cyls * drv_hds * drv_secs;
+ TRACE2(("gdth_search_dr() cdr. %d size %ld hds %ld scs %ld\n",
+ i,ha->id[b][t].size,drv_hds,drv_secs));
+
+ /* get informations about device */
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_DEVTYPE,i,
+ 0,0)) {
+ TRACE(("gdth_search_dr() cache drive %d devtype %ld\n",
+ i,ha->info));
+ ha->id[b][t].devtype = (ushort)ha->info;
+ }
+ }
+ }
+
+ TRACE(("gdth_search_drives() OK\n"));
+ 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));
+ save_flags(flags);
+ cli();
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ scp->SCp.this_residual = (int)priority;
+ gdth_update_timeout(scp, scp->timeout * 6);
+#if LINUX_VERSION_CODE >= 0x020000
+ b = scp->channel;
+#else
+ b = NUMDATA(nscp->host)->busnum;
+#endif
+ t = scp->target;
+#if LINUX_VERSION_CODE >= 0x010300
+ if (priority >= DEFAULT_PRI && ha->id[b][t].lock) {
+ TRACE2(("gdth_putq(): locked IO -> update_timeout()\n"));
+ scp->SCp.buffers_residual = gdth_update_timeout(scp, 0);
+ }
+#endif
+
+ 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;
+ }
+ restore_flags(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, next_cmd, firsttime;
+ ushort hdrive;
+ ulong flags;
+ int cmd_index;
+
+ TRACE(("gdth_next() hanum %d\n",hanum));
+ save_flags(flags);
+ cli();
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ ha->cmd_cnt = ha->cmd_offs_dpmem = 0;
+ next_cmd = firsttime = 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;
+#if LINUX_VERSION_CODE >= 0x020000
+ b = nscp->channel;
+#else
+ b = NUMDATA(nscp->host)->busnum;
+#endif
+ t = nscp->target;
+ if (nscp->SCp.this_residual < DEFAULT_PRI || !ha->id[b][t].lock) {
+
+ if (firsttime) {
+ if (gdth_test_busy(hanum)) { /* controller busy ? */
+ TRACE(("gdth_next() controller %d busy !\n",hanum));
+ if (!gdth_polling) {
+ restore_flags(flags);
+ return;
+ }
+ while (gdth_test_busy(hanum))
+ udelay(1000);
+ }
+ firsttime = FALSE;
+ }
+
+#if LINUX_VERSION_CODE >= 0x010300
+ if (nscp->done == gdth_scsi_done) {
+ if (!(cmd_index=gdth_special_cmd(hanum,nscp,b)))
+ next_cmd = FALSE;
+ } else
+#endif
+ if (ha->id[b][t].type != CACHE_DTYP) {
+ if (!(cmd_index=gdth_fill_raw_cmd(hanum,nscp,b)))
+ next_cmd = FALSE;
+ } else {
+ hdrive = ha->id[b][t].hostdrive;
+ switch (nscp->cmnd[0]) {
+ case TEST_UNIT_READY:
+ case INQUIRY:
+ case REQUEST_SENSE:
+ case READ_CAPACITY:
+ case VERIFY:
+ case START_STOP:
+ case MODE_SENSE:
+ TRACE2(("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]));
+ gdth_internal_cache_cmd(hanum,nscp,b,&flags);
+ break;
+
+ case ALLOW_MEDIUM_REMOVAL:
+ TRACE2(("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->id[b][t].devtype&1) ) {
+ TRACE2(("Prevent r. nonremov. drive->do nothing\n"));
+ nscp->result = DID_OK << 16;
+ restore_flags( flags );
+ nscp->scsi_done(nscp);
+ save_flags( flags );
+ cli();
+ } else {
+ nscp->cmnd[3] = (ha->id[b][t].devtype&1) ? 1:0;
+ TRACE2(("Prevent/allow r. %d rem. drive %d\n",
+ nscp->cmnd[4],nscp->cmnd[3]));
+ if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,hdrive)))
+ next_cmd = FALSE;
+ }
+ break;
+
+ case READ_6:
+ case WRITE_6:
+ case READ_10:
+ case WRITE_10:
+ if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,hdrive)))
+ next_cmd = FALSE;
+ break;
+
+ default:
+ TRACE2(("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]));
+ printk("GDT: Unknown SCSI command 0x%x to cache service !\n",
+ nscp->cmnd[0]);
+ nscp->result = DID_ABORT << 16;
+ restore_flags( flags );
+ nscp->scsi_done( nscp );
+ save_flags( flags );
+ cli();
+ break;
+ }
+ }
+
+ if (!next_cmd)
+ break;
+ if (nscp == ha->req_first)
+ ha->req_first = pscp = (Scsi_Cmnd *)nscp->SCp.ptr;
+ else
+ pscp->SCp.ptr = nscp->SCp.ptr;
+ if (gdth_polling)
+ break;
+ }
+ }
+
+ if (ha->cmd_cnt > 0) {
+ gdth_release_event(hanum);
+ }
+
+ restore_flags(flags);
+
+ if (gdth_polling && ha->cmd_cnt > 0) {
+ if (!gdth_wait(hanum,cmd_index,POLL_TIMEOUT))
+ printk("GDT: Controller %d: Command %d timed out !\n",
+ hanum,cmd_index);
+ }
+}
+
+static void gdth_copy_internal_data(Scsi_Cmnd *scp,char *buffer,ushort count)
+{
+ ushort cpcount,i;
+ ushort cpsum,cpnow;
+ struct scatterlist *sl;
+
+ cpcount = count<=(ushort)scp->bufflen ? count:(ushort)scp->bufflen;
+ if (scp->use_sg) {
+ sl = (struct scatterlist *)scp->request_buffer;
+ for (i=0,cpsum=0; i<scp->use_sg; ++i,++sl) {
+ 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;
+ memcpy((char*)sl->address,buffer,cpnow);
+ 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,
+ unchar b,ulong *flags)
+{
+ register gdth_ha_str *ha;
+ ushort hdrive;
+ 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->target;
+ hdrive = ha->id[b][t].hostdrive;
+ TRACE(("gdth_internal_cache_cmd() cmd 0x%x hdrive %d\n",
+ scp->cmnd[0],hdrive));
+
+ if (scp->lun !=0)
+ scp->result = DID_BAD_TARGET << 16;
+ else {
+ switch (scp->cmnd[0]) {
+ case TEST_UNIT_READY:
+ case VERIFY:
+ case START_STOP:
+ TRACE2(("Test/Verify/Start hdrive %d\n",hdrive));
+ break;
+
+ case INQUIRY:
+ TRACE2(("Inquiry hdrive %d devtype %d\n",
+ hdrive,ha->id[b][t].devtype));
+ inq.type_qual = (ha->id[b][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 = ha->id[b][t].devtype&1 ? 0x80:0x00;
+ inq.version = 2;
+ inq.resp_aenc = 2;
+ inq.add_length= 32;
+ strcpy(inq.vendor,"ICP ");
+ sprintf(inq.product,"Host Drive #%02d",hdrive);
+ strcpy(inq.revision," ");
+ gdth_copy_internal_data(scp,(char*)&inq,sizeof(gdth_inq_data));
+ break;
+
+ case REQUEST_SENSE:
+ TRACE2(("Request sense hdrive %d\n",hdrive));
+ sd.errorcode = 0x70;
+ sd.segno = 0x00;
+ sd.key = NO_SENSE;
+ sd.info = 0;
+ sd.add_length= 0;
+ gdth_copy_internal_data(scp,(char*)&sd,sizeof(gdth_sense_data));
+ break;
+
+ case MODE_SENSE:
+ TRACE2(("Mode sense hdrive %d\n",hdrive));
+ memset((char*)&mpd,0,sizeof(gdth_modep_data));
+ mpd.hd.data_length = sizeof(gdth_modep_data);
+ mpd.hd.dev_par = (ha->id[b][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(scp,(char*)&mpd,sizeof(gdth_modep_data));
+ break;
+
+ case READ_CAPACITY:
+ TRACE2(("Read capacity hdrive %d\n",hdrive));
+ rdc.last_block_no = ntohl(ha->id[b][t].size-1);
+ rdc.block_length = ntohl(SECTOR_SIZE);
+ gdth_copy_internal_data(scp,(char*)&rdc,sizeof(gdth_rdcap_data));
+ break;
+
+ default:
+ TRACE2(("Internal cache cmd 0x%x unknown\n",scp->cmnd[0]));
+ break;
+ }
+ scp->result = DID_OK << 16;
+ }
+
+ restore_flags(*flags);
+ scp->scsi_done(scp);
+ save_flags(*flags);
+ cli();
+ return 1;
+}
+
+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;
+ ushort i;
+ int cmd_index;
+
+ 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;
+
+ 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 */
+ 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) {
+ if (gdth_write_through)
+ cmdp->OpCode = GDT_WRITE_THR;
+ else
+ cmdp->OpCode = GDT_WRITE;
+ } else {
+ cmdp->OpCode = GDT_READ;
+ }
+ }
+
+ cmdp->BoardNode = LOCALBOARD;
+ cmdp->u.cache.DeviceNo = hdrive;
+
+ if (scp->cmnd[0]==ALLOW_MEDIUM_REMOVAL) {
+ cmdp->u.cache.BlockNo = 1;
+ cmdp->u.cache.sg_canz = 0;
+ } else {
+ if (scp->cmd_len != 6) {
+ cmdp->u.cache.BlockNo = ntohl(*(ulong*)&scp->cmnd[2]);
+ cmdp->u.cache.BlockCnt= (ulong)ntohs(*(ushort*)&scp->cmnd[7]);
+ } else {
+ cmdp->u.cache.BlockNo = ntohl(*(ulong*)&scp->cmnd[0]) & 0x001fffffUL;
+ cmdp->u.cache.BlockCnt= scp->cmnd[4]==0 ? 0x100 : scp->cmnd[4];
+ }
+
+ if (scp->use_sg) {
+ cmdp->u.cache.DestAddr= -1UL;
+ sl = (struct scatterlist *)scp->request_buffer;
+ for (i=0; i<scp->use_sg; ++i,++sl) {
+ cmdp->u.cache.sg_lst[i].sg_ptr = virt_to_bus(sl->address);
+ cmdp->u.cache.sg_lst[i].sg_len = (ulong)sl->length;
+ }
+ cmdp->u.cache.sg_canz = (ulong)i;
+
+#ifdef GDTH_STATISTICS
+ if (max_sg < (ulong)i) {
+ max_sg = (ulong)i;
+ TRACE3(("GDT: max_sg = %d\n",i));
+ }
+#endif
+ if (i<GDTH_MAXSG)
+ cmdp->u.cache.sg_lst[i].sg_len = 0;
+ } else {
+ if (ha->cache_feat & SCATTER_GATHER) {
+ cmdp->u.cache.DestAddr = -1UL;
+ cmdp->u.cache.sg_canz = 1;
+ cmdp->u.cache.sg_lst[0].sg_ptr = virt_to_bus(scp->request_buffer);
+ 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 = virt_to_bus(scp->request_buffer);
+ cmdp->u.cache.sg_canz= 0;
+ }
+ }
+ }
+ TRACE(("cache cmd: addr. %lx sganz %lx sgptr0 %lx sglen0 %lx\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. %ld, blockcnt %ld\n",
+ cmdp->OpCode,cmdp->u.cache.BlockNo,cmdp->u.cache.BlockCnt));
+
+ /* evaluate command size, check space */
+ 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"));
+ gdth_cmd_tab[cmd_index-2][hanum].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;
+ int cmd_index;
+ unchar t,l;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ t = scp->target;
+ l = scp->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;
+
+ 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 */
+ cmdp->OpCode = GDT_WRITE; /* always */
+ cmdp->BoardNode = LOCALBOARD;
+ 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 = NULL;
+ cmdp->u.raw.sdlen = scp->request_bufflen;
+ cmdp->u.raw.sense_len = 16;
+ cmdp->u.raw.sense_data = virt_to_bus(scp->sense_buffer);
+ cmdp->u.raw.direction =
+ gdth_direction_tab[scp->cmnd[0]]==DOU ? DATA_OUT : DATA_IN;
+ memcpy(cmdp->u.raw.cmd,scp->cmnd,12);
+
+ if (scp->use_sg) {
+ cmdp->u.raw.sdata = -1UL;
+ sl = (struct scatterlist *)scp->request_buffer;
+ for (i=0; i<scp->use_sg; ++i,++sl) {
+ cmdp->u.raw.sg_lst[i].sg_ptr = virt_to_bus(sl->address);
+ cmdp->u.raw.sg_lst[i].sg_len = (ulong)sl->length;
+ }
+ cmdp->u.raw.sg_ranz = (ulong)i;
+
+#ifdef GDTH_STATISTICS
+ if (max_sg < (ulong)i) {
+ max_sg = (ulong)i;
+ TRACE3(("GDT: max_sg = %d\n",i));
+ }
+#endif
+ if (i<GDTH_MAXSG)
+ cmdp->u.raw.sg_lst[i].sg_len = 0;
+ } else {
+ if (ha->raw_feat & SCATTER_GATHER) {
+ cmdp->u.raw.sdata = -1UL;
+ cmdp->u.raw.sg_ranz= 1;
+ cmdp->u.raw.sg_lst[0].sg_ptr = virt_to_bus(scp->request_buffer);
+ 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 = virt_to_bus(scp->request_buffer);
+ cmdp->u.raw.sg_ranz= 0;
+ }
+ }
+ TRACE(("raw cmd: addr. %lx sganz %lx sgptr0 %lx sglen0 %lx\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, check space */
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst) +
+ (ushort)cmdp->u.raw.sg_ranz * 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_raw() DPMEM overflow\n"));
+ gdth_cmd_tab[cmd_index-2][hanum].cmnd = UNUSED_CMND;
+ return 0;
+ }
+ }
+
+ /* copy command */
+ gdth_copy_command(hanum);
+ return cmd_index;
+}
+
+static int gdth_special_cmd(int hanum,Scsi_Cmnd *scp,unchar b)
+{
+ 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(ulong);
+ } else if (cmdp->Service == CACHESERVICE) {
+ TRACE2(("cache command %d\n",cmdp->OpCode));
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.cache.sg_lst) + sizeof(gdth_sg_str);
+ } else if (cmdp->Service == SCSIRAWSERVICE) {
+ TRACE2(("raw command %d/%d\n",cmdp->OpCode,cmdp->u.raw.cmd[0]));
+ 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"));
+ gdth_cmd_tab[cmd_index-2][hanum].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(ushort source, ushort idx,
+ gdth_evt_data *evt)
+{
+ gdth_evt_str *e;
+ ulong flags;
+ struct timeval tv;
+
+ TRACE2(("gdth_store_event() source %d idx %d\n", source, idx));
+ if (source == 0) /* no source -> no event */
+ return 0;
+
+ save_flags(flags);
+ cli();
+ if (ebuffer[elastidx].event_source == source &&
+ ebuffer[elastidx].event_idx == idx &&
+ !memcmp((char *)&ebuffer[elastidx].event_data.eu,
+ (char *)&evt->eu, evt->size)) {
+ 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;
+ }
+ restore_flags(flags);
+ return e;
+}
+
+static int gdth_read_event(int handle, gdth_evt_str *estr)
+{
+ gdth_evt_str *e;
+ int eindex;
+ ulong flags;
+
+ TRACE2(("gdth_read_event() handle %d\n", handle));
+ save_flags(flags);
+ cli();
+ if (handle == -1)
+ eindex = eoldidx;
+ else
+ eindex = handle;
+ estr->event_source = 0;
+
+ if (eindex >= MAX_EVENTS) {
+ restore_flags(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));
+ }
+ restore_flags(flags);
+ return eindex;
+}
+
+static void gdth_readapp_event(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));
+ save_flags(flags);
+ cli();
+ 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;
+ restore_flags(flags);
+}
+
+static void gdth_clear_events()
+{
+ ulong flags;
+
+ TRACE(("gdth_clear_events()"));
+ save_flags(flags);
+ cli();
+
+ eoldidx = elastidx = 0;
+ ebuffer[0].event_source = 0;
+ restore_flags(flags);
+}
+
+
+/* SCSI interface functions */
+
+#if LINUX_VERSION_CODE >= 0x010346
+static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs)
+#else
+static void gdth_interrupt(int irq,struct pt_regs *regs)
+#endif
+{
+ register gdth_ha_str *ha;
+ gdt6m_dpram_str *dp6m_ptr;
+ gdt6_dpram_str *dp6_ptr;
+ gdt2_dpram_str *dp2_ptr;
+ Scsi_Cmnd *scp;
+ int hanum;
+ unchar IStatus;
+ ushort CmdStatus, Service = 0;
+ ulong InfoBytes, InfoBytes2 = 0;
+ gdth_evt_data dvr;
+
+ TRACE(("gdth_interrupt() IRQ %d\n",irq));
+
+ /* if polling and not from gdth_wait() -> return */
+ if (gdth_polling) {
+ if (!gdth_from_wait) {
+ return;
+ }
+ }
+
+ wait_index = 0;
+
+ /* search controller */
+ if ((hanum = gdth_get_status(&IStatus,irq)) == -1) {
+ /*
+ TRACE2(("gdth_interrupt(): Spurious interrupt received\n"));
+ */
+ return;
+ }
+
+#ifdef GDTH_STATISTICS
+ ++act_ints;
+#endif
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ if (ha->type == GDT_EISA) {
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+ CmdStatus = inw(ha->bmic + MAILBOXREG+8);
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+ if (IStatus == ASYNCINDEX) { /* async. event ? */
+ Service = inw(ha->bmic + MAILBOXREG+10);
+ InfoBytes2 = inl(ha->bmic + MAILBOXREG+4);
+ }
+ } else /* no error */
+ CmdStatus = S_OK;
+ InfoBytes = inl(ha->bmic + MAILBOXREG+12);
+ if (gdth_polling) /* init. -> more info */
+ InfoBytes2 = 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 = (gdt2_dpram_str *)ha->brd;
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+ CmdStatus = readw(&dp2_ptr->u.ic.Status);
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+ if (IStatus == ASYNCINDEX) { /* async. event ? */
+ Service = readw(&dp2_ptr->u.ic.Service);
+ InfoBytes2 = readl(&dp2_ptr->u.ic.Info[1]);
+ }
+ } else /* no error */
+ CmdStatus = S_OK;
+ InfoBytes = readl(&dp2_ptr->u.ic.Info[0]);
+ if (gdth_polling) /* init. -> more info */
+ InfoBytes2 = readl(&dp2_ptr->u.ic.Info[1]);
+ writeb(0xff, &dp2_ptr->io.irqdel); /* acknowledge interrupt */
+ writeb(0, &dp2_ptr->u.ic.Cmd_Index); /* reset command index */
+ writeb(0, &dp2_ptr->io.Sema1); /* reset status semaphore */
+ } else if (ha->type == GDT_PCI) {
+ dp6_ptr = (gdt6_dpram_str *)ha->brd;
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+ CmdStatus = readw(&dp6_ptr->u.ic.Status);
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+ if (IStatus == ASYNCINDEX) { /* async. event ? */
+ Service = readw(&dp6_ptr->u.ic.Service);
+ InfoBytes2 = readl(&dp6_ptr->u.ic.Info[1]);
+ }
+ } else /* no error */
+ CmdStatus = S_OK;
+ InfoBytes = readl(&dp6_ptr->u.ic.Info[0]);
+ if (gdth_polling) /* init. -> more info */
+ InfoBytes2 = readl(&dp6_ptr->u.ic.Info[1]);
+ writeb(0xff, &dp6_ptr->io.irqdel); /* acknowledge interrupt */
+ writeb(0, &dp6_ptr->u.ic.Cmd_Index); /* reset command index */
+ writeb(0, &dp6_ptr->io.Sema1); /* reset status semaphore */
+ } else if (ha->type == GDT_PCINEW) {
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+ CmdStatus = inw(PTR2USHORT(&ha->plx->status));
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+ if (IStatus == ASYNCINDEX) { /* async. event ? */
+ Service = inw(PTR2USHORT(&ha->plx->service));
+ InfoBytes2 = inl(PTR2USHORT(&ha->plx->info[1]));
+ }
+ } else
+ CmdStatus = S_OK;
+
+ InfoBytes = inl(PTR2USHORT(&ha->plx->info[0]));
+ if (gdth_polling) /* init. -> more info */
+ InfoBytes2 = 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 = (gdt6m_dpram_str *)ha->brd;
+ if (IStatus & 0x80) { /* error flag */
+ IStatus &= ~0x80;
+ CmdStatus = readw(&dp6m_ptr->i960r.status);
+ TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus));
+ if (IStatus == ASYNCINDEX) { /* async. event ? */
+ Service = readw(&dp6m_ptr->i960r.service);
+ InfoBytes2 = readl(&dp6m_ptr->i960r.info[1]);
+ }
+ } else /* no error */
+ CmdStatus = S_OK;
+ InfoBytes = readl(&dp6m_ptr->i960r.info[0]);
+ if (gdth_polling) /* init. -> more info */
+ InfoBytes2 = readl(&dp6m_ptr->i960r.info[1]);
+ writeb(0xff, &dp6m_ptr->i960r.edoor_reg);
+ writeb(0, &dp6m_ptr->i960r.sema1_reg);
+ } else {
+ TRACE2(("gdth_interrupt() unknown controller type\n"));
+ return;
+ }
+
+ TRACE(("gdth_interrupt() index %d stat %d info %ld\n",
+ IStatus,CmdStatus,InfoBytes));
+ ha->status = CmdStatus;
+ ha->info = InfoBytes;
+ ha->info2 = InfoBytes2;
+
+ if (gdth_from_wait) {
+ wait_hanum = hanum;
+ wait_index = (int)IStatus;
+ }
+
+ if (IStatus == ASYNCINDEX) {
+ TRACE2(("gdth_interrupt() async. event\n"));
+ gdth_async_event(hanum,Service);
+ } else {
+ if (IStatus == SPEZINDEX) {
+ TRACE2(("Service unknown or not initialized !\n"));
+ dvr.size = sizeof(dvr.eu.driver);
+ dvr.eu.driver.ionode = hanum;
+ gdth_store_event(ES_DRIVER, 4, &dvr);
+ return;
+ }
+ scp = gdth_cmd_tab[IStatus-2][hanum].cmnd;
+ Service = gdth_cmd_tab[IStatus-2][hanum].service;
+ gdth_cmd_tab[IStatus-2][hanum].cmnd = UNUSED_CMND;
+ if (scp == UNUSED_CMND) {
+ TRACE2(("gdth_interrupt() index to unused command (%d)\n",IStatus));
+ dvr.size = sizeof(dvr.eu.driver);
+ dvr.eu.driver.ionode = hanum;
+ dvr.eu.driver.index = IStatus;
+ gdth_store_event(ES_DRIVER, 1, &dvr);
+ return;
+ }
+ if (scp == INTERNAL_CMND) {
+ TRACE(("gdth_interrupt() answer to internal command\n"));
+ return;
+ }
+ TRACE(("gdth_interrupt() sync. status\n"));
+ gdth_sync_event(hanum,Service,IStatus,scp);
+ }
+ gdth_next(hanum);
+}
+
+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;
+ char c='\r';
+ ushort i;
+ gdth_evt_data dvr;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cmdp = ha->pccb;
+ TRACE(("gdth_sync_event() scp %lx serv %d status %d\n",
+ (ulong)scp,service,ha->status));
+
+ if (service == SCREENSERVICE) {
+ msg = (gdth_msg_str *)ha->pscratch;
+ TRACE(("len: %ld, answer: %d, ext: %d, alen: %ld\n",
+ msg->msg_len,msg->msg_answer,msg->msg_ext,msg->msg_alen));
+ 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))
+ udelay(1);
+ 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.msg_handle= msg->msg_handle;
+ cmdp->u.screen.msg_addr = (ulong)msg;
+ ha->cmd_offs_dpmem = 0;
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr)
+ + sizeof(ulong);
+ ha->cmd_cnt = 0;
+ gdth_copy_command(hanum);
+ gdth_release_event(hanum);
+ return 1;
+ }
+
+ if (msg->msg_answer && msg->msg_alen) {
+ for (i=0; i<msg->msg_alen && i<MSGLEN; ++i) {
+ /* getchar() ?? */
+ /* .. */
+ if (c == '\r')
+ break;
+ msg->msg_text[i] = c;
+ }
+ msg->msg_alen -= i;
+ if (c!='\r' && msg->msg_alen!=0) {
+ msg->msg_answer = 1;
+ msg->msg_ext = 1;
+ } else {
+ msg->msg_ext = 0;
+ msg->msg_answer = 0;
+ }
+ msg->msg_len = i;
+ while (gdth_test_busy(hanum))
+ udelay(1);
+ 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.msg_handle= msg->msg_handle;
+ cmdp->u.screen.msg_addr = (ulong)msg;
+ ha->cmd_offs_dpmem = 0;
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr)
+ + sizeof(ulong);
+ ha->cmd_cnt = 0;
+ gdth_copy_command(hanum);
+ gdth_release_event(hanum);
+ return 1;
+ }
+ printk("\n");
+
+ } else {
+ scp->SCp.Message = (int)ha->status;
+ /* cache or raw service */
+ if (ha->status == S_OK) {
+ scp->result = DID_OK << 16;
+ } else if (ha->status == S_BSY) {
+ TRACE2(("Controller busy -> retry !\n"));
+ gdth_putq(hanum,scp,DEFAULT_PRI);
+ return 1;
+ } else {
+ if (service == CACHESERVICE) {
+ 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);
+
+ if (scp->done != gdth_scsi_done) {
+ dvr.size = sizeof(dvr.eu.sync);
+ dvr.eu.sync.ionode = hanum;
+ dvr.eu.sync.service = service;
+ dvr.eu.sync.status = ha->status;
+ dvr.eu.sync.info = ha->info;
+ dvr.eu.sync.hostdrive =
+#if LINUX_VERSION_CODE >= 0x020000
+ ha->id[scp->channel][scp->target].hostdrive;
+#else
+ ha->id[NUMDATA(scp->host)->busnum][scp->target].hostdrive;
+#endif
+ if (ha->status >= 0x8000)
+ gdth_store_event(ES_SYNC, 0, &dvr);
+ else
+ gdth_store_event(ES_SYNC, service, &dvr);
+ }
+ } else {
+ if (ha->status!=S_RAW_SCSI || ha->status==S_RAW_ILL) {
+ scp->result = DID_BAD_TARGET << 16;
+ } else {
+ scp->result = (DID_OK << 16) | ha->info;
+ }
+ }
+ }
+ scp->SCp.have_data_in++;
+ scp->scsi_done(scp);
+ }
+
+ return 1;
+}
+
+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*/ "\007\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 illegal",
+/*33*/ "\007\000\002\012\001\013\001"
+ "GDT HA %u, Fault bus %u, ID %u: illegal 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",
+};
+
+
+static int gdth_async_event(int hanum,int service)
+{
+ gdth_stackframe stack;
+ gdth_evt_data dvr;
+ char *f = NULL;
+ int i,j;
+ gdth_ha_str *ha;
+ gdth_msg_str *msg;
+ gdth_cmd_str *cmdp;
+ int cmd_index;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ cmdp= ha->pccb;
+ msg = (gdth_msg_str *)ha->pscratch;
+ TRACE2(("gdth_async_event() ha %d serv %d\n",
+ hanum,service));
+
+ if (service == SCREENSERVICE) {
+ if (ha->status == MSG_REQUEST) {
+ while (gdth_test_busy(hanum))
+ udelay(1);
+ 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.msg_handle= MSG_INV_HANDLE;
+ cmdp->u.screen.msg_addr = (ulong)msg;
+ ha->cmd_offs_dpmem = 0;
+ ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr)
+ + sizeof(ulong);
+ 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 {
+ dvr.size = sizeof(dvr.eu.async);
+ dvr.eu.async.ionode = hanum;
+ dvr.eu.async.service = service;
+ dvr.eu.async.status = ha->status;
+ dvr.eu.async.info = ha->info;
+ *(ulong *)dvr.eu.async.scsi_coord = ha->info2;
+ gdth_store_event(ES_ASYNC, service, &dvr);
+
+ if (service==CACHESERVICE && INDEX_OK(ha->status,async_cache_tab)) {
+ TRACE2(("GDT: Async. event cache service, event no.: %d\n",
+ ha->status));
+
+ f = async_cache_tab[ha->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++] = *(ulong*)&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;
+ }
+ }
+
+ printk(&f[f[0]],stack); printk("\n");
+
+ } else {
+ printk("GDT: Unknown async. event service %d event no. %d\n",
+ service,ha->status);
+ }
+ }
+ return 1;
+}
+
+#ifdef GDTH_STATISTICS
+void gdth_timeout(void)
+{
+ ulong flags,i;
+ Scsi_Cmnd *nscp;
+ gdth_ha_str *ha;
+ int hanum = 0;
+
+ save_flags(flags);
+ cli();
+
+ for (act_stats=0,i=0; i<GDTH_MAXCMDS; ++i)
+ if (gdth_cmd_tab[i][hanum].cmnd != UNUSED_CMND)
+ ++act_stats;
+
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ for (act_rq=0,nscp=ha->req_first; nscp; nscp=(Scsi_Cmnd*)nscp->SCp.ptr)
+ ++act_rq;
+
+ TRACE2(("gdth_to(): ints %ld, ios %ld, act_stats %ld, act_rq %ld\n",
+ act_ints, act_ios, act_stats, act_rq));
+ act_ints = act_ios = 0;
+
+ timer_table[GDTH_TIMER].expires = jiffies + 30*HZ;
+ timer_active |= 1<<GDTH_TIMER;
+ sti();
+}
+#endif
+
+int gdth_detect(Scsi_Host_Template *shtp)
+{
+ struct Scsi_Host *shp;
+ gdth_ha_str *ha;
+ unsigned long flags;
+ ulong isa_bios;
+ ushort eisa_slot,device_id,index;
+ gdth_pci_str pcistr;
+ int i,j,hanum;
+ 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
+ WAITSEC(3);
+#endif
+
+ TRACE(("gdth_detect()\n"));
+
+ if (disable_gdth_scan) {
+ printk("GDT: Controller driver disabled from command line !\n");
+ return 0;
+ }
+
+ /* initializations */
+ gdth_polling = TRUE; b = 0;
+ for (i=0; i<GDTH_MAXCMDS; ++i)
+ for (j=0; j<MAXHA; ++j)
+ gdth_cmd_tab[i][j].cmnd = UNUSED_CMND;
+ for (i=0; i<4; ++i)
+ for (j=0; j<MAXHA; ++j)
+ gdth_ioctl_tab[i][j] = NULL;
+ gdth_clear_events();
+
+ /* scanning for controllers, at first: ISA controller */
+ for (isa_bios=0xc8000UL; isa_bios<=0xd8000UL; isa_bios+=0x8000UL) {
+ if (gdth_search_isa(isa_bios)) { /* controller found */
+ shp = scsi_register(shtp,sizeof(gdth_ext_str));
+ ha = HADATA(shp);
+ if (!gdth_init_isa(isa_bios,ha)) {
+ scsi_unregister(shp);
+ continue;
+ }
+ /* controller found and initialized */
+ printk("Configuring GDT-ISA HA at BIOS 0x%05lX IRQ %u DRQ %u\n",
+ isa_bios,ha->irq,ha->drq);
+
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= 0x010346
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL))
+#else
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth"))
+#endif
+ {
+ printk("GDT-ISA: Unable to allocate IRQ\n");
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+ if (request_dma(ha->drq,"gdth")) {
+ printk("GDT-ISA: Unable to allocate DMA channel\n");
+#if LINUX_VERSION_CODE >= 0x010346
+ free_irq(ha->irq,NULL);
+#else
+ free_irq(ha->irq);
+#endif
+ restore_flags(flags);
+ 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;
+ for (i=0; i<MAXID; ++i) {
+ if (ha->id[0][i].type==SIOP_DTYP) {
+ shp->this_id = i;
+ break;
+ }
+ }
+ 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->pscratch = DMADATA(shp);
+ ha->req_first = NULL;
+ for (i=0; i<MAXBUS; ++i) {
+ for (j=0; j<MAXID; ++j) {
+ ha->id[i][j].type = EMPTY_DTYP;
+ ha->id[i][j].lock = 0;
+ }
+ }
+ restore_flags(flags);
+
+ if (!gdth_search_drives(hanum)) {
+ printk("GDT-ISA: Error during device scan\n");
+ --gdth_ctr_count;
+ --gdth_ctr_vcount;
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= 0x010346
+ free_irq(ha->irq,NULL);
+#else
+ free_irq(ha->irq);
+#endif
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+
+#if LINUX_VERSION_CODE >= 0x020000
+ shp->max_id = 8;
+ shp->max_lun = 8;
+ shp->max_channel = ha->bus_cnt - 1;
+#else
+ /* register addit. SCSI channels as virtual controllers */
+ for (b=1; b<ha->bus_cnt; ++b) {
+ shp = scsi_register(shtp,sizeof(gdth_num_str));
+ shp->unchecked_isa_dma = 1;
+ shp->irq = ha->irq;
+ shp->dma_channel = ha->drq;
+ for (i=0; i<MAXID; ++i) {
+ if (ha->id[b][i].type==SIOP_DTYP) {
+ shp->this_id = i;
+ break;
+ }
+ }
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum = b;
+ }
+#endif
+
+ gdth_enable_int(hanum);
+ }
+ }
+
+ /* scanning for EISA controllers */
+ for (eisa_slot=0x1000; eisa_slot<=0x8000; eisa_slot+=0x1000) {
+ if (gdth_search_eisa(eisa_slot)) { /* controller found */
+ shp = scsi_register(shtp,sizeof(gdth_ext_str));
+ 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);
+
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= 0x010346
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL))
+#else
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth"))
+#endif
+ {
+ printk("GDT-EISA: Unable to allocate IRQ\n");
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+ shp->unchecked_isa_dma = 0;
+ shp->irq = ha->irq;
+ shp->dma_channel = 0xff;
+ for (i=0; i<MAXID; ++i) {
+ if (ha->id[0][i].type==SIOP_DTYP) {
+ shp->this_id = i;
+ break;
+ }
+ }
+ 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: shp %lx hanum %d\n",
+ (ulong)shp,NUMDATA(shp)->hanum));
+
+ ha->pccb = CMDDATA(shp);
+ ha->pscratch = DMADATA(shp);
+ ha->req_first = NULL;
+ for (i=0; i<MAXBUS; ++i) {
+ for (j=0; j<MAXID; ++j) {
+ ha->id[i][j].type = EMPTY_DTYP;
+ ha->id[i][j].lock = 0;
+ }
+ }
+ restore_flags(flags);
+
+ if (!gdth_search_drives(hanum)) {
+ printk("GDT-EISA: Error during device scan\n");
+ --gdth_ctr_count;
+ --gdth_ctr_vcount;
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= 0x010346
+ free_irq(ha->irq,NULL);
+#else
+ free_irq(ha->irq);
+#endif
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+
+#if LINUX_VERSION_CODE >= 0x020000
+ shp->max_id = 8;
+ shp->max_lun = 8;
+ shp->max_channel = ha->bus_cnt - 1;
+#else
+ /* register addit. SCSI channels as virtual controllers */
+ for (b=1; b<ha->bus_cnt; ++b) {
+ shp = scsi_register(shtp,sizeof(gdth_num_str));
+ shp->unchecked_isa_dma = 0;
+ shp->irq = ha->irq;
+ shp->dma_channel = 0xff;
+ for (i=0; i<MAXID; ++i) {
+ if (ha->id[b][i].type==SIOP_DTYP) {
+ shp->this_id = i;
+ break;
+ }
+ }
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum = b;
+ TRACE2(("EISA detect Bus %d: shp %lx hanum %d\n",
+ NUMDATA(shp)->busnum,(ulong)shp,
+ NUMDATA(shp)->hanum));
+ }
+#endif
+
+ gdth_enable_int(hanum);
+ }
+ }
+
+ /* scanning for PCI controllers */
+ for (device_id = 0; device_id <= PCI_DEVICE_ID_VORTEX_GDT6x21RP2; ++device_id) {
+ if (device_id > PCI_DEVICE_ID_VORTEX_GDT6555 &&
+ device_id < PCI_DEVICE_ID_VORTEX_GDT6x17RP)
+ continue;
+ for (index = 0; ; ++index) {
+ if (!gdth_search_pci(device_id,index,&pcistr))
+ break; /* next device_id */
+ shp = scsi_register(shtp,sizeof(gdth_ext_str));
+ ha = HADATA(shp);
+ if (!gdth_init_pci(&pcistr,ha)) {
+ scsi_unregister(shp);
+ continue;
+ }
+ /* controller found and initialized */
+ printk("Configuring GDT-PCI HA at %d/%d IRQ %u\n",
+ pcistr.bus,pcistr.device_fn>>3,ha->irq);
+
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= 0x010346
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL))
+#else
+ if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth"))
+#endif
+ {
+ printk("GDT-PCI: Unable to allocate IRQ\n");
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+ shp->unchecked_isa_dma = 0;
+ shp->irq = ha->irq;
+ shp->dma_channel = 0xff;
+ for (i=0; i<MAXID; ++i) {
+ if (ha->id[0][i].type==SIOP_DTYP) {
+ shp->this_id = i;
+ break;
+ }
+ }
+ 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->pscratch = DMADATA(shp);
+ ha->req_first = NULL;
+ for (i=0; i<MAXBUS; ++i) {
+ for (j=0; j<MAXID; ++j) {
+ ha->id[i][j].type = EMPTY_DTYP;
+ ha->id[i][j].lock = 0;
+ }
+ }
+ restore_flags(flags);
+
+ if (!gdth_search_drives(hanum)) {
+ printk("GDT-PCI: Error during device scan\n");
+ --gdth_ctr_count;
+ --gdth_ctr_vcount;
+ save_flags(flags);
+ cli();
+#if LINUX_VERSION_CODE >= 0x010346
+ free_irq(ha->irq,NULL);
+#else
+ free_irq(ha->irq);
+#endif
+ restore_flags(flags);
+ scsi_unregister(shp);
+ continue;
+ }
+
+#if LINUX_VERSION_CODE >= 0x020000
+ shp->max_id = 8;
+ shp->max_lun = 8;
+ shp->max_channel = ha->bus_cnt - 1;
+#else
+ /* register addit. SCSI channels as virtual controllers */
+ for (b=1; b<ha->bus_cnt; ++b) {
+ shp = scsi_register(shtp,sizeof(gdth_num_str));
+ shp->unchecked_isa_dma = 0;
+ shp->irq = ha->irq;
+ shp->dma_channel = 0xff;
+ for (i=0; i<MAXID; ++i) {
+ if (ha->id[b][i].type==SIOP_DTYP) {
+ shp->this_id = i;
+ break;
+ }
+ }
+ gdth_ctr_vtab[gdth_ctr_vcount++] = shp;
+ NUMDATA(shp)->hanum = (ushort)hanum;
+ NUMDATA(shp)->busnum = b;
+ }
+#endif
+
+ 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"));
+ timer_table[GDTH_TIMER].fn = gdth_timeout;
+ timer_table[GDTH_TIMER].expires = jiffies + HZ;
+ timer_active |= 1<<GDTH_TIMER;
+#endif
+#if LINUX_VERSION_CODE >= 0x020100
+ register_reboot_notifier(&gdth_notifier);
+#endif
+ }
+ gdth_polling = FALSE;
+ return gdth_ctr_vcount;
+}
+
+
+int gdth_release(struct Scsi_Host *shp)
+{
+ unsigned long flags;
+
+ TRACE2(("gdth_release()\n"));
+
+ save_flags(flags);
+ cli();
+ if (NUMDATA(shp)->busnum == 0) {
+ if (shp->irq) {
+#if LINUX_VERSION_CODE >= 0x010346
+ free_irq(shp->irq,NULL);
+#else
+ free_irq(shp->irq);
+#endif
+ }
+ if (shp->dma_channel != 0xff) {
+ free_dma(shp->dma_channel);
+ }
+ }
+
+ restore_flags(flags);
+ 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 (EISA)");
+ case GDT3A_ID:
+ return("GDT3000A/3020A/3050A (EISA)");
+ case GDT3B_ID:
+ return("GDT3000B/3010A (EISA)");
+ }
+ } else if (ha->type == GDT_ISA) {
+ return("GDT2000/2020 (ISA)");
+ } else if (ha->type == GDT_PCI) {
+ switch (ha->stype) {
+ case PCI_DEVICE_ID_VORTEX_GDT60x0:
+ return("GDT6000/6020/6050 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6000B:
+ return("GDT6000B/6010 (PCI)");
+ }
+ } else if (ha->type == GDT_PCINEW) {
+ switch (ha->stype) {
+ case PCI_DEVICE_ID_VORTEX_GDT6x10:
+ return("GDT6110/6510 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x20:
+ return("GDT6120/6520 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6530:
+ return("GDT6530 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6550:
+ return("GDT6550 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x17:
+ return("GDT6117/6517 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x27:
+ return("GDT6127/6527 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6537:
+ return("GDT6537 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6557:
+ return("GDT6557/6557-ECC (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x15:
+ return("GDT6115/6515 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x25:
+ return("GDT6125/6525 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6535:
+ return("GDT6535 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6555:
+ return("GDT6555/6555-ECC (PCI)");
+ }
+ } else if (ha->type == GDT_PCIMPR) {
+ switch (ha->stype) {
+ case PCI_DEVICE_ID_VORTEX_GDT6x17RP:
+ return("GDT6117RP/GDT6517RP (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x27RP:
+ return("GDT6127RP/GDT6527RP (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6537RP:
+ return("GDT6537RP (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6557RP:
+ return("GDT6557RP (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x11RP:
+ return("GDT6111RP/GDT6511RP (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x21RP:
+ return("GDT6121RP/GDT6521RP (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x17RP1:
+ return("GDT6117RP1/GDT6517RP1 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x27RP1:
+ return("GDT6127RP1/GDT6527RP1 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6537RP1:
+ return("GDT6537RP1 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6557RP1:
+ return("GDT6557RP1 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x11RP1:
+ return("GDT6111RP1/GDT6511RP1 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x21RP1:
+ return("GDT6121RP1/GDT6521RP1 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x17RP2:
+ return("GDT6117RP2/GDT6517RP2 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x27RP2:
+ return("GDT6127RP2/GDT6527RP2 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6537RP2:
+ return("GDT6537RP2 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6557RP2:
+ return("GDT6557RP2 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x11RP2:
+ return("GDT6111RP2/GDT6511RP2 (PCI)");
+ case PCI_DEVICE_ID_VORTEX_GDT6x21RP2:
+ return("GDT6121RP2/GDT6521RP2 (PCI)");
+ }
+ }
+ return("");
+}
+
+const char *gdth_info(struct Scsi_Host *shp)
+{
+ int hanum;
+
+ TRACE2(("gdth_info()\n"));
+ hanum = NUMDATA(shp)->hanum;
+
+ return (gdth_ctr_name(hanum));
+}
+
+
+int gdth_abort(Scsi_Cmnd *scp)
+{
+ TRACE2(("gdth_abort() reason %d\n",scp->abort_reason));
+ return SCSI_ABORT_SNOOZE;
+}
+
+#if LINUX_VERSION_CODE >= 0x010346
+int gdth_reset(Scsi_Cmnd *scp, unsigned int reset_flags)
+#else
+int gdth_reset(Scsi_Cmnd *scp)
+#endif
+{
+ TRACE2(("gdth_reset()\n"));
+ return SCSI_RESET_PUNT;
+}
+
+
+#if LINUX_VERSION_CODE >= 0x010300
+int gdth_bios_param(Disk *disk,kdev_t dev,int *ip)
+#else
+int gdth_bios_param(Disk *disk,int dev,int *ip)
+#endif
+{
+ unchar b, t;
+ int hanum;
+ gdth_ha_str *ha;
+
+ hanum = NUMDATA(disk->device->host)->hanum;
+ b = disk->device->channel;
+ t = disk->device->id;
+ TRACE2(("gdth_bios_param() ha %d bus %d target %d\n", hanum, b, t));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ ip[0] = ha->id[b][t].heads;
+ ip[1] = ha->id[b][t].secs;
+ ip[2] = disk->capacity / ip[0] / ip[1];
+
+ TRACE2(("gdth_bios_param(): %d heads, %d secs, %d cyls\n",
+ ip[0],ip[1],ip[2]));
+ return 0;
+}
+
+
+static void internal_done(Scsi_Cmnd *scp)
+{
+ scp->SCp.sent_command++;
+}
+
+int gdth_command(Scsi_Cmnd *scp)
+{
+ TRACE2(("gdth_command()\n"));
+
+ scp->SCp.sent_command = 0;
+ gdth_queuecommand(scp,internal_done);
+
+ while (!scp->SCp.sent_command)
+ barrier();
+ return scp->result;
+}
+
+
+int gdth_queuecommand(Scsi_Cmnd *scp,void (*done)(Scsi_Cmnd *))
+{
+ int hanum;
+ int priority;
+
+ TRACE(("gdth_queuecommand() cmd 0x%x id %d lun %d\n",
+ scp->cmnd[0],scp->target,scp->lun));
+
+ scp->scsi_done = (void *)done;
+ scp->SCp.have_data_in = 0;
+ hanum = NUMDATA(scp->host)->hanum;
+#ifdef GDTH_STATISTICS
+ ++act_ios;
+#endif
+
+ priority = DEFAULT_PRI;
+#if LINUX_VERSION_CODE >= 0x010300
+ if (scp->done == gdth_scsi_done)
+ priority = scp->SCp.this_residual;
+#endif
+ gdth_putq( hanum, scp, priority );
+ gdth_next( hanum );
+ return 0;
+}
+
+
+/* shutdown routine */
+#if LINUX_VERSION_CODE >= 0x020100
+static int gdth_halt(struct notifier_block *nb, ulong event, void *buf)
+#else
+void gdth_halt(void)
+#endif
+{
+ int hanum, i, j;
+ gdth_ha_str *ha;
+ Scsi_Cmnd scp;
+ Scsi_Device sdev;
+ gdth_cmd_str gdtcmd;
+ char cmnd[12];
+
+#if LINUX_VERSION_CODE >= 0x020100
+ TRACE2(("gdth_halt() event %d\n",event));
+ if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF)
+ return NOTIFY_DONE;
+#else
+ TRACE2(("gdth_halt()\n"));
+#endif
+ printk("GDT: Flushing all host drives .. ");
+ for (hanum = 0; hanum < gdth_ctr_count; ++hanum) {
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ memset(&sdev,0,sizeof(Scsi_Device));
+ memset(&scp, 0,sizeof(Scsi_Cmnd));
+ sdev.host = gdth_ctr_tab[hanum];
+ sdev.id = sdev.host->this_id;
+ scp.cmd_len = 12;
+ scp.host = gdth_ctr_tab[hanum];
+ scp.target = sdev.host->this_id;
+ scp.device = &sdev;
+ scp.use_sg = 0;
+
+ /* flush */
+ for (i = 0; i < MAXBUS; ++i) {
+ for (j = 0; j < MAXID; ++j) {
+ if (ha->id[i][j].type == CACHE_DTYP) {
+ gdtcmd.BoardNode = LOCALBOARD;
+ gdtcmd.Service = CACHESERVICE;
+ gdtcmd.OpCode = GDT_FLUSH;
+ gdtcmd.u.cache.DeviceNo = ha->id[i][j].hostdrive;
+ gdtcmd.u.cache.BlockNo = 1;
+ gdtcmd.u.cache.sg_canz = 0;
+ TRACE2(("gdth_halt(): flush ha %d drive %d\n",
+ hanum, ha->id[i][j].hostdrive));
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ scp.request.rq_status = RQ_SCSI_BUSY;
+ scp.request.sem = &sem;
+ scsi_do_cmd(&scp, cmnd, &gdtcmd,
+ sizeof(gdth_cmd_str), gdth_scsi_done,
+ 30*HZ, 1);
+ down(&sem);
+ }
+ }
+ }
+ }
+
+ /* controller reset */
+ gdtcmd.BoardNode = LOCALBOARD;
+ gdtcmd.Service = CACHESERVICE;
+ gdtcmd.OpCode = GDT_RESET;
+ TRACE2(("gdth_halt(): reset controller %d\n", hanum));
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ scp.request.rq_status = RQ_SCSI_BUSY;
+ scp.request.sem = &sem;
+ scsi_do_cmd(&scp, cmnd, &gdtcmd,
+ sizeof(gdth_cmd_str), gdth_scsi_done,
+ 10*HZ, 1);
+ down(&sem);
+ }
+ }
+ printk("Done.\n");
+
+#ifdef GDTH_STATISTICS
+ timer_active &= ~(1<<GDTH_TIMER);
+#endif
+#if LINUX_VERSION_CODE >= 0x020100
+ unregister_reboot_notifier(&gdth_notifier);
+ return NOTIFY_OK;
+#endif
+}
+
+
+/* called from init/main.c */
+void gdth_setup(char *str,int *ints)
+{
+ static size_t setup_idx = 0;
+
+ TRACE2(("gdth_setup() str %s ints[0] %d ints[1] %d\n",
+ str ? str:"NULL", ints[0],
+ ints[0] ? ints[1]:0));
+
+ if (setup_idx >= MAXHA) {
+ printk("GDT: gdth_setup() called too many times. Bad LILO params ?\n");
+ return;
+ }
+ if (ints[0] != 1) {
+ printk("GDT: Illegal command line !\n");
+ printk("Usage: gdth=<IRQ>\n");
+ printk("Where: <IRQ>: valid EISA controller IRQ (10,11,12,14)\n");
+ printk(" or 0 to disable controller driver\n");
+ return;
+ }
+ if (ints[1] == 10 || ints[1] == 11 || ints[1] == 12 || ints[1] == 14) {
+ irqs[setup_idx++] = ints[1];
+ irqs[setup_idx] = 0xff;
+ return;
+ }
+ if (ints[1] == 0) {
+ disable_gdth_scan = TRUE;
+ return;
+ }
+ printk("GDT: Invalid IRQ (%d) specified\n",ints[1]);
+}
+
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = GDTH;
+#include "scsi_module.c"
+#endif
+
diff --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h
new file mode 100644
index 000000000..4e2c47742
--- /dev/null
+++ b/drivers/scsi/gdth.h
@@ -0,0 +1,720 @@
+#ifndef _GDTH_H
+#define _GDTH_H
+
+/*
+ * Header file for the GDT ISA/EISA/PCI Disk Array Controller driver for Linux
+ *
+ * gdth.h Copyright (C) 1995-97 ICP vortex Computersysteme GmbH, Achim Leubner
+ * See gdth.c for further informations and
+ * below for supported controller types
+ *
+ * <achim@vortex.de>
+ *
+ * $Id: gdth.h,v 1.9 1997/11/04 09:55:42 achim Exp $
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+
+#ifndef NULL
+#define NULL 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/* defines, macros */
+
+/* driver version */
+#define GDTH_VERSION_STR "1.02"
+#define GDTH_VERSION 1
+#define GDTH_SUBVERSION 2
+
+/* protocol version */
+#define PROTOCOL_VERSION 1
+
+/* 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_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 */
+/* GDT_MPR, RP1 series, wide/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP1 0x110 /* GDT6117RP1/GDT6517RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP1 0x111 /* GDT6127RP1/GDT6527RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP1 0x112 /* GDT6537RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP1 0x113 /* GDT6557RP1 */
+/* GDT_MPR, RP1 series, narrow/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP1 0x114 /* GDT6111RP1/GDT6511RP1 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP1 0x115 /* GDT6121RP1/GDT6521RP1 */
+/* GDT_MPR, RP2 series, wide/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP2 0x120 /* GDT6117RP2/GDT6517RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP2 0x121 /* GDT6127RP2/GDT6527RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP2 0x122 /* GDT6537RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP2 0x123 /* GDT6557RP2 */
+/* GDT_MPR, RP2 series, narrow/ultra SCSI */
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP2 0x124 /* GDT6111RP2/GDT6511RP2 */
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP2 0x125 /* GDT6121RP2/GDT6521RP2 */
+#endif
+
+/* limits */
+#define GDTH_SCRATCH 4096 /* 4KB scratch buffer */
+#define GDTH_MAXCMDS 124
+#define GDTH_MAXC_P_L 16 /* max. cmds per lun */
+#define MAXOFFSETS 128
+#define MAXHA 8
+#define MAXID 8
+#define MAXLUN 8
+#define MAXBUS 5
+#define MAX_HDRIVES 35 /* max. host drive count */
+#define MAX_EVENTS 100 /* event buffer count */
+#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)
+
+/* device types */
+#define EMPTY_DTYP 0
+#define CACHE_DTYP 1
+#define RAW_DTYP 2
+#define SIOP_DTYP 3 /* the SCSI processor */
+
+/* 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 sector */
+
+/* DPMEM constants */
+#define IC_HEADER_BYTES 48
+#define IC_QUEUE_BYTES 4
+#define DPMEM_COMMAND_OFFSET IC_HEADER_BYTES+IC_QUEUE_BYTES*MAXOFFSETS
+
+/* service commands */
+#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_RESERVE 14 /* reserve dev. to raw service */
+#define GDT_WRITE_THR 16 /* write through */
+#define GDT_EXT_INFO 18 /* extended info */
+#define GDT_RESET 19 /* controller reset */
+
+/* IOCTL command defines */
+#define SCSI_CHAN_CNT 5 /* subfunctions */
+#define L_CTRL_PATTERN 0x20000000L
+#define CACHE_INFO 4
+#define CACHE_CONFIG 5
+#define IO_CHANNEL 0x00020000L /* channels */
+#define INVALID_CHANNEL 0x0000ffffL
+
+/* 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_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 */
+
+/* service errors */
+#define S_OK 1 /* no error */
+#define S_BSY 7 /* controller busy */
+#define S_RAW_SCSI 12 /* raw serv.: target error */
+#define S_RAW_ILL 0xff /* raw serv.: illegal */
+
+/* timeout values */
+#define INIT_RETRIES 10000 /* 10000 * 1ms = 10s */
+#define INIT_TIMEOUT 100000 /* 1000 * 1ms = 1s */
+#define POLL_TIMEOUT 10000 /* 10000 * 1ms = 10s */
+
+/* priorities */
+#define DEFAULT_PRI 0x20
+#define IOCTL_PRI 0x10
+
+/* data directions */
+#define DATA_IN 0x01000000L /* data from target */
+#define 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 enable */
+#define EDOORREG 0x0c8f /* EISA system doorbell */
+#define MAILBOXREG 0x0c90 /* mailbox reg. (16 bytes) */
+#define EISAREG 0x0cc0 /* EISA configuration */
+
+/* other defines */
+#define LINUX_OS 8 /* used for cache optim. */
+#define SCATTER_GATHER 1 /* s/g feature */
+#define GDTH_MAXSG 32 /* max. s/g elements */
+#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 GDT_WR_THROUGH 0x100 /* WRITE_THROUGH supported */
+
+/* typedefs */
+
+#pragma pack(1)
+
+typedef struct {
+ char buffer[GDTH_SCRATCH]; /* scratch buffer */
+} gdth_scratch_str;
+
+/* screenservice message */
+typedef struct {
+ ulong msg_handle; /* message handle */
+ ulong msg_len; /* size of message */
+ ulong 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 */
+} gdth_msg_str;
+
+/* get channel count IOCTL */
+typedef struct {
+ ulong channel_no; /* number of channel */
+ ulong drive_cnt; /* number of drives */
+ unchar siop_id; /* SCSI processor ID */
+ unchar siop_state; /* SCSI processor state */
+} gdth_getch_str;
+
+/* cache info/config IOCTL */
+typedef struct {
+ ulong 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 */
+} gdth_cpar_str;
+
+typedef struct {
+ ulong csize; /* cache size */
+ ulong read_cnt; /* read/write counter */
+ ulong write_cnt;
+ ulong tr_hits; /* hits */
+ ulong sec_hits;
+ ulong sec_miss; /* misses */
+} gdth_cstat_str;
+
+typedef struct {
+ gdth_cpar_str cpar;
+ gdth_cstat_str cstat;
+} gdth_cinfo_str;
+
+/* scatter/gather element */
+typedef struct {
+ ulong sg_ptr; /* address */
+ ulong sg_len; /* length */
+} gdth_sg_str;
+
+/* command structure */
+typedef struct {
+ ulong BoardNode; /* board node (always 0) */
+ ulong CommandIndex; /* command number */
+ ushort OpCode; /* the command (READ,..) */
+ union {
+ struct {
+ ushort DeviceNo; /* number of cache drive */
+ ulong BlockNo; /* block number */
+ ulong BlockCnt; /* block count */
+ ulong DestAddr; /* dest. addr. (if s/g: -1) */
+ ulong sg_canz; /* s/g element count */
+ gdth_sg_str sg_lst[GDTH_MAXSG]; /* s/g list */
+ } cache; /* cache service cmd. str. */
+ struct {
+ ushort param_size; /* size of p_param buffer */
+ ulong subfunc; /* IOCTL function */
+ ulong channel; /* device */
+ ulong p_param; /* buffer */
+ } ioctl; /* IOCTL command structure */
+ struct {
+ ushort reserved;
+ ulong msg_handle; /* message handle */
+ ulong msg_addr; /* message buffer address */
+ } screen; /* screen service cmd. str. */
+ struct {
+ ushort reserved;
+ ulong direction; /* data direction */
+ ulong mdisc_time; /* disc. time (0: no timeout)*/
+ ulong mcon_time; /* connect time(0: no to.) */
+ ulong sdata; /* dest. addr. (if s/g: -1) */
+ ulong sdlen; /* data length (bytes) */
+ ulong 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 */
+ ulong sense_len; /* sense data length */
+ ulong sense_data; /* sense data addr. */
+ struct raw *link_p; /* linked cmds (not supp.) */
+ ulong sg_ranz; /* s/g element count */
+ gdth_sg_str sg_lst[GDTH_MAXSG]; /* s/g list */
+ } raw; /* raw service cmd. struct. */
+ } u;
+ /* additional variables */
+ unchar Service; /* controller service */
+ ushort Status; /* command result */
+ ulong Info; /* additional information */
+ Scsi_Cmnd *RequestBuffer; /* request buffer */
+} 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;
+ ulong index;
+ } driver;
+ struct {
+ ushort ionode;
+ ushort service;
+ ushort status;
+ ulong info;
+ unchar scsi_coord[3];
+ } async;
+ struct {
+ ushort ionode;
+ ushort service;
+ ushort status;
+ ulong info;
+ ushort hostdrive;
+ unchar scsi_coord[3];
+ unchar sense_key;
+ } sync;
+ struct {
+ ulong l1, l2, l3, l4;
+ } test;
+ } eu;
+} gdth_evt_data;
+
+typedef struct {
+ ulong first_stamp;
+ ulong last_stamp;
+ ushort same_count;
+ ushort event_source;
+ ushort event_idx;
+ unchar application;
+ unchar reserved;
+ gdth_evt_data event_data;
+} gdth_evt_str;
+
+
+/* DPRAM structures */
+
+/* interface area ISA/PCI */
+typedef struct {
+ unchar S_Cmd_Indx; /* special command */
+ unchar volatile S_Status; /* status special command */
+ ushort reserved1;
+ ulong 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) */
+ ulong Info[2]; /* additional info */
+ struct {
+ ushort offset; /* command offs. in the DPRAM*/
+ ushort serv_id; /* service */
+ } comm_queue[MAXOFFSETS]; /* command queue */
+ ulong bios_reserved[2];
+ unchar gdt_dpr_cmd[1]; /* commands */
+} gdt_dpr_if;
+
+/* SRAM structure PCI controllers */
+typedef struct {
+ ulong 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 */
+} 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;
+} gdt_eisa_sram;
+
+
+/* DPRAM ISA controllers */
+typedef struct {
+ union {
+ struct {
+ unchar bios_used[0x3c00-32]; /* 15KB - 32Bytes BIOS */
+ ulong 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 */
+ } 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 */
+ } io;
+} 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];
+ } io;
+} 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 */
+ ulong 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];
+} 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 */
+} 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 */
+ ulong 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];
+ ulong unused7[1004]; /* size: 4 KB */
+} 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 */
+} gdt6m_dpram_str;
+
+
+/* PCI resources */
+typedef struct {
+ ushort device_id; /* device ID (0,..,9) */
+ 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 */
+ ulong bios; /* BIOS address */
+ unchar irq; /* IRQ */
+} gdth_pci_str;
+
+
+/* controller information structure */
+typedef struct {
+ unchar bus_cnt; /* SCSI bus count */
+ unchar type; /* controller class */
+ ushort raw_feat; /* feat. raw service (s/g,..) */
+ ulong stype; /* controller subtype */
+ ushort cache_feat; /* feat. cache serv. (s/g,..) */
+ ushort bmic; /* BMIC address (EISA) */
+ void *brd; /* DPRAM address */
+ ulong brd_phys; /* slot number/BIOS address */
+ gdt6c_plx_regs *plx; /* PLX regs (new PCI contr.) */
+ gdth_cmd_str *pccb; /* address command structure */
+ gdth_scratch_str *pscratch;
+ unchar irq; /* IRQ */
+ unchar drq; /* DRQ (ISA controllers) */
+ ushort status; /* command status */
+ ulong info;
+ ulong info2; /* additional info */
+ Scsi_Cmnd *req_first; /* top of request queue */
+ struct {
+ unchar type; /* device type */
+ unchar heads; /* mapping */
+ unchar secs;
+ unchar lock; /* drive locked ? (hot plug) */
+ ushort hostdrive; /* host drive number */
+ ushort devtype; /* further information */
+ ulong size; /* capacity */
+ } id[MAXBUS][MAXID];
+ 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 */
+ unchar reserved;
+ unchar mode; /* information from /proc */
+ ushort param_size;
+ gdth_cpar_str cpar; /* controller cache par. */
+} 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_scratch_str dmaext;
+} 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];
+} gdth_inq_data;
+
+/* READ_CAPACITY data format */
+typedef struct {
+ ulong last_block_no;
+ ulong block_length;
+} gdth_rdcap_data;
+
+/* REQUEST_SENSE data format */
+typedef struct {
+ unchar errorcode;
+ unchar segno;
+ unchar key;
+ ulong info;
+ unchar add_length;
+ ulong cmd_info;
+ unchar adsc;
+ unchar adsq;
+ unchar fruc;
+ unchar key_spec[3];
+} gdth_sense_data;
+
+/* MODE_SENSE data format */
+typedef struct {
+ struct {
+ unchar data_length;
+ unchar med_type;
+ unchar dev_par;
+ unchar bd_length;
+ } hd;
+ struct {
+ unchar dens_code;
+ unchar block_count[3];
+ unchar reserved;
+ unchar block_length[3];
+ } bd;
+} gdth_modep_data;
+
+typedef struct {
+ ulong b[10]; /* 32 bit compiler ! */
+} gdth_stackframe;
+
+#pragma pack()
+
+/* function prototyping */
+
+int gdth_detect(Scsi_Host_Template *);
+int gdth_release(struct Scsi_Host *);
+int gdth_command(Scsi_Cmnd *);
+int gdth_queuecommand(Scsi_Cmnd *,void (*done)(Scsi_Cmnd *));
+int gdth_abort(Scsi_Cmnd *);
+#if LINUX_VERSION_CODE >= 0x010346
+int gdth_reset(Scsi_Cmnd *, unsigned int reset_flags);
+#else
+int gdth_reset(Scsi_Cmnd *);
+#endif
+const char *gdth_info(struct Scsi_Host *);
+
+
+#if LINUX_VERSION_CODE >= 0x010300
+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);
+#define GDTH { NULL, NULL, \
+ &proc_scsi_gdth, \
+ gdth_proc_info, \
+ "GDT SCSI Disk Array Controller", \
+ gdth_detect, \
+ gdth_release, \
+ gdth_info, \
+ gdth_command, \
+ gdth_queuecommand, \
+ gdth_abort, \
+ gdth_reset, \
+ NULL, \
+ gdth_bios_param, \
+ GDTH_MAXCMDS, \
+ -1, \
+ GDTH_MAXSG, \
+ GDTH_MAXC_P_L, \
+ 0, \
+ 1, \
+ ENABLE_CLUSTERING}
+#else
+int gdth_bios_param(Disk *,int,int *);
+#define GDTH { NULL, NULL, \
+ "GDT SCSI Disk Array Controller", \
+ gdth_detect, \
+ gdth_release, \
+ gdth_info, \
+ gdth_command, \
+ gdth_queuecommand, \
+ gdth_abort, \
+ gdth_reset, \
+ NULL, \
+ gdth_bios_param, \
+ GDTH_MAXCMDS, \
+ -1, \
+ GDTH_MAXSG, \
+ GDTH_MAXC_P_L, \
+ 0, \
+ 1, \
+ ENABLE_CLUSTERING}
+#endif
+
+#endif
+
diff --git a/drivers/scsi/gdth_ioctl.h b/drivers/scsi/gdth_ioctl.h
new file mode 100644
index 000000000..01f5db4c5
--- /dev/null
+++ b/drivers/scsi/gdth_ioctl.h
@@ -0,0 +1,86 @@
+#ifndef _GDTH_IOCTL_H
+#define _GDTH_IOCTL_H
+
+/* gdth_ioctl.h
+ * $Id: gdth_ioctl.h,v 1.1 1997/02/21 08:07:27 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_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_MAGIC 0x06030f07UL
+
+
+/* IOCTL structure (write) */
+typedef struct {
+ ulong 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[35]; /* drives */
+ } lockdrv;
+ struct {
+ unchar lock; /* lock/unlock */
+ unchar channel; /* channel */
+ } lockchn;
+ struct {
+ int erase; /* erase event ? */
+ int handle;
+ } event;
+ } iu;
+} gdth_iowr_str;
+
+/* IOCTL structure (read) */
+typedef struct {
+ ulong size; /* buffer size */
+ ulong 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 */
+ } 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[32]; /* event structure */
+ } event;
+ } iu;
+} gdth_iord_str;
+
+
+#endif
+
diff --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c
new file mode 100644
index 000000000..1a34996a1
--- /dev/null
+++ b/drivers/scsi/gdth_proc.c
@@ -0,0 +1,635 @@
+/* gdth_proc.c
+ * $Id: gdth_proc.c,v 1.6 1997/10/31 10:36:24 achim Exp $
+ */
+
+#include "gdth_ioctl.h"
+
+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 ha %d offs %d inout %d\n",
+ length,hostno,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,i,hanum,busnum));
+ else
+ return(gdth_get_info(buffer,start,offset,length,i,hanum,busnum));
+}
+
+static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum)
+{
+ int ret_val;
+ Scsi_Cmnd scp;
+ Scsi_Device sdev;
+ gdth_iowr_str *piowr;
+
+ TRACE2(("gdth_set_info() ha %d bus %d\n",hanum,busnum));
+ piowr = (gdth_iowr_str *)buffer;
+
+ memset(&sdev,0,sizeof(Scsi_Device));
+ memset(&scp, 0,sizeof(Scsi_Cmnd));
+ sdev.host = gdth_ctr_vtab[vh];
+ sdev.id = sdev.host->this_id;
+ scp.cmd_len = 12;
+ scp.host = gdth_ctr_vtab[vh];
+ scp.target = sdev.host->this_id;
+ scp.device = &sdev;
+ scp.use_sg = 0;
+
+ if (length >= 4) {
+ if (strncmp(buffer,"gdth",4) == 0) {
+ buffer += 5;
+ length -= 5;
+ ret_val = gdth_set_asc_info( buffer, length, hanum, scp );
+ } else if (piowr->magic == GDTIOCTL_MAGIC) {
+ ret_val = gdth_set_bin_info( buffer, length, hanum, scp );
+ } else {
+ printk("GDT: Wrong signature: %6s\n",buffer);
+ ret_val = -EINVAL;
+ }
+ } else {
+ ret_val = -EINVAL;
+ }
+ return ret_val;
+}
+
+static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp)
+{
+ int orig_length, drive, wb_mode;
+ char cmnd[12];
+ int i, j, found;
+ gdth_ha_str *ha;
+ gdth_cmd_str gdtcmd;
+ gdth_cpar_str *pcpar;
+
+ TRACE2(("gdth_set_asc_info() ha %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ memset(cmnd, 0,10);
+ 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 < MAXBUS; ++i) {
+ for (j = 0; j < MAXID; ++j) {
+ if (ha->id[i][j].type == CACHE_DTYP) {
+ if (drive != -1 &&
+ ha->id[i][j].hostdrive != (ushort)drive)
+ continue;
+ found = TRUE;
+ gdtcmd.BoardNode = LOCALBOARD;
+ gdtcmd.Service = CACHESERVICE;
+ gdtcmd.OpCode = GDT_FLUSH;
+ gdtcmd.u.cache.DeviceNo = ha->id[i][j].hostdrive;
+ gdtcmd.u.cache.BlockNo = 1;
+ gdtcmd.u.cache.sg_canz = 0;
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ scp.request.rq_status = RQ_SCSI_BUSY;
+ scp.request.sem = &sem;
+ scsi_do_cmd(&scp, cmnd, &gdtcmd,
+ sizeof(gdth_cmd_str), gdth_scsi_done,
+ 30*HZ, 1);
+ down(&sem);
+ }
+ }
+ }
+ }
+ 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) {
+ pcpar = (gdth_cpar_str *)kmalloc( sizeof(gdth_cpar_str),
+ GFP_ATOMIC | GFP_DMA );
+ if (pcpar == NULL) {
+ TRACE2(("gdth_set_info(): Unable to allocate memory.\n"));
+ printk("Unable to allocate memory.\n");
+ return(-EINVAL);
+ }
+ memcpy( pcpar, &ha->cpar, sizeof(gdth_cpar_str) );
+ gdtcmd.BoardNode = LOCALBOARD;
+ gdtcmd.Service = CACHESERVICE;
+ gdtcmd.OpCode = GDT_IOCTL;
+ gdtcmd.u.ioctl.p_param = virt_to_bus(pcpar);
+ 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;
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ scp.request.rq_status = RQ_SCSI_BUSY;
+ scp.request.sem = &sem;
+ scsi_do_cmd(&scp, cmnd, &gdtcmd, sizeof(gdth_cmd_str),
+ gdth_scsi_done, 30*HZ, 1);
+ down(&sem);
+ }
+ kfree( pcpar );
+ printk("Done.\n");
+ return(orig_length);
+ }
+
+ printk("GDT: Unknown command: %s Length: %d\n",buffer,length);
+ return(-EINVAL);
+}
+
+static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp)
+{
+ char cmnd[12];
+ int id;
+ unchar i, j, k, found;
+ gdth_ha_str *ha;
+ gdth_iowr_str *piowr;
+ gdth_iord_str *piord;
+ gdth_cmd_str *pcmd;
+ ulong *ppadd;
+ ulong add_size, flags;
+
+
+ TRACE2(("gdth_set_bin_info() ha %d\n",hanum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ memset(cmnd, 0,10);
+ piowr = (gdth_iowr_str *)buffer;
+ piord = NULL;
+ pcmd = NULL;
+
+ if (length < GDTOFFSOF(gdth_iowr_str,iu))
+ return(-EINVAL);
+
+ switch (piowr->ioctl) {
+ case GDTIOCTL_GENERAL:
+ if (length < GDTOFFSOF(gdth_iowr_str,iu.general.data[0]))
+ return(-EINVAL);
+ pcmd = (gdth_cmd_str *)piowr->iu.general.command;
+ pcmd->Service = piowr->service;
+ if (pcmd->OpCode == GDT_IOCTL) {
+ ppadd = &pcmd->u.ioctl.p_param;
+ add_size = pcmd->u.ioctl.param_size;
+ } else if (piowr->service == CACHESERVICE) {
+ add_size = pcmd->u.cache.BlockCnt * SECTOR_SIZE;
+ if (ha->cache_feat & SCATTER_GATHER) {
+ ppadd = &pcmd->u.cache.sg_lst[0].sg_ptr;
+ pcmd->u.cache.DestAddr = -1UL;
+ pcmd->u.cache.sg_lst[0].sg_len = add_size;
+ pcmd->u.cache.sg_canz = 1;
+ } else {
+ ppadd = &pcmd->u.cache.DestAddr;
+ pcmd->u.cache.sg_canz = 0;
+ }
+ } else if (piowr->service == SCSIRAWSERVICE) {
+ add_size = pcmd->u.raw.sdlen;
+ if (ha->raw_feat & SCATTER_GATHER) {
+ ppadd = &pcmd->u.raw.sg_lst[0].sg_ptr;
+ pcmd->u.raw.sdata = -1UL;
+ pcmd->u.raw.sg_lst[0].sg_len = add_size;
+ pcmd->u.raw.sg_ranz = 1;
+ } else {
+ ppadd = &pcmd->u.raw.sdata;
+ pcmd->u.raw.sg_ranz = 0;
+ }
+ } else {
+ return(-EINVAL);
+ }
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) + add_size );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+
+ piord->size = sizeof(gdth_iord_str) + add_size;
+ if (add_size > 0) {
+ memcpy(piord->iu.general.data, piowr->iu.general.data, add_size);
+ *ppadd = virt_to_bus(piord->iu.general.data);
+ }
+ /* do IOCTL */
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ scp.request.rq_status = RQ_SCSI_BUSY;
+ scp.request.sem = &sem;
+ scp.SCp.this_residual = IOCTL_PRI;
+ scsi_do_cmd(&scp, cmnd, pcmd,
+ sizeof(gdth_cmd_str), gdth_scsi_done,
+ piowr->timeout*HZ, 1);
+ down(&sem);
+ piord->status = (ulong)scp.SCp.Message;
+ }
+ break;
+
+ case GDTIOCTL_DRVERS:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ piord->iu.drvers.version = (GDTH_VERSION<<8) | GDTH_SUBVERSION;
+ break;
+
+ case GDTIOCTL_CTRTYPE:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ if (ha->type == GDT_ISA || ha->type == GDT_EISA) {
+ piord->iu.ctrtype.type = (unchar)((ha->stype>>20) - 10);
+ } else if (ha->type != GDT_PCIMPR) {
+ piord->iu.ctrtype.type = (unchar)((ha->stype<<8) + 6);
+ } else {
+ piord->iu.ctrtype.type = 0xfe;
+ piord->iu.ctrtype.ext_type = 0x6000 | ha->stype;
+ }
+ piord->iu.ctrtype.info = ha->brd_phys;
+ piord->iu.ctrtype.oem_id = (ushort)GDT3_ID;
+ break;
+
+ case GDTIOCTL_CTRCNT:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ piord->iu.ctrcnt.count = (ushort)gdth_ctr_count;
+ break;
+
+ case GDTIOCTL_OSVERS:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ piord->iu.osvers.version = (unchar)(LINUX_VERSION_CODE >> 16);
+ piord->iu.osvers.subversion = (unchar)(LINUX_VERSION_CODE >> 8);
+ piord->iu.osvers.revision = (ushort)(LINUX_VERSION_CODE & 0xff);
+ break;
+
+ case GDTIOCTL_LOCKDRV:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ for (i = k = 0; i < piowr->iu.lockdrv.drive_cnt; ++i) {
+ found = FALSE;
+ for (j = 0; j < ha->bus_cnt; ++j) {
+ for (k = 0; k < MAXID; ++k) {
+ if (ha->id[j][k].type == CACHE_DTYP &&
+ ha->id[j][k].hostdrive == piowr->iu.lockdrv.drives[i]) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+ if (!found)
+ continue;
+
+ if (piowr->iu.lockdrv.lock) {
+ save_flags( flags );
+ cli();
+ ha->id[j][k].lock = 1;
+ restore_flags( flags );
+ gdth_wait_completion( hanum, j, k );
+ gdth_stop_timeout( hanum, j, k );
+ } else {
+ save_flags( flags );
+ cli();
+ ha->id[j][k].lock = 0;
+ restore_flags( flags );
+ gdth_start_timeout( hanum, j, k );
+ gdth_next( hanum );
+ }
+ }
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ break;
+
+ case GDTIOCTL_LOCKCHN:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ for (k = 0, j = piowr->iu.lockchn.channel; k < MAXID; ++k) {
+ if (ha->id[j][k].type != RAW_DTYP)
+ continue;
+
+ if (piowr->iu.lockchn.lock) {
+ save_flags( flags );
+ cli();
+ ha->id[j][k].lock = 1;
+ restore_flags( flags );
+ gdth_wait_completion( hanum, j, k );
+ gdth_stop_timeout( hanum, j, k );
+ } else {
+ save_flags( flags );
+ cli();
+ ha->id[j][k].lock = 0;
+ restore_flags( flags );
+ gdth_start_timeout( hanum, j, k );
+ gdth_next( hanum );
+ }
+ }
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ break;
+
+ case GDTIOCTL_EVENT:
+ id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) );
+ if (id == -1)
+ return(-EBUSY);
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ if (piowr->iu.event.erase == 0) {
+ piord->iu.event.handle = gdth_read_event( piowr->iu.event.handle,
+ (gdth_evt_str *)piord->iu.event.evt );
+ } else {
+ piord->iu.event.handle = piowr->iu.event.handle;
+ gdth_readapp_event( (unchar)piowr->iu.event.erase,
+ (gdth_evt_str *)piord->iu.event.evt );
+ }
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ break;
+
+ default:
+ return(-EINVAL);
+ }
+ /* we return a buffer ID to detect the right buffer during READ-IOCTL */
+ return id;
+}
+
+static int gdth_get_info(char *buffer,char **start,off_t offset,
+ int length,int vh,int hanum,int busnum)
+{
+ int size = 0,len = 0;
+ off_t begin = 0,pos = 0;
+ gdth_ha_str *ha;
+ gdth_iord_str *piord;
+ int id;
+
+ TRACE2(("gdth_get_info() ha %d bus %d\n",hanum,busnum));
+ ha = HADATA(gdth_ctr_tab[hanum]);
+ id = length;
+
+ /* look for buffer ID in length */
+ if (id > 4) {
+#if LINUX_VERSION_CODE >= 0x020000
+ size = sprintf(buffer+len,
+ "%s SCSI Disk Array Controller\n",
+ gdth_ctr_name(hanum));
+#else
+ size = sprintf(buffer+len,
+ "%s SCSI Disk Array Controller (SCSI Bus %d)\n",
+ gdth_ctr_name(hanum),busnum);
+#endif
+ len += size; pos = begin + len;
+ size = sprintf(buffer+len,
+ "Firmware Version: %d.%2d\tDriver Version: %s\n",
+ (unchar)(ha->cpar.version>>8),
+ (unchar)(ha->cpar.version),GDTH_VERSION_STR);
+ len += size; pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+
+ } else {
+ piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum];
+ if (piord == NULL)
+ goto stop_output;
+ length = piord->size;
+ memcpy(buffer+len, (char *)piord, length);
+ gdth_ioctl_free(hanum, id);
+ len += length; pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+ }
+
+stop_output:
+ *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,pos,begin,offset,length,size));
+ return(len);
+}
+
+
+void gdth_scsi_done(Scsi_Cmnd *scp)
+{
+ TRACE2(("gdth_scsi_done()\n"));
+
+ scp->request.rq_status = RQ_SCSI_DONE;
+
+ if (scp->request.sem != NULL)
+ up(scp->request.sem);
+}
+
+static int gdth_ioctl_alloc(int hanum, ushort size)
+{
+ ulong flags;
+ int i;
+
+ if (size == 0)
+ return -1;
+
+ save_flags(flags);
+ cli();
+
+ for (i = 0; i < 4; ++i) {
+ if (gdth_ioctl_tab[i][hanum] == NULL) {
+ gdth_ioctl_tab[i][hanum] = kmalloc( size, GFP_ATOMIC | GFP_DMA );
+ break;
+ }
+ }
+
+ restore_flags(flags);
+ if (i == 4 || gdth_ioctl_tab[i][hanum] == NULL)
+ return -1;
+ return (i+1);
+}
+
+static void gdth_ioctl_free(int hanum, int idx)
+{
+ ulong flags;
+
+ save_flags(flags);
+ cli();
+
+ kfree( gdth_ioctl_tab[idx-1][hanum] );
+ gdth_ioctl_tab[idx-1][hanum] = NULL;
+
+ restore_flags(flags);
+}
+
+static void gdth_wait_completion(int hanum, int busnum, int id)
+{
+ ulong flags;
+ int i;
+ Scsi_Cmnd *scp;
+
+ save_flags(flags);
+ cli();
+
+ for (i = 0; i < GDTH_MAXCMDS; ++i) {
+ scp = gdth_cmd_tab[i][hanum].cmnd;
+ if (!SPECIAL_SCP(scp) && scp->target == (unchar)id &&
+#if LINUX_VERSION_CODE >= 0x020000
+ scp->channel == (unchar)busnum)
+#else
+ NUMDATA(scp->host)->busnum == (unchar)busnum)
+#endif
+ {
+ restore_flags(flags);
+ while (!scp->SCp.have_data_in)
+ barrier();
+ save_flags(flags);
+ cli();
+ }
+ }
+ restore_flags(flags);
+}
+
+static void gdth_stop_timeout(int hanum, int busnum, int id)
+{
+ ulong flags;
+ Scsi_Cmnd *scp;
+ gdth_ha_str *ha;
+
+ save_flags(flags);
+ cli();
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) {
+ if (scp->target == (unchar)id &&
+#if LINUX_VERSION_CODE >= 0x020000
+ scp->channel == (unchar)busnum)
+#else
+ NUMDATA(scp->host)->busnum == (unchar)busnum)
+#endif
+ {
+ TRACE2(("gdth_stop_timeout(): update_timeout()\n"));
+ scp->SCp.buffers_residual = gdth_update_timeout(scp, 0);
+ }
+ }
+ restore_flags(flags);
+}
+
+static void gdth_start_timeout(int hanum, int busnum, int id)
+{
+ ulong flags;
+ Scsi_Cmnd *scp;
+ gdth_ha_str *ha;
+
+ save_flags(flags);
+ cli();
+ ha = HADATA(gdth_ctr_tab[hanum]);
+
+ for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) {
+ if (scp->target == (unchar)id &&
+#if LINUX_VERSION_CODE >= 0x020000
+ scp->channel == (unchar)busnum)
+#else
+ NUMDATA(scp->host)->busnum == (unchar)busnum)
+#endif
+ {
+ TRACE2(("gdth_start_timeout(): update_timeout()\n"));
+ gdth_update_timeout(scp, scp->SCp.buffers_residual);
+ }
+ }
+ restore_flags(flags);
+}
+
+static int gdth_update_timeout(Scsi_Cmnd *scp, int timeout)
+{
+ ulong flags;
+ int oldto;
+
+ save_flags(flags);
+ cli();
+
+ oldto = scp->timeout;
+ scp->timeout = timeout;
+ if (timeout > 0) {
+ if (timer_table[SCSI_TIMER].expires == 0) {
+ timer_table[SCSI_TIMER].expires = jiffies + timeout;
+ timer_active |= 1 << SCSI_TIMER;
+ } else {
+ if (jiffies + timeout < timer_table[SCSI_TIMER].expires)
+ timer_table[SCSI_TIMER].expires = jiffies + timeout;
+ }
+ }
+
+ restore_flags(flags);
+ return oldto;
+}
+
diff --git a/drivers/scsi/gdth_proc.h b/drivers/scsi/gdth_proc.h
new file mode 100644
index 000000000..a3d5dcd71
--- /dev/null
+++ b/drivers/scsi/gdth_proc.h
@@ -0,0 +1,24 @@
+#ifndef _GDTH_PROC_H
+#define _GDTH_PROC_H
+
+/* gdth_proc.h
+ * $Id: gdth_proc.h,v 1.2 1997/02/21 08:08:51 achim Exp $
+ */
+
+static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum);
+static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp);
+static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp);
+static int gdth_get_info(char *buffer,char **start,off_t offset,
+ int length,int vh,int hanum,int busnum);
+
+static int gdth_ioctl_alloc(int hanum, ushort size);
+static void gdth_ioctl_free(int hanum, int id);
+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(Scsi_Cmnd *scp, int timeout);
+
+void gdth_scsi_done(Scsi_Cmnd *scp);
+
+#endif
+
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index e67ec54b5..83c912247 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -198,6 +198,22 @@
#include "mac53c94.h"
#endif
+#ifdef CONFIG_SCSI_GDTH
+#include "gdth.h"
+#endif
+
+#ifdef CONFIG_SCSI_PCI2000
+#include "pci2000.h"
+#endif
+
+#ifdef CONFIG_SCSI_PCI2220I
+#include "pci2220i.h"
+#endif
+
+#ifdef CONFIG_SCSI_PSI240I
+#include "psi240i.h"
+#endif
+
#ifdef CONFIG_SCSI_DEBUG
#include "scsi_debug.h"
#endif
@@ -260,6 +276,17 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
#ifdef CONFIG_SCSI_ADVANSYS
ADVANSYS,
#endif
+
+#ifdef CONFIG_SCSI_PCI2000
+ PCI2000,
+#endif
+#ifdef CONFIG_SCSI_PCI2220I
+ PCI2220I,
+#endif
+#ifdef CONFIG_SCSI_PSI240I
+ PSI240I,
+#endif
+
/* BusLogic must come before aha1542.c */
#ifdef CONFIG_SCSI_BUSLOGIC
BUSLOGIC,
@@ -345,6 +372,9 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
#ifdef CONFIG_SCSI_SUNESP
SCSI_SPARC_ESP,
#endif
+#ifdef CONFIG_SCSI_GDTH
+ GDTH,
+#endif
#ifdef CONFIG_SCSI_QLOGICPTI
QLOGICPTI,
#endif
diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c
index 55ac986a0..1bd283a45 100644
--- a/drivers/scsi/ibmmca.c
+++ b/drivers/scsi/ibmmca.c
@@ -7,6 +7,7 @@
/* Update history:
Jan 15 1996: First public release.
+ - Martin Kolinek
Jan 23 1996: Scrapped code which reassigned scsi devices to logical
device numbers. Instead, the existing assignment (created
@@ -16,15 +17,18 @@
and also the hard disks are ordered under Linux the
same way as they are under dos (i.e., C: disk is sda,
D: disk is sdb, etc.).
+ - Martin Kolinek
I think that the CD-ROM is now detected only if a CD is
inside CD_ROM while Linux boots. This can be fixed later,
once the driver works on all types of PS/2's.
+ - Martin Kolinek
Feb 7 1996: Modified biosparam function. Fixed the CD-ROM detection.
For now, devices other than harddisk and CD_ROM are
ignored. Temporarily modified abort() function
- to behave like reset().
+ to behave like reset().
+ - Martin Kolinek
Mar 31 1996: The integrated scsi subsystem is correctly found
in PS/2 models 56,57, but not in model 76. Therefore
@@ -32,21 +36,200 @@
This function allows the user to force detection of
scsi subsystem. The kernel option has format
ibmmcascsi=n
- where n is the scsi_id (pun) of the subsystem. Most
- likely, n is 7.
+ where n is the scsi_id (pun) of the subsystem. Most likely, n is 7.
+ - Martin Kolinek
Aug 21 1996: Modified the code which maps ldns to (pun,0). It was
insufficient for those of us with CD-ROM changers.
- Chris Beauregard
-
- Mar 16 1997: Modified driver to run as a module and to support
- multiple adapters.
+
+ Dec 14 1996: More improvements to the ldn mapping. See check_devices
+ for details. Did more fiddling with the integrated SCSI detection,
+ but I think it's ultimately hopeless without actually testing the
+ model of the machine. The 56, 57, 76 and 95 (ultimedia) all have
+ different integrated SCSI register configurations. However, the 56
+ and 57 are the only ones that have problems with forced detection.
+ - Chris Beauregard
+
+ Mar 8-16 1997: Modified driver to run as a module and to support
+ multiple adapters. A structure, called ibmmca_hostdata, is now
+ present, containing all the variables, that were once only
+ available for one single adapter. The find_subsystem-routine has vanished.
+ The hardware recognition is now done in ibmmca_detect directly.
+ This routine checks for presence of MCA-bus, checks the interrupt
+ level and continues with checking the installed hardware.
+ Certain PS/2-models do not recognize a SCSI-subsystem automatically.
+ Hence, the setup defined by command-line-parameters is checked first.
+ Thereafter, the routine probes for an integrated SCSI-subsystem.
+ Finally, adapters are checked. This method has the advantage to cover all
+ possible combinations of multiple SCSI-subsystems on one MCA-board. Up to
+ eight SCSI-subsystems can be recognized and announced to the upper-level
+ drivers with this improvement. A set of defines made changes to other
+ routines as small as possible.
- Klaus Kudielka
- */
+
+ May 30 1997: (v1.5b)
+ 1) SCSI-command capability enlarged by the recognition of MODE_SELECT.
+ This needs the RD-Bit to be disabled on IM_OTHER_SCSI_CMD_CMD which
+ allows data to be written from the system to the device. It is a
+ necessary step to be allowed to set blocksize of SCSI-tape-drives and
+ the tape-speed, whithout confusing the SCSI-Subsystem.
+ 2) The recognition of a tape is included in the check_devices routine.
+ This is done by checking for TYPE_TAPE, that is already defined in
+ the kernel-scsi-environment. The markup of a tape is done in the
+ global ldn_is_tape[] array. If the entry on index ldn
+ is 1, there is a tapedrive connected.
+ 3) The ldn_is_tape[] array is necessary to distinguish between tape- and
+ other devices. Fixed blocklength devices should not cause a problem
+ with the SCB-command for read and write in the ibmmca_queuecommand
+ subroutine. Therefore, I only derivate the READ_XX, WRITE_XX for
+ the tape-devices, as recommended by IBM in this Technical Reference,
+ mentioned below. (IBM recommends to avoid using the read/write of the
+ subsystem, but the fact was, that read/write causes a command error from
+ the subsystem and this causes kernel-panic.)
+ 4) In addition, I propose to use the ldn instead of a fix char for the
+ display of PS2_DISK_LED_ON(). On 95, one can distinguish between the
+ devices that are accessed. It shows activity and easyfies debugging.
+ The tape-support has been tested with a SONY SDT-5200 and a HP DDS-2
+ (I do not know yet the type). Optimization and CD-ROM audio-support,
+ I am working on ...
+ - Michael Lang
+
+ June 19 1997: (v1.6b)
+ 1) Submitting the extra-array ldn_is_tape[] -> to the local ld[]
+ device-array.
+ 2) CD-ROM Audio-Play seems to work now.
+ 3) When using DDS-2 (120M) DAT-Tapes, mtst shows still density-code
+ 0x13 for ordinary DDS (61000 BPM) instead 0x24 for DDS-2. This appears
+ also on Adaptec 2940 adaptor in a PCI-System. Therefore, I assume that
+ the problem is independent of the low-level-driver/bus-architecture.
+ 4) Hexadecimal ldn on PS/2-95 LED-display.
+ 5) Fixing of the PS/2-LED on/off that it works right with tapedrives and
+ does not confuse the disk_rw_in_progress counter.
+ - Michael Lang
+
+ June 21 1997: (v1.7b)
+ 1) Adding of a proc_info routine to inform in /proc/scsi/ibmmca/<host> the
+ outer-world about operational load statistics on the different ldns,
+ seen by the driver. Everybody that has more than one IBM-SCSI should
+ test this, because I only have one and cannot see what happens with more
+ than one IBM-SCSI hosts.
+ 2) Definition of a driver version-number to have a better recognition of
+ the source when there are existing too much releases that may confuse
+ the user, when reading about release-specific problems. Up to know,
+ I calculated the version-number to be 1.7. Because we are in BETA-test
+ yet, it is today 1.7b.
+ 3) Sorry for the heavy bug I programmed on June 19 1997! After that, the
+ CD-ROM did not work any more! The C7-command was a fake impression
+ I got while programming. Now, the READ and WRITE commands for CD-ROM are
+ no longer running over the subsystem, but just over
+ IM_OTHER_SCSI_CMD_CMD. On my observations (PS/2-95), now CD-ROM mounts
+ much faster(!) and hopefully all fancy multimedia-functions, like direct
+ digital recording from audio-CDs also work. (I tried it with cdda2wav
+ from the cdwtools-package and it filled up the harddisk immediately :-).)
+ To easify boolean logics, a further local device-type in ld[], called
+ is_cdrom has been included.
+ 4) If one uses a SCSI-device of unsupported type/commands, one
+ immediately runs into a kernel-panic caused by Command Error. To better
+ understand which SCSI-command caused the problem, I extended this
+ specific panic-message slightly.
+ - Michael Lang
+
+ June 25 1997: (v1.8b)
+ 1) Some cosmetical changes for the handling of SCSI-device-types.
+ Now, also CD-Burners / WORMs and SCSI-scanners should work. For
+ MO-drives I have no experience, therefore not yet supported.
+ In logical_devices I changed from different type-variables to one
+ called 'device_type' where the values, corresponding to scsi.h,
+ of a SCSI-device are stored.
+ 2) There existed a small bug, that maps a device, coming after a SCSI-tape
+ wrong. Therefore, e.g. a CD-ROM changer would have been mapped wrong
+ -> problem removed.
+ 3) Extension of the logical_device structure. Now it contains also device,
+ vendor and revision-level of a SCSI-device for internal usage.
+ - Michael Lang
+
+ June 26-29 1997: (v2.0b)
+ 1) The release number 2.0b is necessary because of the completely new done
+ recognition and handling of SCSI-devices with the adapter. As I got
+ from Chris the hint, that the subsystem can reassign ldns dynamically,
+ I remembered this immediate_assign-command, I found once in the handbook.
+ Now, the driver first kills all ldn assignments that are set by default
+ on the SCSI-subsystem. After that, it probes on all puns and luns for
+ devices by going through all combinations with immediate_assign and
+ probing for devices, using device_inquiry. The found physical(!) pun,lun
+ structure is stored in get_scsi[][] as device types. This is followed
+ by the assignment of all ldns to existing SCSI-devices. If more ldns
+ than devices are available, they are assigned to non existing pun,lun
+ combinations to satisfy the adapter. With this, the dynamical mapping
+ was possible to implement. (For further info see the text in the
+ source-code and in the description below. Read the description
+ below BEFORE installing this driver on your system!)
+ 2) Changed the name IBMMCA_DRIVER_VERSION to IBMMCA_SCSI_DRIVER_VERSION.
+ 3) The LED-display shows on PS/2-95 no longer the ldn, but the SCSI-ID
+ (pun) of the accessed SCSI-device. This is now senseful, because the
+ pun known within the driver is exactly the pun of the physical device
+ and no longer a fake one.
+ 4) The /proc/scsi/ibmmca/<host_no> consists now of the first part, where
+ hit-statistics of ldns is shown and a second part, where the maps of
+ physical and logical SCSI-devices are displayed. This could be very
+ interesting, when one is using more than 15 SCSI-devices in order to
+ follow the dynamical remapping of ldns.
+ - Michael Lang
+
+ June 26-29 1997: (v2.0b-1)
+ 1) I forgot to switch the local_checking_phase_flag to 1 and back to 0
+ in the dynamical remapping part in ibmmca_queuecommand for the
+ device_exist routine. Sorry.
+ - Michael Lang
+
+ July 1-13 1997: (v3.0b,c)
+ 1) Merging of the driver-developments of Klaus Kudielka and Michael Lang
+ in order to get a optimum and unified driver-release for the
+ IBM-SCSI-Subsystem-Adapter(s).
+ For people, using the Kernel-release >=2.1.0, module-support should
+ be no problem. For users, running under <2.1.0, module-support may not
+ work, because the methods have changed between 2.0.x and 2.1.x.
+ 2) Added some more effective statistics for /proc-output.
+ 3) Change typecasting at necessary points from (unsigned long) to
+ virt_to_bus().
+ 4) Included #if... at special points to have specific adaption of the
+ driver to kernel 2.0.x and 2.1.x. It should therefore also run with
+ later releases.
+ 5) Magneto-Optical drives and medium-changers are also recognized, now.
+ Therefore, we have a completely gapfree recognition of all SCSI-
+ device-types, that are known by Linux up to kernel 2.1.31.
+ 6) The flag SCSI_IBMMCA_DEV_RESET has been inserted. If it is set within
+ the configuration, each connected SCSI-device will get a reset command
+ during boottime. This can be necessary for some special SCSI-devices.
+ This flag should be included in Config.in.
+ (See also the new Config.in file.)
+ Probable next improvement: bad disk handler.
+ - Michael Lang
+
+ Sept 14 1997: (v3.0c)
+ 1) Some debugging and speed optimization applied.
+ - Michael Lang
+
+
+
+ TODO:
+
+ - It seems that the handling of bad disks is really bad -
+ non-existent, in fact.
+ - More testing of the full driver-controlled dynamical ldn
+ (re)mapping for up to 56 SCSI-devices.
+ - Support more SCSI-device-types, if Linux defines more.
+ - Support more of the SCSI-command set.
+ - Support some of the caching abilities, particularly Read Prefetch.
+ This fetches data into the cache, which later gets hit by the
+ regular Read Data.
+ - Abort and Reset functions still slightly buggy. Especially when
+ floppydisk(!) operations report errors.
+
+******************************************************************************/
#include <linux/module.h>
-
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/head.h>
#include <linux/types.h>
@@ -65,8 +248,13 @@
#include "hosts.h"
#include "ibmmca.h"
+#include <linux/config.h> /* for CONFIG_SCSI_IBMMCA etc. */
+
/*--------------------------------------------------------------------*/
+/* current version of this driver-source: */
+#define IBMMCA_SCSI_DRIVER_VERSION "3.0d"
+
/*
Driver Description
@@ -115,6 +303,47 @@
ldn -> (ldn/8,ldn%8). We end up with a real mishmash of puns
and luns, but it all seems to work. - Chris Beaurgard
+ And that last paragraph is also no longer correct. It uses a
+ slightly more complex mapping that will always map hard disks to
+ (x,0), for some x, and consecutive none disk devices will usually
+ share puns.
+
+ Again, the last paragraphs are no longer correct. Now, the physical
+ SCSI-devices on the SCSI-bus are probed via immediate_assign- and
+ device_inquiry-commands. This delivers a exact map of the physical
+ SCSI-world that is now stored in the get_scsi[][]-array. This means,
+ that the once hidden pun,lun assignment is now known to this driver.
+ It no longer believes in default-settings of the subsystem and maps all
+ ldns to existing pun,lun by foot. This assures full control of the ldn
+ mapping and allows dynamical remapping of ldns to different pun,lun, if
+ there are more SCSI-devices installed than ldns available (n>15). The
+ ldns from 0 to 6 get 'hardwired' by this driver to puns 0 to 7 at lun=0,
+ excluding the pun of the subsystem. This assures, that at least simple
+ SCSI-installations have optimum access-speed and are not touched by
+ dynamical remapping. The ldns 7 to 14 are put to existing devices with
+ lun>0 or to non-existing devices, in order to satisfy the subsystem, if
+ there are less than 15 SCSI-devices connected. In the case of more than 15
+ devices, the dynamical mapping goes active. If the get_scsi[][] reports a
+ device to be existant, but it has no ldn assigned, it gets a ldn out of 7
+ to 14. The numbers are assigned in cyclic order. Therefore it takes 8
+ dynamical assignments on SCSI-devices, until a certain device
+ looses its ldn again. This assures, that dynamical remapping is avoided
+ during intense I/O between up to eight SCSI-devices (means pun,lun
+ combinations). A further advantage of this method is, that people who
+ build their kernel without probing on all luns will get what they expect.
+
+ IMPORTANT: Because of the now correct recognition of physical pun,lun, and
+ their report to mid-level- and higher-level-drivers, the new reported puns
+ can be different from the old, faked puns. Therefore, Linux will eventually
+ change /dev/sdXXX assignments and prompt you for corrupted superblock
+ repair on boottime. In this case DO NOT PANIC, YOUR DISKS ARE STILL OK!!!
+ You have to reboot (CTRL-D) with a old kernel and set the /etc/fstab-file
+ entries right. After that, the system should come up as errorfree as before.
+ If your boot-partition is not coming up, also edit the /etc/lilo.conf-file
+ in a Linux session booted on old kernel and run lilo before reboot. Check
+ lilo.conf anyway to get boot on other partitions with foreign OSes right
+ again.
+
(C) Regular Processing
Only three functions get involved: ibmmca_queuecommand(), issue_cmd(),
and interrupt_handler().
@@ -123,7 +352,8 @@
ibmmca_queuecommand(). This function fills a "subsystem control block"
(scb) and calls a local function issue_cmd(), which writes a scb
command into subsystem I/O ports. Once the scb command is carried out,
- interrupt_handler() is invoked.
+ interrupt_handler() is invoked. If a device is determined to be existant
+ and it has not assigned any ldn, it gets one dynamically.
(D) Abort, Reset.
These are implemented with busy waiting for interrupt to arrive.
@@ -137,12 +367,24 @@
100% sure that it is correct for larger disks.
(F) Kernel Boot Option
- The function ibmmca_scsi_setup() is called if option ibmmcascsi=...
+ The function ibmmca_scsi_setup() is called if option ibmmcascsi=n
is passed to the kernel. See file linux/init/main.c for details.
+
+ (G) Driver Module Support
+ Is implemented and tested by K. Kudielka. This could probably not work
+ on kernels <2.1.0.
+
+ (H) Multiple Hostadapter Support
+ This driver supports up to eight interfaces of type IBM-SCSI-Subsystem.
+ Integrated-, and MCA-adapters are automatically recognized. Unrecognizable
+ IBM-SCSI-Subsystem interfaces can be specified as kernel-parameters.
+
+ (I) /proc-Filesystem Information
+ Information about the driver condition is given in
+ /proc/scsi/ibmmca/<host_no>. ibmmca_proc_info provides this information.
*/
/*--------------------------------------------------------------------*/
-
/* Here are the values and structures specific for the subsystem.
* The source of information is "Update for the PS/2 Hardware
* Interface Technical Reference, Common Interfaces", September 1991,
@@ -152,26 +394,57 @@
* In addition to SCSI subsystem, this update contains fairly detailed
* (at hardware register level) sections on diskette controller,
* keyboard controller, serial port controller, VGA, and XGA.
+ *
+ * Additional information from "Personal System/2 Micro Channel SCSI
+ * Adapter with Cache Technical Reference", March 1990, PN 68X2365,
+ * probably available from the same source (or possibly found buried
+ * in officemates desk).
+ *
+ * Further literature/program-sources referred for this driver:
+ *
+ * Friedhelm Schmidt, "SCSI-Bus und IDE-Schnittstelle - Moderne Peripherie-
+ * Schnittstellen: Hardware, Protokollbeschreibung und Anwendung", 2. Aufl.
+ * Addison Wesley, 1996.
+ *
+ * Michael K. Johnson, "The Linux Kernel Hackers' Guide", Version 0.6, Chapel
+ * Hill - North Carolina, 1995
+ *
+ * Andreas Kaiser, "SCSI TAPE BACKUP for OS/2 2.0", Version 2.12, Stuttgart
+ * 1993
*/
+/*--------------------------------------------------------------------*/
+
/* driver configuration */
#define IM_MAX_HOSTS 8 /* maximum number of host adapters */
#define IM_RESET_DELAY 10 /* seconds allowed for a reset */
/* driver debugging - #undef all for normal operation */
-#undef IM_DEBUG_TIMEOUT 50 /* if defined: count interrupts
- and ignore this special one */
-#undef IM_DEBUG_INT /* verbose interrupt */
-#undef IM_DEBUG_CMD /* verbose queuecommand */
-
-/* addresses of hardware registers on the subsystem */
-#define IM_CMD_REG (shpnt->io_port) /*Command Interface, (4 bytes long) */
-#define IM_ATTN_REG (shpnt->io_port+4) /*Attention (1 byte) */
-#define IM_CTR_REG (shpnt->io_port+5) /*Basic Control (1 byte) */
-#define IM_INTR_REG (shpnt->io_port+6) /*Interrupt Status (1 byte, r/o) */
-#define IM_STAT_REG (shpnt->io_port+7) /*Basic Status (1 byte, read only) */
+/* if defined: count interrupts and ignore this special one: */
+#undef IM_DEBUG_TIMEOUT 50
+/* 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 (shpnt->io_port) /*Command Interface, (4 bytes long) */
+#define IM_ATTN_REG (shpnt->io_port+4) /*Attention (1 byte) */
+#define IM_CTR_REG (shpnt->io_port+5) /*Basic Control (1 byte) */
+#define IM_INTR_REG (shpnt->io_port+6) /*Interrupt Status (1 byte, r/o) */
+#define IM_STAT_REG (shpnt->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 find */
#define IM_N_IO_PORT 8
/*requests going into the upper nibble of the Attention register */
@@ -204,11 +477,11 @@
/*immediate commands (word written into low 2 bytes of command reg) */
#define IM_RESET_IMM_CMD 0x0400
-#define IM_FORMAT_PREP_IMM_CMD 0x0417
#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
@@ -257,6 +530,13 @@ struct im_sge
#define IM_DEVICE_INQUIRY_CMD 0x1c0b
#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
@@ -286,10 +566,41 @@ struct im_tsb
/*subsystem uses interrupt request level 14 */
#define IM_IRQ 14
-/*PS2 disk led is turned on/off by bits 6,7 of system control port */
-#define PS2_SYS_CTR 0x92
-#define PS2_DISK_LED_ON() outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR)
-#define PS2_DISK_LED_OFF() outb(inb(PS2_SYS_CTR) & 0x3f, PS2_SYS_CTR)
+/*--------------------------------------------------------------------*/
+/*
+ The model 95 doesn't have a standard activity light. Instead it
+ has a row of LEDs on the front. We use the last one as the activity
+ indicator if we think we're on a model 95. I suspect the model id
+ check will be either too narrow or too general, and some machines
+ won't have an activity indicator. Oh well...
+
+ 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
+
+/* 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. */
+
+#define PS2_DISK_LED_ON(ad,id) {\
+ if( machine_id == 0xf8 ) { outb((char)(id+48), MOD95_LED_PORT ); \
+ outb((char)(ad+48), MOD95_LED_PORT+1); } \
+ else outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR); \
+}
+
+#define PS2_DISK_LED_OFF() {\
+ if( machine_id == 0xf8 ) { outb( ' ', MOD95_LED_PORT ); \
+ outb(' ', MOD95_LED_PORT+1); } \
+ else outb(inb(PS2_SYS_CTR) | 0x3f, PS2_SYS_CTR); \
+}
/*--------------------------------------------------------------------*/
@@ -315,7 +626,8 @@ struct proc_dir_entry proc_scsi_ibmmca =
S_IFDIR | S_IRUGO | S_IXUGO, 2
};
-/*max number of logical devices (can be up to 15) */
+/* 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 */
@@ -324,50 +636,108 @@ struct logical_device
struct im_scb scb;
struct im_tsb tsb;
struct im_sge sge[16];
- Scsi_Cmnd *cmd;
- int is_disk;
+ Scsi_Cmnd *cmd; /* SCSI-command that is currently in progress */
+
+ int device_type; /* type of the SCSI-device. See include/scsi/scsi.h
+ for interpreation of the possible values */
int block_length;
};
+/* 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 total_accesses; /* total accesses on all ldns */
+ int total_interrupts; /* total interrupts (should be
+ same as total_accesses) */
+ /* 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 */
+{
+ /* array of logical devices */
struct logical_device _ld[MAX_LOG_DEV];
- /* array to convert (pun, lun) into logical device number */
+ /* array to convert (pun, lun) into logical device number */
unsigned char _get_ldn[8][8];
- /* used only when checking logical devices */
+ /*array that contains the information about the physical SCSI-devices
+ attached to this host adapter */
+ unsigned char _get_scsi[8][8];
+ /* used only when checking logical devices */
int _local_checking_phase_flag;
int _got_interrupt;
int _stat_result;
- /* reset status (used only when doing reset) */
+ /* reset status (used only when doing reset) */
int _reset_status;
- };
-
-/* reset status values */
-#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
+ /* code of the last SCSI command (needed for panic info) */
+ int _last_scsi_command;
+ /* counter that points on next reassignable ldn for dynamical remapping */
+ /* The default value is 7, that is the first reassignable number in
+ the list on startup. */
+ int _next_ldn;
+ /* Statistics for this IBM-SCSI-host */
+ struct Driver_Statistics _IBM_DS;
+};
/* macros to access host data structure */
#define HOSTDATA(shpnt) ((struct ibmmca_hostdata *) shpnt->hostdata)
#define subsystem_pun (shpnt->this_id)
#define ld (HOSTDATA(shpnt)->_ld)
#define get_ldn (HOSTDATA(shpnt)->_get_ldn)
+#define get_scsi (HOSTDATA(shpnt)->_get_scsi)
#define local_checking_phase_flag (HOSTDATA(shpnt)->_local_checking_phase_flag)
#define got_interrupt (HOSTDATA(shpnt)->_got_interrupt)
#define stat_result (HOSTDATA(shpnt)->_stat_result)
#define reset_status (HOSTDATA(shpnt)->_reset_status)
+#define last_scsi_command (HOSTDATA(shpnt)->_last_scsi_command)
+#define next_ldn (HOSTDATA(shpnt)->_next_ldn)
+#define IBM_DS (HOSTDATA(shpnt)->_IBM_DS)
+
+/* Define a arbitrary number as subsystem-marker-type. This number is, as
+ described in the 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 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, define
+ this type here. */
+#ifndef TYPE_MEDIUM_CHANGER
+#define TYPE_MEDIUM_CHANGER 0x08
+#endif
-/*--------------------------------------------------------------------*/
+/* define operations for immediate_assign */
+#define SET_LDN 0
+#define REMOVE_LDN 1
+
+/* 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
+
+/*-----------------------------------------------------------------------*/
/* if this is nonzero, ibmmcascsi option has been passed to the kernel */
-static int io_port[IM_MAX_HOSTS] = { 0 };
-static int scsi_id[IM_MAX_HOSTS] = { 7 };
+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 >=2.1.0) */
+#ifdef MODULE_PARM
MODULE_PARM(io_port, "1-" __MODULE_STRING(IM_MAX_HOSTS) "i");
-MODULE_PARM(scsi_id, "1-" __MODULE_STRING(IM_MAX_HOSTS) "i");
+MODULE_PARM(scsi_id, "1-" __MODULE_STRING(IM_MAX_HOSTS) "i");
+#endif
/*counter of concurrent disk read/writes, to turn on/off disk led */
static int disk_rw_in_progress = 0;
@@ -375,26 +745,34 @@ static int disk_rw_in_progress = 0;
/* host information */
static int found = 0;
static struct Scsi_Host *hosts[IM_MAX_HOSTS+1] = { NULL };
+/*-----------------------------------------------------------------------*/
-/*--------------------------------------------------------------------*/
-
-/*local functions */
-static void interrupt_handler (int irq, void *dev_id,
- struct pt_regs *regs);
-static void issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg,
- unsigned char attn_reg);
+/*local functions in forward declaration */
+static void interrupt_handler (int irq, void *dev_id, struct pt_regs *regs);
+static void issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg,
+ unsigned char attn_reg);
static void internal_done (Scsi_Cmnd * cmd);
static void check_devices (struct Scsi_Host *shpnt);
-static int device_exists (struct Scsi_Host *shpnt, int ldn, int *is_disk,
- int *block_length);
-static struct Scsi_Host *ibmmca_register(Scsi_Host_Template * template,
+static int immediate_assign(struct Scsi_Host *shpnt, unsigned int pun,
+ unsigned int lun, unsigned int ldn,
+ unsigned int operation);
+static int device_inquiry(struct Scsi_Host *shpnt, int ldn,
+ unsigned char *buf);
+static char *ti_p(int value);
+static char *ti_l(int value);
+static int device_exists (struct Scsi_Host *shpnt, int ldn, int *block_length,
+ int *device_type);
+static struct Scsi_Host *ibmmca_register(Scsi_Host_Template * template,
int port, int id);
+/* local functions needed for proc_info */
+static int ldn_access_load(struct Scsi_Host *shpnt, int ldn);
+static int ldn_access_total_read_write(struct Scsi_Host *shpnt);
+
/*--------------------------------------------------------------------*/
static void
-interrupt_handler (int irq, void *dev_id,
- struct pt_regs *regs)
+interrupt_handler (int irq, void *dev_id, struct pt_regs *regs)
{
int i = 0;
struct Scsi_Host *shpnt;
@@ -402,21 +780,24 @@ interrupt_handler (int irq, void *dev_id,
unsigned int cmd_result;
unsigned int ldn;
- do shpnt = hosts[i++];
+ /* search for one adapter-response on shared interrupt */
+ do
+ shpnt = hosts[i++];
while (shpnt && !(inb(IM_STAT_REG) & IM_INTR_REQUEST));
+
+ /* return if some other device on this IRQ caused the interrupt */
if (!shpnt) return;
/*get command result and logical device */
- intr_reg = inb(IM_INTR_REG);
+ intr_reg = inb (IM_INTR_REG);
cmd_result = intr_reg & 0xf0;
ldn = intr_reg & 0x0f;
/*must wait for attention reg not busy, then send EOI to subsystem */
- while (1)
- {
+ while (1) {
cli ();
- if (!(inb (IM_STAT_REG) & IM_BUSY))
- break;
+ if (!(inb (IM_STAT_REG) & IM_BUSY))
+ break;
sti ();
}
outb (IM_EOI | ldn, IM_ATTN_REG);
@@ -424,17 +805,24 @@ interrupt_handler (int irq, void *dev_id,
/*these should never happen (hw fails, or a local programming bug) */
if (cmd_result == IM_ADAPTER_HW_FAILURE)
- panic ("IBM MCA SCSI: subsystem hardware failure.\n");
+ panic ("IBM MCA SCSI: subsystem hardware failure. Last SCSI_CMD=0x%X. \n",
+ last_scsi_command);
if (cmd_result == IM_CMD_ERROR)
- panic ("IBM MCA SCSI: command error.\n");
+ panic ("IBM MCA SCSI: command error. Last SCSI_CMD=0x%X. \n",
+ last_scsi_command);
if (cmd_result == IM_SOFTWARE_SEQUENCING_ERROR)
- panic ("IBM MCA SCSI: software sequencing error.\n");
+ panic ("IBM MCA SCSI: software sequencing error. Last SCSI_CMD=0x%X. \n",
+ last_scsi_command);
+ /* if no panic appeared, increase the interrupt-counter */
+ IBM_DS.total_interrupts++;
+
/*only for local checking phase */
if (local_checking_phase_flag)
{
stat_result = cmd_result;
got_interrupt = 1;
+ reset_status = IM_RESET_FINISHED_OK;
return;
}
@@ -454,6 +842,9 @@ interrupt_handler (int irq, void *dev_id,
}
else
{
+ /*reset disk led counter, turn off disk led */
+ disk_rw_in_progress = 0;
+ PS2_DISK_LED_OFF ();
reset_status = IM_RESET_FINISHED_OK;
}
return;
@@ -464,12 +855,12 @@ interrupt_handler (int irq, void *dev_id,
#ifdef IM_DEBUG_TIMEOUT
{
- static int count = 0;
+ static int count = 0;
- if (++count == IM_DEBUG_TIMEOUT) {
- printk("IBM MCA SCSI: Ignoring interrupt.\n");
- return;
- }
+ if (++count == IM_DEBUG_TIMEOUT) {
+ printk("IBM MCA SCSI: Ignoring interrupt.\n");
+ return;
+ }
}
#endif
@@ -481,25 +872,28 @@ interrupt_handler (int irq, void *dev_id,
#ifdef IM_DEBUG_INT
printk("cmd=%02x ireg=%02x ds=%02x cs=%02x de=%02x ce=%02x\n",
- cmd->cmnd[0], intr_reg,
- ld[ldn].tsb.dev_status, ld[ldn].tsb.cmd_status,
- ld[ldn].tsb.dev_error, ld[ldn].tsb.cmd_error);
+ cmd->cmnd[0], intr_reg,
+ ld[ldn].tsb.dev_status, ld[ldn].tsb.cmd_status,
+ ld[ldn].tsb.dev_error, ld[ldn].tsb.cmd_error);
#endif
- /*if this is end of disk read/write, may turn off PS/2 disk led */
- if (ld[ldn].is_disk)
- {
+ /*if this is end of media read/write, may turn off PS/2 disk led */
+ if ((ld[ldn].device_type!=TYPE_NO_LUN)&&
+ (ld[ldn].device_type!=TYPE_NO_DEVICE))
+ { /* only access this, if there was a valid device addressed */
switch (cmd->cmnd[0])
{
case READ_6:
case WRITE_6:
case READ_10:
case WRITE_10:
+ case READ_12:
+ case WRITE_12:
if (--disk_rw_in_progress == 0)
PS2_DISK_LED_OFF ();
}
}
-
+
/*write device status into cmd->result, and call done function */
if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE)
cmd->result = ld[ldn].tsb.dev_status & 0x1e;
@@ -512,8 +906,8 @@ interrupt_handler (int irq, void *dev_id,
/*--------------------------------------------------------------------*/
static void
-issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg,
- unsigned char attn_reg)
+issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg,
+ unsigned char attn_reg)
{
/*must wait for attention reg not busy */
while (1)
@@ -540,51 +934,23 @@ internal_done (Scsi_Cmnd * cmd)
/*--------------------------------------------------------------------*/
-static int
-ibmmca_getinfo (char *buf, int slot, void *dev)
+static int ibmmca_getinfo (char *buf, int slot, void *dev)
{
struct Scsi_Host *shpnt = dev;
int len = 0;
len += sprintf (buf + len, "Subsystem PUN: %d\n", subsystem_pun);
- len += sprintf (buf + len, "I/O base address: 0x%x\n", shpnt->io_port);
+ len += sprintf (buf + len, "I/O base address: 0x%x\n", IM_CMD_REG);
return len;
}
/*--------------------------------------------------------------------*/
-static void
-check_devices(struct Scsi_Host *shpnt)
-{
- int is_disk, block_length;
- int ldn;
- int num_ldn = 0;
-
- /* check ldn's from 0 to MAX_LOG_DEV to find which devices exist */
- for (ldn = 0; ldn < MAX_LOG_DEV; ldn++)
- {
- if (device_exists(shpnt, ldn, &is_disk, &block_length))
- {
- printk("IBM MCA SCSI: logical device found at ldn=%d.\n", ldn);
- ld[ldn].is_disk = is_disk;
- ld[ldn].block_length = block_length;
- get_ldn[num_ldn / 8][num_ldn % 8] = ldn;
- num_ldn++;
- }
- }
-
- return;
-}
-
-/*--------------------------------------------------------------------*/
-
-static int
-device_exists(struct Scsi_Host *shpnt, int ldn, int *is_disk,
- int *block_length)
-{
+/* SCSI-SCB-command for device_inquiry */
+static int device_inquiry(struct Scsi_Host *shpnt, int ldn, unsigned char *buf)
+{
struct im_scb scb;
struct im_tsb tsb;
- unsigned char buf[256];
int retries;
for (retries = 0; retries < 3; retries++)
@@ -592,7 +958,6 @@ device_exists(struct Scsi_Host *shpnt, int ldn, int *is_disk,
/*fill scb with inquiry command */
scb.command = IM_DEVICE_INQUIRY_CMD;
scb.enable = IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
- /* I think this virt_to_bus is needed.. ??? AC */
scb.sys_buf_adr = virt_to_bus(buf);
scb.sys_buf_length = 255;
scb.tsb_adr = virt_to_bus(&tsb);
@@ -611,19 +976,340 @@ device_exists(struct Scsi_Host *shpnt, int ldn, int *is_disk,
/*if all three retries failed, return "no device at this ldn" */
if (retries >= 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(struct Scsi_Host *shpnt, unsigned int pun,
+ unsigned int lun, unsigned int ldn,
+ unsigned int operation)
+{
+ int retries;
+ unsigned long imm_command;
+
+ for (retries=0; retries<3; retries ++)
+ {
+ imm_command = inl(IM_CMD_REG);
+ imm_command &= (unsigned long)(0xF8000000); /* keep reserved bits */
+ imm_command |= (unsigned long)(IM_ASSIGN_IMM_CMD);
+ imm_command |= (unsigned long)((lun & 7) << 24);
+ imm_command |= (unsigned long)((operation & 1) << 23);
+ imm_command |= (unsigned long)((pun & 7) << 20);
+ imm_command |= (unsigned long)((ldn & 15) << 16);
+
+ got_interrupt = 0;
+ issue_cmd (shpnt, (unsigned long)(imm_command), IM_IMM_CMD | 0xf);
+ while (!got_interrupt)
+ barrier ();
+
+ /*if command succesful, break */
+ if (stat_result == IM_IMMEDIATE_CMD_COMPLETED)
+ break;
+ }
+
+ if (retries >= 3)
+ return 0;
+ else
+ return 1;
+}
+
+/* type-interpreter for physical device numbers */
+static char *ti_p(int value)
+{
+ switch (value)
+ {
+ case TYPE_IBM_SCSI_ADAPTER: return("A"); break;
+ case TYPE_DISK: return("D"); break;
+ case TYPE_TAPE: return("T"); break;
+ case TYPE_PROCESSOR: return("P"); break;
+ case TYPE_WORM: return("W"); break;
+ case TYPE_ROM: return("R"); break;
+ case TYPE_SCANNER: return("S"); break;
+ case TYPE_MOD: return("M"); break;
+ case TYPE_MEDIUM_CHANGER: return("C"); break;
+ case TYPE_NO_LUN: return("+"); break; /* show NO_LUN */
+ case TYPE_NO_DEVICE:
+ default: return("-"); break;
+ }
+ return("-");
+}
+
+/* type-interpreter for logical devices
+ (A bit stupid, but it was necessary to get the '-' and the Hex-codes
+ into one type.) */
+static char *ti_l(int value)
+{
+ switch (value)
+ {
+ case 0: return("0"); break; case 1: return("1"); break;
+ case 2: return("2"); break; case 3: return("3"); break;
+ case 4: return("4"); break; case 5: return("5"); break;
+ case 6: return("6"); break; case 7: return("7"); break;
+ case 8: return("8"); break; case 9: return("9"); break;
+ case 10: return("a"); break; case 11: return("b"); break;
+ case 12: return("c"); break; case 13: return("d"); break;
+ case 14: return("e"); break; case 15: return("f"); break;
+ default: return("-"); break;
+ }
+ return("-");
+}
+
+/*
+ The following routine probes the SCSI-devices in four steps:
+ 1. The current ldn -> pun,lun mapping is removed on the SCSI-adapter.
+ 2. ldn 0 is used to go through all possible combinations of pun,lun and
+ a device_inquiry is done to fiddle out whether there is a device
+ responding or not. This physical map is stored in get_scsi[][].
+ 3. The 15 available ldns (0-14) are mapped to existing pun,lun.
+ If there are more devices than ldns, it stops at 14 for the boot
+ time. Dynamical remapping will be done in ibmmca_queuecommand.
+ 4. If there are less than 15 valid pun,lun, the remaining ldns are
+ mapped to NON-existing pun,lun to satisfy the adapter. Information
+ about pun,lun -> ldn is stored as before in get_ldn[][].
+ This method leads to the result, that the SCSI-pun,lun shown to Linux
+ mid-level- and higher-level-drivers is exactly corresponding to the
+ physical reality on the SCSI-bus. Therefore, it is possible that users
+ of older releases of this driver have to rewrite their fstab-file, because
+ the /dev/sdXXX could have changed due to the right pun,lun report, now.
+ The assignment of ALL ldns avoids dynamical remapping by the adapter
+ itself.
+ */
+static void check_devices (struct Scsi_Host *shpnt)
+{
+ int id, lun, ldn;
+ unsigned char buf[256];
+ int count_devices = 0; /* local counter for connected device */
+
+ /* assign default values to certain variables */
+
+ IBM_DS.dyn_flag = 0; /* normally no need for dynamical ldn management */
+ next_ldn = 7; /* next ldn to be assigned is 7, because 0-6 is 'hardwired'*/
+ last_scsi_command = 0; /* emptify last SCSI-command storage */
+
+ /* initialize the very important driver-informational arrays/structs */
+ memset (ld, 0, sizeof ld);
+ memset (get_ldn, TYPE_NO_DEVICE, sizeof get_ldn); /* this is essential ! */
+ memset (get_scsi, TYPE_NO_DEVICE, sizeof get_scsi); /* this is essential ! */
+
+ for (lun=0; lun<=7; lun++) /* mark the adapter at its pun on all luns*/
+ {
+ get_scsi[subsystem_pun][lun] = TYPE_IBM_SCSI_ADAPTER;
+ get_ldn[subsystem_pun][lun] = MAX_LOG_DEV; /* make sure, the subsystem
+ ldn is active for all
+ luns. */
+ }
+
+ /* STEP 1: */
+ printk("IBM MCA SCSI: Removing current logical SCSI-device mapping.");
+ for (ldn=0; ldn<MAX_LOG_DEV; ldn++)
+ {
+#ifdef IM_DEBUG_PROBE
+ printk(".");
+#endif
+ immediate_assign(shpnt,0,0,ldn,REMOVE_LDN); /* remove ldn (wherever)*/
+ }
+
+ lun = 0;
+
+ /* STEP 2: */
+ printk("\nIBM MCA SCSI: Probing SCSI-devices.");
+ for (id=0; id<=7; id++)
+#ifdef CONFIG_SCSI_MULTI_LUN
+ for (lun=0; lun<=7; lun++)
+#endif
+ {
+#ifdef IM_DEBUG_PROBE
+ printk(".");
+#endif
+ if (id != subsystem_pun)
+ { /* if pun is not the adapter: */
+ immediate_assign(shpnt,id,lun,0,SET_LDN); /*set ldn=0 to pun,lun*/
+ if (device_inquiry(shpnt, 0, buf)) /* probe device */
+ {
+ get_scsi[id][lun]=(unsigned char)buf[0]; /* entry, even
+ for NO_LUN */
+ if (buf[0] != TYPE_NO_LUN)
+ count_devices++; /* a existing device is found */
+ }
+ immediate_assign(shpnt,id,lun,0,REMOVE_LDN); /* remove ldn */
+ }
+ }
+
+ /* STEP 3: */
+ printk("\nIBM MCA SCSI: Mapping SCSI-devices.");
+
+ ldn = 0;
+ lun = 0;
+
+#ifdef CONFIG_SCSI_MULTI_LUN
+ for (lun=0; lun<=7 && ldn<MAX_LOG_DEV; lun++)
+#endif
+ for (id=0; id<=7 && ldn<MAX_LOG_DEV; id++)
+ {
+#ifdef IM_DEBUG_PROBE
+ printk(".");
+#endif
+ if (id != subsystem_pun)
+ {
+ if (get_scsi[id][lun] != TYPE_NO_LUN &&
+ get_scsi[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(shpnt,id,lun,ldn,SET_LDN);
+ get_ldn[id][lun]=ldn; /* map ldn */
+ if (device_exists (shpnt, ldn, &ld[ldn].block_length,
+ &ld[ldn].device_type))
+ {
+#ifdef SCSI_IBMMCA_DEV_RESET
+ int ticks;
+ printk("(resetting)");
+ ticks = IM_RESET_DELAY*HZ;
+ reset_status = IM_RESET_IN_PROGRESS;
+ issue_cmd (shpnt, IM_RESET_IMM_CMD, IM_IMM_CMD | ldn);
+ while (reset_status == IM_RESET_IN_PROGRESS && --ticks)
+ {
+ udelay(1000000/HZ);
+ barrier();
+ }
+ /* if reset did not complete, just claim */
+ if (!ticks)
+ {
+ printk("IBM MCA SCSI: reset did not complete within %d seconds.\n",
+ IM_RESET_DELAY);
+ reset_status = IM_RESET_FINISHED_OK;
+ /* did not work, finish */
+ }
+#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[id][lun]=TYPE_NO_DEVICE;
+ immediate_assign(shpnt,0,0,ldn,REMOVE_LDN);
+ }
+ else ldn++;
+ }
+ }
+ else if (lun == 0)
+ {
+ /* map lun == 0, even if no device exists */
+ immediate_assign(shpnt,id,lun,ldn,SET_LDN);
+ get_ldn[id][lun]=ldn; /* map ldn */
+ ldn++;
+ }
+ }
+ }
+
+ /* STEP 4: */
+
+ /* map remaining ldns to non-existing devices */
+ for (lun=1; lun<=7 && ldn<MAX_LOG_DEV; lun++)
+ for (id=0; id<=7 && ldn<MAX_LOG_DEV; id++)
+ {
+ if (get_scsi[id][lun] == TYPE_NO_LUN ||
+ get_scsi[id][lun] == TYPE_NO_DEVICE)
+ {
+ /* 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(shpnt,id,lun,ldn,SET_LDN);
+ get_ldn[id][lun]=ldn;
+ ldn++;
+ }
+ }
+
+ printk("\n");
+
+#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<=7; id++)
+ {
+ printk("%2d %2s %2s %2s %2s %2s %2s %2s %2s",
+ id, ti_p(get_scsi[id][0]), ti_p(get_scsi[id][1]),
+ ti_p(get_scsi[id][2]), ti_p(get_scsi[id][3]),
+ ti_p(get_scsi[id][4]), ti_p(get_scsi[id][5]),
+ ti_p(get_scsi[id][6]), ti_p(get_scsi[id][7]));
+ printk(" %2d %2s %2s %2s %2s %2s %2s %2s %2s\n",
+ id, ti_l(get_ldn[id][0]), ti_l(get_ldn[id][1]),
+ ti_l(get_ldn[id][2]), ti_l(get_ldn[id][3]),
+ ti_l(get_ldn[id][4]), ti_l(get_ldn[id][5]),
+ ti_l(get_ldn[id][6]), ti_l(get_ldn[id][7]));
+ }
+#endif
+
+ /* assign total number of found SCSI-devices to the statistics struct */
+ IBM_DS.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.dyn_flag = 1; /* dynamical assignment is necessary */
+ else
+ IBM_DS.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/assignable!\n");
+
+ /* reset the counters for statistics on the current adapter */
+ IBM_DS.total_accesses = 0;
+ IBM_DS.total_interrupts = 0;
+ IBM_DS.dynamical_assignments = 0;
+ memset (IBM_DS.ldn_access, 0x0, sizeof (IBM_DS.ldn_access));
+ memset (IBM_DS.ldn_read_access, 0x0, sizeof (IBM_DS.ldn_read_access));
+ memset (IBM_DS.ldn_write_access, 0x0, sizeof (IBM_DS.ldn_write_access));
+ memset (IBM_DS.ldn_assignments, 0x0, sizeof (IBM_DS.ldn_assignments));
+
+ return;
+}
+
+/*--------------------------------------------------------------------*/
+
+static int
+device_exists (struct Scsi_Host *shpnt, int ldn, int *block_length,
+ int *device_type)
+{
+ struct im_scb scb;
+ struct im_tsb tsb;
+ unsigned char buf[256];
+ int retries;
+ /* if no valid device found, return immediately with 0 */
+ if (!(device_inquiry(shpnt, ldn, buf))) return 0;
+
/*if device is CD_ROM, assume block size 2048 and return */
if (buf[0] == TYPE_ROM)
{
- *is_disk = 0;
+ *device_type = TYPE_ROM;
+ *block_length = 2048; /* (standard blocksize for yellow-/red-book) */
+ return 1;
+ }
+
+ if (buf[0] == TYPE_WORM) /* CD-burner, WORM, Linux handles this as CD-ROM
+ therefore, the block_length is also 2048. */
+ {
+ *device_type = TYPE_WORM;
*block_length = 2048;
return 1;
}
-
- /*if device is disk, use "read capacity" to find its block size */
+
+ /* if device is disk, use "read capacity" to find its block size */
if (buf[0] == TYPE_DISK)
{
- *is_disk = 1;
+ *device_type = TYPE_DISK;
for (retries = 0; retries < 3; retries++)
{
@@ -653,7 +1339,70 @@ device_exists(struct Scsi_Host *shpnt, int ldn, int *is_disk,
return 0;
}
- /*for now, ignore tape and other devices - return 0 */
+ /* if this is a magneto-optical drive, treat it like a harddisk */
+ if (buf[0] == TYPE_MOD)
+ {
+ *device_type = TYPE_MOD;
+
+ for (retries = 0; retries < 3; retries++)
+ {
+ /*fill scb with read capacity command */
+ scb.command = IM_READ_CAPACITY_CMD;
+ scb.enable = IM_READ_CONTROL;
+ scb.sys_buf_adr = virt_to_bus(buf);
+ scb.sys_buf_length = 8;
+ scb.tsb_adr = virt_to_bus(&tsb);
+
+ /*issue scb to passed ldn, and busy wait for interrupt */
+ got_interrupt = 0;
+ issue_cmd (shpnt, virt_to_bus(&scb), IM_SCB | ldn);
+ while (!got_interrupt)
+ barrier ();
+
+ /*if got capacity, get block length and return one device found */
+ if (stat_result == IM_SCB_CMD_COMPLETED)
+ {
+ *block_length = buf[7] + (buf[6] << 8) + (buf[5] << 16) + (buf[4] << 24);
+ return 1;
+ }
+ }
+
+ /*if all three retries failed, return "no device at this ldn" */
+ if (retries >= 3)
+ return 0;
+ }
+
+ if (buf[0] == TYPE_TAPE) /* TAPE-device found */
+ {
+ *device_type = TYPE_TAPE;
+ *block_length = 0; /* not in use (setting by mt and mtst in op.) */
+ return 1;
+ }
+
+ if (buf[0] == TYPE_PROCESSOR) /* HP-Scanners, diverse SCSI-processing units*/
+ {
+ *device_type = TYPE_PROCESSOR;
+ *block_length = 0; /* they set their stuff on drivers */
+ return 1;
+ }
+
+ if (buf[0] == TYPE_SCANNER) /* other SCSI-scanners */
+ {
+ *device_type = TYPE_SCANNER;
+ *block_length = 0; /* they set their stuff on drivers */
+ return 1;
+ }
+
+ if (buf[0] == TYPE_MEDIUM_CHANGER) /* Medium-Changer */
+ {
+ *device_type = TYPE_MEDIUM_CHANGER;
+ *block_length = 0; /* One never knows, what to expect on a medium
+ changer device. */
+ return 1;
+ }
+
+ /* Up to now, no SCSI-devices that are known up to kernel 2.1.31 are
+ ignored! MO-drives are now supported and treated as harddisk. */
return 0;
}
@@ -664,19 +1413,17 @@ device_exists(struct Scsi_Host *shpnt, int ldn, int *is_disk,
void
ibmmca_scsi_setup (char *str, int *ints)
{
- int i;
+ int i;
- for (i = 0; i < IM_MAX_HOSTS && i < ints[0]; i++)
- {
+ for (i = 0; i < IM_MAX_HOSTS && i < ints[0]; i++)
io_port[i] = ints[i+1];
- }
}
#endif
/*--------------------------------------------------------------------*/
-int
+int
ibmmca_detect (Scsi_Host_Template * template)
{
struct Scsi_Host *shpnt;
@@ -697,11 +1444,11 @@ ibmmca_detect (Scsi_Host_Template * template)
/* 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 detection, io=0x%x, scsi id=%d.\n",
- io_port[i], scsi_id[i]);
+ io_port[i], scsi_id[i]);
ibmmca_register(template, io_port[i], scsi_id[i]);
- }
+ }
if (found) return found;
/* first look for the SCSI integrated on the motherboard */
@@ -712,13 +1459,13 @@ ibmmca_detect (Scsi_Host_Template * template)
port = IM_IO_PORT + ((pos2 & 0x0e) << 2);
id = (pos3 & 0xe0) >> 5;
printk("IBM MCA SCSI: integrated SCSI found, io=0x%x, scsi id=%d.\n",
- port, id);
+ port, id);
if ((shpnt = ibmmca_register(template, port, id)))
- {
- mca_set_adapter_name(MCA_INTEGSCSI, "PS/2 Integrated SCSI");
- mca_set_adapter_procfn(MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo,
- shpnt);
- }
+ {
+ mca_set_adapter_name(MCA_INTEGSCSI, "PS/2 Integrated SCSI");
+ mca_set_adapter_procfn(MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo,
+ shpnt);
+ }
}
/* now look for other adapters */
@@ -727,22 +1474,22 @@ ibmmca_detect (Scsi_Host_Template * template)
{
slot = 0;
while ((slot = mca_find_adapter(subsys_list[i].mca_id, slot))
- != MCA_NOTFOUND)
- {
- pos2 = mca_read_stored_pos(slot, 2);
- pos3 = mca_read_stored_pos(slot, 3);
- port = IM_IO_PORT + ((pos2 & 0x0e) << 2);
- id = (pos3 & 0xe0) >> 5;
- printk ("IBM MCA SCSI: %s found in slot %d, io=0x%x, scsi id=%d.\n",
- subsys_list[i].description, slot + 1, port, id);
- if ((shpnt = ibmmca_register(template, port, id)))
- {
- mca_set_adapter_name (slot, subsys_list[i].description);
- mca_set_adapter_procfn (slot, (MCA_ProcFn) ibmmca_getinfo,
- shpnt);
- }
- slot++;
- }
+ != MCA_NOTFOUND)
+ {
+ pos2 = mca_read_stored_pos(slot, 2);
+ pos3 = mca_read_stored_pos(slot, 3);
+ port = IM_IO_PORT + ((pos2 & 0x0e) << 2);
+ id = (pos3 & 0xe0) >> 5;
+ printk ("IBM MCA SCSI: %s found in slot %d, io=0x%x, scsi id=%d.\n",
+ subsys_list[i].description, slot + 1, port, id);
+ if ((shpnt = ibmmca_register(template, port, id)))
+ {
+ mca_set_adapter_name (slot, subsys_list[i].description);
+ mca_set_adapter_procfn (slot, (MCA_ProcFn) ibmmca_getinfo,
+ shpnt);
+ }
+ slot++;
+ }
}
if (!found) {
@@ -753,8 +1500,6 @@ ibmmca_detect (Scsi_Host_Template * template)
return found;
}
-/*--------------------------------------------------------------------*/
-
static struct Scsi_Host *
ibmmca_register(Scsi_Host_Template * template, int port, int id)
{
@@ -765,7 +1510,7 @@ ibmmca_register(Scsi_Host_Template * template, int port, int id)
if (check_region(port, IM_N_IO_PORT))
{
printk("IBM MCA SCSI: Unable to get I/O region 0x%x-0x%x.\n",
- port, port + IM_N_IO_PORT);
+ port, port + IM_N_IO_PORT);
return NULL;
}
@@ -785,6 +1530,9 @@ ibmmca_register(Scsi_Host_Template * template, int port, int id)
shpnt->io_port = port;
shpnt->n_io_port = IM_N_IO_PORT;
shpnt->this_id = id;
+
+ reset_status = IM_RESET_NOT_IN_PROGRESS;
+
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
get_ldn[i][j] = MAX_LOG_DEV;
@@ -800,17 +1548,6 @@ ibmmca_register(Scsi_Host_Template * template, int port, int id)
/*--------------------------------------------------------------------*/
-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;
-}
-
-/*--------------------------------------------------------------------*/
-
int
ibmmca_command (Scsi_Cmnd * cmd)
{
@@ -823,21 +1560,134 @@ ibmmca_command (Scsi_Cmnd * cmd)
/*--------------------------------------------------------------------*/
-int
-ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+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. The old edition is
+ now improved by dynamical reassignment of ldn numbers that are
+ currently not assigned. The mechanism works in a way, that first
+ the physical structure is checked. If at a certain pun,lun a device
+ should be present, the routine proceeds to the ldn check from
+ get_ldn. An answer of 0xff would show-up, that the aimed device is
+ currently not assigned any ldn. At this point, the dynamical
+ remapping algorithm is called. It works in a way, that it goes in
+ cyclic order through the ldns from 7 to 14. If a ldn is assigned,
+ it takes 8 dynamical reassignment calls, until a device looses its
+ ldn again. With this method it is assured, that while doing
+ intense I/O between up to eight devices, no dynamical remapping is
+ done there. ldns 0 through 6(!) are left untouched, which means, that
+ puns 0 through 7(!) on lun=0 are always accessible without remapping.
+ These ldns are statically assigned by this driver. The subsystem always
+ occupies at least one pun, therefore 7 ldns (at lun=0) for other devices
+ are sufficient. (The adapter uses always ldn=15, at whatever pun it is.) */
+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 = cmd->host;
-
- /*if (target,lun) unassigned, return error */
+
+ int current_ldn;
+ int id,lun;
+
+ /*if (target,lun) is NO LUN or not existing at all, return error */
+ if ((get_scsi[cmd->target][cmd->lun] == TYPE_NO_LUN)||
+ (get_scsi[cmd->target][cmd->lun] == TYPE_NO_DEVICE))
+ {
+ cmd->result = DID_NO_CONNECT << 16;
+ done (cmd);
+ return 0;
+ }
+
+ /*if (target,lun) unassigned, do further checks... */
ldn = get_ldn[cmd->target][cmd->lun];
- if (ldn >= MAX_LOG_DEV)
+ if (ldn >= MAX_LOG_DEV) /* on invalid ldn do special stuff */
{
- cmd->result = DID_NO_CONNECT << 16;
- done (cmd);
- return 0;
+ if (ldn > MAX_LOG_DEV) /* dynamical remapping if ldn unassigned */
+ {
+ current_ldn = next_ldn; /* stop-value for one circle */
+ while (ld[next_ldn].cmd) /* search for a occupied, but not in */
+ { /* command-processing ldn. */
+ next_ldn ++;
+ if (next_ldn>=MAX_LOG_DEV)
+ next_ldn = 7;
+ if (current_ldn == next_ldn) /* One circle done ? */
+ { /* no non-processing ldn found */
+ printk("IBM MCA SCSI: Cannot assign SCSI-device dynamically!\n");
+ printk(" On ldn 7-14 SCSI-commands everywhere in progress.\n");
+ printk(" Reporting DID_NO_CONNECT for device (%d,%d).\n",
+ cmd->target, cmd->lun);
+ cmd->result = DID_NO_CONNECT << 16;/* return no connect*/
+ done (cmd);
+ return 0;
+ }
+ }
+
+ /* unmap non-processing ldn */
+ for (id=0; id<=7; id ++)
+ for (lun=0; lun<=7; lun++)
+ {
+ if (get_ldn[id][lun] == next_ldn)
+ {
+ get_ldn[id][lun] = TYPE_NO_DEVICE; /* unmap entry */
+ goto DYN_ASSIGN; /* jump out as fast as possible */
+ }
+ }
+
+DYN_ASSIGN:
+ /* unassign found ldn (pun,lun does not matter for remove) */
+ immediate_assign(shpnt,0,0,next_ldn,REMOVE_LDN);
+ /* assign found ldn to aimed pun,lun */
+ immediate_assign(shpnt,cmd->target,cmd->lun,next_ldn,SET_LDN);
+ /* map found ldn to pun,lun */
+ get_ldn[cmd->target][cmd->lun] = next_ldn;
+ /* change ldn to the right value, that is now next_ldn */
+ ldn = next_ldn;
+ /* set reduced interrupt_handler-mode for checking */
+ local_checking_phase_flag = 1;
+ /* get device information for ld[ldn] */
+ if (device_exists (shpnt, ldn, &ld[ldn].block_length,
+ &ld[ldn].device_type))
+ {
+ ld[ldn].cmd = 0; /* To prevent panic set 0, because
+ devices that were not assigned,
+ should have nothing in progress. */
+
+ /* increase assignment counters for statistics in /proc */
+ IBM_DS.dynamical_assignments++;
+ IBM_DS.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, cmd->target, cmd->lun);
+
+ /* set back to normal interrupt_handling */
+ local_checking_phase_flag = 0;
+
+ /* Information on syslog terminal */
+ printk("IBM MCA SCSI: ldn=0x%x dynamically reassigned to (%d,%d).\n",
+ ldn, cmd->target, cmd->lun);
+
+ /* increase next_ldn for next dynamical assignment */
+ next_ldn ++;
+ if (next_ldn>=MAX_LOG_DEV) next_ldn = 7;
+ }
+ else
+ { /* wall against Linux accesses to the subsystem adapter */
+ cmd->result = DID_NO_CONNECT << 16;
+ done (cmd);
+ return 0;
+ }
}
/*verify there is no command already in progress for this log dev */
@@ -875,71 +1725,149 @@ ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
/*fill scb information dependent on scsi command */
scsi_cmd = cmd->cmnd[0];
+
#ifdef IM_DEBUG_CMD
printk("issue scsi cmd=%02x to ldn=%d\n", scsi_cmd, ldn);
#endif
+
+ /* for specific device debugging: */
+#ifdef IM_DEBUG_CMD_SPEC_DEV
+ if (ld[ldn].device_type==IM_DEBUG_CMD_DEVICE)
+ printk("(SCSI-device-type=0x%x) issue scsi cmd=%02x to ldn=%d\n",
+ ld[ldn].device_type, scsi_cmd, ldn);
+#endif
+
+ /* for possible panics store current command */
+ last_scsi_command = scsi_cmd;
+
+ /* update statistical info */
+ IBM_DS.total_accesses++;
+ IBM_DS.ldn_access[ldn]++;
+
switch (scsi_cmd)
{
case READ_6:
case WRITE_6:
case READ_10:
case WRITE_10:
- if (scsi_cmd == READ_6 || scsi_cmd == READ_10)
- {
- scb->command = IM_READ_DATA_CMD;
- scb->enable |= IM_READ_CONTROL;
- }
- else
- {
- scb->command = IM_WRITE_DATA_CMD;
- }
- 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);
- }
- scb->u2.blk.length = ld[ldn].block_length;
- if (ld[ldn].is_disk)
- {
- if (++disk_rw_in_progress == 1)
- PS2_DISK_LED_ON ();
- }
+ case READ_12:
+ case WRITE_12:
+ /* statistics for proc_info */
+ if ((scsi_cmd == READ_6)||(scsi_cmd == READ_10)||(scsi_cmd == READ_12))
+ IBM_DS.ldn_read_access[ldn]++; /* increase READ-access on ldn stat. */
+ else if ((scsi_cmd == WRITE_6)||(scsi_cmd == WRITE_10)||
+ (scsi_cmd == WRITE_12))
+ IBM_DS.ldn_write_access[ldn]++; /* increase write-count on ldn stat.*/
+
+ /* 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[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)
+ {
+ scb->command = IM_READ_DATA_CMD;
+ scb->enable |= IM_READ_CONTROL;
+ }
+ else
+ {
+ scb->command = IM_WRITE_DATA_CMD;
+ }
+ 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);
+ }
+ scb->u2.blk.length = ld[ldn].block_length;
+ if (++disk_rw_in_progress == 1)
+ PS2_DISK_LED_ON (shpnt->host_no, cmd->target);
+ 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.
+ Good/modern CD-ROM-drives are capable of
+ reading sequential AND random-access. This leads to the problem,
+ that random-accesses are covered by the subsystem, but
+ sequentials are not, as like for tape-drives. Therefore, it is
+ the easiest way to use IM_OTHER_SCSI_CMD_CMD for all read-ops
+ on CD-ROM-drives in order not to run into timing problems and
+ to have a stable state. In addition, data-access on CD-ROMs
+ works faster like that. Strange, but obvious. */
+
+ 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 | IM_SUPRESS_EXCEPTION_SHORT;
+ else
+ scb->enable |= IM_SUPRESS_EXCEPTION_SHORT; /* assume WRITE */
+
+ scb->u1.scsi_cmd_length = cmd->cmd_len;
+ memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
+
+ /* Read/write on this non-disk devices is also displayworthy,
+ so flash-up the LED/display. */
+ if (++disk_rw_in_progress == 1)
+ PS2_DISK_LED_ON (shpnt->host_no, cmd->target);
+ break;
+ }
break;
-
case INQUIRY:
scb->command = IM_DEVICE_INQUIRY_CMD;
- scb->enable |= IM_READ_CONTROL |
- IM_SUPRESS_EXCEPTION_SHORT;
+ scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
break;
case READ_CAPACITY:
scb->command = IM_READ_CAPACITY_CMD;
scb->enable |= IM_READ_CONTROL;
- /*the length of system memory buffer must be exactly 8 bytes */
+ /* the length of system memory buffer must be exactly 8 bytes */
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;
break;
-
+
+ /* Commands that need write-only-mode (system -> device): */
+ case MODE_SELECT:
+ case MODE_SELECT_10:
+ scb->command = IM_OTHER_SCSI_CMD_CMD;
+ scb->enable |= IM_SUPRESS_EXCEPTION_SHORT; /*Select needs WRITE-enabled*/
+ scb->u1.scsi_cmd_length = cmd->cmd_len;
+ memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
+ 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;
+ scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
scb->u1.scsi_cmd_length = cmd->cmd_len;
memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
break;
@@ -958,10 +1886,10 @@ ibmmca_abort (Scsi_Cmnd * cmd)
/* The code below doesn't work right now, so we tell the upper layer
that we can't abort. This eventually causes a reset.
*/
- return SCSI_ABORT_SNOOZE;
+ return SCSI_ABORT_SNOOZE ;
#if 0
- struct Scsi_Host *shpnt = cmd->host;
+ struct Scsi_host *shpnt = cmd->host;
unsigned int ldn;
void (*saved_done) (Scsi_Cmnd *);
@@ -986,7 +1914,7 @@ ibmmca_abort (Scsi_Cmnd * cmd)
saved_done = cmd->scsi_done;
cmd->scsi_done = internal_done;
cmd->SCp.Status = 0;
- issue_cmd (shpnt, IM_ABORT_IMM_CMD, IM_IMM_CMD | ldn);
+ issue_cmd (shpnt, T_IMM_CMD, IM_IMM_CMD | ldn);
while (!cmd->SCp.Status)
barrier ();
@@ -1004,7 +1932,7 @@ ibmmca_abort (Scsi_Cmnd * cmd)
/*--------------------------------------------------------------------*/
-int
+int
ibmmca_reset (Scsi_Cmnd * cmd, unsigned int reset_flags)
{
struct Scsi_Host *shpnt = cmd->host;
@@ -1024,15 +1952,14 @@ ibmmca_reset (Scsi_Cmnd * cmd, unsigned int reset_flags)
udelay(1000000/HZ);
barrier();
}
-
/* if reset did not complete, just return an error*/
if (!ticks) {
printk("IBM MCA SCSI: reset did not complete within %d seconds.\n",
- IM_RESET_DELAY);
+ IM_RESET_DELAY);
reset_status = IM_RESET_FINISHED_FAIL;
return SCSI_RESET_ERROR;
}
-
+
/* if reset failed, just return an error */
if (reset_status == IM_RESET_FINISHED_FAIL) {
printk("IBM MCA SCSI: reset failed.\n");
@@ -1045,13 +1972,13 @@ ibmmca_reset (Scsi_Cmnd * cmd, unsigned int reset_flags)
int i;
for (i = 0; i < MAX_LOG_DEV; i++)
{
- Scsi_Cmnd *cmd = ld[i].cmd;
- if (cmd && cmd->scsi_done)
- {
- ld[i].cmd = 0;
- cmd->result = DID_RESET;
- (cmd->scsi_done) (cmd);
- }
+ Scsi_Cmnd *cmd = ld[i].cmd;
+ if (cmd && cmd->scsi_done)
+ {
+ ld[i].cmd = 0;
+ cmd->result = DID_RESET;
+ (cmd->scsi_done) (cmd);
+ }
}
}
return SCSI_RESET_SUCCESS;
@@ -1082,7 +2009,114 @@ ibmmca_biosparam (Disk * disk, kdev_t dev, int *info)
return 0;
}
-/*--------------------------------------------------------------------*/
+/* calculate percentage of total accesses on a ldn */
+static int ldn_access_load(struct Scsi_Host *shpnt, int ldn)
+{
+ if (IBM_DS.total_accesses == 0) return (0);
+ if (IBM_DS.ldn_access[ldn] == 0) return (0);
+ return((int)(((float)IBM_DS.ldn_access[ldn]/(float)IBM_DS.total_accesses)*(float)100.000));
+}
+
+/* calculate total amount of r/w-accesses */
+static int ldn_access_total_read_write(struct Scsi_Host *shpnt)
+{
+ int a = 0;
+ int i;
+
+ for (i=0; i<=MAX_LOG_DEV; i++)
+ a+=IBM_DS.ldn_read_access[i]+IBM_DS.ldn_write_access[i];
+ return(a);
+}
+
+/* routine to display info in the proc-fs-structure (a deluxe feature) */
+int ibmmca_proc_info (char *buffer, char **start, off_t offset, int length,
+ int hostno, int inout)
+{
+ int len=0;
+ int i,id;
+ struct Scsi_Host *shpnt;
+
+ for (i = 0; hosts[i] && hosts[i]->host_no != hostno; i++);
+ shpnt = hosts[i];
+ if (!shpnt) {
+ len += sprintf(buffer+len, "\nCan't find adapter for host number %d\n", hostno);
+ return len;
+ }
+
+ cli();
+
+ 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");
+#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",
+ hostno);
+ len += sprintf(buffer+len, " Base I/O-Port............: 0x%x\n",
+ IM_CMD_REG);
+ len += sprintf(buffer+len, " (Shared) IRQ.............: %d\n",
+ IM_IRQ);
+ len += sprintf(buffer+len, " Total Interrupts.........: %d\n",
+ IBM_DS.total_interrupts);
+ len += sprintf(buffer+len, " Total SCSI Accesses......: %d\n",
+ IBM_DS.total_accesses);
+ len += sprintf(buffer+len, " Total SCSI READ/WRITE..: %d\n",
+ ldn_access_total_read_write(shpnt));
+ len += sprintf(buffer+len, " Total SCSI other cmds..: %d\n\n",
+ IBM_DS.total_accesses - ldn_access_total_read_write(shpnt));
+
+ 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(shpnt, i), IBM_DS.ldn_read_access[i],
+ IBM_DS.ldn_write_access[i], IBM_DS.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.total_scsi_devices);
+ len += sprintf(buffer+len, " Dynamical Assignment necessaray..: %s\n",
+ IBM_DS.dyn_flag ? "Yes" : "No ");
+ len += sprintf(buffer+len, " Next LDN to be assigned..........: 0x%x\n",
+ next_ldn);
+ len += sprintf(buffer+len, " Dynamical assignments done yet...: %d\n",
+ IBM_DS.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<=7; id++)
+ {
+ len += sprintf(buffer+len, " %2d %2s %2s %2s %2s %2s %2s %2s %2s",
+ id, ti_p(get_scsi[id][0]), ti_p(get_scsi[id][1]),
+ ti_p(get_scsi[id][2]), ti_p(get_scsi[id][3]),
+ ti_p(get_scsi[id][4]), ti_p(get_scsi[id][5]),
+ ti_p(get_scsi[id][6]), ti_p(get_scsi[id][7]));
+ len += sprintf(buffer+len, " %2d %2s %2s %2s %2s %2s %2s %2s %2s\n",
+ id, ti_l(get_ldn[id][0]), ti_l(get_ldn[id][1]),
+ ti_l(get_ldn[id][2]), ti_l(get_ldn[id][3]),
+ ti_l(get_ldn[id][4]), ti_l(get_ldn[id][5]),
+ ti_l(get_ldn[id][6]), ti_l(get_ldn[id][7]));
+ }
+
+ 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)\n\n");
+
+ *start = buffer + offset;
+ len -= offset;
+ if (len > length)
+ len = length;
+
+ sti();
+
+ return len;
+}
#ifdef MODULE
/* Eventually this will go into an include file, but this will be later */
@@ -1091,3 +2125,4 @@ Scsi_Host_Template driver_template = IBMMCA;
#include "scsi_module.c"
#endif
+/*--------------------------------------------------------------------*/
diff --git a/drivers/scsi/ibmmca.h b/drivers/scsi/ibmmca.h
index 90b184829..4306f52ac 100644
--- a/drivers/scsi/ibmmca.h
+++ b/drivers/scsi/ibmmca.h
@@ -1,12 +1,12 @@
-
#ifndef _IBMMCA_H
#define _IBMMCA_H
-/*
+/*
* Low Level Driver for the IBM Microchannel SCSI Subsystem
*/
/*services provided to the higher level of Linux SCSI driver */
+int ibmmca_proc_info (char *, char **, off_t, int, int, int);
int ibmmca_detect (Scsi_Host_Template *);
int ibmmca_release (struct Scsi_Host *);
int ibmmca_command (Scsi_Cmnd *);
@@ -21,9 +21,9 @@ extern struct proc_dir_entry proc_scsi_ibmmca;
/*initialization for Scsi_host_template type */
#define IBMMCA { \
NULL, /*next*/ \
- NULL, /*module*/ \
+ NULL, /*usage_count*/ \
&proc_scsi_ibmmca, /*proc_dir*/ \
- NULL, /*proc info fn*/ \
+ ibmmca_proc_info, /*proc info fn*/ \
"IBMMCA", /*name*/ \
ibmmca_detect, /*detect fn*/ \
ibmmca_release, /*release fn*/ \
diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c
index 9945ece31..2595f77f2 100644
--- a/drivers/scsi/ncr53c8xx.c
+++ b/drivers/scsi/ncr53c8xx.c
@@ -67,7 +67,7 @@
*/
/*
-** 23 August 1997, version 2.5a
+** 20 September 1997, version 2.5c
**
** Supported SCSI-II features:
** Synchronous negotiation
@@ -464,9 +464,9 @@ static int guess_xfer_direction(int opcode);
/*
** Head of list of NCR boards
**
-** Host is retrieved by its irq level.
-** If interrupts are shared, the internal host control block
-** address (struct ncb) is used as device id.
+** For kernel version < 1.3.70, host is retrieved by its irq level.
+** For later kernels, the internal host control block address
+** (struct ncb) is used as device id parameter of the irq stuff.
*/
static struct Scsi_Host *first_host = NULL;
@@ -535,6 +535,9 @@ static struct ncr_driver_setup
#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
static struct ncr_driver_setup
driver_safe_setup __initdata = SCSI_NCR_DRIVER_SAFE_SETUP;
+#ifdef MODULE
+char *ncr53c8xx = 0; /* command line passed by insmod */
+#endif
#endif
/*
@@ -4590,7 +4593,7 @@ printf(KERN_INFO "ncr53c%s-%d: rev=0x%02x, base=0x%x, io_port=0x%x, irq=%d\n",
SA_INTERRUPT|SA_SHIRQ, "ncr53c8xx", np)) {
#else
if (request_irq(device->slot.irq, ncr53c8xx_intr,
- SA_INTERRUPT, "ncr53c8xx", NULL)) {
+ SA_INTERRUPT, "ncr53c8xx", np)) {
#endif
#else
if (request_irq(device->slot.irq, ncr53c8xx_intr,
@@ -4747,7 +4750,9 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
/*---------------------------------------------------
**
- ** Assign a ccb / bind cmd
+ ** 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.
**
@@ -4755,6 +4760,11 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *))
*/
save_flags(flags); cli();
+ if (np->settle_time && cmd->timeout_per_command >= HZ &&
+ np->settle_time > jiffies + cmd->timeout_per_command - HZ) {
+ np->settle_time = jiffies + cmd->timeout_per_command - HZ;
+ }
+
if (np->settle_time || !(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) {
insert_into_waiting_list(np, cmd);
restore_flags(flags);
@@ -5245,7 +5255,7 @@ int ncr_reset_bus (Scsi_Cmnd *cmd, int sync_reset)
* Commands will now be queued in the waiting list until a settle
* delay of 2 seconds will be completed.
*/
- ncr_start_reset(np, 2);
+ ncr_start_reset(np, driver_setup.settle_delay);
/*
* First, look in the wakeup list
*/
@@ -5393,7 +5403,7 @@ static int ncr_abort_command (Scsi_Cmnd *cmd)
*/
#ifdef MODULE
-static int ncr_detach(ncb_p np, int irq)
+static int ncr_detach(ncb_p np)
{
ccb_p cp;
tcb_p tp;
@@ -5432,16 +5442,12 @@ static int ncr_detach(ncb_p np, int irq)
*/
#ifdef DEBUG_NCR53C8XX
- printf("%s: freeing irq %d\n", ncr_name(np), irq);
+ printf("%s: freeing irq %d\n", ncr_name(np), np->irq);
#endif
#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
-# ifdef SCSI_NCR_SHARE_IRQ
- free_irq(irq, np);
-# else
- free_irq(irq, NULL);
-# endif
+ free_irq(np->irq, np);
#else
- free_irq(irq);
+ free_irq(np->irq);
#endif
/*
@@ -6797,14 +6803,20 @@ static void ncr_log_hard_error(ncb_p np, u_short sist, u_char dstat)
script_base = (u_char *) np->script;
script_name = "script";
}
- else {
+ 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->scripth;
script_name = "scripth";
+ } else {
+ script_ofs = dsp;
+ script_size = 0;
+ script_base = 0;
+ script_name = "mem";
}
- printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ %s (%x:%08x).\n",
+ printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ (%s %x:%08x).\n",
ncr_name (np), (unsigned)INB (nc_ctest0)&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,
@@ -6989,13 +7001,13 @@ void ncr_exception (ncb_p np)
if ((sist & (SGE)) ||
(dstat & (MDPE|BF|ABORT|IID))) {
- ncr_start_reset(np, 2);
+ ncr_start_reset(np, driver_setup.settle_delay);
return;
};
if (sist & HTH) {
printf ("%s: handshake timeout\n", ncr_name(np));
- ncr_start_reset(np, 2);
+ ncr_start_reset(np, driver_setup.settle_delay);
return;
};
@@ -7005,7 +7017,7 @@ void ncr_exception (ncb_p np)
OUTB (nc_scr1, HS_UNEXPECTED);
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup));
};
- ncr_start_reset(np, 2);
+ ncr_start_reset(np, driver_setup.settle_delay);
return;
};
@@ -7096,7 +7108,7 @@ static int ncr_int_sbmc (ncb_p np)
ncr_name(np), np->scsi_mode, scsi_mode);
np->scsi_mode = scsi_mode;
- ncr_start_reset(np, 2);
+ ncr_start_reset(np, driver_setup.settle_delay);
return 1;
}
@@ -8925,7 +8937,11 @@ void ncr53c8xx_setup(char *str, int *ints)
else
printf("ncr53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
+#ifdef MODULE
+ if ((cur = strchr(cur, ' ')) != NULL)
+#else
if ((cur = strchr(cur, ',')) != NULL)
+#endif
++cur;
}
#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
@@ -9101,6 +9117,11 @@ int ncr53c8xx_detect(Scsi_Host_Template *tpnt)
# endif
#endif
+#if defined(SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT) && defined(MODULE)
+if (ncr53c8xx)
+ ncr53c8xx_setup(ncr53c8xx, (int *) 0);
+#endif
+
/*
** Detect all 53c8xx hosts and then attach them.
**
@@ -9270,6 +9291,28 @@ printk("ncr53c8xx_pci_init() #1: bus == %d, device_fn == %d\n", bus, device_fn);
return -1;
}
+#ifdef __powerpc__
+ /*
+ * Severall fix-up for power/pc.
+ * Should not be performed by the driver.
+ */
+ if ((command &
+ (PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY)) !=
+ (PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY)) {
+ printk("ncr53c8xx : setting PCI master/io/command bit\n");
+ command |= PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY;
+ pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+ }
+ if (io_port >= 0x10000000) {
+ io_port = (io_port & 0x00FFFFFF) | 0x01000000;
+ pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, io_port);
+ }
+ if (base >= 0x10000000) {
+ base = (base & 0x00FFFFFF) | 0x01000000;
+ pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, base);
+ }
+#endif
+
/*
* Check availability of IO space, memory space and master capability.
*/
@@ -9532,48 +9575,41 @@ printk("ncr53c8xx : command successfully queued\n");
}
/*
-** Linux entry point of the interrupt handler
+** Linux entry point of the interrupt handler.
+** Fort linux versions > 1.3.70, we trust the kernel for
+** passing the internal host descriptor as 'dev_id'.
+** Otherwise, we scan the host list and call the interrupt
+** routine for each host that uses this IRQ.
*/
#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
static void ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
+{
+#ifdef DEBUG_NCR53C8XX
+ printk("ncr53c8xx : interrupt received\n");
+#endif
+
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
+ ncr_exception((ncb_p) dev_id);
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
+}
+
#else
static void ncr53c8xx_intr(int irq, struct pt_regs * regs)
-#endif
{
struct Scsi_Host *host;
struct host_data *host_data;
-#if 0
- u_long flags;
-
- save_flags(flags); cli();
-#endif
-
-printk("Yow, an NCR interrupt!\n");
-#ifdef DEBUG_NCR53C8XX
-printk("ncr53c8xx : interrupt received\n");
-#endif
for (host = first_host; host; host = host->next) {
- if (host->hostt == the_template && host->irq == irq) {
- host_data = (struct host_data *) host->hostdata;
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
-# ifdef SCSI_NCR_SHARE_IRQ
- if (dev_id == host_data->ncb) {
-#else
- if (1) {
-# endif
-#endif
- if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
- ncr_exception(host_data->ncb);
- if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
- }
- }
+ if (host->hostt == the_template && host->irq == irq) {
+ host_data = (struct host_data *) host->hostdata;
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
+ ncr_exception(host_data->ncb);
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
+ }
}
-#if 0
- restore_flags(flags);
-#endif
}
+#endif
/*
** Linux entry point of the timer handler
@@ -9679,17 +9715,10 @@ int ncr53c8xx_abort(Scsi_Cmnd *cmd)
#ifdef MODULE
int ncr53c8xx_release(struct Scsi_Host *host)
{
- struct host_data *host_data;
#ifdef DEBUG_NCR53C8XX
printk("ncr53c8xx : release\n");
#endif
-
- for (host = first_host; host; host = host->next) {
- if (host->hostt == the_template) {
- host_data = (struct host_data *) host->hostdata;
- ncr_detach(host_data->ncb, host->irq);
- }
- }
+ ncr_detach(((struct host_data *) host->hostdata)->ncb);
return 1;
}
diff --git a/drivers/scsi/ncr53c8xx.h b/drivers/scsi/ncr53c8xx.h
index b22565b11..0f8cb7876 100644
--- a/drivers/scsi/ncr53c8xx.h
+++ b/drivers/scsi/ncr53c8xx.h
@@ -45,7 +45,7 @@
/*
** Name and revision of the driver
*/
-#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - revision 2.5a"
+#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - revision 2.5c"
/*
** Check supported Linux versions
diff --git a/drivers/scsi/pci2000.c b/drivers/scsi/pci2000.c
new file mode 100644
index 000000000..0a27c904a
--- /dev/null
+++ b/drivers/scsi/pci2000.c
@@ -0,0 +1,660 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 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: pci2000i.c
+ *
+ *-M*************************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "pci2000.h"
+#include "psi_roy.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry Proc_Scsi_Pci2000 =
+ { PROC_SCSI_PCI2000, 7, "pci2000", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
+
+//#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
+ {
+ ULONG address;
+ ULONG length;
+ } SCATGATH, *PSCATGATH;
+
+typedef struct
+ {
+ Scsi_Cmnd *SCpnt;
+ SCATGATH scatGath[16];
+ UCHAR tag;
+ } DEV2000, *PDEV2000;
+
+typedef struct
+ {
+ USHORT basePort;
+ USHORT mb0;
+ USHORT mb1;
+ USHORT mb2;
+ USHORT mb3;
+ USHORT mb4;
+ USHORT cmd;
+ USHORT tag;
+ DEV2000 dev[MAX_BUS][MAX_UNITS];
+ } ADAPTER2000, *PADAPTER2000;
+
+#define HOSTDATA(host) ((PADAPTER2000)&host->hostdata)
+
+
+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 timer;
+
+ timer = jiffies + TIMEOUT_COMMAND; // calculate the timeout value
+ do {
+ if ( !inb_p (padapter->cmd) )
+ return FALSE;
+ } while ( timer > jiffies ); // test for timeout
+ 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;
+
+ if ( SCpnt->use_sg )
+ {
+ for ( z = 0; z < SCpnt->use_sg; z++ )
+ {
+ pdev->scatGath[z].address = virt_to_bus (((struct scatterlist *)SCpnt->request_buffer)[z].address);
+ pdev->scatGath[z].length = ((struct scatterlist *)SCpnt->request_buffer)[z].length;
+ }
+ outl (virt_to_bus (pdev->scatGath), padapter->mb2);
+ outl ((SCpnt->use_sg << 24) | SCpnt->request_bufflen, padapter->mb3);
+ return FALSE;
+ }
+ outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2);
+ outl (SCpnt->request_bufflen, padapter->mb3);
+ return TRUE;
+ }
+/****************************************************************
+ * 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 = 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;
+
+ DEB(printk ("\npci2000 recieved 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"));
+ return;
+ }
+
+ 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 irqProceed;
+ }
+ }
+ }
+
+ outb_p (0xFF, padapter->tag); // clear the op interrupt
+ outb_p (CMD_DONE, padapter->cmd); // complete the op
+ return; // done, but, with what?
+
+irqProceed:;
+ if ( tag & ERR08_TAGGED ) // is there an error here?
+ {
+ if ( WaitReady (padapter) )
+ {
+ OpDone (SCpnt, DID_TIME_OUT << 16);
+ 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);
+ 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);
+ 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);
+ return;
+ }
+ OpDone (SCpnt, DID_ERROR << 16);
+ return;
+ }
+
+ outb_p (0xFF, padapter->tag); // clear the op interrupt
+ outb_p (CMD_DONE, padapter->cmd); // complete the op
+ OpDone (SCpnt, DID_OK << 16);
+ }
+/****************************************************************
+ * 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 Pci2000_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+ {
+ UCHAR *cdb = (UCHAR *)SCpnt->cmnd; // Pointer to SCSI CDB
+ PADAPTER2000 padapter = HOSTDATA(SCpnt->host); // Pointer to adapter control structure
+ int rc = -1; // command return code
+ UCHAR bus = SCpnt->channel;
+ UCHAR pun = SCpnt->target;
+ UCHAR lun = SCpnt->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;
+ 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);
+ outl (virt_to_bus (cdb), 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 ( SCpnt->use_sg )
+ {
+ outl (virt_to_bus (((struct scatterlist *)(SCpnt->request_buffer))->address), padapter->mb2);
+ }
+ else
+ {
+ outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2);
+ }
+ outl (SCpnt->request_bufflen, padapter->mb3);
+ cmd = CMD_DASD_SCSI_INQ;
+ break;
+ }
+
+ case SCSIOP_TEST_UNIT_READY: // test unit ready CDB
+ outl (virt_to_bus (SCpnt->sense_buffer), padapter->mb2);
+ outl (sizeof (SCpnt->sense_buffer), padapter->mb3);
+ cmd = CMD_TEST_READY;
+ break;
+
+ case SCSIOP_READ_CAPACITY: // read capctiy CDB
+ if ( SCpnt->use_sg )
+ {
+ outl (virt_to_bus (((struct scatterlist *)(SCpnt->request_buffer))->address), padapter->mb2);
+ }
+ else
+ {
+ outl (virt_to_bus (SCpnt->request_buffer), 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 ("pci2220i_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: internal_done :LOCAL
+ *
+ * Description: Done handler for non-queued commands
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ *
+ * Returns: Nothing.
+ *
+ ****************************************************************/
+static void internal_done (Scsi_Cmnd * SCpnt)
+ {
+ SCpnt->SCp.Status++;
+ }
+/****************************************************************
+ * Name: Pci2220i_Command
+ *
+ * Description: Process a command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ *
+ * Returns: Status code.
+ *
+ ****************************************************************/
+int Pci2000_Command (Scsi_Cmnd *SCpnt)
+ {
+ DEB(printk("pci2000_command: ..calling pci2000_queuecommand\n"));
+
+ Pci2000_QueueCommand (SCpnt, internal_done);
+
+ SCpnt->SCp.Status = 0;
+ while (!SCpnt->SCp.Status)
+ barrier ();
+ return SCpnt->result;
+ }
+/****************************************************************
+ * Name: Pci2220i_Detect
+ *
+ * Description: Detect and initialize our boards.
+ *
+ * Parameters: tpnt - Pointer to SCSI host template structure.
+ *
+ * Returns: Number of adapters found.
+ *
+ ****************************************************************/
+int Pci2000_Detect (Scsi_Host_Template *tpnt)
+ {
+ int pci_index = 0;
+ struct Scsi_Host *pshost;
+ PADAPTER2000 padapter;
+ int z;
+ int setirq;
+
+ if ( pcibios_present () )
+ {
+ for ( pci_index = 0; pci_index <= MAXADAPTER; ++pci_index )
+ {
+ UCHAR pci_bus, pci_device_fn;
+
+ if ( pcibios_find_device (VENDOR_PSI, DEVICE_ROY_1, pci_index, &pci_bus, &pci_device_fn) != 0 )
+ break;
+
+ pshost = scsi_register (tpnt, sizeof(ADAPTER2000));
+ padapter = HOSTDATA(pshost);
+
+ pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &padapter->basePort);
+ padapter->basePort &= 0xFFFE;
+ 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
+
+ if ( WaitReady (padapter) )
+ goto unregister;
+ outb_p (0x84, padapter->mb0);
+ outb_p (CMD_SPECIFY, padapter->cmd);
+ if ( WaitReady (padapter) )
+ goto unregister;
+
+ pcibios_read_config_byte (pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pshost->irq);
+ setirq = 1;
+ for ( z = 0; z < pci_index; 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, 0, "pci2000", NULL) )
+ {
+ printk ("Unable to allocate IRQ for PSI-2000 controller.\n");
+ goto unregister;
+ }
+ }
+ PsiHost[pci_index] = pshost; // save SCSI_HOST pointer
+
+ pshost->unique_id = padapter->basePort;
+ pshost->max_id = 16;
+ pshost->max_channel = 1;
+
+ printk("\nPSI-2000 EIDE CONTROLLER: at I/O = %X IRQ = %d\n", padapter->basePort, pshost->irq);
+ printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n");
+ continue;
+unregister:;
+ scsi_unregister (pshost);
+ }
+ }
+ NumAdapters = pci_index;
+ return pci_index;
+ }
+/****************************************************************
+ * Name: Pci2220i_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: 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 Pci2000_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+ {
+ return SCSI_RESET_PUNT;
+ }
+
+#include "sd.h"
+
+/****************************************************************
+ * 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 Pci2000_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[])
+ {
+ PADAPTER2000 padapter;
+
+ padapter = HOSTDATA(disk->device->host);
+
+ if ( WaitReady (padapter) )
+ return 0;
+ outb_p (disk->device->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;
+ }
+
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = PCI2220I;
+
+#include "scsi_module.c"
+#endif
+
diff --git a/drivers/scsi/pci2000.h b/drivers/scsi/pci2000.h
new file mode 100644
index 000000000..ded993958
--- /dev/null
+++ b/drivers/scsi/pci2000.h
@@ -0,0 +1,226 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 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: pci2000.h
+ *
+ * Description: Header file for the SCSI driver for the PCI-2000
+ * interface card.
+ *
+ *-M*************************************************************************/
+#ifndef _PCI2000_H
+#define _PCI2000_H
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+#ifndef PSI_EIDE_SCSIOP
+#define PSI_EIDE_SCSIOP 1
+
+/************************************************/
+/* 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_BiosParam (Disk *disk, kdev_t dev, int geom[]);
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+extern struct proc_dir_entry Proc_Scsi_Pci2000;
+
+#define PCI2000 { NULL, NULL, \
+ &Proc_Scsi_Pci2000,/* proc_dir_entry */ \
+ NULL, \
+ "PCI-2000 SCSI Intelligent Disk Controller",\
+ Pci2000_Detect, \
+ NULL, \
+ NULL, \
+ Pci2000_Command, \
+ Pci2000_QueueCommand, \
+ Pci2000_Abort, \
+ Pci2000_Reset, \
+ NULL, \
+ Pci2000_BiosParam, \
+ 16, \
+ -1, \
+ 16, \
+ 1, \
+ 0, \
+ 0, \
+ DISABLE_CLUSTERING }
+
+#endif
diff --git a/drivers/scsi/pci2220i.c b/drivers/scsi/pci2220i.c
new file mode 100644
index 000000000..124cc7b44
--- /dev/null
+++ b/drivers/scsi/pci2220i.c
@@ -0,0 +1,817 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 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: pci2220i.c
+ *
+ * Description: SCSI driver for the PCI2220I EIDE interface card.
+ *
+ *-M*************************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "pci2220i.h"
+#include "psi_dale.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry Proc_Scsi_Pci2220i =
+ { PROC_SCSI_PCI2220I, 7, "pci2220i", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
+
+//#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
+
+#define MAXADAPTER 4 /* Increase this and the sizes of the arrays below, if you need more. */
+
+#define MAX_BUS_MASTER_BLOCKS 1 // This is the maximum we can bus master for (1024 bytes)
+
+#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_STAT_SEL 8
+#define PORT_FAIL 9
+#define PORT_ALT_STAT 10
+
+typedef struct
+ {
+ UCHAR device; // device code
+ UCHAR byte6; // device select register image
+ UCHAR spigot; // spigot number
+ UCHAR sparebyte; // placeholder
+ 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[12];
+ USHORT regDmaDesc; // address of the DMA discriptor register for direction of transfer
+ USHORT regDmaCmdStat; // Byte #1 of DMA command status register
+ USHORT regDmaAddrPci; // 32 bit register for PCI address of DMA
+ USHORT regDmaAddrLoc; // 32 bit register for local bus address of DMA
+ USHORT regDmaCount; // 32 bit register for DMA transfer count
+ USHORT regDmaMode; // 32 bit register for DMA mode control
+ USHORT regRemap; // 32 bit local space remap
+ USHORT regDesc; // 32 bit local region descriptor
+ USHORT regRange; // 32 bit local range
+ USHORT regIrqControl; // 16 bit Interrupt enable/disable and status
+ USHORT regScratchPad; // scratch pad I/O base address
+ USHORT regBase; // Base I/O register for data space
+ USHORT basePort; // PLX base I/O port
+ USHORT timingMode; // timing mode currently set for adapter
+ ULONG timingAddress; // address to use on adapter for current timing mode
+ OUR_DEVICE device[4];
+ IDE_STRUCT ide;
+ ULONG startSector;
+ USHORT sectorCount;
+ Scsi_Cmnd *SCpnt;
+ VOID *buffer;
+ USHORT expectingIRQ;
+ USHORT readPhase;
+ } ADAPTER2220I, *PADAPTER2220I;
+
+#define HOSTDATA(host) ((PADAPTER2220I)&host->hostdata)
+
+
+static struct Scsi_Host *PsiHost[MAXADAPTER] = {NULL,}; // One for each adapter
+static int NumAdapters = 0;
+static IDENTIFY_DATA identifyData;
+static SETUP DaleSetup;
+
+/****************************************************************
+ * 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 timer;
+ USHORT *pports = padapter->ports;
+
+ timer = jiffies + TIMEOUT_DRQ; // calculate the timeout value
+ do {
+ if ( inb_p (pports[PORT_STAT_CMD]) & IDE_STATUS_DRQ )
+ {
+ outb_p (0, padapter->regDmaDesc); // write operation
+ outl (padapter->timingAddress, padapter->regDmaAddrLoc);
+ outl (virt_to_bus (padapter->buffer), padapter->regDmaAddrPci);
+ outl ((ULONG)padapter->ide.ide.ide[2] * (ULONG)512, padapter->regDmaCount);
+ outb_p (1, padapter->regDmaMode); // interrupts off
+ outb_p (0x03, padapter->regDmaCmdStat); // kick the DMA engine in gear
+ return 0;
+ }
+ } while ( 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 (PADAPTER2220I padapter)
+ {
+ ULONG timer;
+ USHORT *pports = padapter->ports;
+ UCHAR status;
+
+ outb_p (padapter->ide.ide.ides.spigot, pports[PORT_STAT_SEL]); // 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
+ DEB(printk ("\npci2220i Issueing new command: 0x%X",padapter->ide.ide.ides.cmd));
+ 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 ( 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 (PADAPTER2220I 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->ide.ide.ides.sectors = ( padapter->sectorCount > MAX_BUS_MASTER_BLOCKS ) ? MAX_BUS_MASTER_BLOCKS : 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)
+ {
+ PADAPTER2220I 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 ("\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: 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 = NULL; // Pointer to host data block
+ PADAPTER2220I padapter; // Pointer to adapter control structure
+ USHORT *pports; // I/O port array
+ Scsi_Cmnd *SCpnt;
+ UCHAR status;
+ int z;
+
+// DEB(printk ("\npci2220i recieved 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"));
+ return;
+ }
+
+ padapter = HOSTDATA(shost);
+ pports = padapter->ports;
+ SCpnt = padapter->SCpnt;
+
+ if ( !padapter->expectingIRQ )
+ {
+ DEB(printk ("\npci2220i 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;
+
+ switch ( padapter->ide.ide.ides.cmd ) // decide how to handle the interrupt
+ {
+ case IDE_CMD_READ_MULTIPLE:
+ if ( padapter->readPhase == 1 ) // is this a bus master channel complete?
+ {
+ DEB(printk ("\npci2220i processing read interrupt cleanup"));
+ outb_p (0x08, padapter->regDmaCmdStat); // cancel interrupt from DMA engine
+ 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;
+ }
+ padapter->readPhase = 0;
+ if ( !(status = IdeCmd (padapter)) )
+ {
+ DEB (printk ("\npci2220i interrupt complete, waiting for another"));
+ return;
+ }
+ }
+ if ( status & IDE_STATUS_DRQ )
+ {
+ DEB(printk ("\npci2220i processing read interrupt start bus master cycle"));
+ outb_p (8, padapter->regDmaDesc); // read operation
+ padapter->readPhase = 1;
+ padapter->expectingIRQ = 1;
+ outl (padapter->timingAddress, padapter->regDmaAddrLoc);
+ outl (virt_to_bus (padapter->buffer), padapter->regDmaAddrPci);
+ outl ((ULONG)padapter->ide.ide.ides.sectors * (ULONG)512, padapter->regDmaCount);
+ outb_p (5, padapter->regDmaMode); // interrupt enable/disable
+ outb_p (0x03, padapter->regDmaCmdStat); // kick the DMA engine in gear
+ return;
+ }
+ break;
+
+ case IDE_CMD_WRITE_MULTIPLE:
+ DEB(printk ("\npci2220i processing write interrupt cleanup"));
+ 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)) )
+ {
+ DEB (printk ("\npci2220i interrupt complete, waiting for another"));
+ return;
+ }
+ break;
+
+ case IDE_COMMAND_IDENTIFY:
+ {
+ PINQUIRYDATA pinquiryData = SCpnt->request_buffer;
+
+ DEB(printk ("\npci2220i processing verify interrupt cleanup"));
+ 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:
+ DEB(printk ("\npci2220i no real process here!"));
+ SCpnt->result = DID_OK << 16;
+ padapter->SCpnt = NULL;
+ SCpnt->scsi_done (SCpnt);
+ return;
+ }
+
+irqerror:;
+ DEB(printk ("\npci2220i error Device Status: %X\n", status));
+ SCpnt->result = DecodeError (shost, status);
+ SCpnt->scsi_done (SCpnt);
+ }
+/****************************************************************
+ * 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->host); // Pointer to adapter control structure
+ POUR_DEVICE pdev = &padapter->device[SCpnt->target];// 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("pci2220i_queuecommand: %02X: done can't be NULL\n", *cdb);
+ return 0;
+ }
+
+ 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]));
+ 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;
+ padapter->readPhase = 0;
+ 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;
+ padapter->readPhase = 0;
+ 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 ("pci2220i_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 ("pci2220i_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;
+ }
+ if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE )
+ {
+ if ( WriteData (padapter) )
+ {
+ padapter->expectingIRQ = 0;
+ DEB (printk ("pci2220i_queuecommand: %02X, %02X: Device failed to accept data\n", *cdb, padapter->ide.ide.ides.cmd));
+ SCpnt->result = DID_ERROR << 16;
+ done (SCpnt);
+ return 0;
+ }
+ }
+ DEB (printk(" now waiting for initial interrupt "));
+ return 0;
+ }
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+ {
+ SCpnt->SCp.Status++;
+ }
+/****************************************************************
+ * Name: Pci2220i_Command
+ *
+ * Description: Process a command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ *
+ * Returns: Status code.
+ *
+ ****************************************************************/
+int Pci2220i_Command (Scsi_Cmnd *SCpnt)
+ {
+ DEB(printk("pci2220i_command: ..calling pci2220i_queuecommand\n"));
+
+ Pci2220i_QueueCommand (SCpnt, internal_done);
+
+ SCpnt->SCp.Status = 0;
+ while (!SCpnt->SCp.Status)
+ barrier ();
+ return SCpnt->result;
+ }
+/****************************************************************
+ * Name: ReadFlash
+ *
+ * Description: Read information from controller Flash memory.
+ *
+ * Parameters: hostdata - 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.
+ *
+ ****************************************************************/
+VOID ReadFlash (PADAPTER2220I hostdata, VOID *pdata, ULONG base, ULONG length)
+ {
+ ULONG oldremap;
+ UCHAR olddesc;
+ ULONG z;
+ UCHAR *pd = (UCHAR *)pdata;
+
+ oldremap = inl (hostdata->regRemap); // save values to restore later
+ olddesc = inb_p (hostdata->regDesc);
+
+ outl (base | 1, hostdata->regRemap); // remap to Flash space as specified
+ outb_p (0x40, hostdata->regDesc); // describe remap region as 8 bit
+ for ( z = 0; z < length; z++) // get "length" data count
+ *pd++ = inb_p (hostdata->regBase + z); // read in the data
+
+ outl (oldremap, hostdata->regRemap); // restore remap register values
+ outb_p (olddesc, hostdata->regDesc);
+ }
+
+/****************************************************************
+ * Name: Pci2220i_Detect
+ *
+ * Description: Detect and initialize our boards.
+ *
+ * Parameters: tpnt - Pointer to SCSI host template structure.
+ *
+ * Returns: Number of adapters found.
+ *
+ ****************************************************************/
+int Pci2220i_Detect (Scsi_Host_Template *tpnt)
+ {
+ int pci_index = 0;
+ struct Scsi_Host *pshost;
+ PADAPTER2220I hostdata;
+ ULONG modearray[] = {DALE_DATA_MODE2, DALE_DATA_MODE3, DALE_DATA_MODE4, DALE_DATA_MODE4P};
+ int unit;
+ int z;
+ int setirq;
+
+ if ( pcibios_present () )
+ {
+ for ( pci_index = 0; pci_index <= MAXADAPTER; ++pci_index )
+ {
+ UCHAR pci_bus, pci_device_fn;
+
+ if ( pcibios_find_device (VENDOR_PSI, DEVICE_DALE_1, pci_index, &pci_bus, &pci_device_fn) != 0 )
+ break;
+
+ pshost = scsi_register (tpnt, sizeof(ADAPTER2220I));
+ hostdata = HOSTDATA(pshost);
+
+ pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &hostdata->basePort);
+ hostdata->basePort &= 0xFFFE;
+ DEB (printk ("\nBase Regs = %#04X", hostdata->basePort));
+ hostdata->regRemap = hostdata->basePort + RTR_LOCAL_REMAP; // 32 bit local space remap
+ DEB (printk (" %#04X", hostdata->regRemap));
+ hostdata->regDesc = hostdata->basePort + RTR_REGIONS; // 32 bit local region descriptor
+ DEB (printk (" %#04X", hostdata->regDesc));
+ hostdata->regRange = hostdata->basePort + RTR_LOCAL_RANGE; // 32 bit local range
+ DEB (printk (" %#04X", hostdata->regRange));
+ hostdata->regIrqControl = hostdata->basePort + RTR_INT_CONTROL_STATUS; // 16 bit interupt control and status
+ DEB (printk (" %#04X", hostdata->regIrqControl));
+ hostdata->regScratchPad = hostdata->basePort + RTR_MAILBOX; // 16 byte scratchpad I/O base address
+ DEB (printk (" %#04X", hostdata->regScratchPad));
+
+ pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_2, &hostdata->regBase);
+ hostdata->regBase &= 0xFFFE;
+ for ( z = 0; z < 9; z++ ) // build regester address array
+ hostdata->ports[z] = hostdata->regBase + 0x80 + (z * 4);
+ hostdata->ports[PORT_FAIL] = hostdata->regBase + REG_FAIL;
+ hostdata->ports[PORT_ALT_STAT] = hostdata->regBase + REG_ALT_STAT;
+ DEB (printk ("\nPorts ="));
+ DEB (for (z=0;z<11;z++) printk(" %#04X", hostdata->ports[z]););
+
+ hostdata->regDmaDesc = hostdata->regBase + RTL_DMA1_DESC_PTR; // address of the DMA discriptor register for direction of transfer
+ DEB (printk ("\nDMA Regs = %#04X", hostdata->regDmaDesc));
+ hostdata->regDmaCmdStat = hostdata->regBase + RTL_DMA_COMMAND_STATUS + 1; // Byte #1 of DMA command status register
+ DEB (printk (" %#04X", hostdata->regDmaCmdStat));
+ hostdata->regDmaAddrPci = hostdata->regBase + RTL_DMA1_PCI_ADDR; // 32 bit register for PCI address of DMA
+ DEB (printk (" %#04X", hostdata->regDmaAddrPci));
+ hostdata->regDmaAddrLoc = hostdata->regBase + RTL_DMA1_LOCAL_ADDR; // 32 bit register for local bus address of DMA
+ DEB (printk (" %#04X", hostdata->regDmaAddrLoc));
+ hostdata->regDmaCount = hostdata->regBase + RTL_DMA1_COUNT; // 32 bit register for DMA transfer count
+ DEB (printk (" %#04X", hostdata->regDmaCount));
+ hostdata->regDmaMode = hostdata->regBase + RTL_DMA1_MODE + 1; // 32 bit register for DMA mode control
+ DEB (printk (" %#04X", hostdata->regDmaMode));
+
+ if ( !inb_p (hostdata->regScratchPad + DALE_NUM_DRIVES) ) // if no devices on this board
+ goto unregister;
+
+ pcibios_read_config_byte (pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pshost->irq);
+ setirq = 1;
+ for ( z = 0; z < pci_index; 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, 0, "pci2220i", NULL) )
+ {
+ printk ("Unable to allocate IRQ for PSI-2220I controller.\n");
+ goto unregister;
+ }
+ }
+ PsiHost[pci_index] = pshost; // save SCSI_HOST pointer
+
+ pshost->unique_id = hostdata->regBase;
+ pshost->max_id = 4;
+
+ outb_p (0x01, hostdata->regRange); // fix our range register because other drivers want to tromp on it
+
+ hostdata->timingMode = inb_p (hostdata->regScratchPad + DALE_TIMING_MODE);
+ hostdata->timingAddress = modearray[hostdata->timingMode - 2];
+ ReadFlash (hostdata, &DaleSetup, DALE_FLASH_SETUP, sizeof (SETUP));
+
+ for ( z = 0; z < inb_p (hostdata->regScratchPad + DALE_NUM_DRIVES); ++z )
+ {
+ unit = inb_p (hostdata->regScratchPad + DALE_CHANNEL_DEVICE_0 + z) & 0x0F;
+ hostdata->device[unit].device = inb_p (hostdata->regScratchPad + DALE_SCRATH_DEVICE_0 + unit);
+ hostdata->device[unit].byte6 = (UCHAR)(((unit & 1) << 4) | 0xE0);
+ hostdata->device[unit].spigot = (UCHAR)(1 << (unit >> 1));
+ hostdata->device[unit].sectors = DaleSetup.setupDevice[unit].sectors;
+ hostdata->device[unit].heads = DaleSetup.setupDevice[unit].heads;
+ hostdata->device[unit].cylinders = DaleSetup.setupDevice[unit].cylinders;
+ hostdata->device[unit].blocks = DaleSetup.setupDevice[unit].blocks;
+ DEB (printk ("\nHOSTDATA->device = %X", hostdata->device[unit].device));
+ DEB (printk ("\n byte6 = %X", hostdata->device[unit].byte6));
+ DEB (printk ("\n spigot = %X", hostdata->device[unit].spigot));
+ DEB (printk ("\n sectors = %X", hostdata->device[unit].sectors));
+ DEB (printk ("\n heads = %X", hostdata->device[unit].heads));
+ DEB (printk ("\n cylinders = %X", hostdata->device[unit].cylinders));
+ DEB (printk ("\n blocks = %lX", hostdata->device[unit].blocks));
+ }
+
+ printk("\nPSI-2220I EIDE CONTROLLER: at I/O = %X/%X IRQ = %d\n", hostdata->basePort, hostdata->regBase, pshost->irq);
+ printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n");
+ continue;
+unregister:
+ scsi_unregister (pshost);
+ NumAdapters++;
+ }
+ }
+ return NumAdapters;
+ }
+/****************************************************************
+ * 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)
+ {
+ DEB (printk ("pci2220i_abort\n"));
+ 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)
+ {
+ return SCSI_RESET_PUNT;
+ }
+
+#include "sd.h"
+
+/****************************************************************
+ * 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 (Scsi_Disk *disk, kdev_t dev, int geom[])
+ {
+ POUR_DEVICE pdev;
+
+ pdev = &(HOSTDATA(disk->device->host)->device[disk->device->id]);
+
+ geom[0] = pdev->heads;
+ geom[1] = pdev->sectors;
+ geom[2] = pdev->cylinders;
+ return 0;
+ }
+
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = PCI2220I;
+
+#include "scsi_module.c"
+#endif
diff --git a/drivers/scsi/pci2220i.h b/drivers/scsi/pci2220i.h
new file mode 100644
index 000000000..0fafc2648
--- /dev/null
+++ b/drivers/scsi/pci2220i.h
@@ -0,0 +1,345 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 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: pci2220i.h
+ *
+ * Description: Header file for the SCSI driver for the PCI2220I
+ * EIDE interface card.
+ *
+ *-M*************************************************************************/
+
+#ifndef _PCI2220I_H
+#define _PCI2220I_H
+
+#include <linux/types.h>
+#include <linux/kdev_t.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
+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
+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 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_BiosParam (Disk *disk, kdev_t dev, int geom[]);
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+extern struct proc_dir_entry Proc_Scsi_Pci2220i;
+
+#define PCI2220I { NULL, NULL, \
+ &Proc_Scsi_Pci2220i,/* proc_dir_entry */ \
+ NULL, \
+ "PCI-2220I EIDE Disk Controller", \
+ Pci2220i_Detect, \
+ NULL, \
+ NULL, \
+ Pci2220i_Command, \
+ Pci2220i_QueueCommand, \
+ Pci2220i_Abort, \
+ Pci2220i_Reset, \
+ NULL, \
+ Pci2220i_BiosParam, \
+ 1, \
+ -1, \
+ SG_NONE, \
+ 1, \
+ 0, \
+ 0, \
+ DISABLE_CLUSTERING }
+
+#endif
diff --git a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c
index c28958703..3c2f083bd 100644
--- a/drivers/scsi/ppa.c
+++ b/drivers/scsi/ppa.c
@@ -6,369 +6,291 @@
* (c) 1995,1996 Grant R. Guenther, grant@torque.net,
* under the terms of the GNU Public License.
*
- */
-
-/* This driver was developed without the benefit of any technical
- * specifications for the interface. Instead, a modified version of
- * DOSemu was used to monitor the protocol used by the DOS driver
- * for this adapter. I have no idea how my programming model relates
- * to IOMEGA's design.
- *
- * IOMEGA's driver does not generate linked commands. I've never
- * observed a SCSI message byte in the protocol transactions, so
- * I am assuming that as long as linked commands are not used
- * we won't see any.
- *
- * For more information, see the file drivers/scsi/README.ppa.
- *
- */
-
-/*
- * 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 (campbell@tirian.che.curtin.edu.au)
- * 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]
- *
- * Really hacked it out of existance (David Campbell)
- * EPP timeouts no longer occur (yet better handling)
- * Probes known parallel ports
- * Autodetects EPP / ECP / PS2 / NIBBLE
- * Support for multiple devices (does anyone need this??)
- * [0.29-Curtin]
- * [ Stuff removed ]
- *
- * Modified PEDANTIC for less PEDANTIC drivers as people
- * were complaining about speed (I received a report indicating
- * that PEDANTIC is necessary for WinBond chipsets.
- * Updated config_ppa and Makefile
- * [0.36b-Curtin]
- *
- * First round of cleanups
- * Remove prior 1.3.34 kernel support
- * SMC support changed
- * ECP+EPP detection always invoked.
- * If compat mode => PS/2
- * else ecp_sync() called (ECP+EPP uses FIFO).
- * Added routine to detect interrupt channel for ECP (not used)
- * Changed version numbering
- * 1 Major number
- * 00 Minor revision number
- * ALPHA Expected stability (alpha, beta, stable)
- * [Curtin-1-00-ALPHA]
- * Second round of cleanups
- * Clean up timer queues
- * Fixed problem with non-detection of PS/2 ports
- * SMC ECP+EPP confirmed to work, remove option from config_ppa
- * [Curtin-1-01-ALPHA]
- *
- * Parport hits with a vengance!!
- * Several internal revisions have been made with huge amounts of
- * fixes including:
- * ioport_2_hostno removed (unique_id is quicker)
- * SMC compat option is history
- * Driver name / info hardwired
- * Played with inlines and saved 4k on module
- * Parport support
- * Using PnP Parport allows use of printer attached to
- * ZIP drive.
- * Numerous fixups for device registration and to allow
- * proper aborts.
- * Version jumps a few numbers here - considered BETA
- * Shipping Parport with monolithic driver :)
- * [Curtin-1-05-BETA]
- *
- * Fixed code to ensure SCSI abort will release the SCSI command
- * if the driver is STILL trying to claim the parport (PNP ver)
- * Now I have to fix the lp driver then there will NEVER be a
- * problem.
- * Got around to doing the ppa_queuecommand() clean up
- * Fixed bug relating to SMC EPP+ECP and monolithic driver
- * [Curtin-1-06-BETA]
- *
- * Where did the ppa_setup() code disappear to ??
- * Back in now...
- * Distribution of ppa now independent of parport (less work for me).
- * Also cleaned up the port detection to allow for variations on
- * IO aliasing (in an attempt to fix a few problems with some
- * machines...)
- * [Curtin-1-07-BETA]
- *
- * Rewrote detection code for monolithic driver and ported changes to
- * parport driver. Result is more stable detection of hardware and
- * better immunity to port aliasing (old XT cards).
- * Parport 0.16 (or better) is required for parport operation and
- * ECP+EPP modes, otherwise the latest parport edition is recommended.
- *
- * When using EPP and writing to disk CPU usage > 40%, while reading <10%.
- * This is due to ZIP drive IO scheduling, the drive does a verify after
- * write to ensure data integrity (removable media is ALWAYS questionable
- * since you never know where it has been).
- * Some fancy programing *MAY* fix the problem but at 30 Mb/min is just
- * over 10 sectors per jiffy.
- *
- * Hmm... I think I know a way but it will send the driver into
- * ALPHA state again.
- * [Curtin-1-08-STABLE]
+ * Current Maintainer: David Campbell (Perth, Western Australia, GMT+0800)
+ * campbell@gear.torque.net
+ * dcampbel@p01.as17.honeywell.com.au
*/
#include <linux/config.h>
/* The following #define is to avoid a clash with hosts.c */
#define PPA_CODE 1
-#include "ppa.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",
- "EPP 32 bit",
- "Unknown"};
+#include <linux/blk.h>
+#include <asm/io.h>
+#include <linux/parport.h>
+#include "sd.h"
+#include "hosts.h"
+int ppa_release(struct Scsi_Host *);
typedef struct {
- struct pardevice *dev; /* Parport device entry */
- int speed; /* General PPA delay constant */
- int speed_fast; /* Const for nibble/byte modes */
- int epp_speed; /* Reset time period */
- int mode; /* Transfer mode */
- int timeout; /* Number of timeouts */
- int host; /* Host number (for proc) */
- int abort_flag; /* Abort flag */
- int error_code; /* Error code */
- int ppa_failed; /* Failure flag */
- Scsi_Cmnd *cur_cmd; /* Current queued command */
- void (*done) (Scsi_Cmnd *); /* Done func for queuecommand */
- struct tq_struct ppa_tq; /* Polling interupt stuff */
- struct wait_queue *ppa_wait_q; /* Used for PnP stuff */
+ struct pardevice *dev; /* Parport device entry */
+ int base; /* Actual port address */
+ int mode; /* Transfer mode */
+ int host; /* Host number (for proc) */
+ Scsi_Cmnd *cur_cmd; /* Current queued command */
+ struct tq_struct ppa_tq; /* Polling interupt stuff */
+ unsigned long jstart; /* Jiffies at start */
+ unsigned int failed:1; /* Failure flag */
+ unsigned int p_busy:1; /* Parport sharing busy flag */
} ppa_struct;
-static void ppa_interrupt(void *data);
-/* I know that this is a mess but it works!! */
+#define PPA_EMPTY \
+{NULL, /* dev */ \
+-1, /* base */ \
+PPA_AUTODETECT, /* mode */ \
+-1, /* host */ \
+NULL, /* cur_cmd */ \
+{0, 0, ppa_interrupt, NULL}, \
+0, /* jstart */ \
+0, /* failed */ \
+0 /* p_busy */ \
+}
+
+#include "ppa.h"
+#include <linux/parport.h>
+
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#ifndef PARPORT_MODULES
+#define PARPORT_MODULES "parport_pc"
+#endif
+#endif
+
#define NO_HOSTS 4
static ppa_struct ppa_hosts[NO_HOSTS] =
-{
- {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
- {0, 0, ppa_interrupt, NULL}, NULL},
- {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
- {0, 0, ppa_interrupt, NULL}, NULL},
- {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
- {0, 0, ppa_interrupt, NULL}, NULL},
- {0, 6, 1, CONFIG_SCSI_PPA_EPP_TIME, PPA_AUTODETECT, 0, -1, 0, DID_ERROR, 1, NULL, NULL,
- {0, 0, ppa_interrupt, NULL}, NULL}
-};
-
-/* This is a global option */
-static int ppa_speed = -1; /* Set to >0 to act as a global value */
-static int ppa_speed_fast = -1; /* ditto.. */
-static int ppa_sg = SG_ALL; /* enable/disable scatter-gather. */
-
-/* other options */
-#define PPA_CAN_QUEUE 1 /* use "queueing" interface */
-#define PPA_BURST_SIZE 512 /* block size for bulk transfers */
-#define PPA_SELECT_TMO 5000 /* how long to wait for target ? */
-#define PPA_SPIN_TMO 500000 /* ppa_wait loop limiter */
-
-#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 PPA_BASE(x) ppa_hosts[(x)].dev->port->base
-
-/* Port IO - Sorry Grant but I prefer the following symbols */
-#define r_dtr(x) inb(PPA_BASE(x))
-#define r_str(x) inb(PPA_BASE(x)+1)
-#define r_ctr(x) inb(PPA_BASE(x)+2)
-#define r_epp(x) inb(PPA_BASE(x)+4)
-#define r_fifo(x) inb(PPA_BASE(x)+0x400)
-#define r_ecr(x) inb(PPA_BASE(x)+0x402)
-
-#define w_dtr(x,y) outb(y, PPA_BASE(x))
-#define w_str(x,y) outb(y, PPA_BASE(x)+1)
-#define w_ctr(x,y) outb(y, PPA_BASE(x)+2);\
- udelay( ppa_hosts[(x)].speed)
-#define w_epp(x,y) outb(y, PPA_BASE(x)+4)
-#define w_fifo(x,y) outb(y, PPA_BASE(x)+0x400)
-#define w_ecr(x,y) outb(y, PPA_BASE(x)+0x402)
-
-static void ppa_wakeup(void *ref)
-{
- ppa_struct *ppa_dev = (ppa_struct *) ref;
+{PPA_EMPTY, PPA_EMPTY, PPA_EMPTY, PPA_EMPTY};
- if (!ppa_dev->ppa_wait_q)
- return; /* Wake up whom ? */
+#define PPA_BASE(x) ppa_hosts[(x)].base
- /* Claim the Parport */
- if (parport_claim(ppa_dev->dev))
- return; /* Shouldn't happen */
+void ppa_wakeup(void *ref)
+{
+ ppa_struct *ppa_dev = (ppa_struct *) ref;
- wake_up(&ppa_dev->ppa_wait_q);
+ if (!ppa_dev->p_busy)
return;
+
+ if (parport_claim(ppa_dev->dev)) {
+ printk("ppa: bug in ppa_wakeup\n");
+ return;
+ }
+
+ ppa_dev->p_busy = 0;
+ ppa_dev->base = ppa_dev->dev->port->base;
+ if (ppa_dev->cur_cmd)
+ ppa_dev->cur_cmd->SCp.phase++;
+ return;
}
-static int ppa_release(struct Scsi_Host *host)
+int ppa_release(struct Scsi_Host *host)
{
- int host_no = host->unique_id;
+ int host_no = host->unique_id;
- printk("Releasing ppa%i\n", host_no);
- parport_unregister_device(ppa_hosts[host_no].dev);
- return 0;
+ printk("Releasing ppa%i\n", host_no);
+ parport_unregister_device(ppa_hosts[host_no].dev);
+ return 0;
}
static int ppa_pb_claim(int host_no)
{
- if (parport_claim(ppa_hosts[host_no].dev)) {
- sleep_on(&ppa_hosts[host_no].ppa_wait_q);
- ppa_hosts[host_no].ppa_wait_q = NULL;
-
- /* Check to see if we were aborted or reset */
- if (ppa_hosts[host_no].dev->port->cad !=
- ppa_hosts[host_no].dev) {
- printk("Abort detected on ppa%i\n", host_no);
- return 1;
- }
- }
- return 0;
-}
+ if (parport_claim(ppa_hosts[host_no].dev)) {
+ ppa_hosts[host_no].p_busy = 1;
+ return 1;
+ }
-static void ppa_pb_release(int host_no)
-{
- parport_release(ppa_hosts[host_no].dev);
+ PPA_BASE(host_no) = ppa_hosts[host_no].dev->port->base;
+ if (ppa_hosts[host_no].cur_cmd)
+ ppa_hosts[host_no].cur_cmd->SCp.phase++;
+ return 0;
}
+#define ppa_pb_release(x) parport_release(ppa_hosts[(x)].dev)
+
+/***************************************************************************
+ * Parallel port probing routines *
+ ***************************************************************************/
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = PPA;
+#include "scsi_module.c"
+#endif
+
+/*
+ * Start of Chipset kludges
+ */
-/* Placed here so everyone knows what ecp_sync does.. */
-static void ecp_sync(int host_no)
+int ppa_detect(Scsi_Host_Template * host)
{
- int i, r;
+ struct Scsi_Host *hreg;
+ int ports;
+ int i, nhosts, try_again;
+ struct parport *pb = parport_enumerate();
+
+ printk("ppa: Version %s\n", PPA_VERSION);
+ nhosts = 0;
+ try_again = 0;
+
+#ifdef CONFIG_KERNELD
+ if (!pb) {
+ request_module(PARPORT_MODULES);
+ pb = parport_enumerate();
+ }
+#endif
- r = r_ecr(host_no);
- if ((r & 0xe0) != 0x80)
- return;
+ if (!pb) {
+ printk("ppa: parport reports no devices.\n");
+ return 0;
+ }
+ retry_entry:
+ for (i = 0; pb; i++, pb = pb->next) {
+ int modes, ppb;
- for (i = 0; i < 100; i++) {
- r = r_ecr(host_no);
- if (r & 0x01)
- return;
- udelay(5);
- }
+ ppa_hosts[i].dev =
+ parport_register_device(pb, "ppa", NULL, ppa_wakeup,
+ NULL, PARPORT_DEV_TRAN, (void *) &ppa_hosts[i]);
- printk("ppa: ECP sync failed as data still present in FIFO.\n");
-}
+ /* Claim the bus so it remembers what we do to the control
+ * registers. [ CTR and ECP ]
+ */
+ if (ppa_pb_claim(i))
+ while (ppa_hosts[i].p_busy)
+ schedule(); /* Whe can safe schedule() here */
+ ppb = PPA_BASE(i);
+ w_ctr(ppb, 0x0c);
+ modes = ppa_hosts[i].dev->port->modes;
+
+ /* Mode detection works up the chain of speed
+ * This avoids a nasty if-then-else-if-... tree
+ */
+ ppa_hosts[i].mode = PPA_NIBBLE;
-static inline void ppa_d_pulse(int host_no, char b)
-{
- w_dtr(host_no, b);
- w_ctr(host_no, 0xc);
- w_ctr(host_no, 0xe);
- w_ctr(host_no, 0xc);
- w_ctr(host_no, 0x4);
- w_ctr(host_no, 0xc);
-}
+ if (modes & PARPORT_MODE_PCPS2)
+ ppa_hosts[i].mode = PPA_PS2;
-static void ppa_disconnect(int host_no)
-{
- ppa_d_pulse(host_no, 0);
- ppa_d_pulse(host_no, 0x3c);
- ppa_d_pulse(host_no, 0x20);
- ppa_d_pulse(host_no, 0xf);
+ if (modes & PARPORT_MODE_PCECPPS2) {
+ w_ecr(ppb, 0x20);
+ ppa_hosts[i].mode = PPA_PS2;
+ }
+ if (modes & PARPORT_MODE_PCECPEPP)
+ w_ecr(ppb, 0x80);
- ppa_pb_release(host_no);
-}
+ /* Done configuration */
+ ppa_pb_release(i);
-static inline void ppa_c_pulse(int host_no, char b)
-{
- w_dtr(host_no, b);
- w_ctr(host_no, 0x4);
- w_ctr(host_no, 0x6);
- w_ctr(host_no, 0x4);
- w_ctr(host_no, 0xc);
+ if (ppa_init(i)) {
+ parport_unregister_device(ppa_hosts[i].dev);
+ continue;
+ }
+ /* now the glue ... */
+ switch (ppa_hosts[i].mode) {
+ case PPA_NIBBLE:
+ ports = 3;
+ break;
+ case PPA_PS2:
+ ports = 3;
+ break;
+ case PPA_EPP_8:
+ case PPA_EPP_16:
+ case PPA_EPP_32:
+ ports = 8;
+ break;
+ default: /* Never gets here */
+ continue;
+ }
+
+ host->can_queue = PPA_CAN_QUEUE;
+ host->sg_tablesize = ppa_sg;
+ hreg = scsi_register(host, 0);
+ hreg->io_port = pb->base;
+ hreg->n_io_port = ports;
+ hreg->dma_channel = -1;
+ hreg->unique_id = i;
+ ppa_hosts[i].host = hreg->host_no;
+ nhosts++;
+ }
+ if (nhosts == 0) {
+ if (try_again == 1)
+ return 0;
+ try_again = 1;
+ goto retry_entry;
+ } else
+ return 1; /* return number of hosts detected */
}
-static int ppa_connect(int host_no, int flag)
+/* 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_strncmp(const char *a, const char *b, int len)
{
- int retv = ppa_pb_claim(host_no);
-
- ppa_c_pulse(host_no, 0);
- ppa_c_pulse(host_no, 0x3c);
- ppa_c_pulse(host_no, 0x20);
- if ((flag == CONNECT_EPP_MAYBE) &&
- IN_EPP_MODE(ppa_hosts[host_no].mode))
- ppa_c_pulse(host_no, 0xcf);
- else
- ppa_c_pulse(host_no, 0x8f);
+ int loop;
+ for (loop = 0; loop < len; loop++)
+ if (a[loop] != b[loop])
+ return 1;
- return retv;
+ return 0;
}
-static void ppa_do_reset(int host_no)
+static inline int ppa_proc_write(int hostno, char *buffer, int length)
{
- /*
- * SCSI reset taken from ppa_init and checked with
- * Iomega document that Grant has (via email :(
- */
- ppa_pb_claim(host_no);
- ppa_disconnect(host_no);
-
- ppa_connect(host_no, CONNECT_NORMAL);
-
- w_ctr(host_no, 0x6);
- w_ctr(host_no, 0x4);
- w_dtr(host_no, 0x40);
- w_ctr(host_no, 0x8);
- udelay(50);
- w_ctr(host_no, 0xc);
-
- ppa_disconnect(host_no);
+ unsigned long x;
+
+ if ((length > 5) && (ppa_strncmp(buffer, "mode=", 5) == 0)) {
+ x = simple_strtoul(buffer + 5, NULL, 0);
+ ppa_hosts[hostno].mode = x;
+ return length;
+ }
+ printk("ppa /proc: invalid variable\n");
+ return (-EINVAL);
}
-static char ppa_select(int host_no, int initiator, int target)
+int ppa_proc_info(char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout)
{
- char r;
- int k;
+ int i;
+ int len = 0;
- r = r_str(host_no); /* TODO */
+ for (i = 0; i < 4; i++)
+ if (ppa_hosts[i].host == hostno)
+ break;
- w_dtr(host_no, (1 << target));
- w_ctr(host_no, 0xe);
- w_ctr(host_no, 0xc);
- w_dtr(host_no, (1 << initiator));
- w_ctr(host_no, 0x8);
+ if (inout)
+ return ppa_proc_write(i, buffer, length);
- k = 0;
- while (!(r = (r_str(host_no) & 0xf0)) && (k++ < PPA_SELECT_TMO))
- barrier();
+ len += sprintf(buffer + len, "Version : %s\n", PPA_VERSION);
+ len += sprintf(buffer + len, "Parport : %s\n", ppa_hosts[i].dev->port->name);
+ len += sprintf(buffer + len, "Mode : %s\n", PPA_MODE_STRING[ppa_hosts[i].mode]);
- if (k >= PPA_SELECT_TMO)
- return 0;
+ /* Request for beyond end of buffer */
+ if (offset > length)
+ return 0;
- return r;
+ *start = buffer + offset;
+ len -= offset;
+ if (len > length)
+ len = length;
+ return len;
}
-static void ppa_fail(int host_no, int error_code)
+static int device_check(int host_no);
+
+#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(int host_no, int error_code)
+#else
+static inline void ppa_fail(int host_no, int error_code)
+#endif
{
- ppa_hosts[host_no].error_code = error_code;
- ppa_hosts[host_no].ppa_failed = 1;
- ppa_disconnect(host_no);
+ /* If we fail a device then we trash status / message bytes */
+ if (ppa_hosts[host_no].cur_cmd) {
+ ppa_hosts[host_no].cur_cmd->result = error_code << 16;
+ ppa_hosts[host_no].failed = 1;
+ }
}
/*
@@ -378,483 +300,539 @@ static void ppa_fail(int host_no, int error_code)
* doesn't appear to be designed to support interrupts. We spin on
* the 0x80 ready bit.
*/
-static char ppa_wait(int host_no)
+static unsigned char ppa_wait(int host_no)
{
- int k;
- char r;
-
- k = 0;
- while (!((r = r_str(host_no)) & 0x80)
- && (k++ < PPA_SPIN_TMO) && !ppa_hosts[host_no].abort_flag)
- barrier();
-
- /* check if we were interrupted */
- if (ppa_hosts[host_no].abort_flag) {
- if (ppa_hosts[host_no].abort_flag == 1)
- ppa_fail(host_no, DID_ABORT);
- else {
- ppa_do_reset(host_no);
- ppa_fail(host_no, DID_RESET);
- }
- return 0;
- }
- /* check if timed out */
- if (k >= PPA_SPIN_TMO) {
- ppa_fail(host_no, DID_TIME_OUT);
- return 0; /* command timed out */
- }
- /*
- * return some status information.
- * Semantics: 0xc0 = ZIP wants more data
- * 0xd0 = ZIP wants to send more data
- * 0xf0 = end of transfer, ZIP is sending status
- */
+ int k;
+ unsigned short ppb = PPA_BASE(host_no);
+ unsigned char r;
+
+ k = PPA_SPIN_TMO;
+ do {
+ r = r_str(ppb);
+ k--;
+ udelay(1);
+ }
+ while (!(r & 0x80) && (k));
+
+ /*
+ * 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 occured */
+ ppa_fail(host_no, DID_TIME_OUT);
+ printk("ppa timeout in ppa_wait\n");
+ return 0; /* command timed out */
+}
-/*
- * 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.
- *
+/*
+ * output a string, in whatever mode is available, according to the
+ * PPA protocol.
*/
-static int ppa_init(int host_no)
+static inline void epp_reset(unsigned short ppb)
{
- if (ppa_pb_claim(host_no))
- return 1;
- ppa_disconnect(host_no);
+ int i;
- ppa_connect(host_no, CONNECT_NORMAL);
+ i = r_str(ppb);
+ w_str(ppb, i);
+ w_str(ppb, i & 0xfe);
+}
- w_ctr(host_no, 0x6);
- if ((r_str(host_no) & 0xf0) != 0xf0) {
- ppa_pb_release(host_no);
- return 2;
- }
- w_ctr(host_no, 0x4);
- if ((r_str(host_no) & 0xf0) != 0x80) {
- ppa_pb_release(host_no);
- return 3;
- }
- /* This is a SCSI reset signal */
- w_dtr(host_no, 0x40);
- w_ctr(host_no, 0x8);
- udelay(50);
- w_ctr(host_no, 0xc);
+static inline void ecp_sync(unsigned short ppb)
+{
+ int i;
- ppa_disconnect(host_no);
+ if ((r_ecr(ppb) & 0xe0) != 0x80)
+ return;
- return 0;
+ for (i = 0; i < 100; i++) {
+ if (r_ecr(ppb) & 0x01)
+ return;
+ udelay(5);
+ }
+ printk("ppa: ECP sync failed as data still present in FIFO.\n");
}
-/*
- * check the epp status. After a EPP transfer, it should be true that
- * 1) the TIMEOUT bit (SPP_STR.0) is clear
- * 2) the READY bit (SPP_STR.7) is set
+/*
+ * Here is the asm code for the SPP/PS2 protocols for the i386.
+ * This has been optimised for speed on 386/486 machines. There will
+ * be very little improvement on the current 586+ machines as it is the
+ * IO statements which will limit throughput.
*/
-static int ppa_check_epp_status(int host_no)
+#ifdef __i386__
+#define BYTE_OUT(reg) \
+ " movb " #reg ",%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " addl $2,%%edx\n" \
+ " movb $0x0e,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " movb $0x0c,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " subl $2,%%edx\n"
+
+static inline int ppa_byte_out(unsigned short base, char *buffer, unsigned int len)
{
- char r;
- r = r_str(host_no);
-
- if (r & 1) {
- /* EPP timeout, according to the PC87332 manual */
- /* Semantics of clearing EPP timeout bit.
- * PC87332 - reading SPP_STR does it...
- * SMC - write 1 to EPP timeout bit
- * Others - (???) write 0 to EPP timeout bit
- */
- w_str(host_no, r);
- w_str(host_no, r & 0xfe);
- ppa_hosts[host_no].timeout++;
- printk("PPA: EPP timeout on port 0x%04x\n",
- PPA_BASE(host_no));
- ppa_fail(host_no, DID_BUS_BUSY);
- return 0;
- }
- if (!(r & 0x80)) {
- ppa_fail(host_no, DID_ERROR);
- return 0;
- }
- return 1;
+ /*
+ * %eax scratch
+ * %ebx Data to transfer
+ * %ecx Counter (Don't touch!!)
+ * %edx Port
+ * %esi Source buffer (mem pointer)
+ *
+ * In case you are wondering what the last line of the asm does...
+ * <output allocation> : <input allocation> : <trashed registers>
+ */
+ asm("shr $2,%%ecx\n" \
+ " jz .no_more_bulk_bo\n" \
+ " .align 4\n" \
+ ".loop_bulk_bo:\n" \
+ " movl (%%esi),%%ebx\n" \
+ BYTE_OUT(%%bl) \
+ BYTE_OUT(%%bh) \
+ " rorl $16,%%ebx\n" \
+ BYTE_OUT(%%bl) \
+ BYTE_OUT(%%bh) \
+ " addl $4,%%esi\n" \
+ " loop .loop_bulk_bo\n" \
+ " .align 4\n" \
+ ".no_more_bulk_bo:" \
+ : "=S"(buffer): "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx");
+
+ asm("andl $3,%%ecx\n" \
+ " jz .no_more_loose_bo\n" \
+ " .align 4\n" \
+ ".loop_loose_bo:\n" \
+ BYTE_OUT((%%esi)) \
+ " incl %%esi\n" \
+ " loop .loop_loose_bo\n" \
+ ".no_more_loose_bo:\n" \
+ : /* no output */ : "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx");
+ return 1; /* All went well - we hope! */
}
-static inline int ppa_force_epp_byte(int host_no, char x)
+#define BYTE_IN(reg) \
+ " inb (%%dx),%%al\n" \
+ " movb %%al," #reg "\n" \
+ " addl $2,%%edx\n" \
+ " movb $0x27,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " movb $0x25,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " subl $2,%%edx\n"
+
+static inline int ppa_byte_in(unsigned short base, char *buffer, int len)
{
-/* This routine forces a byte down the EPP data port whether the
- * device is ready or not...
- */
- char r;
-
- w_epp(host_no, x);
-
- r = r_str(host_no);
- if (!(r & 1))
- return 1;
-
- if (ppa_hosts[host_no].epp_speed > 0) {
- /* EPP timeout, according to the PC87332 manual */
- /* Semantics of clearing EPP timeout bit.
- * PC87332 - reading SPP_STR does it...
- * SMC - write 1 to EPP timeout bit
- * Others - (???) write 0 to EPP timeout bit
- */
- w_str(host_no, r);
- w_str(host_no, r & 0xfe);
-
- /* Take a deep breath, count to 10 and then... */
- udelay(ppa_hosts[host_no].epp_speed);
-
- /* Second time around */
- w_epp(host_no, x);
-
- r = r_str(host_no);
- }
- if (r & 1) {
- w_str(host_no, r);
- w_str(host_no, r & 0xfe);
- ppa_hosts[host_no].timeout++;
- printk("PPA: warning: EPP timeout\n");
- ppa_fail(host_no, DID_BUS_BUSY);
- return 0;
- } else
- return 1;
+ /*
+ * %eax scratch
+ * %ebx Data to transfer
+ * %ecx Counter (Don't touch!!)
+ * %edx Port
+ * %esi Source buffer (mem pointer)
+ *
+ * In case you are wondering what the last line of the asm does...
+ * <output allocation> : <input allocation> : <trashed registers>
+ */
+ asm("shr $2,%%ecx\n" \
+ " jz .no_more_bulk_bi\n" \
+ " .align 4\n" \
+ ".loop_bulk_bi:\n" \
+ BYTE_IN(%%bl) \
+ BYTE_IN(%%bh) \
+ " rorl $16,%%ebx\n" \
+ BYTE_IN(%%bl) \
+ BYTE_IN(%%bh) \
+ " rorl $16,%%ebx\n" \
+ " movl %%ebx,(%%esi)\n" \
+ " addl $4,%%esi\n" \
+ " loop .loop_bulk_bi\n" \
+ " .align 4\n" \
+ ".no_more_bulk_bi:" \
+ : "=S"(buffer): "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx");
+
+ asm("andl $3,%%ecx\n" \
+ " jz .no_more_loose_bi\n" \
+ " .align 4\n" \
+ ".loop_loose_bi:\n" \
+ BYTE_IN((%%esi)) \
+ " incl %%esi\n" \
+ " loop .loop_loose_bi\n" \
+ ".no_more_loose_bi:\n" \
+ : /* no output */ : "c"(len), "d"(base), "S"(buffer):"eax", "ebx", "ecx");
+ return 1; /* All went well - we hope! */
}
-static inline int ppa_send_command_epp(Scsi_Cmnd * cmd)
+#define NIBBLE_IN(reg) \
+ " incl %%edx\n" \
+ " movb $0x04,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " decl %%edx\n" \
+ " inb (%%dx),%%al\n" \
+ " andb $0xf0,%%al\n" \
+ " movb %%al," #reg "\n" \
+ " incl %%edx\n" \
+ " movb $0x06,%%al\n" \
+ " outb %%al,(%%dx)\n" \
+ " decl %%edx\n" \
+ " inb (%%dx),%%al\n" \
+ " shrb $4,%%al\n" \
+ " orb %%al," #reg "\n"
+
+static inline int ppa_nibble_in(unsigned short str_p, char *buffer, int len)
{
- int host_no = cmd->host->unique_id;
- int k;
+ /*
+ * %eax scratch
+ * %ebx Data to transfer
+ * %ecx Counter (Don't touch!!)
+ * %edx Port
+ * %esi Source buffer (mem pointer)
+ *
+ * In case you are wondering what the last line of the asm does...
+ * <output allocation> : <input allocation> : <trashed registers>
+ */
+ asm("shr $2,%%ecx\n" \
+ " jz .no_more_bulk_ni\n" \
+ " .align 4\n" \
+ ".loop_bulk_ni:\n" \
+ NIBBLE_IN(%%bl) \
+ NIBBLE_IN(%%bh) \
+ " rorl $16,%%ebx\n" \
+ NIBBLE_IN(%%bl) \
+ NIBBLE_IN(%%bh) \
+ " rorl $16,%%ebx\n" \
+ " movl %%ebx,(%%esi)\n" \
+ " addl $4,%%esi\n" \
+ " loop .loop_bulk_ni\n" \
+ " .align 4\n" \
+ ".no_more_bulk_ni:" \
+ : "=S"(buffer): "c"(len), "d"(str_p), "S"(buffer):"eax", "ebx", "ecx");
+
+ asm("andl $3,%%ecx\n" \
+ " jz .no_more_loose_ni\n" \
+ " .align 4\n" \
+ ".loop_loose_ni:\n" \
+ NIBBLE_IN((%%esi)) \
+ " incl %%esi\n" \
+ " loop .loop_loose_ni\n" \
+ ".no_more_loose_ni:\n" \
+ : /* no output */ : "c"(len), "d"(str_p), "S"(buffer):"eax", "ebx", "ecx");
+ return 1; /* All went well - we hope! */
+}
+#else /* Old style C routines */
- w_ctr(host_no, 0x4);
- for (k = 0; k < cmd->cmd_len; k++) { /* send the command */
- if (!ppa_force_epp_byte(host_no, cmd->cmnd[k]))
- return 0;
- }
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return 1;
+static inline int ppa_byte_out(unsigned short base, const char *buffer, int len)
+{
+ unsigned short ctr_p = base + 2;
+ int i;
+
+ for (i = len; i; i--) {
+ outb(*buffer++, base);
+ outb(0xe, ctr_p);
+ outb(0xc, ctr_p);
+ }
+ return 1; /* All went well - we hope! */
}
-static inline int ppa_send_command_normal(Scsi_Cmnd * cmd)
+static inline int ppa_byte_in(unsigned short base, char *buffer, int len)
{
- int host_no = cmd->host->unique_id;
- int k;
+ unsigned short ctr_p = base + 2;
+ int i;
+
+ for (i = len; i; i--) {
+ *buffer++ = inb(base);
+ outb(0x27, ctr_p);
+ outb(0x25, ctr_p);
+ }
+ return 1; /* All went well - we hope! */
+}
- w_ctr(host_no, 0xc);
+static inline int ppa_nibble_in(unsigned short str_p, char *buffer, int len)
+{
+ unsigned short ctr_p = str_p + 1;
+ unsigned char h, l;
+ int i;
+
+ for (i = len; i; i--) {
+ outb(0x4, ctr_p);
+ h = inb(str_p);
+ outb(0x6, ctr_p);
+ l = inb(str_p);
+ *buffer++ = (h & 0xf0) | ((l & 0xf0) >> 4);
+ }
+ return 1; /* All went well - we hope! */
+}
+#endif
- for (k = 0; k < cmd->cmd_len; k++) { /* send the command */
- if (!ppa_wait(host_no))
- return 0;
- w_dtr(host_no, cmd->cmnd[k]);
- w_ctr(host_no, 0xe);
- w_ctr(host_no, 0xc);
- }
- return 1;
+static inline int ppa_epp_out(unsigned short epp_p, unsigned short str_p, const char *buffer, int len)
+{
+ int i;
+ for (i = len; i; i--) {
+ outb(*buffer++, epp_p);
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 2
+ if (inb(str_p) & 0x01)
+ return 0;
+#endif
+ }
+ return 1;
}
-static int ppa_start(Scsi_Cmnd * cmd)
+static int ppa_out(int host_no, char *buffer, int len)
{
- int host_no = cmd->host->unique_id;
+ int r;
+ unsigned short ppb = PPA_BASE(host_no);
- /*
- * by default, the command failed,
- * unless explicitly completed in ppa_completion().
- */
- ppa_hosts[host_no].error_code = DID_ERROR;
- ppa_hosts[host_no].abort_flag = 0;
- ppa_hosts[host_no].ppa_failed = 0;
-
- if (cmd->target == PPA_INITIATOR) {
- ppa_hosts[host_no].error_code = DID_BAD_TARGET;
- ppa_hosts[host_no].ppa_failed = 1;
- return 0;
- }
- if (ppa_connect(host_no, CONNECT_EPP_MAYBE))
- return 0;
+ r = ppa_wait(host_no);
- if (!ppa_select(host_no, PPA_INITIATOR, cmd->target)) {
- ppa_fail(host_no, DID_NO_CONNECT);
- return 0;
- }
- if (IN_EPP_MODE(ppa_hosts[host_no].mode))
- return ppa_send_command_epp(cmd);
+ if ((r & 0x50) != 0x40) {
+ ppa_fail(host_no, DID_ERROR);
+ return 0;
+ }
+ switch (ppa_hosts[host_no].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);
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 1
+ r = ppa_epp_out(ppb + 4, ppb + 1, buffer, len);
+#else
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC == 0
+ if (!(((long) buffer | len) & 0x03))
+ outsl(ppb + 4, buffer, len >> 2);
else
- return ppa_send_command_normal(cmd);
+#endif
+ outsb(ppb + 4, buffer, len);
+ w_ctr(ppb, 0xc);
+ r = !(r_str(ppb) & 0x01);
+#endif
+ w_ctr(ppb, 0xc);
+ ecp_sync(ppb);
+ break;
+
+ default:
+ printk("PPA: bug in ppa_out()\n");
+ r = 0;
+ }
+ return r;
}
-/*
- * output a string, in whatever mode is available, according to the
- * PPA protocol.
- */
-static inline int ppa_outs(int host_no, char *buffer, int len)
+static inline int ppa_epp_in(int epp_p, int str_p, char *buffer, int len)
{
- int k;
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
- int r;
+ int i;
+ for (i = len; i; i--) {
+ *buffer++ = inb(epp_p);
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 2
+ if (inb(str_p) & 0x01)
+ return 0;
#endif
+ }
+ return 1;
+}
- switch (ppa_hosts[host_no].mode) {
- case PPA_NIBBLE:
- case PPA_PS2:
- /* 8 bit output, with a loop */
- for (k = len; k; k--) {
- w_dtr(host_no, *buffer++);
- w_ctr(host_no, 0xe);
- w_ctr(host_no, 0xc);
- }
- return 1; /* assume transfer went OK */
+static int ppa_in(int host_no, char *buffer, int len)
+{
+ int r;
+ unsigned short ppb = PPA_BASE(host_no);
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
- case PPA_EPP_32:
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 2
- w_ctr(host_no, 0x4);
- for (k = len; k; k -= 4) {
- w_epp(host_no, *buffer++);
- w_epp(host_no, *buffer++);
- w_epp(host_no, *buffer++);
- w_epp(host_no, *buffer++);
- r = ppa_check_epp_status(host_no);
- if (!r)
- return r;
- }
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return 1;
-#endif
- case PPA_EPP_16:
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 3
- w_ctr(host_no, 0x4);
- for (k = len; k; k -= 2) {
- w_epp(host_no, *buffer++);
- w_epp(host_no, *buffer++);
- r = ppa_check_epp_status(host_no);
- if (!r)
- return r;
- }
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return 1;
-#endif
- case PPA_EPP_8:
- w_ctr(host_no, 0x4);
- for (k = len; k; k--) {
- w_epp(host_no, *buffer++);
- r = ppa_check_epp_status(host_no);
- if (!r)
- return r;
- }
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return 1;
+ r = ppa_wait(host_no);
+
+ if ((r & 0x50) != 0x50) {
+ ppa_fail(host_no, DID_ERROR);
+ return 0;
+ }
+ switch (ppa_hosts[host_no].mode) {
+ case PPA_NIBBLE:
+ /* 4 bit input, with a loop */
+ r = ppa_nibble_in(ppb + 1, 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);
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 1
+ r = ppa_epp_in(ppb + 4, ppb + 1, buffer, len);
#else
- case PPA_EPP_32:
- case PPA_EPP_16:
- case PPA_EPP_8:
- w_ctr(host_no, 0x4);
- switch (ppa_hosts[host_no].mode) {
- case PPA_EPP_8:
- outsb(PPA_BASE(host_no) + 0x04,
- buffer, len);
- break;
- case PPA_EPP_16:
- outsw(PPA_BASE(host_no) + 0x04,
- buffer, len / 2);
- break;
- case PPA_EPP_32:
- outsl(PPA_BASE(host_no) + 0x04,
- buffer, len / 4);
- break;
- }
- k = ppa_check_epp_status(host_no);
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return k;
+#if CONFIG_SCSI_PPA_HAVE_PEDANTIC == 0
+ if (!(((long) buffer | len) & 0x03))
+ insl(ppb + 4, buffer, len >> 2);
+ else
+#endif
+ insb(ppb + 4, buffer, len);
+ w_ctr(ppb, 0x2c);
+ r = !(r_str(ppb) & 0x01);
#endif
+ w_ctr(ppb, 0x2c);
+ ecp_sync(ppb);
+ break;
+
+ default:
+ printk("PPA: bug in ppa_ins()\n");
+ r = 0;
+ break;
+ }
+ return r;
+}
- default:
- printk("PPA: bug in ppa_outs()\n");
- }
- return 0;
+/* 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 inline int ppa_outb(int host_no, char d)
+static void ppa_disconnect(int host_no)
{
- int k;
+ unsigned short ppb = PPA_BASE(host_no);
- switch (ppa_hosts[host_no].mode) {
- case PPA_NIBBLE:
- case PPA_PS2:
- w_dtr(host_no, d);
- w_ctr(host_no, 0xe);
- w_ctr(host_no, 0xc);
- return 1; /* assume transfer went OK */
+ ppa_d_pulse(ppb, 0);
+ ppa_d_pulse(ppb, 0x3c);
+ ppa_d_pulse(ppb, 0x20);
+ ppa_d_pulse(ppb, 0xf);
+}
- case PPA_EPP_8:
- case PPA_EPP_16:
- case PPA_EPP_32:
- w_ctr(host_no, 0x4);
- w_epp(host_no, d);
- k = ppa_check_epp_status(host_no);
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return k;
-
- default:
- printk("PPA: bug in ppa_outb()\n");
- }
+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(int host_no, int flag)
+{
+ unsigned short ppb = PPA_BASE(host_no);
+
+ ppa_c_pulse(ppb, 0);
+ ppa_c_pulse(ppb, 0x3c);
+ ppa_c_pulse(ppb, 0x20);
+ if ((flag == CONNECT_EPP_MAYBE) &&
+ IN_EPP_MODE(ppa_hosts[host_no].mode))
+ ppa_c_pulse(ppb, 0xcf);
+ else
+ ppa_c_pulse(ppb, 0x8f);
+}
+
+static int ppa_select(int host_no, int target)
+{
+ int k;
+ unsigned short ppb = PPA_BASE(host_no);
+
+ /*
+ * 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--;
+ } 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--;
+ }
+ while (!(r_str(ppb) & 0x40) && (k));
+ if (!k)
+ return 0;
+
+ return 1;
}
-static inline int ppa_ins(int host_no, char *buffer, int len)
+/*
+ * 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(int host_no)
{
- int k, h, l;
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
- int r;
+ int retv;
+ unsigned short ppb = PPA_BASE(host_no);
+
+#if defined(CONFIG_PARPORT) || defined(CONFIG_PARPORT_MODULE)
+ if (ppa_pb_claim(host_no))
+ while (ppa_hosts[host_no].p_busy)
+ schedule(); /* Whe can safe schedule() here */
#endif
- switch (ppa_hosts[host_no].mode) {
- case PPA_NIBBLE:
- /* 4 bit input, with a loop */
- for (k = len; k; k--) {
- w_ctr(host_no, 0x4);
- h = r_str(host_no);
- w_ctr(host_no, 0x6);
- l = r_str(host_no);
- *buffer++ = ((l >> 4) & 0x0f) + (h & 0xf0);
- }
- w_ctr(host_no, 0xc);
- return 1; /* assume transfer went OK */
+ ppa_disconnect(host_no);
+ ppa_connect(host_no, CONNECT_NORMAL);
- case PPA_PS2:
- /* 8 bit input, with a loop */
- for (k = len; k; k--) {
- w_ctr(host_no, 0x25);
- *buffer++ = r_dtr(host_no);
- w_ctr(host_no, 0x27);
- }
- w_ctr(host_no, 0x5);
- w_ctr(host_no, 0x4);
- w_ctr(host_no, 0xc);
- return 1; /* assume transfer went OK */
+ retv = 2; /* Failed */
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC > 0
- case PPA_EPP_32:
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 2
- w_ctr(host_no, 0x24);
- for (k = len; k; k -= 4) {
- *buffer++ = r_epp(host_no);
- *buffer++ = r_epp(host_no);
- *buffer++ = r_epp(host_no);
- *buffer++ = r_epp(host_no);
- r = ppa_check_epp_status(host_no);
- if (!r)
- return r;
- }
- w_ctr(host_no, 0x2c);
- ecp_sync(host_no);
- return 1;
-#endif
- case PPA_EPP_16:
-#if CONFIG_SCSI_PPA_HAVE_PEDANTIC < 3
- w_ctr(host_no, 0x24);
- for (k = len; k; k -= 2) {
- *buffer++ = r_epp(host_no);
- *buffer++ = r_epp(host_no);
- r = ppa_check_epp_status(host_no);
- if (!r)
- return r;
- }
- w_ctr(host_no, 0x2c);
- ecp_sync(host_no);
- return 1;
-#endif
- case PPA_EPP_8:
- w_ctr(host_no, 0x24);
- for (k = len; k; k--) {
- *buffer++ = r_epp(host_no);
- r = ppa_check_epp_status(host_no);
- if (!r)
- return r;
- }
- w_ctr(host_no, 0x2c);
- ecp_sync(host_no);
- return 1;
- break;
-#else
- case PPA_EPP_8:
- case PPA_EPP_16:
- case PPA_EPP_32:
- w_ctr(host_no, 0x24);
- switch (ppa_hosts[host_no].mode) {
- case PPA_EPP_8:
- insb(PPA_BASE(host_no) + 0x04,
- buffer, len);
- break;
- case PPA_EPP_16:
- insw(PPA_BASE(host_no) + 0x04,
- buffer, len / 2);
- break;
- case PPA_EPP_32:
- insl(PPA_BASE(host_no) + 0x04,
- buffer, len / 4);
- break;
- }
- k = ppa_check_epp_status(host_no);
- w_ctr(host_no, 0x2c);
- ecp_sync(host_no);
- return k;
-#endif
+ w_ctr(ppb, 0xe);
+ if ((r_str(ppb) & 0x08) == 0x08)
+ retv--;
- default:
- printk("PPA: bug in ppa_ins()\n");
- }
- return 0;
+ w_ctr(ppb, 0xc);
+ if ((r_str(ppb) & 0x08) == 0x00)
+ retv--;
+
+ /* This is a SCSI BUS reset signal */
+ if (!retv) {
+ w_dtr(ppb, 0x40);
+ w_ctr(ppb, 0x08);
+ udelay(30);
+ w_ctr(ppb, 0x0c);
+ udelay(1000); /* Allow devices to settle down */
+ }
+ ppa_disconnect(host_no);
+ udelay(1000); /* Another delay to allow devices to settle */
+
+ if (!retv)
+ retv = device_check(host_no);
+
+ ppa_pb_release(host_no);
+ return retv;
}
-static int ppa_inb(int host_no, char *buffer)
+static inline int ppa_send_command(Scsi_Cmnd * cmd)
{
- int h, l, k;
-
- switch (ppa_hosts[host_no].mode) {
- case PPA_NIBBLE:
- /* 4 bit input */
- w_ctr(host_no, 0x4);
- h = r_str(host_no);
- w_ctr(host_no, 0x6);
- l = r_str(host_no);
- *buffer = ((l >> 4) & 0x0f) + (h & 0xf0);
- w_ctr(host_no, 0xc);
- return 1; /* assume transfer went OK */
+ int host_no = cmd->host->unique_id;
+ int k;
- case PPA_PS2:
- /* 8 bit input */
- w_ctr(host_no, 0x25);
- *buffer++ = r_dtr(host_no);
- w_ctr(host_no, 0x27);
- w_ctr(host_no, 0x5);
- w_ctr(host_no, 0x4);
- w_ctr(host_no, 0xc);
- return 1; /* assume transfer went OK */
+ w_ctr(PPA_BASE(host_no), 0x0c);
- case PPA_EPP_8:
- case PPA_EPP_16:
- case PPA_EPP_32:
- w_ctr(host_no, 0x24);
- *buffer = r_epp(host_no);
- k = ppa_check_epp_status(host_no);
- w_ctr(host_no, 0xc);
- ecp_sync(host_no);
- return k;
-
- default:
- printk("PPA: bug in ppa_inb()\n");
- }
- return 0;
+ for (k = 0; k < cmd->cmd_len; k++)
+ if (!ppa_out(host_no, &cmd->cmnd[k], 1))
+ return 0;
+ return 1;
}
/*
@@ -867,115 +845,107 @@ static int ppa_inb(int host_no, char *buffer)
*/
static int ppa_completion(Scsi_Cmnd * cmd)
{
- int host_no = cmd->host->unique_id;
-
- char r, l, h, v;
- int dir, cnt, blen, fast, bulk, status;
- char *buffer;
- struct scatterlist *sl;
- int current_segment, nsegment;
-
- v = cmd->cmnd[0];
- bulk = ((v == READ_6) ||
- (v == READ_10) ||
- (v == WRITE_6) ||
- (v == WRITE_10));
-
- /* code for scatter/gather: */
- if (cmd->use_sg) {
- /* if many buffers are available, start filling the first */
- sl = (struct scatterlist *) cmd->request_buffer;
- blen = sl->length;
- buffer = sl->address;
- } else {
- /* else fill the only available buffer */
- sl = NULL;
- buffer = cmd->request_buffer;
- blen = cmd->request_bufflen;
- }
- current_segment = 0;
- nsegment = cmd->use_sg;
-
- cnt = 0;
-
- /* detect transfer direction */
- dir = 0;
- if (!(r = ppa_wait(host_no)))
- return 0;
- if (r == (char) 0xc0)
- dir = 1; /* d0 = read c0 = write f0 = status */
-
- while (r != (char) 0xf0) {
- if (((r & 0xc0) != 0xc0) || (cnt >= blen)) {
- ppa_fail(host_no, DID_ERROR);
- return 0;
- }
- /* determine if we should use burst I/O */
- fast = (bulk && ((blen - cnt) >= PPA_BURST_SIZE) &&
- ((((long)buffer + cnt)) & 0x3) == 0);
-
- if (fast) {
- if (dir)
- status = ppa_outs(host_no, &buffer[cnt], PPA_BURST_SIZE);
- else
- status = ppa_ins(host_no, &buffer[cnt], PPA_BURST_SIZE);
- cnt += PPA_BURST_SIZE;
- } else {
- if (dir)
- status = ppa_outb(host_no, buffer[cnt]);
- else
- status = ppa_inb(host_no, &buffer[cnt]);
- cnt++;
- }
+ /* Return codes:
+ * -1 Error
+ * 0 Told to schedule
+ * 1 Finished data transfer
+ */
+ int host_no = cmd->host->unique_id;
+ unsigned short ppb = PPA_BASE(host_no);
+ 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 (jiffies > start_jiffies + 1)
+ return 0;
- if (!status || !(r = ppa_wait(host_no)))
- return 0;
-
- if (sl && cnt == blen) {
- /* if scatter/gather, advance to the next segment */
- if (++current_segment < nsegment) {
- ++sl;
- blen = sl->length;
- buffer = sl->address;
- cnt = 0;
- }
- /*
- * the else case will be captured by the (cnt >= blen)
- * test above.
- */
- }
+ if (((r & 0xc0) != 0xc0) || (cmd->SCp.this_residual <= 0)) {
+ ppa_fail(host_no, DID_ERROR);
+ return -1; /* ERROR_RETURN */
}
+ /* determine if we should use burst I/O */ fast = (bulk && (cmd->SCp.this_residual >= PPA_BURST_SIZE))
+ ? PPA_BURST_SIZE : 1;
- /* read status and message bytes */
- if (!ppa_inb(host_no, &l)) /* read status byte */
- return 0;
- if (!(ppa_wait(host_no)))
- return 0;
- if (!ppa_inb(host_no, &h)) /* read message byte */
- return 0;
+ if (r == (unsigned char) 0xc0)
+ status = ppa_out(host_no, cmd->SCp.ptr, fast);
+ else
+ status = ppa_in(host_no, cmd->SCp.ptr, fast);
- ppa_disconnect(host_no);
+ cmd->SCp.ptr += fast;
+ cmd->SCp.this_residual -= fast;
- ppa_hosts[host_no].error_code = DID_OK;
- return (h << 8) | (l & STATUS_MASK);
+ if (!status) {
+ ppa_fail(host_no, 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 = cmd->SCp.buffer->address;
+ }
+ }
+ /* 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 */
}
/* deprecated synchronous interface */
-
int ppa_command(Scsi_Cmnd * cmd)
{
- int host_no = cmd->host->unique_id;
- int s;
-
- sti();
- s = 0;
- if (ppa_start(cmd))
- if (ppa_wait(host_no))
- s = ppa_completion(cmd);
- return s + (ppa_hosts[host_no].error_code << 16);
+ static int first_pass = 1;
+ int host_no = cmd->host->unique_id;
+
+ if (first_pass) {
+ printk("ppa: using non-queuing interface\n");
+ first_pass = 0;
+ }
+ if (ppa_hosts[host_no].cur_cmd) {
+ printk("PPA: bug in ppa_command\n");
+ return 0;
+ }
+ ppa_hosts[host_no].failed = 0;
+ ppa_hosts[host_no].jstart = jiffies;
+ ppa_hosts[host_no].cur_cmd = cmd;
+ cmd->result = DID_ERROR << 16; /* default return code */
+ cmd->SCp.phase = 0;
+
+ ppa_pb_claim(host_no);
+
+ while (ppa_engine(&ppa_hosts[host_no], cmd))
+ schedule();
+
+ if (cmd->SCp.phase) /* Only disconnect if we have connected */
+ ppa_disconnect(cmd->host->unique_id);
+
+ ppa_pb_release(host_no);
+ ppa_hosts[host_no].cur_cmd = 0;
+ return cmd->result;
}
-/* pseudo-interrupt queueing interface */
/*
* Since the PPA itself doesn't generate interrupts, we use
* the scheduler's task queue to generate a stream of call-backs and
@@ -983,82 +953,200 @@ int ppa_command(Scsi_Cmnd * cmd)
*/
static void ppa_interrupt(void *data)
{
- ppa_struct *tmp = (ppa_struct *) data;
- Scsi_Cmnd *cmd = tmp->cur_cmd;
- void (*done) (Scsi_Cmnd *) = tmp->done;
- int host_no = cmd->host->unique_id;
-
- if (!cmd) {
- printk("PPA: bug in ppa_interrupt\n");
- return;
- }
- /* First check for any errors that may of occured
- * Here we check for internal errors
- */
- if (tmp->ppa_failed) {
- printk("PPA: ppa_failed bug: ppa_error_code = %d\n",
- tmp->error_code);
- cmd->result = DID_ERROR << 16;
- tmp->cur_cmd = 0;
- done(cmd);
- return;
+ ppa_struct *tmp = (ppa_struct *) data;
+ Scsi_Cmnd *cmd = tmp->cur_cmd;
+
+ if (!cmd) {
+ printk("PPA: bug in ppa_interrupt\n");
+ return;
+ }
+ if (ppa_engine(tmp, cmd)) {
+ tmp->ppa_tq.data = (void *) tmp;
+ tmp->ppa_tq.sync = 0;
+ queue_task(&tmp->ppa_tq, &tq_timer);
+ 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->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(cmd->host->unique_id);
+ if (cmd->SCp.phase > 0)
+ ppa_pb_release(cmd->host->unique_id);
+ tmp->cur_cmd = 0;
+ cmd->scsi_done(cmd);
+ return;
+}
+
+static int ppa_engine(ppa_struct * tmp, Scsi_Cmnd * cmd)
+{
+ int host_no = cmd->host->unique_id;
+ unsigned short ppb = PPA_BASE(host_no);
+ unsigned char l = 0, h = 0;
+ int retv;
+
+ /* First check for any errors that may of occured
+ * Here we check for internal errors
+ */
+ if (tmp->failed)
+ return 0;
+
+ switch (cmd->SCp.phase) {
+ case 0: /* Phase 0 - Waiting for parport */
+ if ((jiffies - tmp->jstart) > HZ) {
+ /*
+ * We waited more than a second
+ * for parport to call us
+ */
+ ppa_fail(host_no, DID_BUS_BUSY);
+ return 0;
}
- /* Occasionally the mid level driver will abort a SCSI
- * command because we are taking to long, if this occurs
- * we should abort the command.
- */
- if (tmp->abort_flag) {
- ppa_disconnect(host_no);
- if (tmp->abort_flag == 1)
- cmd->result = DID_ABORT << 16;
- else {
- ppa_do_reset(host_no);
- cmd->result = DID_RESET << 16;
+ return 1; /* wait that ppa_wakeup claims parport */
+ case 1: /* Phase 1 - Connected */
+ { /* Perform a sanity check for cable unplugged */
+ int retv = 2; /* Failed */
+
+ ppa_connect(host_no, 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 - tmp->jstart) > (1 * HZ)) {
+ printk("ppa: Parallel port cable is unplugged!!\n");
+ ppa_fail(host_no, DID_BUS_BUSY);
+ return 0;
+ } else {
+ ppa_disconnect(host_no);
+ return 1; /* Try again in a jiffy */
}
- tmp->cur_cmd = 0;
- done(cmd);
- return;
+ cmd->SCp.phase++;
}
- /* Check to see if the device is now free, if not
- * then throw this function onto the scheduler queue
- * to be called back in a jiffy.
- * (i386: 1 jiffy = 0.01 seconds)
- */
- if (!(r_str(host_no) & 0x80)) {
- tmp->ppa_tq.data = (void *) tmp;
- queue_task(&tmp->ppa_tq, &tq_scheduler);
- return;
+
+ case 2: /* Phase 2 - We are now talking to the scsi bus */
+ if (!ppa_select(host_no, cmd->target)) {
+ ppa_fail(host_no, DID_NO_CONNECT);
+ return 0;
}
- /* Device is now free and no errors have occured so
- * it is safe to do the data phase
- */
- cmd->result = ppa_completion(cmd) + (tmp->error_code << 16);
- tmp->cur_cmd = 0;
- done(cmd);
- return;
-}
+ cmd->SCp.phase++;
-int ppa_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
-{
- int host_no = cmd->host->unique_id;
+ 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++;
- if (ppa_hosts[host_no].cur_cmd) {
- printk("PPA: bug in ppa_queuecommand\n");
- return 0;
+ 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 = cmd->SCp.buffer->address;
+ } 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;
+ 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(host_no) != (unsigned char) 0xf0) {
+ ppa_fail(host_no, DID_ERROR);
+ return 0;
}
- sti();
- ppa_hosts[host_no].cur_cmd = cmd;
- ppa_hosts[host_no].done = done;
-
- if (!ppa_start(cmd)) {
- cmd->result = ppa_hosts[host_no].error_code << 16;
- ppa_hosts[host_no].cur_cmd = 0;
- done(cmd);
- return 0;
+ if (ppa_in(host_no, &l, 1)) { /* read status byte */
+ /* Check for optional message byte */
+ if (ppa_wait(host_no) == (unsigned char) 0xf0)
+ ppa_in(host_no, &h, 1);
+ cmd->result = (DID_OK << 16) + (h << 8) + (l & STATUS_MASK);
}
- ppa_interrupt(ppa_hosts + host_no);
+ return 0; /* Finished */
+ break;
+
+ default:
+ printk("ppa: Invalid scsi phase\n");
+ }
+ return 0;
+}
+
+int ppa_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+ int host_no = cmd->host->unique_id;
+ if (ppa_hosts[host_no].cur_cmd) {
+ printk("PPA: bug in ppa_queuecommand\n");
return 0;
+ }
+ ppa_hosts[host_no].failed = 0;
+ ppa_hosts[host_no].jstart = jiffies;
+ ppa_hosts[host_no].cur_cmd = cmd;
+ cmd->scsi_done = done;
+ cmd->result = DID_ERROR << 16; /* default return code */
+ cmd->SCp.phase = 0; /* bus free */
+
+ ppa_pb_claim(host_no);
+
+ ppa_hosts[host_no].ppa_tq.data = ppa_hosts + host_no;
+ ppa_hosts[host_no].ppa_tq.sync = 0;
+ queue_task(&ppa_hosts[host_no].ppa_tq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+
+ return 0;
}
/*
@@ -1069,263 +1157,166 @@ int ppa_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
*/
int ppa_biosparam(Disk * disk, kdev_t dev, int ip[])
{
- ip[0] = 0x40;
- ip[1] = 0x20;
+ ip[0] = 0x40;
+ ip[1] = 0x20;
+ ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]);
+ if (ip[2] > 1024) {
+ ip[0] = 0xff;
+ ip[1] = 0x3f;
ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]);
- if (ip[2] > 1024) {
- ip[0] = 0xff;
- ip[1] = 0x3f;
- ip[2] = (disk->capacity + 1) / (ip[0] * ip[1]);
- if (ip[2] > 1023)
- ip[2] = 1023;
- }
- return 0;
+ if (ip[2] > 1023)
+ ip[2] = 1023;
+ }
+ return 0;
}
int ppa_abort(Scsi_Cmnd * cmd)
{
- int host_no = cmd->host->unique_id;
-
- ppa_hosts[host_no].abort_flag = 1;
- ppa_hosts[host_no].error_code = DID_ABORT;
- if (ppa_hosts[host_no].ppa_wait_q)
- wake_up(&ppa_hosts[host_no].ppa_wait_q);
-
- return SCSI_ABORT_SNOOZE;
+ /*
+ * 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 */
+ cmd->result = DID_ABORT;
+ cmd->done(cmd);
+ return SCSI_ABORT_SUCCESS;
+ break;
+ default: /* SCSI command sent, can not abort */
+ return SCSI_ABORT_BUSY;
+ break;
+ }
}
int ppa_reset(Scsi_Cmnd * cmd, unsigned int x)
{
- int host_no = cmd->host->unique_id;
-
- ppa_hosts[host_no].abort_flag = 2;
- ppa_hosts[host_no].error_code = DID_RESET;
- if (ppa_hosts[host_no].ppa_wait_q)
- wake_up(&ppa_hosts[host_no].ppa_wait_q);
-
- return SCSI_RESET_PUNT;
+ int host_no = cmd->host->unique_id;
+ int ppb = PPA_BASE(host_no);
+
+ /*
+ * PHASE1:
+ * Bring the interface crashing down on whatever is running
+ * hopefully this will kill the request.
+ * Bring back up the interface, reset the drive (and anything
+ * attached for that manner)
+ */
+ if (cmd)
+ if (cmd->SCp.phase)
+ ppa_disconnect(cmd->host->unique_id);
+
+ ppa_connect(host_no, CONNECT_NORMAL);
+ w_dtr(ppb, 0x40);
+ w_ctr(ppb, 0x8);
+ udelay(30);
+ w_ctr(ppb, 0xc);
+ udelay(1000); /* delay for devices to settle down */
+ ppa_disconnect(host_no);
+ udelay(1000); /* Additional delay to allow devices to settle down */
+
+ /*
+ * PHASE2:
+ * Sanity check for the sake of mid-level driver
+ */
+ if (!cmd) {
+ printk("ppa bus reset called for invalid command.\n");
+ return SCSI_RESET_NOT_RUNNING;
+ }
+ /*
+ * PHASE3:
+ * Flag the current command as having died due to reset
+ */
+ ppa_connect(host_no, CONNECT_NORMAL);
+ ppa_fail(host_no, DID_RESET);
+
+ /* Since the command was already on the timer queue ppa_interrupt
+ * will be called shortly.
+ */
+ return SCSI_RESET_PENDING;
}
-
-
-/***************************************************************************
- * Parallel port probing routines *
- ***************************************************************************/
-
-
-#ifdef MODULE
-Scsi_Host_Template driver_template = PPA;
-#include "scsi_module.c"
-#endif
-
-/*
- * Start of Chipset kludges
- */
-
-int ppa_detect(Scsi_Host_Template * host)
-{
- struct Scsi_Host *hreg;
- int rs;
- int ports;
- int i, nhosts;
- struct parport *pb = parport_enumerate();
-
- printk("PPA driver version: %s\n", PPA_VERSION);
- nhosts = 0;
-
- for (i = 0; pb; i++, pb=pb->next) {
- int modes = pb->modes;
-
- /* We only understand PC-style ports */
- if (modes & PARPORT_MODE_PCSPP) {
-
- /* transfer global values here */
- if (ppa_speed >= 0)
- ppa_hosts[i].speed = ppa_speed;
- if (ppa_speed_fast >= 0)
- ppa_hosts[i].speed_fast = ppa_speed_fast;
-
- ppa_hosts[i].dev = parport_register_device(pb, "ppa",
- NULL, ppa_wakeup, NULL,
- PARPORT_DEV_TRAN, (void *) &ppa_hosts[i]);
-
- /* Claim the bus so it remembers what we do to the
- * control registers. [ CTR and ECP ]
- */
- ppa_pb_claim(i);
- w_ctr(i, 0x0c);
-
- ppa_hosts[i].mode = PPA_NIBBLE;
- if (modes & (PARPORT_MODE_PCEPP | PARPORT_MODE_PCECPEPP)) {
- ppa_hosts[i].mode = PPA_EPP_32;
- printk("PPA: Parport [ PCEPP ]\n");
- } else if (modes & PARPORT_MODE_PCECP) {
- w_ecr(i, 0x20);
- ppa_hosts[i].mode = PPA_PS2;
- printk("PPA: Parport [ PCECP in PS2 submode ]\n");
- } else if (modes & PARPORT_MODE_PCPS2) {
- ppa_hosts[i].mode = PPA_PS2;
- printk("PPA: Parport [ PCPS2 ]\n");
- }
- /* Done configuration */
- ppa_pb_release(i);
-
- rs = ppa_init(i);
- if (rs) {
- parport_unregister_device(ppa_hosts[i].dev);
- continue;
- }
- /* now the glue ... */
- switch (ppa_hosts[i].mode) {
- case PPA_NIBBLE:
- case PPA_PS2:
- ports = 3;
- break;
- case PPA_EPP_8:
- case PPA_EPP_16:
- case PPA_EPP_32:
- ports = 8;
- break;
- default: /* Never gets here */
- continue;
- }
-
- host->can_queue = PPA_CAN_QUEUE;
- host->sg_tablesize = ppa_sg;
- hreg = scsi_register(host, 0);
- hreg->io_port = pb->base;
- hreg->n_io_port = ports;
- hreg->dma_channel = -1;
- hreg->unique_id = i;
- ppa_hosts[i].host = hreg->host_no;
- nhosts++;
- }
- }
- if (nhosts == 0)
- return 0;
- else
- return 1; /* return number of hosts detected */
-}
-
-/* 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 int ppa_strncmp(const char *a, const char *b, int len)
-{
- int loop;
- for (loop = 0; loop < len; loop++)
- if (a[loop] != b[loop])
- return 1;
-
- return 0;
-}
-
-static int ppa_proc_write(int hostno, char *buffer, int length)
+static int device_check(int host_no)
{
- unsigned long x;
- const char *inv_num = "ppa /proc entry passed invalid number\n";
-
- if ((length > 15) && (ppa_strncmp(buffer, "ppa_speed_fast=", 15) == 0)) {
- x = simple_strtoul(buffer + 15, NULL, 0);
- if (x <= ppa_hosts[hostno].speed)
- ppa_hosts[hostno].speed_fast = x;
- else
- printk(inv_num);
- return length;
+ /* 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 = PPA_BASE(host_no);
+ unsigned char l;
+
+ old_mode = ppa_hosts[host_no].mode;
+ for (loop = 0; loop < 8; loop++) {
+ /* Attempt to use EPP for Test Unit Ready */
+ if ((ppb & 0x0007) == 0x0000)
+ ppa_hosts[host_no].mode = PPA_EPP_32;
+
+ second_pass:
+ ppa_connect(host_no, CONNECT_EPP_MAYBE);
+ /* Select SCSI device */
+ if (!ppa_select(host_no, loop)) {
+ ppa_disconnect(host_no);
+ continue;
}
- if ((length > 10) && (ppa_strncmp(buffer, "ppa_speed=", 10) == 0)) {
- x = simple_strtoul(buffer + 10, NULL, 0);
- if (x >= ppa_hosts[hostno].speed_fast)
- ppa_hosts[hostno].speed = x;
- else
- printk(inv_num);
- return length;
+ printk("ppa: Found device at ID %i, Attempting to use %s\n", loop,
+ PPA_MODE_STRING[ppa_hosts[host_no].mode]);
+
+ /* Send SCSI command */
+ status = 1;
+ w_ctr(ppb, 0x0c);
+ for (l = 0; (l < 6) && (status); l++)
+ status = ppa_out(host_no, cmd, 1);
+
+ if (!status) {
+ ppa_disconnect(host_no);
+ ppa_connect(host_no, CONNECT_EPP_MAYBE);
+ w_dtr(ppb, 0x40);
+ w_ctr(ppb, 0x08);
+ udelay(30);
+ w_ctr(ppb, 0x0c);
+ udelay(1000);
+ ppa_disconnect(host_no);
+ udelay(1000);
+ if (ppa_hosts[host_no].mode == PPA_EPP_32) {
+ ppa_hosts[host_no].mode = old_mode;
+ goto second_pass;
+ }
+ printk("ppa: Unable to establish communication, aborting driver load.\n");
+ return 1;
}
- if ((length > 10) && (ppa_strncmp(buffer, "epp_speed=", 10) == 0)) {
- x = simple_strtoul(buffer + 10, NULL, 0);
- ppa_hosts[hostno].epp_speed = x;
- return length;
+ 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(host_no);
+ ppa_connect(host_no, CONNECT_EPP_MAYBE);
+ w_dtr(ppb, 0x40);
+ w_ctr(ppb, 0x08);
+ udelay(30);
+ w_ctr(ppb, 0x0c);
+ udelay(1000);
+ ppa_disconnect(host_no);
+ udelay(1000);
+ if (ppa_hosts[host_no].mode == PPA_EPP_32) {
+ ppa_hosts[host_no].mode = old_mode;
+ goto second_pass;
+ }
+ printk("ppa: Unable to establish communication, aborting driver load.\n");
+ return 1;
}
- if ((length > 12) && (ppa_strncmp(buffer, "epp_timeout=", 12) == 0)) {
- x = simple_strtoul(buffer + 12, NULL, 0);
- ppa_hosts[hostno].timeout = x;
- return length;
- }
- if ((length > 5) && (ppa_strncmp(buffer, "mode=", 5) == 0)) {
- x = simple_strtoul(buffer + 5, NULL, 0);
- ppa_hosts[hostno].mode = x;
- return length;
- }
- printk("ppa /proc: invalid variable\n");
- return (-EINVAL);
-}
-
-int ppa_proc_info(char *buffer, char **start, off_t offset,
- int length, int hostno, int inout)
-{
- int i;
- int size, len = 0;
- off_t begin = 0;
- off_t pos = 0;
-
- for (i = 0; i < 4; i++)
- if (ppa_hosts[i].host == hostno)
- break;
-
- if (inout)
- return ppa_proc_write(i, buffer, length);
-
- size = sprintf(buffer + len, "Version : %s\n", PPA_VERSION);
- len += size;
- pos = begin + len;
- size = sprintf(buffer + len, "Parport : %s\n",
- ppa_hosts[i].dev->port->name);
- len += size;
- pos = begin + len;
-
- size = sprintf(buffer + len, "Mode : %s\n",
- PPA_MODE_STRING[ppa_hosts[i].mode]);
- len += size;
- pos = begin + len;
-
- size = sprintf(buffer + len, "\nTiming Parameters\n");
- len += size;
- pos = begin + len;
-
- size = sprintf(buffer + len, "ppa_speed %i\n",
- ppa_hosts[i].speed);
- len += size;
- pos = begin + len;
-
- size = sprintf(buffer + len, "ppa_speed_fast %i\n",
- ppa_hosts[i].speed_fast);
- len += size;
- pos = begin + len;
-
- if (IN_EPP_MODE(ppa_hosts[i].mode)) {
- size = sprintf(buffer + len, "epp_speed %i\n",
- ppa_hosts[i].epp_speed);
- len += size;
- pos = begin + len;
-
- size = sprintf(buffer + len, "\nInternal Counters\n");
- len += size;
- pos = begin + len;
-
- size = sprintf(buffer + len, "epp_timeout %i\n",
- ppa_hosts[i].timeout);
- len += size;
- pos = begin + len;
- }
- *start = buffer + (offset + begin);
- len -= (offset - begin);
- if (len > length)
- len = length;
- return len;
+ ppa_disconnect(host_no);
+ printk("ppa: Communication established with ID %i using %s\n", loop,
+ PPA_MODE_STRING[ppa_hosts[host_no].mode]);
+ return 0;
+ }
+ printk("ppa: No devices found, aborting driver load.\n");
+ return 1;
}
-/* end of ppa.c */
diff --git a/drivers/scsi/ppa.h b/drivers/scsi/ppa.h
index cdc2fdaa6..ea86dd17e 100644
--- a/drivers/scsi/ppa.h
+++ b/drivers/scsi/ppa.h
@@ -2,30 +2,67 @@
* 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 "Curtin 1-08-BETA"
-
-/* This driver reqires a 1.3.37 kernel or higher!! */
+#define PPA_VERSION "1.39a"
/* Use the following to enable certain chipset support
* Default is PEDANTIC = 3
*/
-
-#include <linux/config.h>
-
#ifndef CONFIG_SCSI_PPA_HAVE_PEDANTIC
#define CONFIG_SCSI_PPA_HAVE_PEDANTIC 3
#endif
-#ifndef CONFIG_SCSI_PPA_EPP_TIME
-#define CONFIG_SCSI_PPA_EPP_TIME 64
-#endif
+/*
+ * 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 ]
+ *
+ * Compiled against 2.1.53.
+ * Rebuilt ppa_abort() function, should handle unplugged cable.
+ * [1.35s]
+ *
+ * PPA now auto probes for EPP on base address which are aligned on
+ * 8 byte boundaries (0x278 & 0x378) using the attached devices.
+ * This hopefully avoids the nasty problem of trying to detect EPP.
+ * Tested on 2.1.53 [1.36]
+ *
+ * The id_probe utility no longer performs read/write tests.
+ * Additional code included for checking the Intel ECP bug
+ * (Bit 0 of STR stuck low which fools the EPP detection routine)
+ * [1.37]
+ *
+ * Oops! Got the bit sign mixed up for the Intel bug check.
+ * Found that an additional delay is required during SCSI resets
+ * to allow devices to settle down.
+ * [1.38]
+ *
+ * Fixed all problems in the parport sharing scheme. Now ppa can be safe
+ * used with lp or other parport devices on the same parallel port.
+ * 1997 by Andrea Arcangeli <arcangeli@mbox.queen.it>
+ * [1.39]
+ *
+ * Little fix in ppa engine to ensure that ppa don' t release parport
+ * or disconnect in wrong cases.
+ * 1997 by Andrea Arcangeli <arcangeli@mbox.queen.it>
+ * [1.39a]
+ */
/* ------ END OF USER CONFIGURABLE PARAMETERS ----- */
+#ifdef PPA_CODE
#include <linux/stddef.h>
#include <linux/module.h>
#include <linux/kernel.h>
@@ -35,14 +72,76 @@
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/blk.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
#include <asm/io.h>
#include "sd.h"
#include "hosts.h"
-#include <linux/parport.h>
/* batteries not included :-) */
-#define PPA_INITIATOR 7
+/*
+ * 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",
+ "EPP 32 bit",
+ "Unknown"};
+
+/* This is a global option */
+int ppa_sg = SG_ALL; /* enable/disable scatter-gather. */
+
+/* other options */
+#define PPA_CAN_QUEUE 1 /* use "queueing" interface */
+#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_DEBUG 0 /* debuging 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)+0x400)
+#define r_ecr(x) (unsigned char)inb((x)+0x402)
+
+#define w_dtr(x,y) outb(y, (x))
+#define w_str(x,y) outb(y, (x)+1)
+#define w_ctr(x,y) outb(y, (x)+2)
+#define w_epp(x,y) outb(y, (x)+4)
+#define w_fifo(x,y) outb(y, (x)+0x400)
+#define w_ecr(x,y) outb(y, (x)+0x402)
+
+static int ppa_engine(ppa_struct *, Scsi_Cmnd *);
+static int ppa_in(int, char *, int);
+static int ppa_init(int);
+static void ppa_interrupt(void *);
+static int ppa_out(int, char *, int);
+
+struct proc_dir_entry proc_scsi_ppa =
+{PROC_SCSI_PPA, 3, "ppa", S_IFDIR | S_IRUGO | S_IXUGO, 2};
+#else
+extern struct proc_dir_entry proc_scsi_ppa;
+#define ppa_release 0
+#endif
int ppa_detect(Scsi_Host_Template *);
const char *ppa_info(struct Scsi_Host *);
@@ -51,25 +150,13 @@ int ppa_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
int ppa_abort(Scsi_Cmnd *);
int ppa_reset(Scsi_Cmnd *, unsigned int);
int ppa_proc_info(char *, char **, off_t, int, int, int);
-int ppa_biosparam(Disk *, kdev_t, int*);
-static int ppa_release(struct Scsi_Host *);
-
-#ifndef MODULE
-#ifdef PPA_CODE
-#define SKIP_PROC_DIR
-#endif
-#endif
-
-#ifndef SKIP_PROC_DIR
-struct proc_dir_entry proc_scsi_ppa =
-{PROC_SCSI_PPA, 3, "ppa", S_IFDIR | S_IRUGO | S_IXUGO, 2};
-#endif /* !PPA_CODE => hosts.c */
+int ppa_biosparam(Disk *, kdev_t, int *);
#define PPA { /* next */ 0, \
/* usage_count */ 0, \
/* proc_dir */ &proc_scsi_ppa, \
/* proc_info */ ppa_proc_info, \
- /* name */ "Iomega ZIP/JAZ Traveller", \
+ /* name */ "Iomega parport ZIP drive", \
/* detect */ ppa_detect, \
/* release */ ppa_release, \
/* info */ 0, \
@@ -80,7 +167,7 @@ struct proc_dir_entry proc_scsi_ppa =
/* slave_attach */ 0, \
/* bios_param */ ppa_biosparam, \
/* can_queue */ 0, \
- /* this_id */ PPA_INITIATOR, \
+ /* this_id */ -1, \
/* sg_tablesize */ SG_ALL, \
/* cmd_per_lun */ 1, \
/* present */ 0, \
diff --git a/drivers/scsi/psi240i.c b/drivers/scsi/psi240i.c
new file mode 100644
index 000000000..8e12f4b23
--- /dev/null
+++ b/drivers/scsi/psi240i.c
@@ -0,0 +1,717 @@
+/*+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/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "psi240i.h"
+#include "psi_chip.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry Proc_Scsi_Psi240i =
+ { PROC_SCSI_PSI240I, 7, "psi240i", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+#define MAXBOARDS 2 /* 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 ( 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 ( 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 recieved 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);
+ }
+/****************************************************************
+ * 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.
+ *
+ ****************************************************************/
+int Psi240i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+ {
+ UCHAR *cdb = (UCHAR *)SCpnt->cmnd; // Pointer to SCSI CDB
+ PADAPTER240I padapter = HOSTDATA(SCpnt->host); // Pointer to adapter control structure
+ POUR_DEVICE pdev = &padapter->device[SCpnt->target];// 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;
+ }
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+ {
+ SCpnt->SCp.Status++;
+ }
+/****************************************************************
+ * Name: Psi240i_Command
+ *
+ * Description: Process a command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ *
+ * Returns: Status code.
+ *
+ ****************************************************************/
+int Psi240i_Command (Scsi_Cmnd *SCpnt)
+ {
+ DEB(printk("psi240i_command: ..calling psi240i_queuecommand\n"));
+
+ Psi240i_QueueCommand (SCpnt, internal_done);
+
+ SCpnt->SCp.Status = 0;
+ while (!SCpnt->SCp.Status)
+ barrier ();
+ return SCpnt->result;
+ }
+/***************************************************************************
+ * 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.
+ *
+ **************************************************************************/
+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.
+ *
+ ****************************************************************/
+int Psi240i_Detect (Scsi_Host_Template *tpnt)
+ {
+ int board;
+ int count = 0;
+ int unit;
+ int z;
+ USHORT port;
+ CHIP_CONFIG_N chipConfig;
+ CHIP_DEVICE_N chipDevice[8];
+ struct Scsi_Host *pshost;
+ ULONG flags;
+
+ for ( board = 0; board < 6; board++ ) // scan for I/O ports
+ {
+ port = portAddr[board]; // get base address to test
+ if ( check_region (port, 16) ) // test for I/O addresses available
+ continue; // nope
+ if ( inb_p (port + REG_FAIL) != CHIP_ID ) // do the first test for likley hood that it is us
+ continue;
+ 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
+ continue; // nope
+ if ( inb_p (port + 1) != 0xAA ) // test 2nd byte
+ continue; // 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
+ continue;
+
+ pshost = scsi_register (tpnt, sizeof(ADAPTER240I));
+
+ save_flags (flags);
+ cli ();
+ if ( request_irq (chipConfig.irq, Irq_Handler, 0, "psi240i", NULL) )
+ {
+ printk ("Unable to allocate IRQ for PSI-240I controller.\n");
+ restore_flags (flags);
+ goto unregister;
+ }
+
+ 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));
+ }
+
+ restore_flags (flags);
+ 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;
+
+unregister:;
+ scsi_unregister (pshost);
+ }
+ return count;
+ }
+/****************************************************************
+ * Name: Psi240i_Abort
+ *
+ * Description: Process the Abort command from the SCSI manager.
+ *
+ * Parameters: SCpnt - Pointer to SCSI command structure.
+ *
+ * Returns: Allways snooze.
+ *
+ ****************************************************************/
+int Psi240i_Abort (Scsi_Cmnd *SCpnt)
+ {
+ DEB (printk ("psi240i_abort\n"));
+ return SCSI_ABORT_SNOOZE;
+ }
+/****************************************************************
+ * Name: Psi240i_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 Psi240i_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+ {
+ return SCSI_RESET_PUNT;
+ }
+
+#include "sd.h"
+
+/****************************************************************
+ * 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.
+ *
+ ****************************************************************/
+int Psi240i_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[])
+ {
+ POUR_DEVICE pdev;
+
+ pdev = &(HOSTDATA(disk->device->host)->device[disk->device->id]);
+
+ geom[0] = pdev->heads;
+ geom[1] = pdev->sectors;
+ geom[2] = pdev->cylinders;
+ return 0;
+ }
+
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = PSI240I;
+
+#include "scsi_module.c"
+#endif
+
diff --git a/drivers/scsi/psi240i.h b/drivers/scsi/psi240i.h
new file mode 100644
index 000000000..282ee1bb0
--- /dev/null
+++ b/drivers/scsi/psi240i.h
@@ -0,0 +1,344 @@
+/*+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>
+#include <linux/kdev_t.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
+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
+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_Detect (Scsi_Host_Template *tpnt);
+int Psi240i_Command (Scsi_Cmnd *SCpnt);
+int Psi240i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
+int Psi240i_Abort (Scsi_Cmnd *SCpnt);
+int Psi240i_Reset (Scsi_Cmnd *SCpnt, unsigned int flags);
+int Psi240i_BiosParam (Disk *disk, kdev_t dev, int geom[]);
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+extern struct proc_dir_entry Proc_Scsi_Psi240i;
+
+#define PSI240I { NULL, NULL, \
+ &Proc_Scsi_Psi240i,/* proc_dir_entry */ \
+ NULL, \
+ "PSI-240I EIDE Disk Controller", \
+ Psi240i_Detect, \
+ NULL, \
+ NULL, \
+ Psi240i_Command, \
+ Psi240i_QueueCommand, \
+ Psi240i_Abort, \
+ Psi240i_Reset, \
+ NULL, \
+ Psi240i_BiosParam, \
+ 1, \
+ -1, \
+ SG_NONE, \
+ 1, \
+ 0, \
+ 0, \
+ DISABLE_CLUSTERING }
+
+#endif
diff --git a/drivers/scsi/psi_chip.h b/drivers/scsi/psi_chip.h
new file mode 100644
index 000000000..89bc64e6b
--- /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 accessable 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 000000000..d8a407f3d
--- /dev/null
+++ b/drivers/scsi/psi_dale.h
@@ -0,0 +1,187 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 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_dale.h
+ *
+ * Description: This file contains the interface defines and
+ * error codes.
+ *
+ *-M*************************************************************************/
+
+#ifndef PSI_DALE
+#define PSI_DALE
+
+/************************************************/
+/* Dale PCI setup */
+/************************************************/
+#define VENDOR_PSI 0x1256
+#define DEVICE_DALE_1 0x4401 /* 'D1' */
+
+/************************************************/
+/* Misc konstants */
+/************************************************/
+#define DALE_MAXDRIVES 4
+#define SECTORSXFER 8
+#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_MODE4P 0x00070000L
+
+#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 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_SCRATH_DEVICE_0 4 // device type codes
+#define DALE_SCRATH_DEVICE_1 5
+#define DALE_SCRATH_DEVICE_2 6
+#define DALE_SCRATH_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
+
+/************************************************/
+/* Dale cable select bits */
+/************************************************/
+#define SEL_NONE 0x00
+#define SEL_1 0x01
+#define SEL_2 0x02
+
+/************************************************/
+/* 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_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
+
+/************************************************/
+/* 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 rebootRebuil;
+ USHORT timingMode;
+ USHORT spare5;
+ USHORT spare6;
+ SETUP_DEVICE setupDevice[4];
+ } SETUP, *PSETUP;
+
+#endif
diff --git a/drivers/scsi/psi_roy.h b/drivers/scsi/psi_roy.h
new file mode 100644
index 000000000..36a0341a9
--- /dev/null
+++ b/drivers/scsi/psi_roy.h
@@ -0,0 +1,314 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 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_roy.h
+ *
+ * Description: This file contains the host interface command and
+ * error codes.
+ *
+ *-M*************************************************************************/
+
+#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 30 // number of jiffies 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_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_SCSI_INQ 0x36 /* to DASD inquire for DASD info in SCSI inquiry format */
+#define CMD_DASD_CAP 0x37 /* read DASD capacity */
+#define CMD_DASD_INQ 0x38 /* do DASD inquire for type data */
+#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_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 */
+
+
+/************************************************/
+/* */
+/* 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 */
+
+#endif
+
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index a81df6241..9f20c373c 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -3297,6 +3297,7 @@ static int scsi_register_device_module(struct Scsi_Device_Template * tpnt)
* This does any final handling that is required.
*/
if(tpnt->finish && tpnt->nr_dev) (*tpnt->finish)();
+ resize_dma_pool();
MOD_INC_USE_COUNT;
return 0;
}
diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h
index 6404809cc..dd0f18fa1 100644
--- a/drivers/scsi/scsi.h
+++ b/drivers/scsi/scsi.h
@@ -498,8 +498,7 @@ static Scsi_Cmnd * end_scsi_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors
req->nr_sectors -= bh->b_size >> 9;
req->sector += bh->b_size >> 9;
bh->b_reqnext = NULL;
- mark_buffer_uptodate(bh, uptodate);
- unlock_buffer(bh);
+ bh->b_end_io(bh, uptodate);
sectors -= bh->b_size >> 9;
if ((bh = req->bh) != NULL) {
req->current_nr_sectors = bh->b_size >> 9;
diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c
index 073de6095..0971ce954 100644
--- a/drivers/scsi/scsi_ioctl.c
+++ b/drivers/scsi/scsi_ioctl.c
@@ -341,6 +341,11 @@ int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg)
&((Scsi_Idlun *) arg)->dev_id);
put_user(dev->host->unique_id, &((Scsi_Idlun *) arg)->host_unique_id);
return 0;
+ case SCSI_IOCTL_GET_BUS_NUMBER:
+ result = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
+ if (result) return result;
+ put_user( dev->host->host_no, (int *) arg);
+ return 0;
case SCSI_IOCTL_TAGGED_ENABLE:
if(!suser()) return -EACCES;
if(!dev->tagged_supported) return -EINVAL;
diff --git a/drivers/scsi/scsi_module.c b/drivers/scsi/scsi_module.c
index 5316e2e64..8a0de132a 100644
--- a/drivers/scsi/scsi_module.c
+++ b/drivers/scsi/scsi_module.c
@@ -34,7 +34,11 @@
int init_module(void) {
driver_template.module = &__this_module;
scsi_register_module(MODULE_SCSI_HA, &driver_template);
- return (driver_template.present == 0);
+ if (driver_template.present)
+ return 0;
+
+ scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
+ return -1;
}
void cleanup_module( void) {
diff --git a/drivers/scsi/seagate.c b/drivers/scsi/seagate.c
index e9c411e48..d532c13e4 100644
--- a/drivers/scsi/seagate.c
+++ b/drivers/scsi/seagate.c
@@ -72,6 +72,13 @@
* x 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.
@@ -794,6 +801,10 @@ static int internal_command (unsigned char target, unsigned char lun,
unsigned char message = 0;
register unsigned char status_read;
+#ifndef OLDCNTDATASCEME
+ volatile unsigned char tmp_data;
+ volatile unsigned char tmp_control;
+#endif
unsigned transfersize = 0, underflow = 0;
incommand = 0;
@@ -1029,6 +1040,7 @@ static int internal_command (unsigned char target, unsigned char lun,
* try this with a SCSI protocol or logic analyzer to see what is
* going on.
*/
+#ifdef OLDCNTDATASCEME
#ifdef SWAPCNTDATA
cli();
WRITE_CONTROL (BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL |
@@ -1044,6 +1056,16 @@ static int internal_command (unsigned char target, unsigned char lun,
(reselect ? CMD_ATTN : 0));
sti ();
#endif
+#else
+ tmp_data = (unsigned char) ((1 << target) | (controller_type == SEAGATE
+? 0x80 : 0x40));
+ tmp_control = BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL |
+ (reselect ? CMD_ATTN : 0) | CMD_BSY;
+ WRITE_CONTROL(tmp_data);
+ WRITE_DATA(tmp_control);
+ tmp_control ^= CMD_BSY;
+ WRITE_CONTROL(tmp_control);
+#endif /* OLDCNTDATASCEME */
while (!((status_read = STATUS) & STAT_BSY) && (jiffies < clock)
&& !st0x_aborted)
#if 0 && (DEBUG & PHASE_SELECTION)
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index d83b94e26..a94fb569c 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -106,7 +106,7 @@ static int sg_open(struct inode * inode, struct file * filp)
if (flags & O_NONBLOCK)
return -EBUSY;
interruptible_sleep_on(&scsi_generics[dev].generic_wait);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -ERESTARTSYS;
}
scsi_generics[dev].exclude=1;
@@ -121,7 +121,7 @@ static int sg_open(struct inode * inode, struct file * filp)
if (flags & O_NONBLOCK)
return -EBUSY;
interruptible_sleep_on(&scsi_generics[dev].generic_wait);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -ERESTARTSYS;
}
@@ -172,7 +172,7 @@ static char *sg_malloc(int size)
while(big_inuse)
{
interruptible_sleep_on(&big_wait);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return NULL;
}
big_inuse=1;
@@ -200,12 +200,19 @@ static void sg_free(char *buff,int size)
* complete semaphores to tell us whether the buffer is available for us
* and whether the command is actually done.
*/
-static long sg_read(struct inode *inode,struct file *filp,char *buf,unsigned long count)
+static ssize_t sg_read(struct file *filp, char *buf,
+ size_t count, loff_t *ppos)
{
+ struct inode *inode = filp->f_dentry->d_inode;
int dev=MINOR(inode->i_rdev);
int i;
unsigned long flags;
struct scsi_generic *device=&scsi_generics[dev];
+
+ if (ppos != &filp->f_pos) {
+ /* FIXME: Hmm. Seek to the right place, or fail? */
+ }
+
if ((i=verify_area(VERIFY_WRITE,buf,count)))
return i;
@@ -222,7 +229,7 @@ static long sg_read(struct inode *inode,struct file *filp,char *buf,unsigned lon
return -EAGAIN;
}
interruptible_sleep_on(&device->read_wait);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
{
restore_flags(flags);
return -ERESTARTSYS;
@@ -322,8 +329,10 @@ static void sg_command_done(Scsi_Cmnd * SCpnt)
wake_up(&scsi_generics[dev].read_wait);
}
-static long sg_write(struct inode *inode,struct file *filp,const char *buf,unsigned long count)
+static ssize_t sg_write(struct file *filp, const char *buf,
+ size_t count, loff_t *ppos)
{
+ struct inode *inode = filp->f_dentry->d_inode;
int bsize,size,amt,i;
unsigned char cmnd[MAX_COMMAND_SIZE];
kdev_t devt = inode->i_rdev;
@@ -333,6 +342,10 @@ static long sg_write(struct inode *inode,struct file *filp,const char *buf,unsig
unsigned char opcode;
Scsi_Cmnd * SCpnt;
+ if (ppos != &filp->f_pos) {
+ /* FIXME: Hmm. Seek to the right place, or fail? */
+ }
+
if ((i=verify_area(VERIFY_READ,buf,count)))
return i;
/*
@@ -355,7 +368,7 @@ static long sg_write(struct inode *inode,struct file *filp,const char *buf,unsig
printk("sg_write: sleeping on pending request\n");
#endif
interruptible_sleep_on(&device->write_wait);
- if (current->signal & ~current->blocked)
+ if (signal_pending(current))
return -ERESTARTSYS;
}
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 517568346..12a8f3723 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -45,6 +45,8 @@
#include <scsi/scsi_ioctl.h> /* For the door lock/unlock commands */
#include "constants.h"
+MODULE_PARM(xa_test,"i"); /* see sr_ioctl.c */
+
#define MAX_RETRIES 3
#define SR_TIMEOUT (30 * HZ)
@@ -67,12 +69,15 @@ static int * sr_hardsizes = NULL; /* Hardware sector size */
static int sr_open(struct cdrom_device_info*, int);
void get_sectorsize(int);
+void get_capabilities(int);
void requeue_sr_request (Scsi_Cmnd * SCpnt);
static int sr_media_change(struct cdrom_device_info*, int);
static void sr_release(struct cdrom_device_info *cdi)
{
+ if (scsi_CDs[MINOR(cdi->dev)].sector_size > 2048)
+ sr_set_blocklength(MINOR(cdi->dev),2048);
sync_dev(cdi->dev);
scsi_CDs[MINOR(cdi->dev)].device->access_count--;
if (scsi_CDs[MINOR(cdi->dev)].device->host->hostt->module)
@@ -89,14 +94,14 @@ static struct cdrom_device_ops sr_dops = {
sr_media_change, /* media changed */
sr_tray_move, /* tray move */
sr_lock_door, /* lock door */
- NULL, /* select speed */
+ sr_select_speed, /* select speed */
NULL, /* select disc */
sr_get_last_session, /* get last session */
sr_get_mcn, /* get universal product code */
sr_reset, /* hard reset */
sr_audio_ioctl, /* audio ioctl */
sr_dev_ioctl, /* device-specific ioctl */
- CDC_CLOSE_TRAY | CDC_OPEN_TRAY| CDC_LOCK |
+ CDC_CLOSE_TRAY | CDC_OPEN_TRAY| CDC_LOCK | CDC_SELECT_SPEED |
CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO,
0
};
@@ -139,10 +144,9 @@ int sr_media_change(struct cdrom_device_info *cdi, int slot){
/* If the disk changed, the capacity will now be different,
* so we force a re-read of this information */
if (retval) {
-#ifdef CONFIG_BLK_DEV_SR_VENDOR
+ /* check multisession offset etc */
sr_cd_check(cdi);
-#endif
-
+
/*
* If the disk changed, the capacity will now be different,
* so we force a re-read of this information
@@ -311,7 +315,8 @@ static void rw_intr (Scsi_Cmnd * SCpnt)
}
if (SCpnt->sense_buffer[2] == ILLEGAL_REQUEST) {
- printk("CD-ROM error: ");
+ printk("sr%d: CD-ROM error: ",
+ DEVICE_NR(SCpnt->request.rq_dev));
print_sense("sr", SCpnt);
printk("command was: ");
print_command(SCpnt->cmnd);
@@ -329,7 +334,8 @@ static void rw_intr (Scsi_Cmnd * SCpnt)
}
if (SCpnt->sense_buffer[2] == NOT_READY) {
- printk(KERN_INFO "CD-ROM not ready. Make sure you have a disc in the drive.\n");
+ printk(KERN_INFO "sr%d: CD-ROM not ready. Make sure you have a disc in the drive.\n",
+ DEVICE_NR(SCpnt->request.rq_dev));
SCpnt = end_scsi_request(SCpnt, 0, this_count);
requeue_sr_request(SCpnt); /* Do next request */
return;
@@ -358,7 +364,7 @@ static void rw_intr (Scsi_Cmnd * SCpnt)
requeue_sr_request(SCpnt);
return;
}
- }
+ }
/* We only get this far if we have an error we have not recognized */
if(result) {
@@ -443,6 +449,17 @@ static void do_sr_request (void)
SDev->was_reset = 0;
}
+ /* we do lazy blocksize switching (when reading XA sectors,
+ * see CDROMREADMODE2 ioctl) */
+ if (scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].sector_size > 2048) {
+ if (!in_interrupt())
+ sr_set_blocklength(DEVICE_NR(CURRENT->rq_dev),2048);
+#if 1
+ else
+ printk("sr: can't switch blocksize: in interrupt\n");
+#endif
+ }
+
if (flag++ == 0)
SCpnt = allocate_device(&CURRENT,
scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].device, 0);
@@ -649,7 +666,7 @@ void requeue_sr_request (Scsi_Cmnd * SCpnt)
* ensure that all scsi operations are able to do at least a non-scatter/gather
* operation */
if(sgpnt[count].address == NULL){ /* Out of dma memory */
- printk("Warning: Running low on SCSI DMA buffers");
+ printk("Warning: Running low on SCSI DMA buffers\n");
/* Try switching back to a non scatter-gather operation. */
while(--count >= 0){
if(sgpnt[count].alt_address)
@@ -805,17 +822,7 @@ static int sr_attach(Scsi_Device * SDp){
SDp->scsi_request_fn = do_sr_request;
scsi_CDs[i].device = SDp;
- scsi_CDs[i].cdi.ops = &sr_dops;
- scsi_CDs[i].cdi.handle = &scsi_CDs[i];
- scsi_CDs[i].cdi.dev = MKDEV(MAJOR_NR,i);
- scsi_CDs[i].cdi.mask = 0;
- scsi_CDs[i].cdi.speed = 1;
- scsi_CDs[i].cdi.capacity = 1;
- register_cdrom(&scsi_CDs[i].cdi, "sr");
-
-#ifdef CONFIG_BLK_DEV_SR_VENDOR
sr_vendor_init(i);
-#endif
sr_template.nr_dev++;
if(sr_template.nr_dev > sr_template.dev_max)
@@ -902,7 +909,7 @@ void get_sectorsize(int i){
case 512:
break;
default:
- printk ("scd%d : unsupported sector size %d.\n",
+ printk ("sr%d: unsupported sector size %d.\n",
i, scsi_CDs[i].sector_size);
scsi_CDs[i].capacity = 0;
scsi_CDs[i].needs_sector_size = 1;
@@ -919,6 +926,58 @@ void get_sectorsize(int i){
scsi_free(buffer, 512);
}
+void get_capabilities(int i){
+ unsigned char cmd[6];
+ unsigned char *buffer;
+ int rc,n;
+
+ static char *loadmech[] = {
+ "caddy",
+ "tray",
+ "pop-up",
+ "",
+ "changer",
+ "changer",
+ "",
+ ""
+ };
+
+ buffer = (unsigned char *) scsi_malloc(512);
+ cmd[0] = MODE_SENSE;
+ cmd[1] = (scsi_CDs[i].device->lun << 5) & 0xe0;
+ cmd[2] = 0x2a;
+ cmd[4] = 128;
+ cmd[3] = cmd[5] = 0;
+ rc = sr_do_ioctl(i, cmd, buffer, 128, 1);
+
+ if (-EINVAL == rc) {
+ /* failed, drive has'nt this mode page */
+ scsi_CDs[i].cdi.speed = 1;
+ scsi_CDs[i].cdi.capacity = 1;
+ /* disable speed select, drive probably can't do this either */
+ scsi_CDs[i].cdi.mask |= CDC_SELECT_SPEED;
+ } else {
+ n = buffer[3]+4;
+ scsi_CDs[i].cdi.speed = ((buffer[n+8] << 8) + buffer[n+9])/176;
+ scsi_CDs[i].cdi.capacity = 1;
+ scsi_CDs[i].readcd_known = 1;
+ scsi_CDs[i].readcd_cdda = buffer[n+5] & 0x01;
+ /* print some capability bits */
+ printk("sr%i: scsi3-mmc drive: %dx/%dx %s%s%s%s%s\n",i,
+ ((buffer[n+14] << 8) + buffer[n+15])/176,
+ scsi_CDs[i].cdi.speed,
+ buffer[n+3]&0x01 ? "writer " : "", /* CD Writer */
+ 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... */
+ scsi_CDs[i].cdi.mask |= CDC_CLOSE_TRAY;
+ }
+ scsi_free(buffer, 512);
+}
+
static int sr_registered = 0;
static int sr_init()
@@ -984,7 +1043,16 @@ void sr_finish()
scsi_CDs[i].use = 1;
scsi_CDs[i].ten = 1;
scsi_CDs[i].remap = 1;
+ scsi_CDs[i].readcd_known = 0;
+ scsi_CDs[i].readcd_cdda = 0;
sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9);
+
+ scsi_CDs[i].cdi.ops = &sr_dops;
+ scsi_CDs[i].cdi.handle = &scsi_CDs[i];
+ scsi_CDs[i].cdi.dev = MKDEV(MAJOR_NR,i);
+ scsi_CDs[i].cdi.mask = 0;
+ get_capabilities(i);
+ register_cdrom(&scsi_CDs[i].cdi, "sr");
}
diff --git a/drivers/scsi/sr.h b/drivers/scsi/sr.h
index 1c3482f3d..983ff18df 100644
--- a/drivers/scsi/sr.h
+++ b/drivers/scsi/sr.h
@@ -17,8 +17,6 @@
#ifndef _SR_H
#define _SR_H
-#include <linux/config.h>
-
#include "scsi.h"
typedef struct
@@ -34,13 +32,15 @@ typedef struct
unsigned ten:1; /* support ten byte commands */
unsigned remap:1; /* support remapping */
unsigned use:1; /* is this device still supportable */
- unsigned xa_flag:1; /* CD has XA sectors */
+ 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;
} Scsi_CD;
extern Scsi_CD * scsi_CDs;
-int sr_do_ioctl(int, unsigned char*, void*, unsigned);
+int sr_do_ioctl(int, unsigned char*, void*, unsigned, int);
int sr_lock_door(struct cdrom_device_info*, int);
int sr_tray_move(struct cdrom_device_info*, int);
@@ -49,15 +49,16 @@ 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);
-/* vendor-specific */
-#ifdef CONFIG_BLK_DEV_SR_VENDOR
-void sr_vendor_init(int minor);
-int sr_cd_check(struct cdrom_device_info*);
int sr_read_sector(int minor, int lba, int blksize, unsigned char *dest);
+int sr_is_xa(int minor);
-#endif
+/* sr_vendor.c */
+void sr_vendor_init(int minor);
+int sr_cd_check(struct cdrom_device_info*);
+int sr_set_blocklength(int minor, int blocklength);
#endif
diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c
index bc870dfea..628d39c3e 100644
--- a/drivers/scsi/sr_ioctl.c
+++ b/drivers/scsi/sr_ioctl.c
@@ -1,4 +1,3 @@
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
@@ -16,6 +15,17 @@
#include <linux/ucdrom.h>
#include "sr.h"
+#if 0
+# define DEBUG
+#endif
+
+/* for now we borrow the "operation not supported" from the network folks */
+#define EDRIVE_CANT_DO_THIS EOPNOTSUPP
+
+/* 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;
+
extern void get_sectorsize(int);
#define IOCTL_RETRIES 3
@@ -39,12 +49,14 @@ static void sr_ioctl_done(Scsi_Cmnd * SCpnt)
error code is. Normally the UNIT_ATTENTION code will automatically
clear after one error */
-int sr_do_ioctl(int target, unsigned char * sr_cmd, void * buffer, unsigned buflength)
+int sr_do_ioctl(int target, unsigned char * sr_cmd, void * buffer, unsigned buflength, int quiet)
{
Scsi_Cmnd * SCpnt;
- int result;
+ int result, err = 0, retries = 0;
SCpnt = allocate_device(NULL, scsi_CDs[target].device, 1);
+
+retry:
{
struct semaphore sem = MUTEX_LOCKED;
SCpnt->request.sem = &sem;
@@ -61,28 +73,79 @@ int sr_do_ioctl(int target, unsigned char * sr_cmd, void * buffer, unsigned bufl
switch(SCpnt->sense_buffer[2] & 0xf) {
case UNIT_ATTENTION:
scsi_CDs[target].device->changed = 1;
- printk("Disc change detected.\n");
+ printk(KERN_INFO "sr%d: disc change detected.\n", target);
+ if (retries++ < 10)
+ goto retry;
+ err = -ENOMEDIUM;
break;
case NOT_READY: /* This happens if there is no disc in drive */
- printk(KERN_INFO "CDROM not ready. Make sure there is a disc in the drive.\n");
+ if (SCpnt->sense_buffer[12] == 0x04 &&
+ SCpnt->sense_buffer[13] == 0x01) {
+ /* sense: Logical unit is in process of becoming ready */
+ if (!quiet)
+ printk(KERN_INFO "sr%d: CDROM not ready yet.\n", target);
+ if (retries++ < 10) {
+ /* sleep 2 sec and try again */
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + 200;
+ schedule ();
+ goto retry;
+ } else {
+ /* 20 secs are enouth? */
+ err = -ENOMEDIUM;
+ break;
+ }
+ }
+ printk(KERN_INFO "sr%d: CDROM not ready. Make sure there is a disc in the drive.\n",target);
+#ifdef DEBUG
+ print_sense("sr", SCpnt);
+#endif
+ err = -ENOMEDIUM;
break;
case ILLEGAL_REQUEST:
- printk("CDROM (ioctl) reports ILLEGAL REQUEST.\n");
+ if (!quiet)
+ printk("sr%d: CDROM (ioctl) reports ILLEGAL REQUEST.\n",
+ target);
+ if (SCpnt->sense_buffer[12] == 0x20 &&
+ SCpnt->sense_buffer[13] == 0x00) {
+ /* sense: Invalid command operation code */
+ err = -EDRIVE_CANT_DO_THIS;
+ } else {
+ err = -EINVAL;
+ }
+#ifdef DEBUG
+ print_command(sr_cmd);
+ print_sense("sr", SCpnt);
+#endif
break;
default:
+ printk("sr%d: CDROM (ioctl) error, command: ", target);
+ print_command(sr_cmd);
print_sense("sr", SCpnt);
+ err = -EIO;
};
result = SCpnt->result;
SCpnt->request.rq_status = RQ_INACTIVE; /* Deallocate */
+ /* Wake up a process waiting for device */
wake_up(&SCpnt->device->device_wait);
- /* Wake up a process waiting for device*/
- return result;
+
+ return err;
}
/* ---------------------------------------------------------------------- */
/* interface to cdrom.c */
+static int test_unit_ready(int minor)
+{
+ u_char sr_cmd[10];
+
+ sr_cmd[0] = TEST_UNIT_READY;
+ sr_cmd[1] = ((scsi_CDs[minor].device -> lun) << 5);
+ sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
+ return sr_do_ioctl(minor, sr_cmd, NULL, 255, 1);
+}
+
int sr_tray_move(struct cdrom_device_info *cdi, int pos)
{
u_char sr_cmd[10];
@@ -92,7 +155,7 @@ int sr_tray_move(struct cdrom_device_info *cdi, int pos)
sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
sr_cmd[4] = (pos == 0) ? 0x03 /* close */ : 0x02 /* eject */;
- return sr_do_ioctl(MINOR(cdi->dev), sr_cmd, NULL, 255);
+ return sr_do_ioctl(MINOR(cdi->dev), sr_cmd, NULL, 255, 0);
}
int sr_lock_door(struct cdrom_device_info *cdi, int lock)
@@ -109,51 +172,39 @@ int sr_drive_status(struct cdrom_device_info *cdi, int slot)
return -EINVAL;
}
- if (!scsi_ioctl(scsi_CDs[MINOR(cdi->dev)].device,
- SCSI_IOCTL_TEST_UNIT_READY,0))
- return CDS_DISC_OK;
+ if (0 == test_unit_ready(MINOR(cdi->dev)))
+ return CDS_DISC_OK;
-#if 1
- /* Tell tray is open if the drive is not ready. Seems there is
- * no way to check whenever the tray is really open, but this way
- * we get auto-close-on-open work. And it seems to have no ill
- * effects with caddy drives... */
return CDS_TRAY_OPEN;
-#else
- return CDS_NO_DISC;
-#endif
}
int sr_disk_status(struct cdrom_device_info *cdi)
{
struct cdrom_tochdr toc_h;
struct cdrom_tocentry toc_e;
- int i;
+ int i,rc,have_datatracks = 0;
- if (scsi_ioctl(scsi_CDs[MINOR(cdi->dev)].device,SCSI_IOCTL_TEST_UNIT_READY,0))
- return CDS_NO_DISC;
-
- /* if the xa-bit is on, we tell it is XA... */
- if (scsi_CDs[MINOR(cdi->dev)].xa_flag)
- return CDS_XA_2_1;
+ /* look for data tracks */
+ if (0 != (rc = sr_audio_ioctl(cdi, CDROMREADTOCHDR, &toc_h)))
+ return (rc == -ENOMEDIUM) ? CDS_NO_DISC : CDS_NO_INFO;
- /* ...else we look for data tracks */
- if (sr_audio_ioctl(cdi, CDROMREADTOCHDR, &toc_h))
- return 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)
- return CDS_DATA_1;
-#if 0
- if (i == toc_h.cdth_trk0 && toc_e.cdte_addr.lba > 100)
- /* guess: looks like a "hidden track" CD */
- return CDS_DATA_1;
-#endif
+ if (toc_e.cdte_ctrl & CDROM_DATA_TRACK) {
+ have_datatracks = 1;
+ break;
+ }
}
- return CDS_AUDIO;
+ if (!have_datatracks)
+ return CDS_AUDIO;
+
+ if (scsi_CDs[MINOR(cdi->dev)].xa_flag)
+ return CDS_XA_2_1;
+ else
+ return CDS_DATA_1;
}
int sr_get_last_session(struct cdrom_device_info *cdi,
@@ -161,7 +212,7 @@ int sr_get_last_session(struct cdrom_device_info *cdi,
{
ms_info->addr.lba=scsi_CDs[MINOR(cdi->dev)].ms_offset;
ms_info->xa_flag=scsi_CDs[MINOR(cdi->dev)].xa_flag ||
- scsi_CDs[MINOR(cdi->dev)].ms_offset > 0;
+ (scsi_CDs[MINOR(cdi->dev)].ms_offset > 0);
return 0;
}
@@ -185,7 +236,7 @@ int sr_get_mcn(struct cdrom_device_info *cdi,struct cdrom_mcn *mcn)
buffer = (unsigned char*) scsi_malloc(512);
if(!buffer) return -ENOMEM;
- result = sr_do_ioctl(MINOR(cdi->dev), sr_cmd, buffer, 24);
+ result = sr_do_ioctl(MINOR(cdi->dev), sr_cmd, buffer, 24, 0);
memcpy (mcn->medium_catalog_number, buffer + 9, 13);
mcn->medium_catalog_number[13] = 0;
@@ -201,6 +252,26 @@ int sr_reset(struct cdrom_device_info *cdi)
return 0;
}
+int sr_select_speed(struct cdrom_device_info *cdi, int speed)
+{
+ u_char sr_cmd[12];
+
+ if (speed == 0)
+ speed = 0xffff; /* set to max */
+ else
+ speed *= 177; /* Nx to kbyte/s */
+
+ memset(sr_cmd,0,12);
+ sr_cmd[0] = 0xbb; /* SET CD SPEED */
+ sr_cmd[1] = (scsi_CDs[MINOR(cdi->dev)].device->lun) << 5;
+ sr_cmd[2] = (speed >> 8) & 0xff; /* MSB for speed (in kbytes/sec) */
+ sr_cmd[3] = speed & 0xff; /* LSB */
+
+ if (sr_do_ioctl(MINOR(cdi->dev), sr_cmd, NULL, 0, 0))
+ return -EIO;
+ return 0;
+}
+
/* ----------------------------------------------------------------------- */
/* this is called by the generic cdrom driver. arg is a _kernel_ pointer, */
/* becauce the generic cdrom driver does the user access stuff for us. */
@@ -224,7 +295,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
sr_cmd[8] = 0;
sr_cmd[9] = 0;
- result = sr_do_ioctl(target, sr_cmd, NULL, 255);
+ result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
break;
case CDROMRESUME:
@@ -236,7 +307,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
sr_cmd[8] = 1;
sr_cmd[9] = 0;
- result = sr_do_ioctl(target, sr_cmd, NULL, 255);
+ result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
break;
case CDROMPLAYMSF:
@@ -254,7 +325,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
sr_cmd[8] = msf->cdmsf_frame1;
sr_cmd[9] = 0;
- result = sr_do_ioctl(target, sr_cmd, NULL, 255);
+ result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
break;
}
@@ -273,7 +344,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
sr_cmd[8] = blk->len;
sr_cmd[9] = 0;
- result = sr_do_ioctl(target, sr_cmd, NULL, 255);
+ result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
break;
}
@@ -292,7 +363,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
sr_cmd[8] = ti->cdti_ind1;
sr_cmd[9] = 0;
- result = sr_do_ioctl(target, sr_cmd, NULL, 255);
+ result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
break;
}
@@ -312,7 +383,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
buffer = (unsigned char *) scsi_malloc(512);
if(!buffer) return -ENOMEM;
- result = sr_do_ioctl(target, sr_cmd, buffer, 12);
+ result = sr_do_ioctl(target, sr_cmd, buffer, 12, 0);
tochdr->cdth_trk0 = buffer[2];
tochdr->cdth_trk1 = buffer[3];
@@ -338,7 +409,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
buffer = (unsigned char *) scsi_malloc(512);
if(!buffer) return -ENOMEM;
- result = sr_do_ioctl (target, sr_cmd, buffer, 12);
+ result = sr_do_ioctl (target, sr_cmd, buffer, 12, 0);
tocentry->cdte_ctrl = buffer[5] & 0xf;
tocentry->cdte_adr = buffer[5] >> 4;
@@ -361,7 +432,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
sr_cmd[4] = 0;
- result = sr_do_ioctl(target, sr_cmd, NULL, 255);
+ result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
break;
case CDROMSTART:
@@ -370,7 +441,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
sr_cmd[4] = 1;
- result = sr_do_ioctl(target, sr_cmd, NULL, 255);
+ result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
break;
case CDROMVOLCTRL:
@@ -390,7 +461,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
buffer = (unsigned char *) scsi_malloc(512);
if(!buffer) return -ENOMEM;
- if ((result = sr_do_ioctl (target, sr_cmd, buffer, 28))) {
+ if ((result = sr_do_ioctl (target, sr_cmd, buffer, 28, 0))) {
printk ("Hosed while obtaining audio mode page\n");
scsi_free(buffer, 512);
break;
@@ -410,7 +481,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
break;
};
- if ((result = sr_do_ioctl (target, sr_cmd, mask, 28))) {
+ if ((result = sr_do_ioctl (target, sr_cmd, mask, 28, 0))) {
printk ("Hosed while obtaining mask for audio mode page\n");
scsi_free(buffer, 512);
scsi_free(mask, 512);
@@ -431,7 +502,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
sr_cmd[4] = 28;
sr_cmd[5] = 0;
- result = sr_do_ioctl (target, sr_cmd, buffer, 28);
+ result = sr_do_ioctl (target, sr_cmd, buffer, 28, 0);
scsi_free(buffer, 512);
scsi_free(mask, 512);
break;
@@ -454,7 +525,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
buffer = (unsigned char *) scsi_malloc(512);
if(!buffer) return -ENOMEM;
- if ((result = sr_do_ioctl (target, sr_cmd, buffer, 28))) {
+ if ((result = sr_do_ioctl (target, sr_cmd, buffer, 28, 0))) {
printk ("(CDROMVOLREAD) Hosed while obtaining audio mode page\n");
scsi_free(buffer, 512);
break;
@@ -487,7 +558,7 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
buffer = (unsigned char*) scsi_malloc(512);
if(!buffer) return -ENOMEM;
- result = sr_do_ioctl(target, sr_cmd, buffer, 16);
+ result = sr_do_ioctl(target, sr_cmd, buffer, 16, 0);
subchnl->cdsc_audiostatus = buffer[1];
subchnl->cdsc_format = CDROM_MSF;
@@ -516,7 +587,120 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
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
+ */
+
+int
+sr_read_cd(int minor, unsigned char *dest, int lba, int format, int blksize)
+{
+ unsigned char cmd[12];
+
+#ifdef DEBUG
+ printk("sr%d: sr_read_cd lba=%d format=%d blksize=%d\n",
+ minor,lba,format,blksize);
+#endif
+
+ memset(cmd,0,12);
+ cmd[0] = 0xbe /* READ_CD */;
+ cmd[1] = (scsi_CDs[minor].device->lun << 5) | ((format & 7) << 2);
+ cmd[2] = (unsigned char)(lba >> 24) & 0xff;
+ cmd[3] = (unsigned char)(lba >> 16) & 0xff;
+ cmd[4] = (unsigned char)(lba >> 8) & 0xff;
+ cmd[5] = (unsigned char) lba & 0xff;
+ cmd[8] = 1;
+ switch (blksize) {
+ case 2336: cmd[9] = 0x58; break;
+ case 2340: cmd[9] = 0x78; break;
+ case 2352: cmd[9] = 0xf8; break;
+ default: cmd[9] = 0x10; break;
+ }
+ return sr_do_ioctl(minor, cmd, dest, blksize, 0);
+}
+
+/*
+ * read sectors with blocksizes other than 2048
+ */
+
+int
+sr_read_sector(int minor, int lba, int blksize, unsigned char *dest)
+{
+ unsigned char cmd[12]; /* the scsi-command */
+ int rc;
+
+ /* we try the READ CD command first... */
+ if (scsi_CDs[minor].readcd_known) {
+ rc = sr_read_cd(minor, dest, lba, 0, blksize);
+ if (-EDRIVE_CANT_DO_THIS != rc)
+ return rc;
+ scsi_CDs[minor].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 != scsi_CDs[minor].sector_size)
+ if (0 != (rc = sr_set_blocklength(minor, blksize)))
+ return rc;
+
+#ifdef DEBUG
+ printk("sr%d: sr_read_sector lba=%d blksize=%d\n",minor,lba,blksize);
+#endif
+
+ memset(cmd,0,12);
+ cmd[0] = READ_10;
+ cmd[1] = (scsi_CDs[minor].device->lun << 5);
+ cmd[2] = (unsigned char)(lba >> 24) & 0xff;
+ cmd[3] = (unsigned char)(lba >> 16) & 0xff;
+ cmd[4] = (unsigned char)(lba >> 8) & 0xff;
+ cmd[5] = (unsigned char) lba & 0xff;
+ cmd[8] = 1;
+ rc = sr_do_ioctl(minor, cmd, dest, blksize, 0);
+
+ 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(int minor)
+{
+ unsigned char *raw_sector;
+ int is_xa;
+
+ if (!xa_test)
+ return 0;
+
+ raw_sector = (unsigned char *) scsi_malloc(2048+512);
+ if (!raw_sector) return -ENOMEM;
+ if (0 == sr_read_sector(minor,scsi_CDs[minor].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;
+ }
+ scsi_free(raw_sector, 2048+512);
+#ifdef DEBUG
+ printk("sr%d: sr_is_xa: %d\n",minor,is_xa);
+#endif
+ return is_xa;
+}
+
int sr_dev_ioctl(struct cdrom_device_info *cdi,
unsigned int cmd, unsigned long arg)
{
@@ -525,33 +709,31 @@ int sr_dev_ioctl(struct cdrom_device_info *cdi,
target = MINOR(cdi->dev);
switch (cmd) {
- /* these are compatible with the ide-cd driver */
- case CDROMREADRAW:
case CDROMREADMODE1:
case CDROMREADMODE2:
-
-#if CONFIG_BLK_DEV_SR_VENDOR
+ case CDROMREADRAW:
{
unsigned char *raw;
struct cdrom_msf msf;
- int blocksize, lba, rc;
+ int lba, rc;
+ int blocksize = 2048;
- if (cmd == CDROMREADMODE1)
- blocksize = CD_FRAMESIZE; /* 2048 */
- else if (cmd == CDROMREADMODE2)
- blocksize = CD_FRAMESIZE_RAW0; /* 2336 */
- else
- /* some SCSI drives do not allow this one */
- blocksize = CD_FRAMESIZE_RAW; /* 2352 */
+ switch (cmd) {
+ case CDROMREADMODE2: blocksize = CD_FRAMESIZE_RAW0; break; /* 2336 */
+ case CDROMREADRAW: blocksize = CD_FRAMESIZE_RAW; break; /* 2352 */
+ }
if (copy_from_user(&msf,(void*)arg,sizeof(msf)))
return -EFAULT;
if (!(raw = scsi_malloc(2048+512)))
- return -ENOMEM;
+ return -ENOMEM;
lba = (((msf.cdmsf_min0 * CD_SECS) + msf.cdmsf_sec0)
* CD_FRAMES + msf.cdmsf_frame0) - CD_BLOCK_OFFSET;
- rc = sr_read_sector(target, lba, blocksize, raw);
+ if (lba < 0 || lba >= scsi_CDs[target].capacity)
+ return -EINVAL;
+
+ rc = sr_read_sector(target, lba, blocksize, raw);
if (!rc)
if (copy_to_user((void*)arg, raw, blocksize))
rc = -EFAULT;
@@ -559,11 +741,44 @@ int sr_dev_ioctl(struct cdrom_device_info *cdi,
scsi_free(raw,2048+512);
return rc;
}
-#else
- return -EINVAL;
-#endif
+ case CDROMREADAUDIO:
+ {
+ unsigned char *raw;
+ int lba, rc=0;
+ struct cdrom_read_audio ra;
-
+ if (!scsi_CDs[target].readcd_known || !scsi_CDs[target].readcd_cdda)
+ return -EINVAL; /* -EDRIVE_DOES_NOT_SUPPORT_THIS ? */
+
+ if (copy_from_user(&ra,(void*)arg,sizeof(ra)))
+ return -EFAULT;
+
+ if (ra.addr_format == CDROM_LBA)
+ lba = ra.addr.lba;
+ else
+ lba = (((ra.addr.msf.minute * CD_SECS) + ra.addr.msf.second)
+ * CD_FRAMES + ra.addr.msf.frame) - CD_BLOCK_OFFSET;
+
+ if (lba < 0 || lba >= scsi_CDs[target].capacity)
+ return -EINVAL;
+ if (!(raw = scsi_malloc(2048+512)))
+ return -ENOMEM;
+
+ while (ra.nframes > 0) {
+ rc = sr_read_cd(target, raw, lba, 1, CD_FRAMESIZE_RAW);
+ if (!rc)
+ if (copy_to_user(ra.buf, raw, CD_FRAMESIZE_RAW))
+ rc = -EFAULT;
+ if (rc)
+ break;
+
+ ra.buf += CD_FRAMESIZE_RAW;
+ ra.nframes -= 1;
+ lba++;
+ }
+ scsi_free(raw,2048+512);
+ return rc;
+ }
case BLKRAGET:
if (!arg)
return -EINVAL;
diff --git a/drivers/scsi/sr_vendor.c b/drivers/scsi/sr_vendor.c
index e8f73f1b2..82d5ba7d8 100644
--- a/drivers/scsi/sr_vendor.c
+++ b/drivers/scsi/sr_vendor.c
@@ -6,6 +6,12 @@
* the like) are to 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>
*
* --------------------------------------------------------------------------
@@ -20,14 +26,15 @@
* - 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 code.
+ * - 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/errno.h>
#include <linux/string.h>
@@ -40,27 +47,33 @@
#include <linux/ucdrom.h>
#include "sr.h"
+#if 0
+# define DEBUG
+#endif
+
/* here are some constants to sort the vendors into groups */
-#define VENDOR_CAN_NOT_HANDLE 1 /* don't know how to handle */
+#define VENDOR_SCSI3 1 /* default: scsi-3 mmc */
+
#define VENDOR_NEC 2
#define VENDOR_TOSHIBA 3
-#define VENDOR_SONY_LIKE 4 /* much drives are Sony compatible */
-#define VENDOR_HP_4020 5 /* HP 4xxx writers, others too ?? */
-#define VENDOR_HP_6020 6 /* HP 6020 writers */
+#define VENDOR_HP_4020 4 /* HP 4xxx writers, others too ?? */
+#define VENDOR_HP_6020 5 /* HP 6020 writers */
#define VENDOR_ID (scsi_CDs[minor].vendor)
-#if 0
-#define DEBUG
-#endif
-
void
sr_vendor_init(int minor)
{
+#ifndef CONFIG_BLK_DEV_SR_VENDOR
+ VENDOR_ID = VENDOR_SCSI3;
+#else
char *vendor = scsi_CDs[minor].device->vendor;
char *model = scsi_CDs[minor].device->model;
-
+
+ /* default */
+ VENDOR_ID = VENDOR_SCSI3;
+
if ((!strncmp(vendor,"HP",2) || !strncmp(vendor,"PHILIPS",7)) &&
scsi_CDs[minor].device->type == TYPE_WORM) {
if (!strncmp(model,"CD-Writer 6020",14))
@@ -80,29 +93,33 @@ sr_vendor_init(int minor)
} else if (!strncmp (vendor, "TOSHIBA", 7)) {
VENDOR_ID = VENDOR_TOSHIBA;
- } else {
- /* most drives can handled like Sony ones, so we take
- * it as default */
- VENDOR_ID = VENDOR_SONY_LIKE;
-#ifdef DEBUG
- printk(KERN_DEBUG
- "sr: using \"Sony group\" multisession code\n");
-#endif
}
+#endif
}
/* small handy function for switching block length using MODE SELECT,
* used by sr_read_sector() */
-static int
-set_density_and_blocklength(int minor, unsigned char *buffer,
- int density, int blocklength)
+int
+sr_set_blocklength(int minor, int blocklength)
{
+ unsigned char *buffer; /* the buffer for the ioctl */
unsigned char cmd[12]; /* the scsi-command */
struct ccs_modesel_head *modesel;
- int rc;
+ int rc,density = 0;
+
+#ifdef CONFIG_BLK_DEV_SR_VENDOR
+ if (VENDOR_ID == VENDOR_TOSHIBA)
+ density = (blocklength > 2048) ? 0x81 : 0x83;
+#endif
+
+ buffer = (unsigned char *) scsi_malloc(512);
+ if (!buffer) return -ENOMEM;
+#ifdef DEBUG
+ printk("sr%d: MODE SELECT 0x%x/%d\n",minor,density,blocklength);
+#endif
memset(cmd,0,12);
cmd[0] = MODE_SELECT;
cmd[1] = (scsi_CDs[minor].device->lun << 5) | (1 << 4);
@@ -113,53 +130,17 @@ set_density_and_blocklength(int minor, unsigned char *buffer,
modesel->density = density;
modesel->block_length_med = (blocklength >> 8 ) & 0xff;
modesel->block_length_lo = blocklength & 0xff;
- rc = sr_do_ioctl(minor, cmd, buffer, sizeof(*modesel));
+ if (0 == (rc = sr_do_ioctl(minor, cmd, buffer, sizeof(*modesel), 0)))
+ scsi_CDs[minor].sector_size = blocklength;
#ifdef DEBUG
- if (rc)
- printk("sr: switching blocklength to %d bytes failed\n",
- blocklength);
+ else
+ printk("sr%d: switching blocklength to %d bytes failed\n",
+ minor,blocklength);
#endif
- return rc;
-}
-
-
-/* read a sector with other than 2048 bytes length
- * dest is assumed to be allocated with scsi_malloc
- *
- * XXX maybe we have to do some locking here.
- */
-
-int
-sr_read_sector(int minor, int lba, int blksize, unsigned char *dest)
-{
- unsigned char *buffer; /* the buffer for the ioctl */
- unsigned char cmd[12]; /* the scsi-command */
- int rc, density;
-
- density = (VENDOR_ID == VENDOR_TOSHIBA) ? 0x83 : 0;
-
- buffer = (unsigned char *) scsi_malloc(512);
- if (!buffer) return -ENOMEM;
-
- rc = set_density_and_blocklength(minor, buffer, density, blksize);
- if (!rc) {
- memset(cmd,0,12);
- cmd[0] = READ_10;
- cmd[1] = (scsi_CDs[minor].device->lun << 5);
- cmd[2] = (unsigned char)(lba >> 24) & 0xff;
- cmd[3] = (unsigned char)(lba >> 16) & 0xff;
- cmd[4] = (unsigned char)(lba >> 8) & 0xff;
- cmd[5] = (unsigned char) lba & 0xff;
- cmd[8] = 1;
- rc = sr_do_ioctl(minor, cmd, dest, blksize);
- set_density_and_blocklength(minor, buffer, density, 2048);
- }
-
scsi_free(buffer, 512);
return rc;
}
-
/* This function gets called after a media change. Checks if the CD is
multisession, asks for offset etc. */
@@ -169,7 +150,6 @@ int sr_cd_check(struct cdrom_device_info *cdi)
{
unsigned long sector,min,sec,frame;
unsigned char *buffer; /* the buffer for the ioctl */
- unsigned char *raw_sector;
unsigned char cmd[12]; /* the scsi-command */
int rc,is_xa,no_multi,minor;
@@ -187,16 +167,41 @@ int sr_cd_check(struct cdrom_device_info *cdi)
switch(VENDOR_ID) {
+ case VENDOR_SCSI3:
+ memset(cmd,0,12);
+ cmd[0] = READ_TOC;
+ cmd[1] = (scsi_CDs[minor].device->lun << 5);
+ cmd[8] = 12;
+ cmd[9] = 0x40;
+ rc = sr_do_ioctl(minor, cmd, buffer, 12, 0);
+ if (rc != 0)
+ break;
+ if ((buffer[0] << 8) + buffer[1] < 0x0a) {
+ printk(KERN_INFO "sr%d: Hmm, seems the drive "
+ "doesn't support multisession CD's\n",minor);
+ 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:
memset(cmd,0,12);
cmd[0] = 0xde;
cmd[1] = (scsi_CDs[minor].device->lun << 5) | 0x03;
cmd[2] = 0xb0;
- rc = sr_do_ioctl(minor, cmd, buffer, 0x16);
+ rc = sr_do_ioctl(minor, cmd, buffer, 0x16, 0);
if (rc != 0)
break;
if (buffer[14] != 0 && buffer[14] != 0xb0) {
- printk(KERN_INFO "sr (nec): Hmm, seems the cdrom doesn't support multisession CD's\n");
+ printk(KERN_INFO "sr%d: Hmm, seems the cdrom "
+ "doesn't support multisession CD's\n",minor);
no_multi = 1;
break;
}
@@ -205,20 +210,19 @@ int sr_cd_check(struct cdrom_device_info *cdi)
frame = BCD_TO_BIN(buffer[17]);
sector = min*CD_SECS*CD_FRAMES + sec*CD_FRAMES + frame;
break;
-
+
case VENDOR_TOSHIBA:
/* we request some disc information (is it a XA-CD ?,
* where starts the last session ?) */
memset(cmd,0,12);
cmd[0] = 0xc7;
cmd[1] = (scsi_CDs[minor].device->lun << 5) | 3;
- rc = sr_do_ioctl(minor, cmd, buffer, 4);
- if (rc == 0x28000002 &&
- !scsi_ioctl(scsi_CDs[minor].device,
- SCSI_IOCTL_TEST_UNIT_READY, NULL)) {
- printk(KERN_INFO "sr (toshiba): Hmm, seems the drive doesn't support multisession CD's\n");
- no_multi = 1;
- break;
+ rc = sr_do_ioctl(minor, cmd, buffer, 4, 0);
+ if (rc == -EINVAL) {
+ printk(KERN_INFO "sr%d: Hmm, seems the drive "
+ "doesn't support multisession CD's\n",minor);
+ no_multi = 1;
+ break;
}
if (rc != 0)
break;
@@ -239,102 +243,57 @@ int sr_cd_check(struct cdrom_device_info *cdi)
0x04 : 0x0c;
cmd[9] = 0x40;
rc = sr_do_ioctl(minor, cmd, buffer,
- (VENDOR_ID == VENDOR_HP_4020) ? 0x04 : 0x0c);
+ (VENDOR_ID == VENDOR_HP_4020) ? 0x04 : 0x0c, 0);
if (rc != 0) {
break;
}
if ((rc = buffer[2]) == 0) {
printk (KERN_WARNING
- "sr (hp): No finished session\n");
+ "sr%d: No finished session\n",minor);
break;
}
- if (VENDOR_ID == VENDOR_HP_4020) {
- cmd[0] = READ_TOC; /* Read TOC */
- cmd[1] = (scsi_CDs[minor].device->lun << 5);
- cmd[6] = rc & 0x7f; /* number of last session */
- cmd[8] = 0x0c;
- cmd[9] = 0x40;
- rc = sr_do_ioctl(minor, cmd, buffer, 12);
- if (rc != 0) {
- break;
- }
+ if (VENDOR_ID == VENDOR_HP_4020) {
+ cmd[0] = READ_TOC; /* Read TOC */
+ cmd[1] = (scsi_CDs[minor].device->lun << 5);
+ cmd[6] = rc & 0x7f; /* number of last session */
+ cmd[8] = 0x0c;
+ cmd[9] = 0x40;
+ rc = sr_do_ioctl(minor, cmd, buffer, 12, 0);
+ if (rc != 0) {
+ break;
+ }
}
sector = buffer[11] + (buffer[10] << 8) +
(buffer[9] << 16) + (buffer[8] << 24);
break;
-
- case VENDOR_SONY_LIKE:
- memset(cmd,0,12);
- cmd[0] = READ_TOC;
- cmd[1] = (scsi_CDs[minor].device->lun << 5);
- cmd[8] = 12;
- cmd[9] = 0x40;
- rc = sr_do_ioctl(minor, cmd, buffer, 12);
- if (rc != 0) {
- break;
- }
- if ((buffer[0] << 8) + buffer[1] < 0x0a) {
- printk(KERN_INFO "sr (sony): Hmm, seems the drive doesn't support multisession CD's\n");
- 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;
-
- case VENDOR_CAN_NOT_HANDLE:
- sector = 0;
- no_multi = 1;
- break;
+#endif /* CONFIG_BLK_DEV_SR_VENDOR */
default:
/* should not happen */
printk(KERN_WARNING
- "sr: unknown vendor code (%i), not initialized ?\n",
- VENDOR_ID);
+ "sr%d: unknown vendor code (%i), not initialized ?\n",
+ minor,VENDOR_ID);
sector = 0;
no_multi = 1;
break;
}
-
- scsi_CDs[minor].xa_flag = 0;
- if (CDS_AUDIO != sr_disk_status(cdi)) {
- /* read a sector in raw mode to check the sector format */
- raw_sector = (unsigned char *) scsi_malloc(2048+512);
- if (!buffer) return -ENOMEM;
- if (0 == sr_read_sector(minor,sector+16,CD_FRAMESIZE_RAW1,
- raw_sector)){
- is_xa = (raw_sector[3] == 0x02);
- if (sector > 0 && !is_xa)
- printk(KERN_INFO "sr: broken CD found: It is "
- "multisession, but has'nt XA sectors\n");
- } else {
- /* read a raw sector failed for some reason. */
- is_xa = (sector > 0);
- }
- scsi_free(raw_sector, 2048+512);
- }
-#ifdef DEBUG
- else printk("sr: audio CD found\n");
-#endif
-
scsi_CDs[minor].ms_offset = sector;
- scsi_CDs[minor].xa_flag = is_xa;
+ scsi_CDs[minor].xa_flag = 0;
+ if (CDS_AUDIO != sr_disk_status(cdi) && 1 == sr_is_xa(minor))
+ scsi_CDs[minor].xa_flag = 1;
+
+ if (2048 != scsi_CDs[minor].sector_size)
+ sr_set_blocklength(minor,2048);
if (no_multi)
cdi->mask |= CDC_MULTI_SESSION;
#ifdef DEBUG
- printk(KERN_DEBUG
- "sr: multisession offset=%lu, XA=%s\n",
- sector,is_xa ? "yes" : "no");
+ if (sector)
+ printk(KERN_DEBUG "sr%d: multisession offset=%lu\n",
+ minor,sector);
#endif
-
scsi_free(buffer, 512);
return rc;
}
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index 07fa2bf58..b70522280 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -11,7 +11,7 @@
Copyright 1992 - 1997 Kai Makisara
email Kai.Makisara@metla.fi
- Last modified: Tue May 27 22:29:00 1997 by makisara@home
+ Last modified: Wed Nov 5 23:39:52 1997 by makisara@home
Some small formal changes - aeb, 950809
*/
@@ -230,18 +230,9 @@ st_sleep_done (Scsi_Cmnd * SCpnt)
}
else
(STp->buffer)->last_result = SCpnt->result;
-#if 0
- if ((STp->buffer)->writing) {
- /* Process errors before releasing request */
- (STp->buffer)->last_result_fatal = st_chk_result(SCpnt);
- SCpnt->request.rq_status = RQ_INACTIVE;
- }
- else
- SCpnt->request.rq_status = RQ_SCSI_DONE;
-#else
+
SCpnt->request.rq_status = RQ_SCSI_DONE;
(STp->buffer)->last_SCpnt = SCpnt;
-#endif
#if DEBUG
STp->write_pending = 0;
@@ -645,8 +636,10 @@ scsi_tape_open(struct inode * inode, struct file * filp)
}
if ((STp->buffer)->last_result_fatal != 0) {
- if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
- (SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE) {
+ if ((STp->device)->scsi_level >= SCSI_2 &&
+ (SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+ (SCpnt->sense_buffer[2] & 0x0f) == NOT_READY &&
+ SCpnt->sense_buffer[12] == 0x3a) { /* Check ASC */
STp->ready = ST_NO_TAPE;
} else
STp->ready = ST_NOT_READY;
@@ -872,8 +865,6 @@ scsi_tape_close(struct inode * inode, struct file * filp)
if (!SCpnt)
goto out;
- SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
-
if ((STp->buffer)->last_result_fatal != 0 &&
((SCpnt->sense_buffer[0] & 0x70) != 0x70 ||
(SCpnt->sense_buffer[2] & 0x4f) != 0x40 ||
@@ -882,11 +873,13 @@ scsi_tape_close(struct inode * inode, struct file * filp)
SCpnt->sense_buffer[5] |
SCpnt->sense_buffer[6]) == 0))) {
/* Filter out successful write at EOM */
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
printk(KERN_ERR "st%d: Error on write filemark.\n", dev);
if (result == 0)
result = (-EIO);
}
else {
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
if (STps->drv_file >= 0)
STps->drv_file++ ;
STps->drv_block = 0;
@@ -955,12 +948,12 @@ out:
/* Write command */
- static long
-st_write(struct inode * inode, struct file * filp, const char * buf,
- unsigned long count)
+static ssize_t
+st_write(struct file * filp, const char * buf, size_t count, loff_t *ppos)
{
- long total;
- int i, do_count, blks, retval, transfer;
+ struct inode *inode = filp->f_dentry->d_inode;
+ ssize_t total;
+ ssize_t i, do_count, blks, retval, transfer;
int write_threshold;
int doing_write = 0;
static unsigned char cmd[10];
@@ -971,9 +964,18 @@ st_write(struct inode * inode, struct file * filp, const char * buf,
ST_partstat * STps;
int dev = TAPE_NR(inode->i_rdev);
+ if (ppos != &filp->f_pos) {
+ /* "A request was outside the capabilities of the device." */
+ return -ENXIO;
+ }
+
STp = &(scsi_tapes[dev]);
- if (STp->ready != ST_READY)
- return (-EIO);
+ if (STp->ready != ST_READY) {
+ if (STp->ready == ST_NO_TAPE)
+ return (-ENOMEDIUM);
+ else
+ return (-EIO);
+ }
STm = &(STp->modes[STp->current_mode]);
if (!STm->defined)
return (-ENXIO);
@@ -1254,7 +1256,7 @@ st_write(struct inode * inode, struct file * filp, const char * buf,
/* 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. */
- static long
+static long
read_tape(struct inode *inode, long count, Scsi_Cmnd **aSCpnt)
{
int transfer, blks, bytes;
@@ -1344,7 +1346,7 @@ read_tape(struct inode *inode, long count, Scsi_Cmnd **aSCpnt)
}
else {
SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
- SCpnt = NULL;
+ SCpnt = *aSCpnt = NULL;
if (transfer == blks) { /* We did not get anything, error */
printk(KERN_NOTICE "st%d: Incorrect block size.\n", dev);
if (STps->drv_block >= 0)
@@ -1441,11 +1443,12 @@ read_tape(struct inode *inode, long count, Scsi_Cmnd **aSCpnt)
/* Read command */
- static long
-st_read(struct inode * inode, struct file * filp, char * buf, unsigned long count)
+static ssize_t
+st_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
{
- long total;
- int i, transfer;
+ struct inode * inode = filp->f_dentry->d_inode;
+ ssize_t total;
+ ssize_t i, transfer;
int special;
Scsi_Cmnd * SCpnt = NULL;
Scsi_Tape * STp;
@@ -1453,9 +1456,18 @@ st_read(struct inode * inode, struct file * filp, char * buf, unsigned long coun
ST_partstat * STps;
int dev = TAPE_NR(inode->i_rdev);
+ if (ppos != &filp->f_pos) {
+ /* "A request was outside the capabilities of the device." */
+ return -ENXIO;
+ }
+
STp = &(scsi_tapes[dev]);
- if (STp->ready != ST_READY)
- return (-EIO);
+ if (STp->ready != ST_READY) {
+ if (STp->ready == ST_NO_TAPE)
+ return (-ENOMEDIUM);
+ else
+ return (-EIO);
+ }
STm = &(STp->modes[STp->current_mode]);
if (!STm->defined)
return (-ENXIO);
@@ -1533,7 +1545,7 @@ st_read(struct inode * inode, struct file * filp, char * buf, unsigned long coun
if ((STp->buffer)->buffer_bytes > 0) {
#if DEBUG
if (debugging && STps->eof != ST_NOEOF)
- printk(ST_DEB_MSG "st%d: EOF up (%d). Left %d, needed %ld.\n", dev,
+ printk(ST_DEB_MSG "st%d: EOF up (%d). Left %d, needed %d.\n", dev,
STps->eof, (STp->buffer)->buffer_bytes, count - total);
#endif
transfer = (STp->buffer)->buffer_bytes < count - total ?
@@ -1875,8 +1887,12 @@ st_int_ioctl(struct inode * inode,
int dev = TAPE_NR(inode->i_rdev);
STp = &(scsi_tapes[dev]);
- if (STp->ready != ST_READY && cmd_in != MTLOAD)
- return (-EIO);
+ 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;
@@ -2512,6 +2528,7 @@ set_location(struct inode * inode, unsigned int block, int partition,
if (!SCpnt)
return (-EBUSY);
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
STps->drv_block = STps->drv_file = (-1);
STps->eof = ST_NOEOF;
if ((STp->buffer)->last_result_fatal != 0) {
@@ -2537,7 +2554,6 @@ set_location(struct inode * inode, unsigned int block, int partition,
STps->drv_block = STps->drv_file = 0;
result = 0;
}
- SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
return result;
}
diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h
index c25c80cc7..c3e71f293 100644
--- a/drivers/scsi/st.h
+++ b/drivers/scsi/st.h
@@ -64,7 +64,6 @@ typedef struct {
unsigned capacity;
struct wait_queue * waiting;
Scsi_Device* device;
- Scsi_Cmnd SCpnt;
struct semaphore sem;
ST_buffer * buffer;
diff --git a/drivers/sound/.objects b/drivers/sound/.objects
index f2f789a28..e7880fcc2 100644
--- a/drivers/sound/.objects
+++ b/drivers/sound/.objects
@@ -89,3 +89,11 @@ ifdef CONFIG_UART401
OBJS := $(OBJS) uart401.o
endif
+ifdef CONFIG_OPL3SA1
+ OBJS := $(OBJS) opl3sa.o
+endif
+
+ifdef CONFIG_SOFTOSS
+ OBJS := $(OBJS) softoss.o softoss_rs.o
+endif
+
diff --git a/drivers/sound/.version b/drivers/sound/.version
index 56cbb7db2..39159f8e2 100644
--- a/drivers/sound/.version
+++ b/drivers/sound/.version
@@ -1,2 +1,2 @@
-3.8a
+3.8s
0x030804
diff --git a/drivers/sound/CHANGELOG b/drivers/sound/CHANGELOG
index 17c2dad5f..daa9652e5 100644
--- a/drivers/sound/CHANGELOG
+++ b/drivers/sound/CHANGELOG
@@ -1,6 +1,16 @@
-Changelog for version 3.8
+Note these changes relate to Hannu's code and don't include the changes
+made outside of this for modularising the sound
+
+Changelog for version 3.8o
--------------------------
+Since 3.8h
+- Included support for OPL3-SA1 and SoftOSS
+
+Since 3.8
+- Fixed SNDCTL_DSP_GETOSPACE
+- Compatibility fixes for Linux 2.1.47
+
Since 3.8-beta21
- Fixed all known bugs (I think).
diff --git a/drivers/sound/Config.in b/drivers/sound/Config.in
index d14653d45..310bf01bf 100644
--- a/drivers/sound/Config.in
+++ b/drivers/sound/Config.in
@@ -1,277 +1,15 @@
-bool 'ProAudioSpectrum 16 support' CONFIG_PAS
-bool '100%% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SB
-bool 'Generic OPL2/OPL3 FM synthesizer support' CONFIG_ADLIB
-bool 'Gravis Ultrasound support' CONFIG_GUS
-bool 'MPU-401 support (NOT for SB16)' CONFIG_MPU401
-bool 'PSS (ECHO-ADI2111) support' CONFIG_PSS
-bool '16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_GUS16
-bool 'GUS MAX support' CONFIG_GUSMAX
-bool 'Microsoft Sound System support' CONFIG_MSS
-bool 'Ensoniq SoundScape support' CONFIG_SSCAPE
-bool 'MediaTrix AudioTrix Pro support' CONFIG_TRIX
-bool 'Support for MAD16 and/or Mozart based cards' CONFIG_MAD16
-bool 'Support for Crystal CS4232 based (PnP) cards' CONFIG_CS4232
-bool 'Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_MAUI
-bool 'FM synthesizer (YM3812/OPL-3) support' CONFIG_YM3812
-
-if [ "$CONFIG_AEDSP16" = "y" ]; then
-hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220
-fi
-
-if [ "$CONFIG_SB" = "y" ]; then
-hex 'I/O base for SB Check from manual of the card' SBC_BASE 220
-fi
-
-if [ "$CONFIG_SB" = "y" ]; then
-int 'Sound Blaster IRQ Check from manual of the card' SBC_IRQ 7
-fi
-
-if [ "$CONFIG_SB" = "y" ]; then
-int 'Sound Blaster DMA 0, 1 or 3' SBC_DMA 1
-fi
-
-if [ "$CONFIG_SB" = "y" ]; then
-int 'Sound Blaster 16 bit DMA (SB16, Jazz16, SMW) 5, 6 or 7 (use 1 for 8 bit cards)' SB_DMA2 5
-fi
-
-if [ "$CONFIG_SB" = "y" ]; then
-hex 'MPU401 I/O base of SB16, Jazz16 and ES1688 Check from manual of the card' SB_MPU_BASE 330
-fi
-
-
-if [ "$CONFIG_SB" = "y" ]; then
-comment 'MPU401 IRQ is only required with Jazz16, SM Wave and ESS1688.'
-fi
-
-
-if [ "$CONFIG_SB" = "y" ]; then
-comment 'Enter -1 to the following question if you have something else such as SB16/32.'
-fi
-
-if [ "$CONFIG_SB" = "y" ]; then
-int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Check from manual of the card' SB_MPU_IRQ -1
-fi
-
-if [ "$CONFIG_PAS" = "y" ]; then
-int 'PAS16 IRQ 3, 4, 5, 7, 9, 10, 11, 12, 14 or 15' PAS_IRQ 10
-fi
-
-if [ "$CONFIG_PAS" = "y" ]; then
-int 'PAS16 DMA 0, 1, 3, 5, 6 or 7' PAS_DMA 3
-fi
-
-if [ "$CONFIG_GUS" = "y" ]; then
-hex 'I/O base for GUS 210, 220, 230, 240, 250 or 260' GUS_BASE 220
-fi
-
-if [ "$CONFIG_GUS" = "y" ]; then
-int 'GUS IRQ 3, 5, 7, 9, 11, 12 or 15' GUS_IRQ 15
-fi
-
-if [ "$CONFIG_GUS" = "y" ]; then
-int 'GUS DMA 1, 3, 5, 6 or 7' GUS_DMA 6
-fi
-
-if [ "$CONFIG_GUS" = "y" ]; then
-int 'Second DMA channel for GUS 1, 3, 5, 6 or 7' GUS_DMA2 -1
-fi
-
-if [ "$CONFIG_GUS16" = "y" ]; then
-hex 'I/O base for the 16 bit daughtercard of GUS 530, 604, E80 or F40' GUS16_BASE 530
-fi
-
-if [ "$CONFIG_GUS16" = "y" ]; then
-int 'GUS 16 bit daughtercard IRQ 3, 4, 5, 7, or 9' GUS16_IRQ 7
-fi
-
-if [ "$CONFIG_GUS16" = "y" ]; then
-int 'GUS DMA 0, 1 or 3' GUS16_DMA 3
-fi
-
-if [ "$CONFIG_MPU401" = "y" ]; then
-hex 'I/O base for MPU401 Check from manual of the card' MPU_BASE 330
-fi
-
-if [ "$CONFIG_MPU401" = "y" ]; then
-int 'MPU401 IRQ Check from manual of the card' MPU_IRQ 9
-fi
-
-
-if [ "$CONFIG_MAUI" = "y" ]; then
-comment 'ERROR! You have to use old sound configuration method with Maui.'
-fi
-
-if [ "$CONFIG_MAUI" = "y" ]; then
-hex 'I/O base for Maui 210, 230, 260, 290, 300, 320, 338 or 330' MAUI_BASE 330
-fi
-
-if [ "$CONFIG_MAUI" = "y" ]; then
-int 'Maui IRQ 5, 9, 12 or 15' MAUI_IRQ 9
-fi
-
-if [ "$CONFIG_UART6850" = "y" ]; then
-hex 'I/O base for UART 6850 MIDI port (Unknown)' U6850_BASE 0
-fi
-
-if [ "$CONFIG_UART6850" = "y" ]; then
-int 'UART6850 IRQ (Unknown)' U6850_IRQ -1
-fi
-
-
-if [ "$CONFIG_PSS" = "y" ]; then
-comment 'ERROR! You have to use old sound configuration method with PSS cards.'
-fi
-
-if [ "$CONFIG_PSS" = "y" ]; then
-hex 'PSS I/O base 220 or 240' PSS_BASE 220
-fi
-
-if [ "$CONFIG_PSS" = "y" ]; then
-hex 'PSS audio I/O base 530, 604, E80 or F40' PSS_MSS_BASE 530
-fi
-
-if [ "$CONFIG_PSS" = "y" ]; then
-int 'PSS audio IRQ 7, 9, 10 or 11' PSS_MSS_IRQ 11
-fi
-
-if [ "$CONFIG_PSS" = "y" ]; then
-int 'PSS audio DMA 0, 1 or 3' PSS_MSS_DMA 3
-fi
-
-if [ "$CONFIG_PSS" = "y" ]; then
-hex 'PSS MIDI I/O base ' PSS_MPU_BASE 330
-fi
-
-if [ "$CONFIG_PSS" = "y" ]; then
-int 'PSS MIDI IRQ 3, 4, 5, 7 or 9' PSS_MPU_IRQ 9
-fi
-
-if [ "$CONFIG_MSS" = "y" ]; then
-hex 'MSS/WSS I/O base 530, 604, E80 or F40' MSS_BASE 530
-fi
-
-if [ "$CONFIG_MSS" = "y" ]; then
-int 'MSS/WSS IRQ 7, 9, 10 or 11' MSS_IRQ 11
-fi
-
-if [ "$CONFIG_MSS" = "y" ]; then
-int 'MSS/WSS DMA 0, 1 or 3' MSS_DMA 3
-fi
-
-if [ "$CONFIG_MSS" = "y" ]; then
-int 'MSS/WSS second DMA (if possible) 0, 1 or 3' MSS_DMA2 -1
-fi
-
-if [ "$CONFIG_SSCAPE" = "y" ]; then
-hex 'SoundScape MIDI I/O base 320, 330, 340 or 350' SSCAPE_BASE 330
-fi
-
-if [ "$CONFIG_SSCAPE" = "y" ]; then
-int 'SoundScape MIDI IRQ ' SSCAPE_IRQ 9
-fi
-
-if [ "$CONFIG_SSCAPE" = "y" ]; then
-int 'SoundScape initialization DMA 0, 1 or 3' SSCAPE_DMA 3
-fi
-
-if [ "$CONFIG_SSCAPE" = "y" ]; then
-hex 'SoundScape audio I/O base 534, 608, E84 or F44' SSCAPE_MSS_BASE 534
-fi
-
-if [ "$CONFIG_SSCAPE" = "y" ]; then
-int 'SoundScape audio IRQ 7, 9, 10 or 11' SSCAPE_MSS_IRQ 11
-fi
-
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-comment 'ERROR! You have to use old sound configuration method with AudioTrix.'
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-hex 'AudioTrix audio I/O base 530, 604, E80 or F40' TRIX_BASE 530
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-int 'AudioTrix audio IRQ 7, 9, 10 or 11' TRIX_IRQ 11
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-int 'AudioTrix audio DMA 0, 1 or 3' TRIX_DMA 0
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-int 'AudioTrix second (duplex) DMA 0, 1 or 3' TRIX_DMA2 3
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-hex 'AudioTrix MIDI I/O base 330, 370, 3B0 or 3F0' TRIX_MPU_BASE 330
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-int 'AudioTrix MIDI IRQ 3, 4, 5, 7 or 9' TRIX_MPU_IRQ 9
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-hex 'AudioTrix SB I/O base 220, 210, 230, 240, 250, 260 or 270' TRIX_SB_BASE 220
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-int 'AudioTrix SB IRQ 3, 4, 5 or 7' TRIX_SB_IRQ 7
-fi
-
-if [ "$CONFIG_TRIX" = "y" ]; then
-int 'AudioTrix SB DMA 1 or 3' TRIX_SB_DMA 1
-fi
-
-if [ "$CONFIG_CS4232" = "y" ]; then
-hex 'CS4232 audio I/O base 530, 604, E80 or F40' CS4232_BASE 530
-fi
-
-if [ "$CONFIG_CS4232" = "y" ]; then
-int 'CS4232 audio IRQ 5, 7, 9, 11, 12 or 15' CS4232_IRQ 11
-fi
-
-if [ "$CONFIG_CS4232" = "y" ]; then
-int 'CS4232 audio DMA 0, 1 or 3' CS4232_DMA 0
-fi
-
-if [ "$CONFIG_CS4232" = "y" ]; then
-int 'CS4232 second (duplex) DMA 0, 1 or 3' CS4232_DMA2 3
-fi
-
-if [ "$CONFIG_CS4232" = "y" ]; then
-hex 'CS4232 MIDI I/O base 330, 370, 3B0 or 3F0' CS4232_MPU_BASE 330
-fi
-
-if [ "$CONFIG_CS4232" = "y" ]; then
-int 'CS4232 MIDI IRQ 5, 7, 9, 11, 12 or 15' CS4232_MPU_IRQ 9
-fi
-
-if [ "$CONFIG_MAD16" = "y" ]; then
-hex 'MAD16 audio I/O base 530, 604, E80 or F40' MAD16_BASE 530
-fi
-
-if [ "$CONFIG_MAD16" = "y" ]; then
-int 'MAD16 audio IRQ 7, 9, 10 or 11' MAD16_IRQ 11
-fi
-
-if [ "$CONFIG_MAD16" = "y" ]; then
-int 'MAD16 audio DMA 0, 1 or 3' MAD16_DMA 3
-fi
-
-if [ "$CONFIG_MAD16" = "y" ]; then
-int 'MAD16 second (duplex) DMA 0, 1 or 3' MAD16_DMA2 0
-fi
-
-if [ "$CONFIG_MAD16" = "y" ]; then
-hex 'MAD16 MIDI I/O base 300, 310, 320 or 330 (0 disables)' MAD16_MPU_BASE 330
-fi
-
-if [ "$CONFIG_MAD16" = "y" ]; then
-int 'MAD16 MIDI IRQ 5, 7, 9 or 10' MAD16_MPU_IRQ 9
-fi
#
-$MAKE -C drivers/sound kernelconfig || exit 1
+# Sound driver configuration
+#
+#--------
+# There is another confic script which is compatible with rest of
+# the kernel. It can be activated by running 'make mkscript' in this
+# directory. Please note that this is an _experimental_ feature which
+# doesn't work with all cards (PSS, SM Wave, AudioTriX Pro, Maui).
+#--------
+#
+$MAKE -C drivers/sound config || exit 1
+
bool 'Additional low level drivers' CONFIG_LOWLEVEL_SOUND
if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile
index b0a3483d5..e90ba9a03 100644
--- a/drivers/sound/Makefile
+++ b/drivers/sound/Makefile
@@ -1,3 +1,4 @@
+BUILDCODE=s
# Makefile for the Linux sound card driver
#
# Note 2! The CFLAGS definitions are now inherited from the
@@ -31,108 +32,201 @@ mkscript:
kernelconfig:
else
-.PHONY: dummy
-SUB_DIRS = lowlevel
-VERSION = `head -1 .version`
-TARGET_OS = linux
-USRINCDIR = /usr/include
-MODULEDIR = /lib/modules/misc
-
-FIXEDOBJS = soundcard.o dev_table.o sound_switch.o
-
-ifndef NO_LOWLEVEL
- FIXEDOBJS := $(FIXEDOBJS) lowlevel/lowlevel.o
-endif
ifeq (.defines,$(wildcard .defines))
-include .defines
+#include .defines
include .objects
endif
+TARGET_OS=linux
+
ifndef TOPDIR
TOPDIR=/usr/src/linux
endif
+SUB_DIRS := lowlevel
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
-ifndef HOSTCC
-build:
- @echo Compiling modularized sound driver
- @make sound.o
- @echo Sound module compiled.
+L_TARGET := sound.a
+M_OBJS :=
+L_OBJS :=
-install: sound.o
- cp sound.o $(MODULEDIR)
+ifeq ($(CONFIG_SOUND),y)
+ L_OBJS += soundcard.o dev_table.o sound_switch.o sequencer.o sys_timer.o sound_timer.o lowlevel/lowlevel.o midi_synth.o midibuf.o sound_firmware.o audio.o dmabuf.o
else
+ ifeq ($(CONFIG_SOUND),m)
+ M_OBJS += sound.o
+ MX_OBJS += sound_syms.o
+ endif
endif
-.c.o:
- $(CC) $(CFLAGS) -c $<
+ifeq ($(CONFIG_MIDI),y)
+ L_OBJS += midibuf.o
+ LX_OBJS += midi_synth.o
+endif
-ifeq ($(CONFIG_SOUND),y)
+ifeq ($(CONFIG_MIDI),y)
+ L_OBJS += midibuf.o
+ LX_OBJS += midi_synth.o
+endif
-all: local.h sound.a
+#ifeq ($(CONFIG_AUDIO),y)
+#L_OBJS += dmabuf.o
+#endif
-OBJS += $(FIXEDOBJS)
+ifeq ($(CONFIG_YM3812),y)
+LX_OBJS += opl3.o
+else
+ ifeq ($(CONFIG_YM3812),m)
+ MX_OBJS += opl3.o
+ endif
+endif
+ifeq ($(CONFIG_PAS),y)
+L_OBJS += pas2_card.c pas2_midi.c pas2_mixer.c pas2_pcm.c
else
-all:
+ ifeq ($(CONFIG_PAS),m)
+ M_OBJS += pas2.o
+ endif
endif
-ifndef HOSTCC
-#
-# Running outside the kernel build.
-#
-CC = gcc
-HOSTCC = gcc
-CFLAGS = -O2 -D__KERNEL__ -DMODULE -I/usr/src/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -pipe -m486
-USE_DEPEND=y
+ifeq ($(CONFIG_GUS),y)
+L_OBJS += gus_card.c gus_midi.c gus_vol.c gus_wave.c ics2101.c
else
-include $(TOPDIR)/Rules.make
+ ifeq ($(CONFIG_GUS),m)
+ M_OBJS += gus.o
+ endif
endif
-sound.a: $(OBJS)
- -rm -f sound.a
- $(AR) rcs sound.a $(OBJS)
- sync
+ifeq ($(CONFIG_SB),y)
+L_OBJS += sb_audio.o sb_common.o sb_midi.o sb_mixer.o
+LX_OBJS += sb_card.o uart401.o
+else
+ ifeq ($(CONFIG_SB),m)
+ M_OBJS += sb.o
+ MX_OBJS += sb_card.o uart401.o
+ endif
+endif
-clean:
- rm -f core core.* *.o *.a tmp_make *~ x y z *%
- rm -f configure
- cd lowlevel;make clean
-
-indent:
- for n in *.c;do echo indent $$n;indent $$n;done
-
-local.h:
- $(MAKE) clean
- $(MAKE) setup
- $(MAKE) oldconfig
- $(MAKE) dep
- @echo
- @echo
- @echo
- @echo NOTE! Object file dependencies may not be up to date. Run
- @echo make again if kernel/driver doesn''t link properly. Restarting
- @echo it now may save some time.
- @echo
- @echo
-
-config: configure
- @$(MAKE) setup
- @./configure > local.h
- @echo \#define SOUND_CONFIG_DATE \"`date`\" >> local.h
- @echo \#define SOUND_CONFIG_BY \"`whoami`\" >> local.h
-# @echo \#define SOUND_CONFIG_HOST \"`hostname`\" >> local.h 2>/dev/null
-# @echo \#define SOUND_CONFIG_DOMAIN \"`hostname -d`\" >> local.h 2>/dev/null
- @echo \#define SOUND_UNAME_A \"`uname -a`\" >> local.h
+ifeq ($(CONFIG_ADLIB),y)
+LX_OBJS += ad1848.o
+else
+ ifeq ($(CONFIG_ADLIB),m)
+ MX_OBJS += ad1848.o
+ endif
+endif
-oldconfig: setup configure
- @./configure -o > local.h
- @echo \#define SOUND_CONFIG_DATE \"`date`\" >> local.h
- @echo \#define SOUND_CONFIG_BY \"`whoami`\" >> local.h
-# @echo \#define SOUND_CONFIG_HOST \"`hostname`\" >> local.h 2>/dev/null
-# @echo \#define SOUND_CONFIG_DOMAIN \"`hostname -d`\" >> local.h 2>/dev/null
- @echo \#define SOUND_UNAME_A \"`uname -a`\" >> local.h
+ifeq ($(CONFIG_ADLIB),y)
+LX_OBJS += adlib_card.o
+else
+ ifeq ($(CONFIG_ADLIB),m)
+ MX_OBJS += adlib_card.o
+ endif
+endif
+
+ifeq ($(CONFIG_MPU401),y)
+LX_OBJS += mpu401.o
+else
+ ifeq ($(CONFIG_MPU401),m)
+ MX_OBJS += mpu401.o
+ endif
+endif
+
+ifeq ($(CONFIG_UART401),y)
+LX_OBJS += uart401.o
+else
+ ifeq ($(CONFIG_UART401),m)
+ MX_OBJS += uart401.o
+ endif
+endif
+
+ifeq ($(CONFIG_UART6850),y)
+LX_OBJS += uart6850.o
+else
+ ifeq ($(CONFIG_UART6850),m)
+ MX_OBJS += uart6850.o
+ endif
+endif
+
+ifeq ($(CONFIG_PSS),y)
+L_OBJS += pss.o
+else
+ ifeq ($(CONFIG_PSS),m)
+ M_OBJS += pss.o
+ endif
+endif
+
+ifeq ($(CONFIG_SSCAPE),y)
+L_OBJS += sscape.o
+else
+ ifeq ($(CONFIG_SSCAPE),m)
+ M_OBJS += sscape.o
+ endif
+endif
+
+ifeq ($(CONFIG_TRIX),y)
+L_OBJS += trix.o
+else
+ ifeq ($(CONFIG_TRIX),m)
+ M_OBJS += trix.o
+ endif
+endif
+
+ifeq ($(CONFIG_MAD16),y)
+L_OBJS += mad16.o
+else
+ ifeq ($(CONFIG_MAD16),m)
+ M_OBJS += mad16.o
+ endif
+endif
+
+ifeq ($(CONFIG_CS4232),y)
+LX_OBJS += cs4232.o
+else
+ ifeq ($(CONFIG_CS4232),m)
+ MX_OBJS += cs4232.o
+ endif
+endif
+
+ifeq ($(CONFIG_MAUI),y)
+L_OBJS += maui.o
+else
+ ifeq ($(CONFIG_MAUI),m)
+ M_OBJS += maui.o
+ endif
+endif
+
+ifeq ($(CONFIG_SOFTOSS),y)
+L_OBJS += softoss.o softoss_rs.o
+else
+ ifeq ($(CONFIG_SOFTOSS),m)
+ M_OBJS += softoss2.o
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+softoss2.o: softoss.o softoss_rs.o
+ ld -r -o softoss2.o softoss.o softoss_rs.o
+
+pas2.o: pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o
+ ld -r -o pas2.o pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o
+
+sb.o: sb_audio.o sb_card.o sb_common.o sb_midi.o sb_mixer.o
+ ld -r -o sb.o sb_audio.o sb_card.o sb_common.o sb_midi.o sb_mixer.o
+
+lowlevel/lowlevel.o:
+ cd lowlevel; make
+
+sound.o: soundcard.o dev_table.o sound_switch.o audio.o dmabuf.o sequencer.o sys_timer.o sound_timer.o lowlevel/lowlevel.o midi_synth.o midibuf.o sound_firmware.o
+ ld -r -o sound.o soundcard.o dev_table.o sound_switch.o audio.o dmabuf.o \
+ sequencer.o sys_timer.o sound_timer.o lowlevel/lowlevel.o \
+ midi_synth.o midibuf.o sound_firmware.o
+ rm sound_syms.o
+
+gus.o: gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o
+ ld -r -o gus.o gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o
kernelconfig: setup
rm -f configure
@@ -145,47 +239,13 @@ kernelconfig: setup
# @echo \#define SOUND_CONFIG_DOMAIN \"`hostname -d`\" >> local.h 2>/dev/null
@echo \#define SOUND_UNAME_A \"`uname -a`\" >> local.h
-mkscript: setup
- rm -f configure
- $(HOSTCC) -o configure configure.c
- ./configure script > Config.in
- cat lowlevel/Config.tmpl >> Config.in
- ./configure fixedlocal > local.h
- ./configure fixeddefines > .defines
-
-clrconf:
- rm -f local.h .depend synth-ld.h trix_boot.h smw-midi0001.h maui_boot.h .defines
-
configure: configure.c
$(HOSTCC) -o configure configure.c
@cat .blurb
-dep:
- $(CPP) -M $(CFLAGS) -I. *.c > .depend
-
setup:
@echo Compiling Sound Driver v $(VERSION) for Linux
-sound.o: local.h $(FIXEDOBJS) sound.a
- -rm -f sound.o
- $(LD) -r -o sound.o $(FIXEDOBJS) sound.a
-
-modules: local.h sound.o
- ln -fs `pwd`/sound.o $(TOPDIR)/modules/sound.o
-
-
-lowlevel/lowlevel.o: dummy
- cd lowlevel;make CC="$(CC)" CFLAGS="$(CFLAGS)"
-
-contrib:
- cd lowlevel;make clean;make module "CC=$(CC)" CFLAGS="$(CFLAGS)"
+mkscript:
-ifdef USE_DEPEND
-#
-# include a dependency file if one exists
-#
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
-endif
endif
diff --git a/drivers/sound/README.FIRST b/drivers/sound/README.FIRST
new file mode 100644
index 000000000..3a6b60483
--- /dev/null
+++ b/drivers/sound/README.FIRST
@@ -0,0 +1,7 @@
+The modular sound driver patches where funded by Red Hat Software
+(www.redhat.com). The sound driver here is thus a modified version of
+Hannu's code. Please bear that in mind when considering the appropriate
+forums for bug reporting.
+
+Alan Cox
+
diff --git a/drivers/sound/Readme b/drivers/sound/Readme
index c5002a1f7..113ad9130 100644
--- a/drivers/sound/Readme
+++ b/drivers/sound/Readme
@@ -1,22 +1,21 @@
-OSS Lite version 3.8 release notes
+OSS/Free version 3.8 release notes
----------------------------------
Most up to date information about this driver is available from
-http://www.4front-tech.com/ossfree or http://personal.eunet.fi/pp/voxware
-(European mirror).
+http://www.4front-tech.com/ossfree.
Please read the SOUND-HOWTO (available from sunsite.unc.edu and other Linux FTP
sites). It gives instructions about using sound with Linux. It's bit out of
date but still very useful. Information about bug fixes and such things
-is available from the web page (see below).
+is available from the web page (see above).
Please check http://www.4front-tech.com/pguide for more info about programming
-with OSS.
+with OSS API.
====================================================
-- THIS VERSION ____REQUIRES____ Linux 2.1.36 OR LATER.
+- THIS VERSION ____REQUIRES____ Linux 2.1.57 OR LATER.
====================================================
Packages "snd-util-3.8.tar.gz" and "snd-data-0.1.tar.Z"
@@ -78,6 +77,18 @@ contributors. (I could have forgotten some names.)
There are probably many other names missing. If you have sent me some
patches and your name is not in the above list, please inform me.
+Sending your contributions or patches
+-------------------------------------
+
+First of all it's highly recommended to contact me before sending anything
+or before even starting to do any work. Tell me what you suggest to be
+changed or what you have planned to do. Also ensure you are using the
+very latest (development) version of OSS/Free since the change may already be
+implemented there. In general it's major waste of time to try to improve
+several months old version. Information about the latest version can be found
+from http://www.4front-tech.com/ossfree. In general there is no point in
+sending me patches relative to production kernels.
+
Sponsors etc.
-------------
@@ -166,7 +177,7 @@ Best regards,
Hannu
Hannu Savolainen
-hannu@voxware.pp.fi, hannu@4front-tech.com
+hannu@4front-tech.com
(Please check http://www.4front-tech.com/ossfree before mailing me).
Snail mail: Hannu Savolainen
diff --git a/drivers/sound/Readme.cards b/drivers/sound/Readme.cards
index 148afd6b0..79bbe464b 100644
--- a/drivers/sound/Readme.cards
+++ b/drivers/sound/Readme.cards
@@ -5,7 +5,13 @@ This document describes configuring soundcards with freeware version of
Open Sound Systems (OSS/Free). Information about the commercial version
(OSS/Linux) and it's configuration is available from
http://www.4front-tech.com/linux.html. Information presented here is
-not valid for OSS/Linux.
+not valid for OSS/Linux.
+
+If you are unsure about how to configure OSS/Free
+you can download the free evaluation version of OSS/Linux from the above
+address. There is a chance that it can autodetect your soundcard. In this case
+you can use the information included in soundon.log when configuring OSS/Free.
+
IMPORTANT! This document covers only cards that were "known" when
this driver version was released. Please look at
@@ -25,7 +31,7 @@ IMPORTANT! This document covers only cards that were "known" when
method to use. After you have used the "new" method once
it will always be used when you use any of the config
programs. To return back to the "old" method you should
- reinstall the kernel sources.
+ execute "cp Config.std Config.in" in linux/drivers/sound.
The /etc/soundconf file (forget it if you don't know what
this file does) contains settings that are used only by
@@ -33,8 +39,8 @@ IMPORTANT! This document covers only cards that were "known" when
are stored there (they really are _NOT_ stored
there). Don't try to edit /etc/soundconf or any other
kernel or sound driver config files manually. The _only_
- proper ways to change the settings are make config,
- make menuconfig or make xconfig.
+ proper ways to change the settings are make config or
+ make menuconfig (the "old" method).
When using make xconfig and/or make menuconfig, you should
carefully check each sound configuration option (particularly
@@ -42,7 +48,6 @@ IMPORTANT! This document covers only cards that were "known" when
offered by these programs are not necessarily valid.
-
THE BIGGEST MISTAKES YOU CAN DO
===============================
@@ -211,7 +216,8 @@ Sound Blasters
is compatible just with SB Pro but there is also a non-SB-
compatible 16 bit mode. Usually it's MSS/WSS but it could also
be a proprietary one like MV Jazz16 or ESS ES688. OPTi
- MAD16 chips are very common in so called "SB 16 bit cards".
+ MAD16 chips are very common in so called "SB 16 bit cards"
+ (try with the MAD16 driver).
======================================================================
"Supposed to be SB compatible" cards.
@@ -292,10 +298,67 @@ Yamaha FM synthesizers (OPL2, OPL3 (not OPL3-SA) and OPL4)
----------------------------------------------------------------
NOTE! OPL3-SA is different chip than the ordinary OPL3. In addition
to the FM synth this chip has also digital audio (WSS) and
- MIDI (MPU401) capabilities. OPL3-SA is not supported by OSS/Free.
- Support for it is included in OSS/Linux v3.8 and later.
+ MIDI (MPU401) capabilities. Support for OPL3-SA is described below.
----------------------------------------------------------------
+Yamaha OPL3-SA1
+
+ Yamaha OPL3-SA1 (YMF701) is an audio controller chip used on some
+ (Intel) motherboards and on cheap soundcards. It should not be
+ confused with the original OPL3 chip (YMF278) which is entirely
+ different chip. OPL3-SA1 has support for MSS, MPU401 and SB Pro
+ (not used in OSS/Free) in addition to the OPL3 FM synth.
+
+ There are also chips called OPL3-SA2, OPL3-SA3, ..., OPL3SA-N. They
+ are PnP chips and will not work with the OPL3-SA1 driver. You should
+ use the standard MSS, MPU401 and OPL3 options with thses chips and to
+ activate the card using isapnptools.
+
+4Front Technologies SoftOSS
+
+ SoftOSS is a software based wave table emulation which works with
+ any 16 bit stereo soundcard. Due to it's nature a fast CPU is
+ required (P133 is minumum). Althoug SoftOSS doesn _not_ use MMX
+ instructions it has proven out that recent processors (which appear
+ to have MMX) perform significantly better with SoftOSS than earlier
+ ones. For example a P166MMX beats a PPro200. SoftOSS should not be used
+ on 486 or 386 machines.
+
+ The amount of CPU load caused by SoftOSS can be controlled by
+ selecting the SOFTOSS_RATE and SOFTOSS_VOICES parameters properly
+ (they will be prompted by make config). It's recommended to set
+ SOFTOSS_VOICES to 32. If you have a P166MMX or faster (PPro200 is
+ not faster) you can set SOFTOSS_RATE to 44100 (kHz). However with
+ slower systems it recommended to use sampling rates around 22050
+ or even 16000 kHz. Selecting too high values for these parameters
+ may hang your system when playing MIDI files with hight degree of
+ polyphony (number of concurrently playing notes). It's also possible to
+ decrease SOFTOSS_VOICES. This makes it possible to use higher sampling
+ rates. However using fewer voices decreases playback quality more than
+ decreasing the sampling rate.
+
+ SoftOSS keeps the samples loaded on system's RAM so large RAM is
+ required. SoftOSS should never be used on machines with less than 16M
+ of RAM since this is potentially dangerous (you may accidently run out
+ of memory which probably crashes the machine).
+
+ SoftOSS implements the wave table API originally designed for GUS. For
+ this reason all applications designed for GUS should work (at least
+ after minor modifications). For example gmod/xgmod and playmidi -g are
+ known to work.
+
+ To work SoftOSS will require GUS compatible
+ patch files to be installed on the system (in /dos/ultrasnd/midi). You
+ can use the public domain MIDIA patchset available from several ftp
+ sites.
+
+ *********************************************************************
+ IMPORTANT NOTICE! The original patch set distributed with Gravis
+ Ultrasound card is not in public domain (even it's available from
+ some ftp sites). You should contact Voice Crystal (www.voicecrystal.com)
+ if you like to use these patches with SoftOSS included in OSS/Free.
+ *********************************************************************
+
PSS based cards (AD1848 + ADSP-2115 + Echo ESC614 ASIC)
Analog Devices and Echo Speech have together defined a soundcard
architecture based on the above chips. The DSP chip is used
@@ -327,7 +390,7 @@ Ensoniq SoundScape and compatibles
VIVO90 cards are not compatible with Soundscapes so the Soundscape driver
will not work with them. You may want to use OSS/Linux with these cards.
-MAD16 and Mozart based cards
+OPTi MAD16 and Mozart based cards
The Mozart (OAK OTI-601), MAD16 (OPTi 82C928), MAD16 Pro (OPTi 82C929),
OPTi 82C924/82C925 (in _non_ PnP mode) and OPTi 82C930 interface
chips are used in many different soundcards, including some
@@ -644,8 +707,12 @@ different operating systems.
Sound Blasters (the original ones by Creative)
---------------------------------------------
+NOTE! Check if you have a PnP Sound Blaster (cards sold after summer 1995
+ are almost certainly PnP ones). With PnP cards you should use isapnptools
+ to activate them (see above).
+
It's possible to configure these cards to use different I/O, IRQ and
-DMA settings. Since the available settings have changed between various
+DMA settings. Since the possible/default settings have changed between various
models, you have to consult manual of your card for the proper ones. It's
a good idea to use the same values than with DOS/Windows. With SB and SB Pro
it's the only choice. SB16 has software selectable IRQ and DMA channels but
@@ -662,6 +729,15 @@ it's possible to use just one (8 bit) DMA channel by answering the 8 bit
one when the configuration program asks for the 16 bit one. This may work
in some systems but is likely to cause terrible noise on some other systems.
+It's possible to use two SB16/32/64 at the same time. To do this you should
+first configure OSS/Free for one card. Then edit local.h manually and define
+SB2_BASE, SB2_IRQ, SB2_DMA and SB2_DMA2 for the second one. You can't get
+the OPL3, MIDI and EMU8000 devices of the second card to work. If you are
+going to use two PnP Sound Blasters, ensure that they are of different model
+and have different PnP ID's. There is no way to get two cards with the same
+card ID and serial number to work. The easiest way to check this is trying
+if isapnptools can see both cards or just one.
+
NOTE! Don't enable the SM Games option (asked by the configuration program)
if you are not 101% sure that your card is a Logitech Soundman Games
(not a SM Wave or SM16).
@@ -1185,12 +1261,10 @@ as free copies of soundcards, SDKs and operating systems.
If you have any corrections and/or comments, please contact me.
Hannu Savolainen
-hannu@voxware.pp.fi
+hannu@4front-tech.com
Personal home page: http://personal.eunet.fi/pp/voxware/hannu.html
www home page of OSS/Free: http://www.4front-tech.com/ossfree
- European/Finnish mirror: http://personal.eunet.fi/pp/voxware
www home page of commercial OSS
(Open Sound System) drivers: http://www.4front-tech.com/oss.html
-
diff --git a/drivers/sound/Readme.linux b/drivers/sound/Readme.linux
index b1121ebbe..103470869 100644
--- a/drivers/sound/Readme.linux
+++ b/drivers/sound/Readme.linux
@@ -67,10 +67,10 @@ Readme.cards for info about configuring the driver with your card. Also
check for possible boot (insmod) time error messages in /var/adm/messages.
- Other messages or problems
-Please check http://www.4front-tech.com/osslite for more info.
+Please check http://www.4front-tech.com/ossfree for more info.
Hannu Savolainen
-hannu@voxware.pp.fi
+hannu@4front-tech.com
----------------- cut here ------------------------------
SURPRISE SURPRISE!!!
diff --git a/drivers/sound/ad1848.c b/drivers/sound/ad1848.c
index 82ec86f03..fc4de23e6 100644
--- a/drivers/sound/ad1848.c
+++ b/drivers/sound/ad1848.c
@@ -21,36 +21,40 @@
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*/
+
#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include "soundmodule.h"
#define DEB(x)
#define DEB1(x)
#include "sound_config.h"
-#if defined(CONFIG_AD1848)
+#ifdef CONFIG_AD1848
#include "ad1848_mixer.h"
typedef struct
{
- int base;
- int irq;
- int dma1, dma2;
- int dual_dma; /* 1, when two DMA channels allocated */
- unsigned char MCE_bit;
- unsigned char saved_regs[16];
- int debug_flag;
-
- int audio_flags;
- int record_dev, playback_dev;
-
- int xfer_count;
- int audio_mode;
- int open_mode;
- int intr_active;
- char *chip_name;
- int model;
+ int base;
+ int irq;
+ int dma1, dma2;
+ int dual_dma; /* 1, when two DMA channels allocated */
+ unsigned char MCE_bit;
+ unsigned char saved_regs[16];
+ int debug_flag;
+
+ int audio_flags;
+ int record_dev, playback_dev;
+
+ int xfer_count;
+ int audio_mode;
+ int open_mode;
+ int intr_active;
+ char *chip_name, *name;
+ int model;
#define MD_1848 1
#define MD_4231 2
#define MD_4231A 3
@@ -59,30 +63,30 @@ typedef struct
#define MD_C930 6
#define MD_IWAVE 7
- /* Mixer parameters */
- int recmask;
- int supported_devices, orig_devices;
- int supported_rec_devices, orig_rec_devices;
- int *levels;
- short mixer_reroute[32];
- int dev_no;
- volatile unsigned long timer_ticks;
- int timer_running;
- int irq_ok;
- mixer_ents *mix_devices;
- int mixer_output_port;
- int c930_password_port;
+ /* Mixer parameters */
+ int recmask;
+ int supported_devices, orig_devices;
+ int supported_rec_devices, orig_rec_devices;
+ int *levels;
+ short mixer_reroute[32];
+ int dev_no;
+ volatile unsigned long timer_ticks;
+ int timer_running;
+ int irq_ok;
+ mixer_ents *mix_devices;
+ int mixer_output_port;
+ int c930_password_port;
}
ad1848_info;
typedef struct ad1848_port_info
{
- int open_mode;
- int speed;
- unsigned char speed_bits;
- int channels;
- int audio_format;
- unsigned char format_bits;
+ int open_mode;
+ int speed;
+ unsigned char speed_bits;
+ int channels;
+ int audio_format;
+ unsigned char format_bits;
}
ad1848_port_info;
@@ -98,14 +102,14 @@ static int timer_installed = -1;
static int ad_format_mask[8 /*devc->model */ ] =
{
- 0,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* AD1845 */
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM
+ 0,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* AD1845 */
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM
};
static ad1848_info adev_info[MAX_AUDIO_DEV];
@@ -115,1956 +119,1897 @@ static ad1848_info adev_info[MAX_AUDIO_DEV];
#define io_Status(d) ((d)->base+2)
#define io_Polled_IO(d) ((d)->base+3)
-static int ad1848_open (int dev, int mode);
-static void ad1848_close (int dev);
-static int ad1848_ioctl (int dev, unsigned int cmd, caddr_t arg);
-static void ad1848_output_block (int dev, unsigned long buf, int count, int intrflag);
-static void ad1848_start_input (int dev, unsigned long buf, int count, int intrflag);
-static int ad1848_prepare_for_output (int dev, int bsize, int bcount);
-static int ad1848_prepare_for_input (int dev, int bsize, int bcount);
-static void ad1848_halt (int dev);
-static void ad1848_halt_input (int dev);
-static void ad1848_halt_output (int dev);
-static void ad1848_trigger (int dev, int bits);
-
-#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
-static int ad1848_tmr_install (int dev);
-static void ad1848_tmr_reprogram (int dev);
+static int ad1848_open(int dev, int mode);
+static void ad1848_close(int dev);
+static int ad1848_ioctl(int dev, unsigned int cmd, caddr_t arg);
+static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag);
+static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag);
+static int ad1848_prepare_for_output(int dev, int bsize, int bcount);
+static int ad1848_prepare_for_input(int dev, int bsize, int bcount);
+static void ad1848_halt(int dev);
+static void ad1848_halt_input(int dev);
+static void ad1848_halt_output(int dev);
+static void ad1848_trigger(int dev, int bits);
+
+#if (defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)) || defined(MODULE)
+static int ad1848_tmr_install(int dev);
+static void ad1848_tmr_reprogram(int dev);
#endif
static int
-ad_read (ad1848_info * devc, int reg)
+ad_read(ad1848_info * devc, int reg)
{
- unsigned long flags;
- int x;
- int timeout = 900000;
+ unsigned long flags;
+ int x;
+ int timeout = 900000;
- while (timeout > 0 && inb (devc->base) == 0x80) /*Are we initializing */
- timeout--;
+ while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */
+ timeout--;
- save_flags (flags);
- cli ();
- outb (((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr (devc));
- x = inb (io_Indexed_Data (devc));
+ save_flags(flags);
+ cli();
+ outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
+ x = inb(io_Indexed_Data(devc));
/* printk("(%02x<-%02x) ", reg|devc->MCE_bit, x); */
- restore_flags (flags);
+ restore_flags(flags);
- return x;
+ return x;
}
static void
-ad_write (ad1848_info * devc, int reg, int data)
+ad_write(ad1848_info * devc, int reg, int data)
{
- unsigned long flags;
- int timeout = 900000;
-
- while (timeout > 0 &&
- inb (devc->base) == 0x80) /*Are we initializing */
- timeout--;
-
- save_flags (flags);
- cli ();
- outb (((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr (devc));
- outb (((unsigned char) (data & 0xff)), io_Indexed_Data (devc));
- /* printk("(%02x->%02x) ", reg|devc->MCE_bit, data); */
- restore_flags (flags);
+ unsigned long flags;
+ int timeout = 900000;
+
+ while (timeout > 0 &&
+ inb(devc->base) == 0x80) /*Are we initializing */
+ timeout--;
+
+ save_flags(flags);
+ cli();
+ outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
+ outb(((unsigned char) (data & 0xff)), io_Indexed_Data(devc));
+ /* printk("(%02x->%02x) ", reg|devc->MCE_bit, data); */
+ restore_flags(flags);
}
static void
-wait_for_calibration (ad1848_info * devc)
+wait_for_calibration(ad1848_info * devc)
{
- int timeout = 0;
-
- /*
- * Wait until the auto calibration process has finished.
- *
- * 1) Wait until the chip becomes ready (reads don't return 0x80).
- * 2) Wait until the ACI bit of I11 gets on and then off.
- */
-
- timeout = 100000;
- while (timeout > 0 && inb (devc->base) == 0x80)
- timeout--;
- if (inb (devc->base) & 0x80)
- printk ("ad1848: Auto calibration timed out(1).\n");
-
- timeout = 100;
- while (timeout > 0 && !(ad_read (devc, 11) & 0x20))
- timeout--;
- if (!(ad_read (devc, 11) & 0x20))
- return;
-
- timeout = 80000;
- while (timeout > 0 && ad_read (devc, 11) & 0x20)
- timeout--;
- if (ad_read (devc, 11) & 0x20)
- if (devc->model != MD_1845)
- printk ("ad1848: Auto calibration timed out(3).\n");
+ int timeout = 0;
+
+ /*
+ * Wait until the auto calibration process has finished.
+ *
+ * 1) Wait until the chip becomes ready (reads don't return 0x80).
+ * 2) Wait until the ACI bit of I11 gets on and then off.
+ */
+
+ timeout = 100000;
+ while (timeout > 0 && inb(devc->base) == 0x80)
+ timeout--;
+ if (inb(devc->base) & 0x80)
+ printk("ad1848: Auto calibration timed out(1).\n");
+
+ timeout = 100;
+ while (timeout > 0 && !(ad_read(devc, 11) & 0x20))
+ timeout--;
+ if (!(ad_read(devc, 11) & 0x20))
+ return;
+
+ timeout = 80000;
+ while (timeout > 0 && ad_read(devc, 11) & 0x20)
+ timeout--;
+ if (ad_read(devc, 11) & 0x20)
+ if (devc->model != MD_1845)
+ printk("ad1848: Auto calibration timed out(3).\n");
}
static void
-ad_mute (ad1848_info * devc)
+ad_mute(ad1848_info * devc)
{
- int i;
- unsigned char prev;
+ int i;
+ unsigned char prev;
- /*
- * Save old register settings and mute output channels
- */
- for (i = 6; i < 8; i++)
- {
- prev = devc->saved_regs[i] = ad_read (devc, i);
- }
+ /*
+ * Save old register settings and mute output channels
+ */
+ for (i = 6; i < 8; i++)
+ {
+ prev = devc->saved_regs[i] = ad_read(devc, i);
+ }
}
static void
-ad_unmute (ad1848_info * devc)
+ad_unmute(ad1848_info * devc)
{
}
static void
-ad_enter_MCE (ad1848_info * devc)
+ad_enter_MCE(ad1848_info * devc)
{
- unsigned long flags;
- int timeout = 1000;
- unsigned short prev;
-
- while (timeout > 0 && inb (devc->base) == 0x80) /*Are we initializing */
- timeout--;
-
- save_flags (flags);
- cli ();
-
- devc->MCE_bit = 0x40;
- prev = inb (io_Index_Addr (devc));
- if (prev & 0x40)
- {
- restore_flags (flags);
- return;
- }
-
- outb ((devc->MCE_bit), io_Index_Addr (devc));
- restore_flags (flags);
+ unsigned long flags;
+ int timeout = 1000;
+ unsigned short prev;
+
+ while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */
+ timeout--;
+
+ save_flags(flags);
+ cli();
+
+ devc->MCE_bit = 0x40;
+ prev = inb(io_Index_Addr(devc));
+ if (prev & 0x40)
+ {
+ restore_flags(flags);
+ return;
+ }
+ outb((devc->MCE_bit), io_Index_Addr(devc));
+ restore_flags(flags);
}
static void
-ad_leave_MCE (ad1848_info * devc)
+ad_leave_MCE(ad1848_info * devc)
{
- unsigned long flags;
- unsigned char prev, acal;
- int timeout = 1000;
-
- while (timeout > 0 && inb (devc->base) == 0x80) /*Are we initializing */
- timeout--;
+ unsigned long flags;
+ unsigned char prev, acal;
+ int timeout = 1000;
- save_flags (flags);
- cli ();
+ while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */
+ timeout--;
- acal = ad_read (devc, 9);
+ save_flags(flags);
+ cli();
- devc->MCE_bit = 0x00;
- prev = inb (io_Index_Addr (devc));
- outb ((0x00), io_Index_Addr (devc)); /* Clear the MCE bit */
+ acal = ad_read(devc, 9);
- if ((prev & 0x40) == 0) /* Not in MCE mode */
- {
- restore_flags (flags);
- return;
- }
+ devc->MCE_bit = 0x00;
+ prev = inb(io_Index_Addr(devc));
+ outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */
- outb ((0x00), io_Index_Addr (devc)); /* Clear the MCE bit */
- if (acal & 0x08) /* Auto calibration is enabled */
- wait_for_calibration (devc);
- restore_flags (flags);
+ if ((prev & 0x40) == 0) /* Not in MCE mode */
+ {
+ restore_flags(flags);
+ return;
+ }
+ outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */
+ if (acal & 0x08) /* Auto calibration is enabled */
+ wait_for_calibration(devc);
+ restore_flags(flags);
}
static int
-ad1848_set_recmask (ad1848_info * devc, int mask)
+ad1848_set_recmask(ad1848_info * devc, int mask)
{
- unsigned char recdev;
- int i, n;
+ unsigned char recdev;
+ int i, n;
+
+ mask &= devc->supported_rec_devices;
+
+ /* Rename the mixer bits if necessary */
+ for (i = 0; i < 32; i++)
+ if (devc->mixer_reroute[i] != i)
+ if (mask & (1 << i))
+ {
+ mask &= ~(1 << i);
+ mask |= (1 << devc->mixer_reroute[i]);
+ }
+ n = 0;
+ for (i = 0; i < 32; i++) /* Count selected device bits */
+ if (mask & (1 << i))
+ n++;
+
+ if (n == 0)
+ mask = SOUND_MASK_MIC;
+ else if (n != 1) /* Too many devices selected */
+ {
+ mask &= ~devc->recmask; /* Filter out active settings */
- mask &= devc->supported_rec_devices;
+ n = 0;
+ for (i = 0; i < 32; i++) /* Count selected device bits */
+ if (mask & (1 << i))
+ n++;
- /* Rename the mixer bits if necessary */
- for (i = 0; i < 32; i++)
- if (devc->mixer_reroute[i] != i)
- if (mask & (1 << i))
- {
- mask &= ~(1 << i);
- mask |= (1 << devc->mixer_reroute[i]);
- }
+ if (n != 1)
+ mask = SOUND_MASK_MIC;
+ }
+ switch (mask)
+ {
+ case SOUND_MASK_MIC:
+ recdev = 2;
+ break;
- n = 0;
- for (i = 0; i < 32; i++) /* Count selected device bits */
- if (mask & (1 << i))
- n++;
-
- if (n == 0)
- mask = SOUND_MASK_MIC;
- else if (n != 1) /* Too many devices selected */
- {
- mask &= ~devc->recmask; /* Filter out active settings */
-
- n = 0;
- for (i = 0; i < 32; i++) /* Count selected device bits */
- if (mask & (1 << i))
- n++;
-
- if (n != 1)
- mask = SOUND_MASK_MIC;
- }
-
- switch (mask)
- {
- case SOUND_MASK_MIC:
- recdev = 2;
- break;
-
- case SOUND_MASK_LINE:
- case SOUND_MASK_LINE3:
- recdev = 0;
- break;
-
- case SOUND_MASK_CD:
- case SOUND_MASK_LINE1:
- recdev = 1;
- break;
-
- case SOUND_MASK_IMIX:
- recdev = 3;
- break;
-
- default:
- mask = SOUND_MASK_MIC;
- recdev = 2;
- }
-
- recdev <<= 6;
- ad_write (devc, 0, (ad_read (devc, 0) & 0x3f) | recdev);
- ad_write (devc, 1, (ad_read (devc, 1) & 0x3f) | recdev);
-
- /* Rename the mixer bits back if necessary */
- for (i = 0; i < 32; i++)
- if (devc->mixer_reroute[i] != i)
- if (mask & (1 << devc->mixer_reroute[i]))
- {
- mask &= ~(1 << devc->mixer_reroute[i]);
- mask |= (1 << i);
- }
+ case SOUND_MASK_LINE:
+ case SOUND_MASK_LINE3:
+ recdev = 0;
+ break;
+
+ case SOUND_MASK_CD:
+ case SOUND_MASK_LINE1:
+ recdev = 1;
+ break;
- devc->recmask = mask;
- return mask;
+ case SOUND_MASK_IMIX:
+ recdev = 3;
+ break;
+
+ default:
+ mask = SOUND_MASK_MIC;
+ recdev = 2;
+ }
+
+ recdev <<= 6;
+ ad_write(devc, 0, (ad_read(devc, 0) & 0x3f) | recdev);
+ ad_write(devc, 1, (ad_read(devc, 1) & 0x3f) | recdev);
+
+ /* Rename the mixer bits back if necessary */
+ for (i = 0; i < 32; i++)
+ if (devc->mixer_reroute[i] != i)
+ if (mask & (1 << devc->mixer_reroute[i]))
+ {
+ mask &= ~(1 << devc->mixer_reroute[i]);
+ mask |= (1 << i);
+ }
+ devc->recmask = mask;
+ return mask;
}
static void
-change_bits (ad1848_info * devc, unsigned char *regval, int dev, int chn, int newval)
+change_bits(ad1848_info * devc, unsigned char *regval, int dev, int chn, int newval)
{
- unsigned char mask;
- int shift;
- int mute;
- int mutemask;
- int set_mute_bit;
-
- set_mute_bit = (newval == 0);
-
- if (devc->mix_devices[dev][chn].polarity == 1) /* Reverse */
- newval = 100 - newval;
-
- mask = (1 << devc->mix_devices[dev][chn].nbits) - 1;
- shift = devc->mix_devices[dev][chn].bitpos;
-
- if (devc->mix_devices[dev][chn].mutepos == 8)
- { /* if there is no mute bit */
- mute = 0; /* No mute bit; do nothing special */
- mutemask = ~0; /* No mute bit; do nothing special */
- }
- else
- {
- mute = (set_mute_bit << devc->mix_devices[dev][chn].mutepos);
- mutemask = ~(1 << devc->mix_devices[dev][chn].mutepos);
- }
-
- newval = (int) ((newval * mask) + 50) / 100; /* Scale it */
- *regval &= (~(mask << shift)) & (mutemask); /* Clear bits */
- *regval |= ((newval & mask) << shift) | mute; /* Set new value */
+ unsigned char mask;
+ int shift;
+ int mute;
+ int mutemask;
+ int set_mute_bit;
+
+ set_mute_bit = (newval == 0);
+
+ if (devc->mix_devices[dev][chn].polarity == 1) /* Reverse */
+ newval = 100 - newval;
+
+ mask = (1 << devc->mix_devices[dev][chn].nbits) - 1;
+ shift = devc->mix_devices[dev][chn].bitpos;
+
+ if (devc->mix_devices[dev][chn].mutepos == 8)
+ { /* if there is no mute bit */
+ mute = 0; /* No mute bit; do nothing special */
+ mutemask = ~0; /* No mute bit; do nothing special */
+ } else
+ {
+ mute = (set_mute_bit << devc->mix_devices[dev][chn].mutepos);
+ mutemask = ~(1 << devc->mix_devices[dev][chn].mutepos);
+ }
+
+ newval = (int) ((newval * mask) + 50) / 100; /* Scale it */
+ *regval &= (~(mask << shift)) & (mutemask); /* Clear bits */
+ *regval |= ((newval & mask) << shift) | mute; /* Set new value */
}
static int
-ad1848_mixer_get (ad1848_info * devc, int dev)
+ad1848_mixer_get(ad1848_info * devc, int dev)
{
- if (!((1 << dev) & devc->supported_devices))
- return -EINVAL;
+ if (!((1 << dev) & devc->supported_devices))
+ return -EINVAL;
- dev = devc->mixer_reroute[dev];
+ dev = devc->mixer_reroute[dev];
- return devc->levels[dev];
+ return devc->levels[dev];
}
static int
-ad1848_mixer_set (ad1848_info * devc, int dev, int value)
+ad1848_mixer_set(ad1848_info * devc, int dev, int value)
{
- int left = value & 0x000000ff;
- int right = (value & 0x0000ff00) >> 8;
- int retvol;
+ int left = value & 0x000000ff;
+ int right = (value & 0x0000ff00) >> 8;
+ int retvol;
- int regoffs;
- unsigned char val;
+ int regoffs;
+ unsigned char val;
- if (dev > 31)
- return -EINVAL;
+ if (dev > 31)
+ return -EINVAL;
- if (!(devc->supported_devices & (1 << dev)))
- return -EINVAL;
+ if (!(devc->supported_devices & (1 << dev)))
+ return -EINVAL;
- dev = devc->mixer_reroute[dev];
+ dev = devc->mixer_reroute[dev];
- if (left > 100)
- left = 100;
- if (right > 100)
- right = 100;
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
- if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0) /* Mono control */
- right = left;
+ if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0) /* Mono control */
+ right = left;
- retvol = left | (right << 8);
+ retvol = left | (right << 8);
- /* Scale volumes */
- left = mix_cvt[left];
- right = mix_cvt[right];
+ /* Scale volumes */
+ left = mix_cvt[left];
+ right = mix_cvt[right];
- /* Scale it again */
- left = mix_cvt[left];
- right = mix_cvt[right];
+ /* Scale it again */
+ left = mix_cvt[left];
+ right = mix_cvt[right];
- if (devc->mix_devices[dev][LEFT_CHN].nbits == 0)
- return -EINVAL;
+ if (devc->mix_devices[dev][LEFT_CHN].nbits == 0)
+ return -EINVAL;
- devc->levels[dev] = retvol;
+ devc->levels[dev] = retvol;
- /*
- * Set the left channel
- */
+ /*
+ * Set the left channel
+ */
- regoffs = devc->mix_devices[dev][LEFT_CHN].regno;
- val = ad_read (devc, regoffs);
- change_bits (devc, &val, dev, LEFT_CHN, left);
- ad_write (devc, regoffs, val);
- devc->saved_regs[regoffs] = val;
+ regoffs = devc->mix_devices[dev][LEFT_CHN].regno;
+ val = ad_read(devc, regoffs);
+ change_bits(devc, &val, dev, LEFT_CHN, left);
+ ad_write(devc, regoffs, val);
+ devc->saved_regs[regoffs] = val;
- /*
- * Set the right channel
- */
+ /*
+ * Set the right channel
+ */
- if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0)
- return retvol; /* Was just a mono channel */
+ if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0)
+ return retvol; /* Was just a mono channel */
- regoffs = devc->mix_devices[dev][RIGHT_CHN].regno;
- val = ad_read (devc, regoffs);
- change_bits (devc, &val, dev, RIGHT_CHN, right);
- ad_write (devc, regoffs, val);
- devc->saved_regs[regoffs] = val;
+ regoffs = devc->mix_devices[dev][RIGHT_CHN].regno;
+ val = ad_read(devc, regoffs);
+ change_bits(devc, &val, dev, RIGHT_CHN, right);
+ ad_write(devc, regoffs, val);
+ devc->saved_regs[regoffs] = val;
- return retvol;
+ return retvol;
}
static void
-ad1848_mixer_reset (ad1848_info * devc)
+ad1848_mixer_reset(ad1848_info * devc)
{
- int i;
- char name[32];
-
- devc->mix_devices = &(ad1848_mix_devices[0]);
-
- sprintf (name, "%s_%d", devc->chip_name, nr_ad1848_devs);
-
- for (i = 0; i < 32; i++)
- devc->mixer_reroute[i] = i;
-
- switch (devc->model)
- {
- case MD_4231:
- case MD_4231A:
- case MD_1845:
- devc->supported_devices = MODE2_MIXER_DEVICES;
- break;
-
- case MD_C930:
- devc->supported_devices = C930_MIXER_DEVICES;
- devc->mix_devices = &(c930_mix_devices[0]);
- break;
-
- case MD_IWAVE:
- devc->supported_devices = MODE3_MIXER_DEVICES;
- devc->mix_devices = &(iwave_mix_devices[0]);
- break;
-
- case MD_4232:
- devc->supported_devices = MODE3_MIXER_DEVICES;
- break;
-
- default:
- devc->supported_devices = MODE1_MIXER_DEVICES;
- }
-
- devc->supported_rec_devices = MODE1_REC_DEVICES;
- devc->orig_devices = devc->supported_devices;
- devc->orig_rec_devices = devc->supported_rec_devices;
-
- devc->levels = load_mixer_volumes (name, default_mixer_levels, 1);
-
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- if (devc->supported_devices & (1 << i))
- ad1848_mixer_set (devc, i, devc->levels[i]);
- ad1848_set_recmask (devc, SOUND_MASK_MIC);
- devc->mixer_output_port = devc->levels[31] | AUDIO_HEADPHONE | AUDIO_LINE_OUT;
- if (devc->mixer_output_port & AUDIO_SPEAKER)
- ad_write (devc, 26, ad_read (devc, 26) & ~0x40); /* Unmute mono out */
- else
- ad_write (devc, 26, ad_read (devc, 26) | 0x40); /* Mute mono out */
-}
+ int i;
+ char name[32];
-static int
-ad1848_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
-{
- ad1848_info *devc = mixer_devs[dev]->devc;
+ devc->mix_devices = &(ad1848_mix_devices[0]);
- if (cmd == SOUND_MIXER_PRIVATE1)
- {
- int val;
+ sprintf(name, "%s_%d", devc->chip_name, nr_ad1848_devs);
- val = *(int *) arg;
+ for (i = 0; i < 32; i++)
+ devc->mixer_reroute[i] = i;
- if (val == 0xffff)
- return (*(int *) arg = devc->mixer_output_port);
+ switch (devc->model)
+ {
+ case MD_4231:
+ case MD_4231A:
+ case MD_1845:
+ devc->supported_devices = MODE2_MIXER_DEVICES;
+ break;
- val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT);
+ case MD_C930:
+ devc->supported_devices = C930_MIXER_DEVICES;
+ devc->mix_devices = &(c930_mix_devices[0]);
+ break;
- devc->mixer_output_port = val;
- val |= AUDIO_HEADPHONE | AUDIO_LINE_OUT; /* Always on */
- devc->mixer_output_port = val;
+ case MD_IWAVE:
+ devc->supported_devices = MODE3_MIXER_DEVICES;
+ devc->mix_devices = &(iwave_mix_devices[0]);
+ break;
- if (val & AUDIO_SPEAKER)
- ad_write (devc, 26, ad_read (devc, 26) & ~0x40); /* Unmute mono out */
- else
- ad_write (devc, 26, ad_read (devc, 26) | 0x40); /* Mute mono out */
+ case MD_4232:
+ devc->supported_devices = MODE3_MIXER_DEVICES;
+ break;
- return (*(int *) arg = devc->mixer_output_port);
- }
+ default:
+ devc->supported_devices = MODE1_MIXER_DEVICES;
+ }
- if (((cmd >> 8) & 0xff) == 'M')
- {
- int val;
+ devc->supported_rec_devices = MODE1_REC_DEVICES;
+ devc->orig_devices = devc->supported_devices;
+ devc->orig_rec_devices = devc->supported_rec_devices;
- if (_SIOC_DIR (cmd) & _SIOC_WRITE)
- switch (cmd & 0xff)
- {
- case SOUND_MIXER_RECSRC:
- val = *(int *) arg;
- return (*(int *) arg = ad1848_set_recmask (devc, val));
- break;
+ devc->levels = load_mixer_volumes(name, default_mixer_levels, 1);
- default:
- val = *(int *) arg;
- return (*(int *) arg = ad1848_mixer_set (devc, cmd & 0xff, val));
- }
- else
- switch (cmd & 0xff) /*
- * Return parameters
- */
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (devc->supported_devices & (1 << i))
+ ad1848_mixer_set(devc, i, devc->levels[i]);
+ ad1848_set_recmask(devc, SOUND_MASK_MIC);
+ devc->mixer_output_port = devc->levels[31] | AUDIO_HEADPHONE | AUDIO_LINE_OUT;
+ if (devc->mixer_output_port & AUDIO_SPEAKER)
+ ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */
+ else
+ ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */
+}
+
+static int
+ad1848_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+ ad1848_info *devc = mixer_devs[dev]->devc;
+
+ if (cmd == SOUND_MIXER_PRIVATE1)
{
+ int val;
- case SOUND_MIXER_RECSRC:
- return (*(int *) arg = devc->recmask);
- break;
+ val = *(int *) arg;
- case SOUND_MIXER_DEVMASK:
- return (*(int *) arg = devc->supported_devices);
- break;
+ if (val == 0xffff)
+ return (*(int *) arg = devc->mixer_output_port);
- case SOUND_MIXER_STEREODEVS:
- if (devc->model == MD_C930)
- return (*(int *) arg = devc->supported_devices);
- else
- return (*(int *) arg = devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX));
- break;
+ val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT);
- case SOUND_MIXER_RECMASK:
- return (*(int *) arg = devc->supported_rec_devices);
- break;
+ devc->mixer_output_port = val;
+ val |= AUDIO_HEADPHONE | AUDIO_LINE_OUT; /* Always on */
+ devc->mixer_output_port = val;
- case SOUND_MIXER_CAPS:
- return (*(int *) arg = SOUND_CAP_EXCL_INPUT);
- break;
+ if (val & AUDIO_SPEAKER)
+ ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */
+ else
+ ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */
- default:
- return (*(int *) arg = ad1848_mixer_get (devc, cmd & 0xff));
+ return (*(int *) arg = devc->mixer_output_port);
}
- }
- else
- return -EINVAL;
+ if (((cmd >> 8) & 0xff) == 'M')
+ {
+ int val;
+
+ if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+ switch (cmd & 0xff)
+ {
+ case SOUND_MIXER_RECSRC:
+ val = *(int *) arg;
+ return (*(int *) arg = ad1848_set_recmask(devc, val));
+ break;
+
+ default:
+ val = *(int *) arg;
+ return (*(int *) arg = ad1848_mixer_set(devc, cmd & 0xff, val));
+ } else
+ switch (cmd & 0xff) /*
+ * Return parameters
+ */
+ {
+
+ case SOUND_MIXER_RECSRC:
+ return (*(int *) arg = devc->recmask);
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ return (*(int *) arg = devc->supported_devices);
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ if (devc->model == MD_C930)
+ return (*(int *) arg = devc->supported_devices);
+ else
+ return (*(int *) arg = devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX));
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ return (*(int *) arg = devc->supported_rec_devices);
+ break;
+
+ case SOUND_MIXER_CAPS:
+ return (*(int *) arg = SOUND_CAP_EXCL_INPUT);
+ break;
+
+ default:
+ return (*(int *) arg = ad1848_mixer_get(devc, cmd & 0xff));
+ }
+ } else
+ return -EINVAL;
}
static int
-ad1848_set_speed (int dev, int arg)
+ad1848_set_speed(int dev, int arg)
{
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- /*
- * The sampling speed is encoded in the least significant nibble of I8. The
- * LSB selects the clock source (0=24.576 MHz, 1=16.9344 MHz) and other
- * three bits select the divisor (indirectly):
- *
- * The available speeds are in the following table. Keep the speeds in
- * the increasing order.
- */
- typedef struct
- {
- int speed;
- unsigned char bits;
- }
- speed_struct;
-
- static speed_struct speed_table[] =
- {
- {5510, (0 << 1) | 1},
- {5510, (0 << 1) | 1},
- {6620, (7 << 1) | 1},
- {8000, (0 << 1) | 0},
- {9600, (7 << 1) | 0},
- {11025, (1 << 1) | 1},
- {16000, (1 << 1) | 0},
- {18900, (2 << 1) | 1},
- {22050, (3 << 1) | 1},
- {27420, (2 << 1) | 0},
- {32000, (3 << 1) | 0},
- {33075, (6 << 1) | 1},
- {37800, (4 << 1) | 1},
- {44100, (5 << 1) | 1},
- {48000, (6 << 1) | 0}
- };
-
- int i, n, selected = -1;
-
- n = sizeof (speed_table) / sizeof (speed_struct);
-
- if (arg <= 0)
- return portc->speed;
-
- if (devc->model == MD_1845) /* AD1845 has different timer than others */
- {
- if (arg < 4000)
- arg = 4000;
- if (arg > 50000)
- arg = 50000;
-
- portc->speed = arg;
- portc->speed_bits = speed_table[3].bits;
- return portc->speed;
- }
-
- if (arg < speed_table[0].speed)
- selected = 0;
- if (arg > speed_table[n - 1].speed)
- selected = n - 1;
-
- for (i = 1 /*really */ ; selected == -1 && i < n; i++)
- if (speed_table[i].speed == arg)
- selected = i;
- else if (speed_table[i].speed > arg)
- {
- int diff1, diff2;
-
- diff1 = arg - speed_table[i - 1].speed;
- diff2 = speed_table[i].speed - arg;
-
- if (diff1 < diff2)
- selected = i - 1;
- else
- selected = i;
- }
-
- if (selected == -1)
- {
- printk ("ad1848: Can't find speed???\n");
- selected = 3;
- }
-
- portc->speed = speed_table[selected].speed;
- portc->speed_bits = speed_table[selected].bits;
- return portc->speed;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+ /*
+ * The sampling speed is encoded in the least significant nibble of I8. The
+ * LSB selects the clock source (0=24.576 MHz, 1=16.9344 MHz) and other
+ * three bits select the divisor (indirectly):
+ *
+ * The available speeds are in the following table. Keep the speeds in
+ * the increasing order.
+ */
+ typedef struct
+ {
+ int speed;
+ unsigned char bits;
+ }
+ speed_struct;
+
+ static speed_struct speed_table[] =
+ {
+ {5510, (0 << 1) | 1},
+ {5510, (0 << 1) | 1},
+ {6620, (7 << 1) | 1},
+ {8000, (0 << 1) | 0},
+ {9600, (7 << 1) | 0},
+ {11025, (1 << 1) | 1},
+ {16000, (1 << 1) | 0},
+ {18900, (2 << 1) | 1},
+ {22050, (3 << 1) | 1},
+ {27420, (2 << 1) | 0},
+ {32000, (3 << 1) | 0},
+ {33075, (6 << 1) | 1},
+ {37800, (4 << 1) | 1},
+ {44100, (5 << 1) | 1},
+ {48000, (6 << 1) | 0}
+ };
+
+ int i, n, selected = -1;
+
+ n = sizeof(speed_table) / sizeof(speed_struct);
+
+ if (arg <= 0)
+ return portc->speed;
+
+ if (devc->model == MD_1845) /* AD1845 has different timer than others */
+ {
+ if (arg < 4000)
+ arg = 4000;
+ if (arg > 50000)
+ arg = 50000;
+
+ portc->speed = arg;
+ portc->speed_bits = speed_table[3].bits;
+ return portc->speed;
+ }
+ if (arg < speed_table[0].speed)
+ selected = 0;
+ if (arg > speed_table[n - 1].speed)
+ selected = n - 1;
+
+ for (i = 1 /*really */ ; selected == -1 && i < n; i++)
+ if (speed_table[i].speed == arg)
+ selected = i;
+ else if (speed_table[i].speed > arg)
+ {
+ int diff1, diff2;
+
+ diff1 = arg - speed_table[i - 1].speed;
+ diff2 = speed_table[i].speed - arg;
+
+ if (diff1 < diff2)
+ selected = i - 1;
+ else
+ selected = i;
+ }
+ if (selected == -1)
+ {
+ printk("ad1848: Can't find speed???\n");
+ selected = 3;
+ }
+ portc->speed = speed_table[selected].speed;
+ portc->speed_bits = speed_table[selected].bits;
+ return portc->speed;
}
static short
-ad1848_set_channels (int dev, short arg)
+ad1848_set_channels(int dev, short arg)
{
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
- if (arg != 1 && arg != 2)
- return portc->channels;
+ if (arg != 1 && arg != 2)
+ return portc->channels;
- portc->channels = arg;
- return arg;
+ portc->channels = arg;
+ return arg;
}
static unsigned int
-ad1848_set_bits (int dev, unsigned int arg)
+ad1848_set_bits(int dev, unsigned int arg)
{
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- static struct format_tbl
- {
- int format;
- unsigned char bits;
- }
- format2bits[] =
- {
- {
- 0, 0
- }
- ,
- {
- AFMT_MU_LAW, 1
- }
- ,
- {
- AFMT_A_LAW, 3
- }
- ,
- {
- AFMT_IMA_ADPCM, 5
- }
- ,
- {
- AFMT_U8, 0
- }
- ,
- {
- AFMT_S16_LE, 2
- }
- ,
- {
- AFMT_S16_BE, 6
- }
- ,
- {
- AFMT_S8, 0
- }
- ,
- {
- AFMT_U16_LE, 0
- }
- ,
- {
- AFMT_U16_BE, 0
- }
- };
- int i, n = sizeof (format2bits) / sizeof (struct format_tbl);
-
- if (arg == 0)
- return portc->audio_format;
-
- if (!(arg & ad_format_mask[devc->model]))
- arg = AFMT_U8;
-
- portc->audio_format = arg;
-
- for (i = 0; i < n; i++)
- if (format2bits[i].format == arg)
- {
- if ((portc->format_bits = format2bits[i].bits) == 0)
- return portc->audio_format = AFMT_U8; /* Was not supported */
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
- return arg;
- }
+ static struct format_tbl
+ {
+ int format;
+ unsigned char bits;
+ }
+ format2bits[] =
+ {
+ {
+ 0, 0
+ }
+ ,
+ {
+ AFMT_MU_LAW, 1
+ }
+ ,
+ {
+ AFMT_A_LAW, 3
+ }
+ ,
+ {
+ AFMT_IMA_ADPCM, 5
+ }
+ ,
+ {
+ AFMT_U8, 0
+ }
+ ,
+ {
+ AFMT_S16_LE, 2
+ }
+ ,
+ {
+ AFMT_S16_BE, 6
+ }
+ ,
+ {
+ AFMT_S8, 0
+ }
+ ,
+ {
+ AFMT_U16_LE, 0
+ }
+ ,
+ {
+ AFMT_U16_BE, 0
+ }
+ };
+ int i, n = sizeof(format2bits) / sizeof(struct format_tbl);
+
+ if (arg == 0)
+ return portc->audio_format;
- /* Still hanging here. Something must be terribly wrong */
- portc->format_bits = 0;
- return portc->audio_format = AFMT_U8;
+ if (!(arg & ad_format_mask[devc->model]))
+ arg = AFMT_U8;
+
+ portc->audio_format = arg;
+
+ for (i = 0; i < n; i++)
+ if (format2bits[i].format == arg)
+ {
+ if ((portc->format_bits = format2bits[i].bits) == 0)
+ return portc->audio_format = AFMT_U8; /* Was not supported */
+
+ return arg;
+ }
+ /* Still hanging here. Something must be terribly wrong */
+ portc->format_bits = 0;
+ return portc->audio_format = AFMT_U8;
}
static struct audio_driver ad1848_audio_driver =
{
- ad1848_open,
- ad1848_close,
- ad1848_output_block,
- ad1848_start_input,
- ad1848_ioctl,
- ad1848_prepare_for_input,
- ad1848_prepare_for_output,
- ad1848_halt,
- NULL,
- NULL,
- ad1848_halt_input,
- ad1848_halt_output,
- ad1848_trigger,
- ad1848_set_speed,
- ad1848_set_bits,
- ad1848_set_channels
+ ad1848_open,
+ ad1848_close,
+ ad1848_output_block,
+ ad1848_start_input,
+ ad1848_ioctl,
+ ad1848_prepare_for_input,
+ ad1848_prepare_for_output,
+ ad1848_halt,
+ NULL,
+ NULL,
+ ad1848_halt_input,
+ ad1848_halt_output,
+ ad1848_trigger,
+ ad1848_set_speed,
+ ad1848_set_bits,
+ ad1848_set_channels
};
static struct mixer_operations ad1848_mixer_operations =
{
- "SOUNDPORT",
- "AD1848/CS4248/CS4231",
- ad1848_mixer_ioctl
+ "SOUNDPORT",
+ "AD1848/CS4248/CS4231",
+ ad1848_mixer_ioctl
};
static int
-ad1848_open (int dev, int mode)
+ad1848_open(int dev, int mode)
{
- ad1848_info *devc = NULL;
- ad1848_port_info *portc;
- unsigned long flags;
-
- if (dev < 0 || dev >= num_audiodevs)
- return -ENXIO;
-
- devc = (ad1848_info *) audio_devs[dev]->devc;
- portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- save_flags (flags);
- cli ();
- if (portc->open_mode || (devc->open_mode & mode))
- {
- restore_flags (flags);
- return -EBUSY;
- }
-
- devc->dual_dma = 0;
-
- if (audio_devs[dev]->flags & DMA_DUPLEX)
- {
- devc->dual_dma = 1;
- }
-
- devc->intr_active = 0;
- devc->audio_mode = 0;
- devc->open_mode |= mode;
- portc->open_mode = mode;
- ad1848_trigger (dev, 0);
-
- if (mode & OPEN_READ)
- devc->record_dev = dev;
- if (mode & OPEN_WRITE)
- devc->playback_dev = dev;
- restore_flags (flags);
+ ad1848_info *devc = NULL;
+ ad1848_port_info *portc;
+ unsigned long flags;
+
+ if (dev < 0 || dev >= num_audiodevs)
+ return -ENXIO;
+
+ devc = (ad1848_info *) audio_devs[dev]->devc;
+ portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+ save_flags(flags);
+ cli();
+ if (portc->open_mode || (devc->open_mode & mode))
+ {
+ restore_flags(flags);
+ return -EBUSY;
+ }
+ devc->dual_dma = 0;
+
+ if (audio_devs[dev]->flags & DMA_DUPLEX)
+ {
+ devc->dual_dma = 1;
+ }
+ devc->intr_active = 0;
+ devc->audio_mode = 0;
+ devc->open_mode |= mode;
+ portc->open_mode = mode;
+ ad1848_trigger(dev, 0);
+
+ if (mode & OPEN_READ)
+ devc->record_dev = dev;
+ if (mode & OPEN_WRITE)
+ devc->playback_dev = dev;
+ restore_flags(flags);
/*
* Mute output until the playback really starts. This decreases clicking (hope so).
*/
- ad_mute (devc);
+ ad_mute(devc);
- return 0;
+ return 0;
}
static void
-ad1848_close (int dev)
+ad1848_close(int dev)
{
- unsigned long flags;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+ unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
- DEB (printk ("ad1848_close(void)\n"));
+ DEB(printk("ad1848_close(void)\n"));
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- devc->intr_active = 0;
- ad1848_halt (dev);
+ devc->intr_active = 0;
+ ad1848_halt(dev);
- devc->audio_mode = 0;
- devc->open_mode &= ~portc->open_mode;
- portc->open_mode = 0;
+ devc->audio_mode = 0;
+ devc->open_mode &= ~portc->open_mode;
+ portc->open_mode = 0;
- ad_unmute (devc);
- restore_flags (flags);
+ ad_unmute(devc);
+ restore_flags(flags);
}
static int
-ad1848_ioctl (int dev, unsigned int cmd, caddr_t arg)
+ad1848_ioctl(int dev, unsigned int cmd, caddr_t arg)
{
- return -EINVAL;
+ return -EINVAL;
}
static void
-ad1848_output_block (int dev, unsigned long buf, int count, int intrflag)
+ad1848_output_block(int dev, unsigned long buf, int count, int intrflag)
{
- unsigned long flags, cnt;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- cnt = count;
-
- if (portc->audio_format == AFMT_IMA_ADPCM)
- {
- cnt /= 4;
- }
- else
- {
- if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
- cnt >>= 1;
- }
- if (portc->channels > 1)
- cnt >>= 1;
- cnt--;
-
- if (devc->audio_mode & PCM_ENABLE_OUTPUT && audio_devs[dev]->flags & DMA_AUTOMODE &&
- intrflag &&
- cnt == devc->xfer_count)
- {
- devc->audio_mode |= PCM_ENABLE_OUTPUT;
- devc->intr_active = 1;
- return; /*
+ unsigned long flags, cnt;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+ cnt = count;
+
+ if (portc->audio_format == AFMT_IMA_ADPCM)
+ {
+ cnt /= 4;
+ } else
+ {
+ if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
+ cnt >>= 1;
+ }
+ if (portc->channels > 1)
+ cnt >>= 1;
+ cnt--;
+
+ if (devc->audio_mode & PCM_ENABLE_OUTPUT && audio_devs[dev]->flags & DMA_AUTOMODE &&
+ intrflag &&
+ cnt == devc->xfer_count)
+ {
+ devc->audio_mode |= PCM_ENABLE_OUTPUT;
+ devc->intr_active = 1;
+ return; /*
* Auto DMA mode on. No need to react
*/
- }
- save_flags (flags);
- cli ();
+ }
+ save_flags(flags);
+ cli();
- ad_write (devc, 15, (unsigned char) (cnt & 0xff));
- ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
+ ad_write(devc, 15, (unsigned char) (cnt & 0xff));
+ ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
- devc->xfer_count = cnt;
- devc->audio_mode |= PCM_ENABLE_OUTPUT;
- devc->intr_active = 1;
- restore_flags (flags);
+ devc->xfer_count = cnt;
+ devc->audio_mode |= PCM_ENABLE_OUTPUT;
+ devc->intr_active = 1;
+ restore_flags(flags);
}
static void
-ad1848_start_input (int dev, unsigned long buf, int count, int intrflag)
+ad1848_start_input(int dev, unsigned long buf, int count, int intrflag)
{
- unsigned long flags, cnt;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- cnt = count;
- if (portc->audio_format == AFMT_IMA_ADPCM)
- {
- cnt /= 4;
- }
- else
- {
- if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
- cnt >>= 1;
- }
- if (portc->channels > 1)
- cnt >>= 1;
- cnt--;
-
- if (devc->audio_mode & PCM_ENABLE_INPUT && audio_devs[dev]->flags & DMA_AUTOMODE &&
- intrflag &&
- cnt == devc->xfer_count)
- {
- devc->audio_mode |= PCM_ENABLE_INPUT;
- devc->intr_active = 1;
- return; /*
- * Auto DMA mode on. No need to react
- */
- }
- save_flags (flags);
- cli ();
-
- if (devc->model == MD_1848)
- {
- ad_write (devc, 15, (unsigned char) (cnt & 0xff));
- ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
- }
- else
- {
- ad_write (devc, 31, (unsigned char) (cnt & 0xff));
- ad_write (devc, 30, (unsigned char) ((cnt >> 8) & 0xff));
- }
-
- ad_unmute (devc);
-
- devc->xfer_count = cnt;
- devc->audio_mode |= PCM_ENABLE_INPUT;
- devc->intr_active = 1;
- restore_flags (flags);
-}
+ unsigned long flags, cnt;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-static int
-ad1848_prepare_for_output (int dev, int bsize, int bcount)
-{
- int timeout;
- unsigned char fs, old_fs, tmp = 0;
- unsigned long flags;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+ cnt = count;
+ if (portc->audio_format == AFMT_IMA_ADPCM)
+ {
+ cnt /= 4;
+ } else
+ {
+ if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
+ cnt >>= 1;
+ }
+ if (portc->channels > 1)
+ cnt >>= 1;
+ cnt--;
- ad_mute (devc);
+ if (devc->audio_mode & PCM_ENABLE_INPUT && audio_devs[dev]->flags & DMA_AUTOMODE &&
+ intrflag &&
+ cnt == devc->xfer_count)
+ {
+ devc->audio_mode |= PCM_ENABLE_INPUT;
+ devc->intr_active = 1;
+ return; /*
+ * Auto DMA mode on. No need to react
+ */
+ }
+ save_flags(flags);
+ cli();
- save_flags (flags);
- cli ();
- fs = portc->speed_bits | (portc->format_bits << 5);
+ if (devc->model == MD_1848)
+ {
+ ad_write(devc, 15, (unsigned char) (cnt & 0xff));
+ ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
+ } else
+ {
+ ad_write(devc, 31, (unsigned char) (cnt & 0xff));
+ ad_write(devc, 30, (unsigned char) ((cnt >> 8) & 0xff));
+ }
- if (portc->channels > 1)
- fs |= 0x10;
+ ad_unmute(devc);
- ad_enter_MCE (devc); /* Enables changes to the format select reg */
+ devc->xfer_count = cnt;
+ devc->audio_mode |= PCM_ENABLE_INPUT;
+ devc->intr_active = 1;
+ restore_flags(flags);
+}
- if (devc->model == MD_1845) /* Use alternate speed select registers */
- {
- fs &= 0xf0; /* Mask off the rate select bits */
+static int
+ad1848_prepare_for_output(int dev, int bsize, int bcount)
+{
+ int timeout;
+ unsigned char fs, old_fs, tmp = 0;
+ unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
- ad_write (devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */
- ad_write (devc, 23, portc->speed & 0xff); /* Speed LSB */
- }
+ ad_mute(devc);
- old_fs = ad_read (devc, 8);
+ save_flags(flags);
+ cli();
+ fs = portc->speed_bits | (portc->format_bits << 5);
- if (devc->model == MD_4232)
- {
- tmp = ad_read (devc, 16);
- ad_write (devc, 16, tmp | 0x30);
- }
+ if (portc->channels > 1)
+ fs |= 0x10;
- if (devc->model == MD_IWAVE)
- ad_write (devc, 17, 0xc2); /* Disable variable frequency select */
+ ad_enter_MCE(devc); /* Enables changes to the format select reg */
- ad_write (devc, 8, fs);
- /*
- * Write to I8 starts resynchronization. Wait until it completes.
- */
- timeout = 0;
- while (timeout < 100 && inb (devc->base) != 0x80)
- timeout++;
- timeout = 0;
- while (timeout < 10000 && inb (devc->base) == 0x80)
- timeout++;
+ if (devc->model == MD_1845) /* Use alternate speed select registers */
+ {
+ fs &= 0xf0; /* Mask off the rate select bits */
- if (devc->model == MD_4232)
- ad_write (devc, 16, tmp & ~0x30);
+ ad_write(devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */
+ ad_write(devc, 23, portc->speed & 0xff); /* Speed LSB */
+ }
+ old_fs = ad_read(devc, 8);
- ad_leave_MCE (devc); /*
+ if (devc->model == MD_4232)
+ {
+ tmp = ad_read(devc, 16);
+ ad_write(devc, 16, tmp | 0x30);
+ }
+ if (devc->model == MD_IWAVE)
+ ad_write(devc, 17, 0xc2); /* Disable variable frequency select */
+
+ ad_write(devc, 8, fs);
+ /*
+ * Write to I8 starts resynchronization. Wait until it completes.
+ */
+ timeout = 0;
+ while (timeout < 100 && inb(devc->base) != 0x80)
+ timeout++;
+ timeout = 0;
+ while (timeout < 10000 && inb(devc->base) == 0x80)
+ timeout++;
+
+ if (devc->model == MD_4232)
+ ad_write(devc, 16, tmp & ~0x30);
+
+ ad_leave_MCE(devc); /*
* Starts the calibration process.
*/
- restore_flags (flags);
- devc->xfer_count = 0;
+ restore_flags(flags);
+ devc->xfer_count = 0;
-#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
- if (dev == timer_installed && devc->timer_running)
- if ((fs & 0x01) != (old_fs & 0x01))
- {
- ad1848_tmr_reprogram (dev);
- }
+#if (defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)) || defined(MODULE)
+ if (dev == timer_installed && devc->timer_running)
+ if ((fs & 0x01) != (old_fs & 0x01))
+ {
+ ad1848_tmr_reprogram(dev);
+ }
#endif
- ad1848_halt_output (dev);
- return 0;
+ ad1848_halt_output(dev);
+ return 0;
}
static int
-ad1848_prepare_for_input (int dev, int bsize, int bcount)
+ad1848_prepare_for_input(int dev, int bsize, int bcount)
{
- int timeout;
- unsigned char fs, old_fs, tmp = 0;
- unsigned long flags;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- if (devc->audio_mode)
- return 0;
-
- save_flags (flags);
- cli ();
- fs = portc->speed_bits | (portc->format_bits << 5);
-
- if (portc->channels > 1)
- fs |= 0x10;
-
- ad_enter_MCE (devc); /* Enables changes to the format select reg */
-
- if (devc->model == MD_1845) /* Use alternate speed select registers */
- {
- fs &= 0xf0; /* Mask off the rate select bits */
-
- ad_write (devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */
- ad_write (devc, 23, portc->speed & 0xff); /* Speed LSB */
- }
-
- if (devc->model == MD_4232)
- {
- tmp = ad_read (devc, 16);
- ad_write (devc, 16, tmp | 0x30);
- }
-
- if (devc->model == MD_IWAVE)
- ad_write (devc, 17, 0xc2); /* Disable variable frequency select */
-
- /*
- * If mode >= 2 (CS4231), set I28. It's the capture format register.
- */
- if (devc->model != MD_1848)
- {
- old_fs = ad_read (devc, 28);
- ad_write (devc, 28, fs);
-
- /*
- * Write to I28 starts resynchronization. Wait until it completes.
- */
- timeout = 0;
- while (timeout < 100 && inb (devc->base) != 0x80)
- timeout++;
-
- timeout = 0;
- while (timeout < 10000 && inb (devc->base) == 0x80)
- timeout++;
-
- if (devc->model != MD_1848 && devc->model != MD_1845)
- {
- /*
- * CS4231 compatible devices don't have separate sampling rate selection
- * register for recording an playback. The I8 register is shared so we have to
- * set the speed encoding bits of it too.
- */
- unsigned char tmp = portc->speed_bits | (ad_read (devc, 8) & 0xf0);
-
- ad_write (devc, 8, tmp);
- /*
- * Write to I8 starts resynchronization. Wait until it completes.
- */
- timeout = 0;
- while (timeout < 100 && inb (devc->base) != 0x80)
- timeout++;
-
- timeout = 0;
- while (timeout < 10000 && inb (devc->base) == 0x80)
- timeout++;
- }
- }
- else
- { /* For AD1848 set I8. */
-
- old_fs = ad_read (devc, 8);
- ad_write (devc, 8, fs);
- /*
- * Write to I8 starts resynchronization. Wait until it completes.
- */
- timeout = 0;
- while (timeout < 100 && inb (devc->base) != 0x80)
- timeout++;
- timeout = 0;
- while (timeout < 10000 && inb (devc->base) == 0x80)
- timeout++;
- }
-
- if (devc->model == MD_4232)
- ad_write (devc, 16, tmp & ~0x30);
-
- ad_leave_MCE (devc); /*
+ int timeout;
+ unsigned char fs, old_fs, tmp = 0;
+ unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+
+ if (devc->audio_mode)
+ return 0;
+
+ save_flags(flags);
+ cli();
+ fs = portc->speed_bits | (portc->format_bits << 5);
+
+ if (portc->channels > 1)
+ fs |= 0x10;
+
+ ad_enter_MCE(devc); /* Enables changes to the format select reg */
+
+ if (devc->model == MD_1845) /* Use alternate speed select registers */
+ {
+ fs &= 0xf0; /* Mask off the rate select bits */
+
+ ad_write(devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */
+ ad_write(devc, 23, portc->speed & 0xff); /* Speed LSB */
+ }
+ if (devc->model == MD_4232)
+ {
+ tmp = ad_read(devc, 16);
+ ad_write(devc, 16, tmp | 0x30);
+ }
+ if (devc->model == MD_IWAVE)
+ ad_write(devc, 17, 0xc2); /* Disable variable frequency select */
+
+ /*
+ * If mode >= 2 (CS4231), set I28. It's the capture format register.
+ */
+ if (devc->model != MD_1848)
+ {
+ old_fs = ad_read(devc, 28);
+ ad_write(devc, 28, fs);
+
+ /*
+ * Write to I28 starts resynchronization. Wait until it completes.
+ */
+ timeout = 0;
+ while (timeout < 100 && inb(devc->base) != 0x80)
+ timeout++;
+
+ timeout = 0;
+ while (timeout < 10000 && inb(devc->base) == 0x80)
+ timeout++;
+
+ if (devc->model != MD_1848 && devc->model != MD_1845)
+ {
+ /*
+ * CS4231 compatible devices don't have separate sampling rate selection
+ * register for recording an playback. The I8 register is shared so we have to
+ * set the speed encoding bits of it too.
+ */
+ unsigned char tmp = portc->speed_bits | (ad_read(devc, 8) & 0xf0);
+
+ ad_write(devc, 8, tmp);
+ /*
+ * Write to I8 starts resynchronization. Wait until it completes.
+ */
+ timeout = 0;
+ while (timeout < 100 && inb(devc->base) != 0x80)
+ timeout++;
+
+ timeout = 0;
+ while (timeout < 10000 && inb(devc->base) == 0x80)
+ timeout++;
+ }
+ } else
+ { /* For AD1848 set I8. */
+
+ old_fs = ad_read(devc, 8);
+ ad_write(devc, 8, fs);
+ /*
+ * Write to I8 starts resynchronization. Wait until it completes.
+ */
+ timeout = 0;
+ while (timeout < 100 && inb(devc->base) != 0x80)
+ timeout++;
+ timeout = 0;
+ while (timeout < 10000 && inb(devc->base) == 0x80)
+ timeout++;
+ }
+
+ if (devc->model == MD_4232)
+ ad_write(devc, 16, tmp & ~0x30);
+
+ ad_leave_MCE(devc); /*
* Starts the calibration process.
*/
- restore_flags (flags);
- devc->xfer_count = 0;
+ restore_flags(flags);
+ devc->xfer_count = 0;
#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
- if (dev == timer_installed && devc->timer_running)
- if ((fs & 0x01) != (old_fs & 0x01))
- {
- ad1848_tmr_reprogram (dev);
- }
+ if (dev == timer_installed && devc->timer_running)
+ if ((fs & 0x01) != (old_fs & 0x01))
+ {
+ ad1848_tmr_reprogram(dev);
+ }
#endif
- ad1848_halt_input (dev);
- return 0;
+ ad1848_halt_input(dev);
+ return 0;
}
static void
-ad1848_halt (int dev)
+ad1848_halt(int dev)
{
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
- unsigned char bits = ad_read (devc, 9);
+ unsigned char bits = ad_read(devc, 9);
- if (bits & 0x01 && portc->open_mode & OPEN_WRITE)
- ad1848_halt_output (dev);
+ if (bits & 0x01 && portc->open_mode & OPEN_WRITE)
+ ad1848_halt_output(dev);
- if (bits & 0x02 && portc->open_mode & OPEN_READ)
- ad1848_halt_input (dev);
- devc->audio_mode = 0;
+ if (bits & 0x02 && portc->open_mode & OPEN_READ)
+ ad1848_halt_input(dev);
+ devc->audio_mode = 0;
}
static void
-ad1848_halt_input (int dev)
+ad1848_halt_input(int dev)
{
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ unsigned long flags;
- if (!(ad_read (devc, 9) & 0x02))
- return; /* Capture not enabled */
+ if (!(ad_read(devc, 9) & 0x02))
+ return; /* Capture not enabled */
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- ad_mute (devc);
+ ad_mute(devc);
- {
- int tmout;
+ {
+ int tmout;
- disable_dma (audio_devs[dev]->dmap_in->dma);
+ disable_dma(audio_devs[dev]->dmap_in->dma);
- for (tmout = 0; tmout < 100000; tmout++)
- if (ad_read (devc, 11) & 0x10)
- break;
- ad_write (devc, 9, ad_read (devc, 9) & ~0x02); /* Stop capture */
+ for (tmout = 0; tmout < 100000; tmout++)
+ if (ad_read(devc, 11) & 0x10)
+ break;
+ ad_write(devc, 9, ad_read(devc, 9) & ~0x02); /* Stop capture */
- enable_dma (audio_devs[dev]->dmap_in->dma);
- devc->audio_mode &= ~PCM_ENABLE_INPUT;
- }
+ enable_dma(audio_devs[dev]->dmap_in->dma);
+ devc->audio_mode &= ~PCM_ENABLE_INPUT;
+ }
- outb ((0), io_Status (devc)); /* Clear interrupt status */
- outb ((0), io_Status (devc)); /* Clear interrupt status */
+ outb((0), io_Status(devc)); /* Clear interrupt status */
+ outb((0), io_Status(devc)); /* Clear interrupt status */
- devc->audio_mode &= ~PCM_ENABLE_INPUT;
+ devc->audio_mode &= ~PCM_ENABLE_INPUT;
- restore_flags (flags);
+ restore_flags(flags);
}
static void
-ad1848_halt_output (int dev)
+ad1848_halt_output(int dev)
{
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ unsigned long flags;
- if (!(ad_read (devc, 9) & 0x01))
- return; /* Playback not enabled */
+ if (!(ad_read(devc, 9) & 0x01))
+ return; /* Playback not enabled */
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- ad_mute (devc);
- {
- int tmout;
+ ad_mute(devc);
+ {
+ int tmout;
- disable_dma (audio_devs[dev]->dmap_out->dma);
+ disable_dma(audio_devs[dev]->dmap_out->dma);
- for (tmout = 0; tmout < 100000; tmout++)
- if (ad_read (devc, 11) & 0x10)
- break;
- ad_write (devc, 9, ad_read (devc, 9) & ~0x01); /* Stop playback */
+ for (tmout = 0; tmout < 100000; tmout++)
+ if (ad_read(devc, 11) & 0x10)
+ break;
+ ad_write(devc, 9, ad_read(devc, 9) & ~0x01); /* Stop playback */
- enable_dma (audio_devs[dev]->dmap_out->dma);
- devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
- }
+ enable_dma(audio_devs[dev]->dmap_out->dma);
+ devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
+ }
- outb ((0), io_Status (devc)); /* Clear interrupt status */
- outb ((0), io_Status (devc)); /* Clear interrupt status */
+ outb((0), io_Status(devc)); /* Clear interrupt status */
+ outb((0), io_Status(devc)); /* Clear interrupt status */
- devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
+ devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
- restore_flags (flags);
+ restore_flags(flags);
}
static void
-ad1848_trigger (int dev, int state)
+ad1848_trigger(int dev, int state)
{
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
- unsigned long flags;
- unsigned char tmp, old;
-
- save_flags (flags);
- cli ();
- state &= devc->audio_mode;
-
- tmp = old = ad_read (devc, 9);
-
- if (portc->open_mode & OPEN_READ)
- {
- if (state & PCM_ENABLE_INPUT)
- tmp |= 0x02;
- else
- tmp &= ~0x02;
- }
-
- if (portc->open_mode & OPEN_WRITE)
- {
- if (state & PCM_ENABLE_OUTPUT)
- tmp |= 0x01;
- else
- tmp &= ~0x01;
- }
-
- /* ad_mute(devc); */
- if (tmp != old)
- {
- ad_write (devc, 9, tmp);
- ad_unmute (devc);
- }
-
- restore_flags (flags);
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
+ unsigned long flags;
+ unsigned char tmp, old;
+
+ save_flags(flags);
+ cli();
+ state &= devc->audio_mode;
+
+ tmp = old = ad_read(devc, 9);
+
+ if (portc->open_mode & OPEN_READ)
+ {
+ if (state & PCM_ENABLE_INPUT)
+ tmp |= 0x02;
+ else
+ tmp &= ~0x02;
+ }
+ if (portc->open_mode & OPEN_WRITE)
+ {
+ if (state & PCM_ENABLE_OUTPUT)
+ tmp |= 0x01;
+ else
+ tmp &= ~0x01;
+ }
+ /* ad_mute(devc); */
+ if (tmp != old)
+ {
+ ad_write(devc, 9, tmp);
+ ad_unmute(devc);
+ }
+ restore_flags(flags);
}
static void
-ad1848_init_hw (ad1848_info * devc)
+ad1848_init_hw(ad1848_info * devc)
{
- int i;
-
- /*
- * Initial values for the indirect registers of CS4248/AD1848.
- */
- static int init_values[] =
- {
- 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
- 0x00, 0x0c, 0x02, 0x00, 0x8a, 0x01, 0x00, 0x00,
+ int i;
- /* Positions 16 to 31 just for CS4231/2 and ad1845 */
- 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
+ /*
+ * Initial values for the indirect registers of CS4248/AD1848.
+ */
+ static int init_values[] =
+ {
+ 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x0c, 0x02, 0x00, 0x8a, 0x01, 0x00, 0x00,
+ /* Positions 16 to 31 just for CS4231/2 and ad1845 */
+ 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
- for (i = 0; i < 16; i++)
- ad_write (devc, i, init_values[i]);
+ for (i = 0; i < 16; i++)
+ ad_write(devc, i, init_values[i]);
- ad_mute (devc); /* Initialize some variables */
- ad_unmute (devc); /* Leave it unmuted now */
- if (devc->model > MD_1848)
- {
- ad_write (devc, 12, ad_read (devc, 12) | 0x40); /* Mode2 = enabled */
+ ad_mute(devc); /* Initialize some variables */
+ ad_unmute(devc); /* Leave it unmuted now */
- if (devc->model == MD_IWAVE)
- ad_write (devc, 12, 0x6c); /* Select codec mode 3 */
+ if (devc->model > MD_1848)
+ {
+ ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */
- for (i = 16; i < 32; i++)
- ad_write (devc, i, init_values[i]);
+ if (devc->model == MD_IWAVE)
+ ad_write(devc, 12, 0x6c); /* Select codec mode 3 */
- if (devc->model == MD_IWAVE)
- ad_write (devc, 16, 0x30); /* Playback and capture counters enabled */
+ for (i = 16; i < 32; i++)
+ ad_write(devc, i, init_values[i]);
- }
+ if (devc->model == MD_IWAVE)
+ ad_write(devc, 16, 0x30); /* Playback and capture counters enabled */
- if (devc->model > MD_1848)
- {
- if (devc->audio_flags & DMA_DUPLEX)
- ad_write (devc, 9, ad_read (devc, 9) & ~0x04); /* Dual DMA mode */
- else
- ad_write (devc, 9, ad_read (devc, 9) | 0x04); /* Single DMA mode */
+ }
+ if (devc->model > MD_1848)
+ {
+ if (devc->audio_flags & DMA_DUPLEX)
+ ad_write(devc, 9, ad_read(devc, 9) & ~0x04); /* Dual DMA mode */
+ else
+ ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */
- if (devc->model == MD_1845)
- ad_write (devc, 27, ad_read (devc, 27) | 0x08); /* Alternate freq select enabled */
+ if (devc->model == MD_1845)
+ ad_write(devc, 27, ad_read(devc, 27) | 0x08); /* Alternate freq select enabled */
- if (devc->model == MD_IWAVE)
- { /* Some magic Interwave specific initialization */
- ad_write (devc, 12, 0x6c); /* Select codec mode 3 */
- ad_write (devc, 16, 0x30); /* Playback and capture counters enabled */
- ad_write (devc, 17, 0xc2); /* Alternate feature enable */
- }
- }
- else
- {
- devc->audio_flags &= ~DMA_DUPLEX;
- ad_write (devc, 9, ad_read (devc, 9) | 0x04); /* Single DMA mode */
- }
+ if (devc->model == MD_IWAVE)
+ { /* Some magic Interwave specific initialization */
+ ad_write(devc, 12, 0x6c); /* Select codec mode 3 */
+ ad_write(devc, 16, 0x30); /* Playback and capture counters enabled */
+ ad_write(devc, 17, 0xc2); /* Alternate feature enable */
+ }
+ } else
+ {
+ devc->audio_flags &= ~DMA_DUPLEX;
+ ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */
+ }
- outb ((0), io_Status (devc)); /* Clear pending interrupts */
+ outb((0), io_Status(devc)); /* Clear pending interrupts */
- /*
- * Toggle the MCE bit. It completes the initialization phase.
- */
+ /*
+ * Toggle the MCE bit. It completes the initialization phase.
+ */
- ad_enter_MCE (devc); /* In case the bit was off */
- ad_leave_MCE (devc);
+ ad_enter_MCE(devc); /* In case the bit was off */
+ ad_leave_MCE(devc);
- ad1848_mixer_reset (devc);
+ ad1848_mixer_reset(devc);
}
int
-ad1848_detect (int io_base, int *ad_flags, int *osp)
+ad1848_detect(int io_base, int *ad_flags, int *osp)
{
- unsigned char tmp;
- ad1848_info *devc = &adev_info[nr_ad1848_devs];
- unsigned char tmp1 = 0xff, tmp2 = 0xff;
- int optiC930 = 0; /* OPTi 82C930 flag */
- int interwave = 0;
- int ad1847_flag = 0;
- int cs4248_flag = 0;
+ unsigned char tmp;
+ ad1848_info *devc = &adev_info[nr_ad1848_devs];
+ unsigned char tmp1 = 0xff, tmp2 = 0xff;
+ int optiC930 = 0; /* OPTi 82C930 flag */
+ int interwave = 0;
+ int ad1847_flag = 0;
+ int cs4248_flag = 0;
- int i;
+ int i;
- DDB (printk ("ad1848_detect(%x)\n", io_base));
-
- if (ad_flags)
- {
- if (*ad_flags == 0x12345678)
- {
- interwave = 1;
- *ad_flags = 0;
- }
-
- if (*ad_flags == 0x12345677)
- {
- cs4248_flag = 1;
- *ad_flags = 0;
- }
- }
-
- if (nr_ad1848_devs >= MAX_AUDIO_DEV)
- {
- printk ("ad1848 - Too many audio devices\n");
- return 0;
- }
- if (check_region (io_base, 4))
- {
- printk ("ad1848.c: Port %x not free.\n", io_base);
- return 0;
- }
-
- devc->base = io_base;
- devc->irq_ok = 0;
- devc->timer_running = 0;
- devc->MCE_bit = 0x40;
- devc->irq = 0;
- devc->open_mode = 0;
- devc->chip_name = "AD1848";
- devc->model = MD_1848; /* AD1848 or CS4248 */
- devc->levels = NULL;
- devc->c930_password_port = 0;
- devc->debug_flag = 0;
-
- /*
- * Check that the I/O address is in use.
- *
- * The bit 0x80 of the base I/O port is known to be 0 after the
- * chip has performed its power on initialization. Just assume
- * this has happened before the OS is starting.
- *
- * If the I/O address is unused, it typically returns 0xff.
- */
+ DDB(printk("ad1848_detect(%x)\n", io_base));
+ if (ad_flags)
+ {
+ if (*ad_flags == 0x12345678)
+ {
+ interwave = 1;
+ *ad_flags = 0;
+ }
+ if (*ad_flags == 0x12345677)
+ {
+ cs4248_flag = 1;
+ *ad_flags = 0;
+ }
+ }
+ if (nr_ad1848_devs >= MAX_AUDIO_DEV)
+ {
+ printk("ad1848 - Too many audio devices\n");
+ return 0;
+ }
+ if (check_region(io_base, 4))
+ {
+ printk("ad1848.c: Port %x not free.\n", io_base);
+ return 0;
+ }
+ devc->base = io_base;
+ devc->irq_ok = 0;
+ devc->timer_running = 0;
+ devc->MCE_bit = 0x40;
+ devc->irq = 0;
+ devc->open_mode = 0;
+ devc->chip_name = devc->name = "AD1848";
+ devc->model = MD_1848; /* AD1848 or CS4248 */
+ devc->levels = NULL;
+ devc->c930_password_port = 0;
+ devc->debug_flag = 0;
+
+ /*
+ * Check that the I/O address is in use.
+ *
+ * The bit 0x80 of the base I/O port is known to be 0 after the
+ * chip has performed its power on initialization. Just assume
+ * this has happened before the OS is starting.
+ *
+ * If the I/O address is unused, it typically returns 0xff.
+ */
+
+ if (inb(devc->base) == 0xff)
+ {
+ DDB(printk("ad1848_detect: The base I/O address appears to be dead\n"));
+ }
/*
* Wait for the device to stop initialization
*/
- DDB (printk ("ad1848_detect() - step 0\n"));
-
- for (i = 0; i < 10000000; i++)
- {
- unsigned char x = inb (devc->base);
-
- if (x == 0xff || !(x & 0x80))
- break;
- }
-
- DDB (printk ("ad1848_detect() - step A\n"));
-
- if (inb (devc->base) == 0x80) /* Not ready. Let's wait */
- ad_leave_MCE (devc);
-
- if ((inb (devc->base) & 0x80) != 0x00) /* Not a AD1848 */
- {
- DDB (printk ("ad1848 detect error - step A (%02x)\n",
- (int) inb (devc->base)));
- return 0;
- }
-
- /*
- * Test if it's possible to change contents of the indirect registers.
- * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only
- * so try to avoid using it.
- */
-
- DDB (printk ("ad1848_detect() - step B\n"));
- ad_write (devc, 0, 0xaa);
- ad_write (devc, 1, 0x45); /* 0x55 with bit 0x10 clear */
-
- if ((tmp1 = ad_read (devc, 0)) != 0xaa || (tmp2 = ad_read (devc, 1)) != 0x45)
- if (tmp2 == 0x65) /* AD1847 has couple of bits hardcoded to 1 */
- ad1847_flag = 1;
- else
- {
- DDB (printk ("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2));
- /* return 0; */
- }
-
- DDB (printk ("ad1848_detect() - step C\n"));
- ad_write (devc, 0, 0x45);
- ad_write (devc, 1, 0xaa);
-
- if ((tmp1 = ad_read (devc, 0)) != 0x45 || (tmp2 = ad_read (devc, 1)) != 0xaa)
- if (tmp2 == 0x8a) /* AD1847 has few bits hardcoded to 1 */
- ad1847_flag = 1;
- else
- {
- DDB (printk ("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2));
- /* return 0; */
- }
-
- /*
- * The indirect register I12 has some read only bits. Lets
- * try to change them.
- */
-
- DDB (printk ("ad1848_detect() - step D\n"));
- tmp = ad_read (devc, 12);
- ad_write (devc, 12, (~tmp) & 0x0f);
-
- if ((tmp & 0x0f) != ((tmp1 = ad_read (devc, 12)) & 0x0f))
- {
- DDB (printk ("ad1848 detect error - step D (%x)\n", tmp1));
- return 0;
- }
-
- /*
- * NOTE! Last 4 bits of the reg I12 tell the chip revision.
- * 0x01=RevB and 0x0A=RevC.
- */
-
- /*
- * The original AD1848/CS4248 has just 15 indirect registers. This means
- * that I0 and I16 should return the same value (etc.).
- * However this doesn't work with CS4248. Actually it seems to be impossible
- * to detect if the chip is a CS4231 or CS4248.
- * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails
- * with CS4231.
- */
+ DDB(printk("ad1848_detect() - step 0\n"));
-/*
- * OPTi 82C930 has mode2 control bit in another place. This test will fail
- * with it. Accept this situation as a possible indication of this chip.
- */
+ for (i = 0; i < 10000000; i++)
+ {
+ unsigned char x = inb(devc->base);
- DDB (printk ("ad1848_detect() - step F\n"));
- ad_write (devc, 12, 0); /* Mode2=disabled */
-
- for (i = 0; i < 16; i++)
- if ((tmp1 = ad_read (devc, i)) != (tmp2 = ad_read (devc, i + 16)))
- {
- DDB (printk ("ad1848 detect step F(%d/%x/%x) - OPTi chip???\n", i, tmp1, tmp2));
- if (!ad1847_flag)
- optiC930 = 1;
- break;
- }
-
- /*
- * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40).
- * The bit 0x80 is always 1 in CS4248 and CS4231.
- */
-
- DDB (printk ("ad1848_detect() - step G\n"));
-
- if (ad_flags && *ad_flags == 400)
- *ad_flags = 0;
- else
- ad_write (devc, 12, 0x40); /* Set mode2, clear 0x80 */
-
-
- if (ad_flags)
- *ad_flags = 0;
-
- tmp1 = ad_read (devc, 12);
- if (tmp1 & 0x80)
- {
- if (ad_flags)
- *ad_flags |= AD_F_CS4248;
-
- devc->chip_name = "CS4248"; /* Our best knowledge just now */
- }
-
- if (optiC930 || (tmp1 & 0xc0) == (0x80 | 0x40))
- {
- /*
- * CS4231 detected - is it?
- *
- * Verify that setting I0 doesn't change I16.
- */
- DDB (printk ("ad1848_detect() - step H\n"));
- ad_write (devc, 16, 0); /* Set I16 to known value */
-
- ad_write (devc, 0, 0x45);
- if ((tmp1 = ad_read (devc, 16)) != 0x45) /* No change -> CS4231? */
- {
+ if (x == 0xff || !(x & 0x80))
+ break;
+ }
- ad_write (devc, 0, 0xaa);
- if ((tmp1 = ad_read (devc, 16)) == 0xaa) /* Rotten bits? */
- {
- DDB (printk ("ad1848 detect error - step H(%x)\n", tmp1));
- return 0;
- }
-
- /*
- * Verify that some bits of I25 are read only.
- */
-
- DDB (printk ("ad1848_detect() - step I\n"));
- tmp1 = ad_read (devc, 25); /* Original bits */
- ad_write (devc, 25, ~tmp1); /* Invert all bits */
- if ((ad_read (devc, 25) & 0xe7) == (tmp1 & 0xe7))
- {
- int id, full_id;
-
- /*
- * It's at least CS4231
- */
- devc->chip_name = "CS4231";
-
- devc->model = MD_4231;
-
- /*
- * It could be an AD1845 or CS4231A as well.
- * CS4231 and AD1845 report the same revision info in I25
- * while the CS4231A reports different.
- */
-
- id = ad_read (devc, 25) & 0xe7;
- full_id = ad_read (devc, 25);
- if (id == 0x80) /* Device busy??? */
- id = ad_read (devc, 25) & 0xe7;
- if (id == 0x80) /* Device still busy??? */
- id = ad_read (devc, 25) & 0xe7;
- DDB (printk ("ad1848_detect() - step J (%02x/%02x)\n", id,
- ad_read (devc, 25)));
-
- switch (id)
- {
+ DDB(printk("ad1848_detect() - step A\n"));
- case 0xa0:
- devc->chip_name = "CS4231A";
- devc->model = MD_4231A;
- break;
+ if (inb(devc->base) == 0x80) /* Not ready. Let's wait */
+ ad_leave_MCE(devc);
- case 0xa2:
- devc->chip_name = "CS4232";
- devc->model = MD_4232;
- break;
+ if ((inb(devc->base) & 0x80) != 0x00) /* Not a AD1848 */
+ {
+ DDB(printk("ad1848 detect error - step A (%02x)\n", (int) inb(devc->base)));
+ return 0;
+ }
+ /*
+ * Test if it's possible to change contents of the indirect registers.
+ * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only
+ * so try to avoid using it.
+ */
+
+ DDB(printk("ad1848_detect() - step B\n"));
+ ad_write(devc, 0, 0xaa);
+ ad_write(devc, 1, 0x45); /* 0x55 with bit 0x10 clear */
+
+ if ((tmp1 = ad_read(devc, 0)) != 0xaa || (tmp2 = ad_read(devc, 1)) != 0x45)
+ if (tmp2 == 0x65) /* AD1847 has couple of bits hardcoded to 1 */
+ ad1847_flag = 1;
+ else
+ {
+ DDB(printk("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2));
+ return 0;
+ }
+ DDB(printk("ad1848_detect() - step C\n"));
+ ad_write(devc, 0, 0x45);
+ ad_write(devc, 1, 0xaa);
+
+ if ((tmp1 = ad_read(devc, 0)) != 0x45 || (tmp2 = ad_read(devc, 1)) != 0xaa)
+ if (tmp2 == 0x8a) /* AD1847 has few bits hardcoded to 1 */
+ ad1847_flag = 1;
+ else
+ {
+ DDB(printk("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2));
+ return 0;
+ }
+ /*
+ * The indirect register I12 has some read only bits. Lets
+ * try to change them.
+ */
- case 0xb2:
- devc->chip_name = "CS4232A";
- devc->model = MD_4232;
- break;
+ DDB(printk("ad1848_detect() - step D\n"));
+ tmp = ad_read(devc, 12);
+ ad_write(devc, 12, (~tmp) & 0x0f);
- case 0x03:
- case 0x83:
- devc->chip_name = "CS4236";
- devc->model = MD_4232;
- break;
+ if ((tmp & 0x0f) != ((tmp1 = ad_read(devc, 12)) & 0x0f))
+ {
+ DDB(printk("ad1848 detect error - step D (%x)\n", tmp1));
+ return 0;
+ }
+ /*
+ * NOTE! Last 4 bits of the reg I12 tell the chip revision.
+ * 0x01=RevB and 0x0A=RevC.
+ */
+
+ /*
+ * The original AD1848/CS4248 has just 15 indirect registers. This means
+ * that I0 and I16 should return the same value (etc.).
+ * However this doesn't work with CS4248. Actually it seems to be impossible
+ * to detect if the chip is a CS4231 or CS4248.
+ * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails
+ * with CS4231.
+ */
- case 0x41:
- devc->chip_name = "CS4236B";
- devc->model = MD_4232;
- break;
+/*
+ * OPTi 82C930 has mode2 control bit in another place. This test will fail
+ * with it. Accept this situation as a possible indication of this chip.
+ */
+
+ DDB(printk("ad1848_detect() - step F\n"));
+ ad_write(devc, 12, 0); /* Mode2=disabled */
- case 0x80:
+ for (i = 0; i < 16; i++)
+ if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16)))
{
- /*
- * It must be a CS4231 or AD1845. The register I23 of
- * CS4231 is undefined and it appears to be read only.
- * AD1845 uses I23 for setting sample rate. Assume
- * the chip is AD1845 if I23 is changeable.
- */
-
- unsigned char tmp = ad_read (devc, 23);
-
- ad_write (devc, 23, ~tmp);
- if (interwave)
- {
- devc->model = MD_IWAVE;
- devc->chip_name = "IWave";
- }
- else if (ad_read (devc, 23) != tmp) /* AD1845 ? */
- {
- devc->chip_name = "AD1845";
- devc->model = MD_1845;
- }
- else if (cs4248_flag)
- {
- if (ad_flags)
- *ad_flags |= AD_F_CS4248;
+ DDB(printk("ad1848 detect step F(%d/%x/%x) - OPTi chip???\n", i, tmp1, tmp2));
+ if (!ad1847_flag)
+ optiC930 = 1;
+ break;
+ }
+ /*
+ * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40).
+ * The bit 0x80 is always 1 in CS4248 and CS4231.
+ */
- devc->chip_name = "CS4248";
- devc->model = MD_1848;
- ad_write (devc, 12, ad_read (devc, 12) & ~0x40); /* Mode2 off */
- }
+ DDB(printk("ad1848_detect() - step G\n"));
- ad_write (devc, 23, tmp); /* Restore */
- }
- break;
+ if (ad_flags && *ad_flags == 400)
+ *ad_flags = 0;
+ else
+ ad_write(devc, 12, 0x40); /* Set mode2, clear 0x80 */
- default: /* Assume CS4231 or OPTi 82C930 */
- DDB (printk ("ad1848: I25 = %02x/%02x\n",
- ad_read (devc, 25),
- ad_read (devc, 25) & 0xe7));
- if (optiC930)
- {
- devc->chip_name = "82C930";
- devc->model = MD_C930;
- }
- else
- {
- devc->model = MD_4231;
- }
- }
- }
- ad_write (devc, 25, tmp1); /* Restore bits */
+ if (ad_flags)
+ *ad_flags = 0;
- DDB (printk ("ad1848_detect() - step K\n"));
- }
- }
+ tmp1 = ad_read(devc, 12);
+ if (tmp1 & 0x80)
+ {
+ if (ad_flags)
+ *ad_flags |= AD_F_CS4248;
- DDB (printk ("ad1848_detect() - step L\n"));
- if (ad_flags)
- {
- if (devc->model != MD_1848)
- *ad_flags |= AD_F_CS4231;
- }
+ devc->chip_name = "CS4248"; /* Our best knowledge just now */
+ }
+ if (optiC930 || (tmp1 & 0xc0) == (0x80 | 0x40))
+ {
+ /*
+ * CS4231 detected - is it?
+ *
+ * Verify that setting I0 doesn't change I16.
+ */
+ DDB(printk("ad1848_detect() - step H\n"));
+ ad_write(devc, 16, 0); /* Set I16 to known value */
+
+ ad_write(devc, 0, 0x45);
+ if ((tmp1 = ad_read(devc, 16)) != 0x45) /* No change -> CS4231? */
+ {
- DDB (printk ("ad1848_detect() - Detected OK\n"));
+ ad_write(devc, 0, 0xaa);
+ if ((tmp1 = ad_read(devc, 16)) == 0xaa) /* Rotten bits? */
+ {
+ DDB(printk("ad1848 detect error - step H(%x)\n", tmp1));
+ return 0;
+ }
+ /*
+ * Verify that some bits of I25 are read only.
+ */
+
+ DDB(printk("ad1848_detect() - step I\n"));
+ tmp1 = ad_read(devc, 25); /* Original bits */
+ ad_write(devc, 25, ~tmp1); /* Invert all bits */
+ if ((ad_read(devc, 25) & 0xe7) == (tmp1 & 0xe7))
+ {
+ int id, full_id;
+
+ /*
+ * It's at least CS4231
+ */
+ devc->chip_name = "CS4231";
+
+ devc->model = MD_4231;
+
+ /*
+ * It could be an AD1845 or CS4231A as well.
+ * CS4231 and AD1845 report the same revision info in I25
+ * while the CS4231A reports different.
+ */
+
+ id = ad_read(devc, 25) & 0xe7;
+ full_id = ad_read(devc, 25);
+ if (id == 0x80) /* Device busy??? */
+ id = ad_read(devc, 25) & 0xe7;
+ if (id == 0x80) /* Device still busy??? */
+ id = ad_read(devc, 25) & 0xe7;
+ DDB(printk("ad1848_detect() - step J (%02x/%02x)\n", id, ad_read(devc, 25)));
+
+ switch (id)
+ {
+
+ case 0xa0:
+ devc->chip_name = "CS4231A";
+ devc->model = MD_4231A;
+ break;
+
+ case 0xa2:
+ devc->chip_name = "CS4232";
+ devc->model = MD_4232;
+ break;
+
+ case 0xb2:
+ devc->chip_name = "CS4232A";
+ devc->model = MD_4232;
+ break;
+
+ case 0x03:
+ case 0x83:
+ devc->chip_name = "CS4236";
+ devc->model = MD_4232;
+ break;
+
+ case 0x41:
+ devc->chip_name = "CS4236B";
+ devc->model = MD_4232;
+ break;
+
+ case 0x80:
+ {
+ /*
+ * It must be a CS4231 or AD1845. The register I23 of
+ * CS4231 is undefined and it appears to be read only.
+ * AD1845 uses I23 for setting sample rate. Assume
+ * the chip is AD1845 if I23 is changeable.
+ */
+
+ unsigned char tmp = ad_read(devc, 23);
+
+ ad_write(devc, 23, ~tmp);
+ if (interwave)
+ {
+ devc->model = MD_IWAVE;
+ devc->chip_name = "IWave";
+ } else if (ad_read(devc, 23) != tmp) /* AD1845 ? */
+ {
+ devc->chip_name = "AD1845";
+ devc->model = MD_1845;
+ } else if (cs4248_flag)
+ {
+ if (ad_flags)
+ *ad_flags |= AD_F_CS4248;
+
+ devc->chip_name = "CS4248";
+ devc->model = MD_1848;
+ ad_write(devc, 12, ad_read(devc, 12) & ~0x40); /* Mode2 off */
+ }
+ ad_write(devc, 23, tmp); /* Restore */
+ }
+ break;
+
+ default: /* Assume CS4231 or OPTi 82C930 */
+ DDB(printk("ad1848: I25 = %02x/%02x\n", ad_read(devc, 25), ad_read(devc, 25) & 0xe7));
+ if (optiC930)
+ {
+ devc->chip_name = "82C930";
+ devc->model = MD_C930;
+ } else
+ {
+ devc->model = MD_4231;
+ }
+
+ }
+ }
+ ad_write(devc, 25, tmp1); /* Restore bits */
+
+ DDB(printk("ad1848_detect() - step K\n"));
+ }
+ }
+ DDB(printk("ad1848_detect() - step L\n"));
+ if (ad_flags)
+ {
+ if (devc->model != MD_1848)
+ *ad_flags |= AD_F_CS4231;
+ }
+ DDB(printk("ad1848_detect() - Detected OK\n"));
- if (devc->model == MD_1848 && ad1847_flag)
- devc->chip_name = "AD1847";
+ if (devc->model == MD_1848 && ad1847_flag)
+ devc->chip_name = "AD1847";
- return 1;
+ return 1;
}
-void
-ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture, int share_dma, int *osp)
+int
+ad1848_init(char *name, int io_base, int irq, int dma_playback, int dma_capture, int share_dma, int *osp)
{
- /*
- * NOTE! If irq < 0, there is another driver which has allocated the IRQ
- * so that this driver doesn't need to allocate/deallocate it.
- * The actually used IRQ is ABS(irq).
- */
-
-
- int my_dev;
- char dev_name[100];
-
- ad1848_info *devc = &adev_info[nr_ad1848_devs];
-
- ad1848_port_info *portc = NULL;
-
- request_region (devc->base, 4, devc->chip_name);
-
- devc->irq = (irq > 0) ? irq : 0;
- devc->open_mode = 0;
- devc->timer_ticks = 0;
- devc->dma1 = dma_playback;
- devc->dma2 = dma_capture;
- devc->audio_flags = DMA_AUTOMODE;
- devc->playback_dev = devc->record_dev = 0;
-
- if (name != NULL && name[0] != 0)
- sprintf (dev_name,
- "%s (%s)", name, devc->chip_name);
- else
- sprintf (dev_name,
- "Generic audio codec (%s)", devc->chip_name);
-
- conf_printf2 (dev_name,
- devc->base, devc->irq, dma_playback, dma_capture);
-
- if (devc->model == MD_1848 || devc->model == MD_C930)
- devc->audio_flags |= DMA_HARDSTOP;
-
- if (devc->model > MD_1848)
- {
- if (devc->dma1 == devc->dma2 || devc->dma2 == -1 || devc->dma1 == -1)
- devc->audio_flags &= ~DMA_DUPLEX;
- else
- devc->audio_flags |= DMA_DUPLEX;
- }
-
- if ((my_dev = sound_install_audiodrv (AUDIO_DRIVER_VERSION,
- dev_name,
- &ad1848_audio_driver,
- sizeof (struct audio_driver),
- devc->audio_flags,
- ad_format_mask[devc->model],
- devc,
- dma_playback,
- dma_capture)) < 0)
- {
- return;
- }
-
-
- portc = (ad1848_port_info *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (ad1848_port_info)));
- sound_mem_sizes[sound_nblocks] = sizeof (ad1848_port_info);
- if (sound_nblocks < 1024)
- sound_nblocks++;;
- audio_devs[my_dev]->portc = portc;
- memset ((char *) portc, 0, sizeof (*portc));
-
- nr_ad1848_devs++;
-
- ad1848_init_hw (devc);
-
- if (irq > 0)
- {
- irq2dev[irq] = devc->dev_no = my_dev;
- if (snd_set_irq_handler (devc->irq, adintr,
- "SoundPort",
- NULL) < 0)
- {
- printk ("ad1848: IRQ in use\n");
- }
+ /*
+ * NOTE! If irq < 0, there is another driver which has allocated the IRQ
+ * so that this driver doesn't need to allocate/deallocate it.
+ * The actually used IRQ is ABS(irq).
+ */
+
+
+ int my_dev;
+ char dev_name[100];
+ int e;
+
+ ad1848_info *devc = &adev_info[nr_ad1848_devs];
+
+ ad1848_port_info *portc = NULL;
+
+ devc->irq = (irq > 0) ? irq : 0;
+ devc->open_mode = 0;
+ devc->timer_ticks = 0;
+ devc->dma1 = dma_playback;
+ devc->dma2 = dma_capture;
+ devc->audio_flags = DMA_AUTOMODE;
+ devc->playback_dev = devc->record_dev = 0;
+ if (name != NULL)
+ devc->name = name;
+
+ if (name != NULL && name[0] != 0)
+ sprintf(dev_name,
+ "%s (%s)", name, devc->chip_name);
+ else
+ sprintf(dev_name,
+ "Generic audio codec (%s)", devc->chip_name);
- if (devc->model != MD_1848 && devc->model != MD_C930)
- {
- int x;
- unsigned char tmp = ad_read (devc, 16);
+ request_region(devc->base, 4, devc->name);
- devc->timer_ticks = 0;
+ conf_printf2(dev_name,
+ devc->base, devc->irq, dma_playback, dma_capture);
- ad_write (devc, 21, 0x00); /* Timer MSB */
- ad_write (devc, 20, 0x10); /* Timer LSB */
+ if (devc->model == MD_1848 || devc->model == MD_C930)
+ devc->audio_flags |= DMA_HARDSTOP;
+
+ if (devc->model > MD_1848)
+ {
+ if (devc->dma1 == devc->dma2 || devc->dma2 == -1 || devc->dma1 == -1)
+ devc->audio_flags &= ~DMA_DUPLEX;
+ else
+ devc->audio_flags |= DMA_DUPLEX;
+ }
+ if ((my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
+ dev_name,
+ &ad1848_audio_driver,
+ sizeof(struct audio_driver),
+ devc->audio_flags,
+ ad_format_mask[devc->model],
+ devc,
+ dma_playback,
+ dma_capture)) < 0)
+ {
+ return -1;
+ }
+ portc = (ad1848_port_info *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(ad1848_port_info)));
+ sound_mem_sizes[sound_nblocks] = sizeof(ad1848_port_info);
+ if (sound_nblocks < 1024)
+ sound_nblocks++;;
+ audio_devs[my_dev]->portc = portc;
+ memset((char *) portc, 0, sizeof(*portc));
- ad_write (devc, 16, tmp | 0x40); /* Enable timer */
- for (x = 0; x < 100000 && devc->timer_ticks == 0; x++);
- ad_write (devc, 16, tmp & ~0x40); /* Disable timer */
+ nr_ad1848_devs++;
- if (devc->timer_ticks == 0)
- printk ("ad1848: Interrupt test failed (IRQ%d)\n", devc->irq);
- else
- {
- DDB (printk ("Interrupt test OK\n"));
- devc->irq_ok = 1;
- }
- }
- else
- devc->irq_ok = 1; /* Couldn't test. assume it's OK */
- }
- else if (irq < 0)
- irq2dev[-irq] = devc->dev_no = my_dev;
+ ad1848_init_hw(devc);
-#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
- if (devc->model != MD_1848 &&
- devc->model != MD_C930 && devc->irq_ok)
- ad1848_tmr_install (my_dev);
+ if (irq > 0)
+ {
+ irq2dev[irq] = devc->dev_no = my_dev;
+ if (snd_set_irq_handler(devc->irq, adintr,
+ devc->name,
+ NULL) < 0)
+ {
+ printk(KERN_WARNING "ad1848: IRQ in use\n");
+ }
+ if (devc->model != MD_1848 && devc->model != MD_C930)
+ {
+ int x;
+ unsigned char tmp = ad_read(devc, 16);
+
+ devc->timer_ticks = 0;
+
+ ad_write(devc, 21, 0x00); /* Timer MSB */
+ ad_write(devc, 20, 0x10); /* Timer LSB */
+
+ ad_write(devc, 16, tmp | 0x40); /* Enable timer */
+ for (x = 0; x < 100000 && devc->timer_ticks == 0; x++);
+ ad_write(devc, 16, tmp & ~0x40); /* Disable timer */
+
+ if (devc->timer_ticks == 0)
+ printk(KERN_WARNING "ad1848: Interrupt test failed (IRQ%d)\n", devc->irq);
+ else
+ {
+ DDB(printk("Interrupt test OK\n"));
+ devc->irq_ok = 1;
+ }
+ } else
+ devc->irq_ok = 1; /* Couldn't test. assume it's OK */
+ } else if (irq < 0)
+ irq2dev[-irq] = devc->dev_no = my_dev;
+
+#if (defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)) || defined(MODULE)
+ if (devc->model != MD_1848 &&
+ devc->model != MD_C930 && devc->irq_ok)
+ ad1848_tmr_install(my_dev);
#endif
- if (!share_dma)
- {
- if (sound_alloc_dma (dma_playback, "Sound System"))
- printk ("ad1848.c: Can't allocate DMA%d\n", dma_playback);
-
- if (dma_capture != dma_playback)
- if (sound_alloc_dma (dma_capture, "Sound System (capture)"))
- printk ("ad1848.c: Can't allocate DMA%d\n", dma_capture);
- }
-
- if (sound_install_mixer (MIXER_DRIVER_VERSION,
- dev_name,
- &ad1848_mixer_operations,
- sizeof (struct mixer_operations),
- devc) >= 0)
- {
- audio_devs[my_dev]->mixer_dev = num_mixers - 1;
- }
+ if (!share_dma)
+ {
+ if (sound_alloc_dma(dma_playback, devc->name))
+ printk("ad1848.c: Can't allocate DMA%d\n", dma_playback);
+ if (dma_capture != dma_playback)
+ if (sound_alloc_dma(dma_capture, devc->name))
+ printk("ad1848.c: Can't allocate DMA%d\n", dma_capture);
+ }
+ if ((e = sound_install_mixer(MIXER_DRIVER_VERSION,
+ dev_name,
+ &ad1848_mixer_operations,
+ sizeof(struct mixer_operations),
+ devc)) >= 0)
+ {
+ audio_devs[my_dev]->mixer_dev = e;
+ }
+ MOD_INC_USE_COUNT;
+ return my_dev;
}
-void
-ad1848_control (int cmd, int arg)
+void ad1848_control(int cmd, int arg)
{
- ad1848_info *devc;
+ ad1848_info *devc;
- if (nr_ad1848_devs < 1)
- return;
+ if (nr_ad1848_devs < 1)
+ return;
- devc = &adev_info[nr_ad1848_devs - 1];
+ devc = &adev_info[nr_ad1848_devs - 1];
- switch (cmd)
- {
- case AD1848_SET_XTAL: /* Change clock frequency of AD1845 (only ) */
- if (devc->model != MD_1845)
- return;
- ad_enter_MCE (devc);
- ad_write (devc, 29, (ad_read (devc, 29) & 0x1f) | (arg << 5));
- ad_leave_MCE (devc);
- break;
-
- case AD1848_MIXER_REROUTE:
- {
- int o = (arg >> 8) & 0xff;
- int n = arg & 0xff;
-
- if (n == SOUND_MIXER_NONE)
- { /* Just hide this control */
- ad1848_mixer_set (devc, o, 0); /* Shut up it */
- devc->supported_devices &= ~(1 << o);
- devc->supported_rec_devices &= ~(1 << o);
- return;
- }
+ switch (cmd)
+ {
+ case AD1848_SET_XTAL: /* Change clock frequency of AD1845 (only ) */
+ if (devc->model != MD_1845)
+ return;
+ ad_enter_MCE(devc);
+ ad_write(devc, 29, (ad_read(devc, 29) & 0x1f) | (arg << 5));
+ ad_leave_MCE(devc);
+ break;
- /* Make the mixer control identified by o to appear as n */
-
- if (o < 0 || o > SOUND_MIXER_NRDEVICES)
- return;
- if (n < 0 || n > SOUND_MIXER_NRDEVICES)
- return;
- if (!(devc->supported_devices & (1 << o)))
- return; /* Not supported */
-
- devc->mixer_reroute[n] = o; /* Rename the control */
- devc->supported_devices &= ~(1 << o);
- devc->supported_devices |= (1 << n);
- if (devc->supported_rec_devices & (1 << o))
- devc->supported_rec_devices |= (1 << n);
- devc->supported_rec_devices &= ~(1 << o);
- }
- break;
- }
+ case AD1848_MIXER_REROUTE:
+ {
+ int o = (arg >> 8) & 0xff;
+ int n = arg & 0xff;
+
+ if (n == SOUND_MIXER_NONE)
+ { /* Just hide this control */
+ ad1848_mixer_set(devc, o, 0); /* Shut up it */
+ devc->supported_devices &= ~(1 << o);
+ devc->supported_rec_devices &= ~(1 << o);
+ return;
+ }
+ /* Make the mixer control identified by o to appear as n */
+
+ if (o < 0 || o > SOUND_MIXER_NRDEVICES)
+ return;
+ if (n < 0 || n > SOUND_MIXER_NRDEVICES)
+ return;
+ if (!(devc->supported_devices & (1 << o)))
+ return; /* Not supported */
+
+ devc->mixer_reroute[n] = o; /* Rename the control */
+ devc->supported_devices &= ~(1 << o);
+ devc->supported_devices |= (1 << n);
+ if (devc->supported_rec_devices & (1 << o))
+ devc->supported_rec_devices |= (1 << n);
+ devc->supported_rec_devices &= ~(1 << o);
+ }
+ break;
+ }
+ return;
}
void
-ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int share_dma)
+ad1848_unload(int io_base, int irq, int dma_playback, int dma_capture, int share_dma)
{
- int i, dev = 0;
- ad1848_info *devc = NULL;
-
- for (i = 0; devc == NULL && i < nr_ad1848_devs; i++)
- if (adev_info[i].base == io_base)
- {
- devc = &adev_info[i];
- dev = devc->dev_no;
- }
+ int i, dev = 0;
+ ad1848_info *devc = NULL;
- if (devc != NULL)
- {
- release_region (devc->base, 4);
+ for (i = 0; devc == NULL && i < nr_ad1848_devs; i++)
+ if (adev_info[i].base == io_base)
+ {
+ devc = &adev_info[i];
+ dev = devc->dev_no;
+ }
+ if (devc != NULL)
+ {
+ release_region(devc->base, 4);
- if (!share_dma)
- {
- if (irq > 0)
- snd_release_irq (devc->irq);
+ if (!share_dma)
+ {
+ if (irq > 0)
+ snd_release_irq(devc->irq);
- sound_free_dma (audio_devs[dev]->dmap_out->dma);
+ sound_free_dma(audio_devs[dev]->dmap_out->dma);
- if (audio_devs[dev]->dmap_in->dma != audio_devs[dev]->dmap_out->dma)
- sound_free_dma (audio_devs[dev]->dmap_in->dma);
- }
- }
- else
- printk ("ad1848: Can't find device to be unloaded. Base=%x\n",
- io_base);
+ if (audio_devs[dev]->dmap_in->dma != audio_devs[dev]->dmap_out->dma)
+ sound_free_dma(audio_devs[dev]->dmap_in->dma);
+ }
+ } else
+ printk("ad1848: Can't find device to be unloaded. Base=%x\n", io_base);
+ MOD_DEC_USE_COUNT;
}
-void
-adintr (int irq, void *dev_id, struct pt_regs *dummy)
+void adintr(int irq, void *dev_id, struct pt_regs *dummy)
{
- unsigned char status;
- ad1848_info *devc;
- int dev;
- int alt_stat = 0xff;
- unsigned char c930_stat = 0;
- int cnt = 0;
-
- if (irq < 0 || irq > 15)
- {
- dev = -1;
- }
- else
- dev = irq2dev[irq];
-
- if (dev < 0 || dev >= num_audiodevs)
- {
- for (irq = 0; irq < 17; irq++)
- if (irq2dev[irq] != -1)
- break;
-
- if (irq > 15)
- {
- /* printk ("ad1848.c: Bogus interrupt %d\n", irq); */
- return;
- }
-
- dev = irq2dev[irq];
- devc = (ad1848_info *) audio_devs[dev]->devc;
- }
- else
- devc = (ad1848_info *) audio_devs[dev]->devc;
-
-interrupt_again: /* Jump back here if int status doesn't reset */
-
- status = inb (io_Status (devc));
+ unsigned char status;
+ ad1848_info *devc;
+ int dev;
+ int alt_stat = 0xff;
+ unsigned char c930_stat = 0;
+ int cnt = 0;
+
+ if (irq < 0 || irq > 15)
+ {
+ dev = -1;
+ } else
+ dev = irq2dev[irq];
- if (status == 0x80)
- printk ("adintr: Why?\n");
- if (devc->model == MD_1848)
- outb ((0), io_Status (devc)); /* Clear interrupt status */
+ if (dev < 0 || dev >= num_audiodevs)
+ {
+ for (irq = 0; irq < 17; irq++)
+ if (irq2dev[irq] != -1)
+ break;
- if (status & 0x01)
- {
- if (devc->model == MD_C930)
- { /* 82C930 has interrupt status register in MAD16 register MC11 */
- unsigned long flags;
+ if (irq > 15)
+ {
+ /* printk("ad1848.c: Bogus interrupt %d\n", irq); */
+ return;
+ }
+ dev = irq2dev[irq];
+ devc = (ad1848_info *) audio_devs[dev]->devc;
+ } else
+ devc = (ad1848_info *) audio_devs[dev]->devc;
- save_flags (flags);
- cli ();
+ interrupt_again: /* Jump back here if int status doesn't reset */
- alt_stat = 0;
+ status = inb(io_Status(devc));
- if (devc->c930_password_port)
- outb ((0xe4), devc->c930_password_port); /* Password */
- outb ((11), 0xe0e);
- c930_stat = inb (0xe0f);
+ if (status == 0x80)
+ printk("adintr: Why?\n");
+ if (devc->model == MD_1848)
+ outb((0), io_Status(devc)); /* Clear interrupt status */
- if (c930_stat & 0x04)
- alt_stat |= 0x10; /* Playback intr */
- if (c930_stat & 0x08)
- alt_stat |= 0x20; /* Playback intr */
- restore_flags (flags);
- }
- else if (devc->model != MD_1848)
- alt_stat = ad_read (devc, 24);
-
- /* Acknowledge the intr before proceeding */
- if (devc->model == MD_C930)
- { /* 82C930 has interrupt status register in MAD16 register MC11 */
- unsigned long flags;
-
- save_flags (flags);
- cli ();
-
- if (devc->c930_password_port)
- outb ((0xe4), devc->c930_password_port); /* Password */
- outb ((11), 0xe0e);
- outb ((~c930_stat), 0xe0f);
- restore_flags (flags);
- }
- else if (devc->model != MD_1848)
- ad_write (devc, 24, ad_read (devc, 24) & ~alt_stat); /* Selective ack */
-
- if (devc->open_mode & OPEN_READ && devc->audio_mode & PCM_ENABLE_INPUT && alt_stat & 0x20)
- {
- DMAbuf_inputintr (devc->record_dev);
- }
-
- if (devc->open_mode & OPEN_WRITE && devc->audio_mode & PCM_ENABLE_OUTPUT &&
- alt_stat & 0x10)
- {
- DMAbuf_outputintr (devc->playback_dev, 1);
- }
-
- if (devc->model != MD_1848 && alt_stat & 0x40) /* Timer interrupt */
- {
- devc->timer_ticks++;
-#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
- if (timer_installed == dev && devc->timer_running)
- sound_timer_interrupt ();
+ if (status & 0x01)
+ {
+ if (devc->model == MD_C930)
+ { /* 82C930 has interrupt status register in MAD16 register MC11 */
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ alt_stat = 0;
+
+ if (devc->c930_password_port)
+ outb((0xe4), devc->c930_password_port); /* Password */
+ outb((11), 0xe0e);
+ c930_stat = inb(0xe0f);
+
+ if (c930_stat & 0x04)
+ alt_stat |= 0x10; /* Playback intr */
+ if (c930_stat & 0x08)
+ alt_stat |= 0x20; /* Playback intr */
+ restore_flags(flags);
+ } else if (devc->model != MD_1848)
+ alt_stat = ad_read(devc, 24);
+
+ /* Acknowledge the intr before proceeding */
+ if (devc->model == MD_C930)
+ { /* 82C930 has interrupt status register in MAD16 register MC11 */
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ if (devc->c930_password_port)
+ outb((0xe4), devc->c930_password_port); /* Password */
+ outb((11), 0xe0e);
+ outb((~c930_stat), 0xe0f);
+ restore_flags(flags);
+ } else if (devc->model != MD_1848)
+ ad_write(devc, 24, ad_read(devc, 24) & ~alt_stat); /* Selective ack */
+
+ if (devc->open_mode & OPEN_READ && devc->audio_mode & PCM_ENABLE_INPUT && alt_stat & 0x20)
+ {
+ DMAbuf_inputintr(devc->record_dev);
+ }
+ if (devc->open_mode & OPEN_WRITE && devc->audio_mode & PCM_ENABLE_OUTPUT &&
+ alt_stat & 0x10)
+ {
+ DMAbuf_outputintr(devc->playback_dev, 1);
+ }
+ if (devc->model != MD_1848 && alt_stat & 0x40) /* Timer interrupt */
+ {
+ devc->timer_ticks++;
+#if (defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)) || defined(MODULE)
+ if (timer_installed == dev && devc->timer_running)
+ sound_timer_interrupt();
#endif
- }
- }
-
+ }
+ }
/*
* Sometimes playback or capture interrupts occur while a timer interrupt
* is being handled. The interrupt will not be retriggered if we don't
* handle it now. Check if an interrupt is still pending and restart
* the handler in this case.
*/
- if (inb (io_Status (devc)) & 0x01 && cnt++ < 4)
- {
- goto interrupt_again;
- }
+ if (inb(io_Status(devc)) & 0x01 && cnt++ < 4)
+ {
+ goto interrupt_again;
+ }
}
#ifdef DESKPROXL
@@ -2074,23 +2019,21 @@ interrupt_again: /* Jump back here if int status doesn't reset */
*/
static int
-init_deskpro (struct address_info *hw_config)
+init_deskpro(struct address_info *hw_config)
{
- unsigned char tmp;
-
- if ((tmp = inb (0xc44)) == 0xff)
- {
- DDB (printk ("init_deskpro: Dead port 0xc44\n"));
- return 0;
- }
-
- outb ((tmp | 0x04), 0xc44); /* Select bank 1 */
- if (inb (0xc44) != 0x04)
- {
- DDB (printk ("init_deskpro: Invalid bank1 signature in port 0xc44\n"));
- return 0;
- }
+ unsigned char tmp;
+ if ((tmp = inb(0xc44)) == 0xff)
+ {
+ DDB(printk("init_deskpro: Dead port 0xc44\n"));
+ return 0;
+ }
+ outb((tmp | 0x04), 0xc44); /* Select bank 1 */
+ if (inb(0xc44) != 0x04)
+ {
+ DDB(printk("init_deskpro: Invalid bank1 signature in port 0xc44\n"));
+ return 0;
+ }
/*
* OK. It looks like a Deskpro so let's proceed.
*/
@@ -2123,45 +2066,44 @@ init_deskpro (struct address_info *hw_config)
*/
#ifdef DEBUGXL
- /* Debug printing */
- printk ("Port 0xc44 (before): ");
- outb ((tmp & ~0x04), 0xc44);
- printk ("%02x ", inb (0xc44));
- outb ((tmp | 0x04), 0xc44);
- printk ("%02x\n", inb (0xc44));
+ /* Debug printing */
+ printk("Port 0xc44 (before): ");
+ outb((tmp & ~0x04), 0xc44);
+ printk("%02x ", inb(0xc44));
+ outb((tmp | 0x04), 0xc44);
+ printk("%02x\n", inb(0xc44));
#endif
- /* Set bank 1 of the register */
- tmp = 0x58; /* MSS Mode, MSS&FM decode enabled */
-
- switch (hw_config->io_base)
- {
- case 0x530:
- tmp |= 0x00;
- break;
- case 0x604:
- tmp |= 0x01;
- break;
- case 0xf40:
- tmp |= 0x02;
- break;
- case 0xe80:
- tmp |= 0x03;
- break;
- default:
- DDB (printk ("init_deskpro: Invalid MSS port %x\n",
- hw_config->io_base));
- return 0;
- }
- outb ((tmp & ~0x04), 0xc44); /* Write to bank=0 */
+ /* Set bank 1 of the register */
+ tmp = 0x58; /* MSS Mode, MSS&FM decode enabled */
+
+ switch (hw_config->io_base)
+ {
+ case 0x530:
+ tmp |= 0x00;
+ break;
+ case 0x604:
+ tmp |= 0x01;
+ break;
+ case 0xf40:
+ tmp |= 0x02;
+ break;
+ case 0xe80:
+ tmp |= 0x03;
+ break;
+ default:
+ DDB(printk("init_deskpro: Invalid MSS port %x\n", hw_config->io_base));
+ return 0;
+ }
+ outb((tmp & ~0x04), 0xc44); /* Write to bank=0 */
#ifdef DEBUGXL
- /* Debug printing */
- printk ("Port 0xc44 (after): ");
- outb ((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk ("%02x ", inb (0xc44));
- outb ((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk ("%02x\n", inb (0xc44));
+ /* Debug printing */
+ printk("Port 0xc44 (after): ");
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ printk("%02x ", inb(0xc44));
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ printk("%02x\n", inb(0xc44));
#endif
/*
@@ -2175,26 +2117,26 @@ init_deskpro (struct address_info *hw_config)
*/
#ifdef DEBUGXL
- /* Debug printing */
- printk ("Port 0xc45 (before): ");
- outb ((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk ("%02x ", inb (0xc45));
- outb ((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk ("%02x\n", inb (0xc45));
+ /* Debug printing */
+ printk("Port 0xc45 (before): ");
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ printk("%02x ", inb(0xc45));
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ printk("%02x\n", inb(0xc45));
#endif
- outb ((tmp & ~0x04), 0xc44); /* Select bank=0 */
- outb ((0x88), 0xc45); /* FM base 7:0 = 0x88 */
- outb ((tmp | 0x04), 0xc44); /* Select bank=1 */
- outb ((0x10), 0xc45); /* MSS ID = 0x10 (MSS port returns 0x04) */
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ outb((0x88), 0xc45); /* FM base 7:0 = 0x88 */
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ outb((0x10), 0xc45); /* MSS ID = 0x10 (MSS port returns 0x04) */
#ifdef DEBUGXL
- /* Debug printing */
- printk ("Port 0xc45 (after): ");
- outb ((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk ("%02x ", inb (0xc45));
- outb ((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk ("%02x\n", inb (0xc45));
+ /* Debug printing */
+ printk("Port 0xc45 (after): ");
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ printk("%02x ", inb(0xc45));
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ printk("%02x\n", inb(0xc45));
#endif
@@ -2206,26 +2148,26 @@ init_deskpro (struct address_info *hw_config)
*/
#ifdef DEBUGXL
- /* Debug printing */
- printk ("Port 0xc46 (before): ");
- outb ((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk ("%02x ", inb (0xc46));
- outb ((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk ("%02x\n", inb (0xc46));
+ /* Debug printing */
+ printk("Port 0xc46 (before): ");
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ printk("%02x ", inb(0xc46));
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ printk("%02x\n", inb(0xc46));
#endif
- outb ((tmp & ~0x04), 0xc44); /* Select bank=0 */
- outb ((0x03), 0xc46); /* FM base 15:8 = 0x03 */
- outb ((tmp | 0x04), 0xc44); /* Select bank=1 */
- outb ((0x11), 0xc46); /* ASIC ID = 0x11 */
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ outb((0x03), 0xc46); /* FM base 15:8 = 0x03 */
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ outb((0x11), 0xc46); /* ASIC ID = 0x11 */
#ifdef DEBUGXL
- /* Debug printing */
- printk ("Port 0xc46 (after): ");
- outb ((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk ("%02x ", inb (0xc46));
- outb ((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk ("%02x\n", inb (0xc46));
+ /* Debug printing */
+ printk("Port 0xc46 (after): ");
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ printk("%02x ", inb(0xc46));
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ printk("%02x\n", inb(0xc46));
#endif
/*
@@ -2236,26 +2178,26 @@ init_deskpro (struct address_info *hw_config)
*/
#ifdef DEBUGXL
- /* Debug printing */
- printk ("Port 0xc47 (before): ");
- outb ((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk ("%02x ", inb (0xc47));
- outb ((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk ("%02x\n", inb (0xc47));
+ /* Debug printing */
+ printk("Port 0xc47 (before): ");
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ printk("%02x ", inb(0xc47));
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ printk("%02x\n", inb(0xc47));
#endif
- outb ((tmp & ~0x04), 0xc44); /* Select bank=0 */
- outb ((0x7c), 0xc47); /* FM decode enable bits = 0x7c */
- outb ((tmp | 0x04), 0xc44); /* Select bank=1 */
- outb ((0x00), 0xc47); /* Reserved bank1 = 0x00 */
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ outb((0x7c), 0xc47); /* FM decode enable bits = 0x7c */
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ outb((0x00), 0xc47); /* Reserved bank1 = 0x00 */
#ifdef DEBUGXL
- /* Debug printing */
- printk ("Port 0xc47 (after): ");
- outb ((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk ("%02x ", inb (0xc47));
- outb ((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk ("%02x\n", inb (0xc47));
+ /* Debug printing */
+ printk("Port 0xc47 (after): ");
+ outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
+ printk("%02x ", inb(0xc47));
+ outb((tmp | 0x04), 0xc44); /* Select bank=1 */
+ printk("%02x\n", inb(0xc47));
#endif
/*
@@ -2263,210 +2205,202 @@ init_deskpro (struct address_info *hw_config)
*/
#ifdef DEBUGXL
- printk ("Port 0xc6f (before) = %02x\n", inb (0xc6f));
+ printk("Port 0xc6f (before) = %02x\n", inb(0xc6f));
#endif
- outb ((0x80), 0xc6f);
+ outb((0x80), 0xc6f);
#ifdef DEBUGXL
- printk ("Port 0xc6f (after) = %02x\n", inb (0xc6f));
+ printk("Port 0xc6f (after) = %02x\n", inb(0xc6f));
#endif
- return 1;
+ return 1;
}
#endif
int
-probe_ms_sound (struct address_info *hw_config)
+probe_ms_sound(struct address_info *hw_config)
{
- unsigned char tmp;
-
- DDB (printk ("Entered probe_ms_sound(%x, %d)\n", hw_config->io_base, hw_config->card_subtype));
-
- if (check_region (hw_config->io_base, 8))
- {
- printk ("MSS: I/O port conflict\n");
- return 0;
- }
+ unsigned char tmp;
- if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */
- {
- /* check_opl3(0x388, hw_config); */
- return ad1848_detect (hw_config->io_base + 4, NULL, hw_config->osp);
- }
+ DDB(printk("Entered probe_ms_sound(%x, %d)\n", hw_config->io_base, hw_config->card_subtype));
+ if (check_region(hw_config->io_base, 8))
+ {
+ printk("MSS: I/O port conflict\n");
+ return 0;
+ }
+ if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */
+ {
+ /* check_opl3(0x388, hw_config); */
+ return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp);
+ }
#ifdef DESKPROXL
- if (hw_config->card_subtype == 2) /* Compaq Deskpro XL */
- {
- if (!init_deskpro (hw_config))
- return 0;
- }
+ if (hw_config->card_subtype == 2) /* Compaq Deskpro XL */
+ {
+ if (!init_deskpro(hw_config))
+ return 0;
+ }
#endif
- /*
- * Check if the IO port returns valid signature. The original MS Sound
- * system returns 0x04 while some cards (AudioTrix Pro for example)
- * return 0x00 or 0x0f.
- */
+ /*
+ * Check if the IO port returns valid signature. The original MS Sound
+ * system returns 0x04 while some cards (AudioTrix Pro for example)
+ * return 0x00 or 0x0f.
+ */
- if ((tmp = inb (hw_config->io_base + 3)) == 0xff) /* Bus float */
- {
- int ret;
+ if ((tmp = inb(hw_config->io_base + 3)) == 0xff) /* Bus float */
+ {
+ int ret;
- DDB (printk ("I/O address is inactive (%x)\n", tmp));
- if (!(ret = ad1848_detect (hw_config->io_base + 4, NULL, hw_config->osp)))
- return 0;
- return 1;
- }
- DDB (printk ("MSS signature = %x\n", tmp & 0x3f));
- if ((tmp & 0x3f) != 0x04 &&
- (tmp & 0x3f) != 0x0f &&
- (tmp & 0x3f) != 0x00)
- {
- int ret;
-
- DDB (printk ("No MSS signature detected on port 0x%x (0x%x)\n",
- hw_config->io_base, (int) inb (hw_config->io_base + 3)));
- DDB (printk ("Trying to detect codec anyway but IRQ/DMA may not work\n"));
- if (!(ret = ad1848_detect (hw_config->io_base + 4, NULL, hw_config->osp)))
- return 0;
+ DDB(printk("I/O address is inactive (%x)\n", tmp));
+ if (!(ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp)))
+ return 0;
+ return 1;
+ }
+ DDB(printk("MSS signature = %x\n", tmp & 0x3f));
+ if ((tmp & 0x3f) != 0x04 &&
+ (tmp & 0x3f) != 0x0f &&
+ (tmp & 0x3f) != 0x00)
+ {
+ int ret;
- hw_config->card_subtype = 1;
- return 1;
- }
-
- if (hw_config->irq > 11)
- {
- printk ("MSS: Bad IRQ %d\n", hw_config->irq);
- return 0;
- }
-
- if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
- {
- printk ("MSS: Bad DMA %d\n", hw_config->dma);
- return 0;
- }
-
- /*
- * Check that DMA0 is not in use with a 8 bit board.
- */
-
- if (hw_config->dma == 0 && inb (hw_config->io_base + 3) & 0x80)
- {
- printk ("MSS: Can't use DMA0 with a 8 bit card/slot\n");
- return 0;
- }
-
- if (hw_config->irq > 7 && hw_config->irq != 9 && inb (hw_config->io_base + 3) & 0x80)
- {
- printk ("MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq);
- return 0;
- }
-
- return ad1848_detect (hw_config->io_base + 4, NULL, hw_config->osp);
+ MDB(printk("No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, (int) inb(hw_config->io_base + 3)));
+ DDB(printk("Trying to detect codec anyway but IRQ/DMA may not work\n"));
+ if (!(ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp)))
+ return 0;
+
+ hw_config->card_subtype = 1;
+ return 1;
+ }
+ if (hw_config->irq > 11)
+ {
+ printk("MSS: Bad IRQ %d\n", hw_config->irq);
+ return 0;
+ }
+ if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
+ {
+ printk("MSS: Bad DMA %d\n", hw_config->dma);
+ return 0;
+ }
+ /*
+ * Check that DMA0 is not in use with a 8 bit board.
+ */
+
+ if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80)
+ {
+ printk("MSS: Can't use DMA0 with a 8 bit card/slot\n");
+ return 0;
+ }
+ if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80)
+ {
+ printk("MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq);
+ return 0;
+ }
+ return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp);
}
void
-attach_ms_sound (struct address_info *hw_config)
+attach_ms_sound(struct address_info *hw_config)
{
- static char interrupt_bits[12] =
- {
- -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20
- };
- char bits, dma2_bit = 0;
+ static char interrupt_bits[12] =
+ {
+ -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20
+ };
+ char bits, dma2_bit = 0;
- static char dma_bits[4] =
- {
- 1, 2, 0, 3
- };
-
- int config_port = hw_config->io_base + 0;
- int version_port = hw_config->io_base + 3;
- int dma = hw_config->dma;
- int dma2 = hw_config->dma2;
-
- if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */
- {
- ad1848_init ("MS Sound System", hw_config->io_base + 4,
- hw_config->irq,
- hw_config->dma,
- hw_config->dma2, 0, hw_config->osp);
- request_region (hw_config->io_base, 4, "WSS config");
- return;
- }
-
- /*
- * Set the IRQ and DMA addresses.
- */
-
- bits = interrupt_bits[hw_config->irq];
- if (bits == -1)
- {
- printk ("MSS: Bad IRQ %d\n", hw_config->irq);
- return;
- }
-
- outb ((bits | 0x40), config_port);
- if ((inb (version_port) & 0x40) == 0)
- printk ("[MSS: IRQ Conflict?]");
+ static char dma_bits[4] =
+ {
+ 1, 2, 0, 3
+ };
+
+ int config_port = hw_config->io_base + 0;
+ int version_port = hw_config->io_base + 3;
+ int dma = hw_config->dma;
+ int dma2 = hw_config->dma2;
+
+ if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */
+ {
+ hw_config->slots[0] = ad1848_init("MS Sound System", hw_config->io_base + 4,
+ hw_config->irq,
+ hw_config->dma,
+ hw_config->dma2, 0, hw_config->osp);
+ request_region(hw_config->io_base, 4, "WSS config");
+ return;
+ }
+ /*
+ * Set the IRQ and DMA addresses.
+ */
+
+ bits = interrupt_bits[hw_config->irq];
+ if (bits == -1)
+ {
+ printk("MSS: Bad IRQ %d\n", hw_config->irq);
+ return;
+ }
+ outb((bits | 0x40), config_port);
+ if ((inb(version_port) & 0x40) == 0)
+ printk("[MSS: IRQ Conflict?]");
/*
* Handle the capture DMA channel
*/
- if (dma2 != -1 && dma2 != dma)
- {
- if (!((dma == 0 && dma2 == 1) ||
- (dma == 1 && dma2 == 0) ||
- (dma == 3 && dma2 == 0)))
- { /* Unsupported combination. Try to swap channels */
- int tmp = dma;
+ if (dma2 != -1 && dma2 != dma)
+ {
+ if (!((dma == 0 && dma2 == 1) ||
+ (dma == 1 && dma2 == 0) ||
+ (dma == 3 && dma2 == 0)))
+ { /* Unsupported combination. Try to swap channels */
+ int tmp = dma;
+
+ dma = dma2;
+ dma2 = tmp;
+ }
+ if ((dma == 0 && dma2 == 1) ||
+ (dma == 1 && dma2 == 0) ||
+ (dma == 3 && dma2 == 0))
+ {
+ dma2_bit = 0x04; /* Enable capture DMA */
+ } else
+ {
+ printk("MSS: Invalid capture DMA\n");
+ dma2 = dma;
+ }
+ } else
+ {
+ dma2 = dma;
+ }
- dma = dma2;
- dma2 = tmp;
- }
+ hw_config->dma = dma;
+ hw_config->dma2 = dma2;
- if ((dma == 0 && dma2 == 1) ||
- (dma == 1 && dma2 == 0) ||
- (dma == 3 && dma2 == 0))
- {
- dma2_bit = 0x04; /* Enable capture DMA */
- }
- else
- {
- printk ("MSS: Invalid capture DMA\n");
- dma2 = dma;
- }
- }
- else
- {
- dma2 = dma;
- }
-
- hw_config->dma = dma;
- hw_config->dma2 = dma2;
-
- outb ((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */
-
- ad1848_init ("MSS audio codec", hw_config->io_base + 4,
- hw_config->irq,
- dma,
- dma2, 0,
- hw_config->osp);
- request_region (hw_config->io_base, 4, "WSS config");
+ outb((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */
+
+ hw_config->slots[0] = ad1848_init("MSS audio codec", hw_config->io_base + 4,
+ hw_config->irq,
+ dma,
+ dma2, 0,
+ hw_config->osp);
+ request_region(hw_config->io_base, 4, "WSS config");
}
void
-unload_ms_sound (struct address_info *hw_config)
+unload_ms_sound(struct address_info *hw_config)
{
- ad1848_unload (hw_config->io_base + 4,
- hw_config->irq,
- hw_config->dma,
- hw_config->dma, 0);
- release_region (hw_config->io_base, 4);
+ int mixer = audio_devs[hw_config->slots[0]]->mixer_dev;
+ ad1848_unload(hw_config->io_base + 4,
+ hw_config->irq,
+ hw_config->dma,
+ hw_config->dma, 0);
+ if(mixer>=0)
+ sound_unload_mixerdev(mixer);
+ sound_unload_audiodev(hw_config->slots[0]);
+ release_region(hw_config->io_base, 4);
+
}
-#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
+#if (defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)) || defined(MODULE)
/*
* Timer stuff (for /dev/music).
*/
@@ -2474,15 +2408,15 @@ unload_ms_sound (struct address_info *hw_config)
static unsigned int current_interval = 0;
static unsigned int
-ad1848_tmr_start (int dev, unsigned int usecs)
+ad1848_tmr_start(int dev, unsigned int usecs)
{
- unsigned long flags;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- unsigned long xtal_nsecs; /* nanoseconds per xtal oscillator tick */
- unsigned long divider;
+ unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ unsigned long xtal_nsecs; /* nanoseconds per xtal oscillator tick */
+ unsigned long divider;
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
/*
* Length of the timer interval (in nanoseconds) depends on the
@@ -2495,91 +2429,162 @@ ad1848_tmr_start (int dev, unsigned int usecs)
* the timer divider.
*/
- if (devc->model == MD_1845)
- xtal_nsecs = 10050;
- else if (ad_read (devc, 8) & 0x01)
- xtal_nsecs = 9920;
- else
- xtal_nsecs = 9969;
+ if (devc->model == MD_1845)
+ xtal_nsecs = 10050;
+ else if (ad_read(devc, 8) & 0x01)
+ xtal_nsecs = 9920;
+ else
+ xtal_nsecs = 9969;
- divider = (usecs * 1000 + xtal_nsecs / 2) / xtal_nsecs;
+ divider = (usecs * 1000 + xtal_nsecs / 2) / xtal_nsecs;
- if (divider < 100) /* Don't allow shorter intervals than about 1ms */
- divider = 100;
+ if (divider < 100) /* Don't allow shorter intervals than about 1ms */
+ divider = 100;
- if (divider > 65535) /* Overflow check */
- divider = 65535;
+ if (divider > 65535) /* Overflow check */
+ divider = 65535;
- ad_write (devc, 21, (divider >> 8) & 0xff); /* Set upper bits */
- ad_write (devc, 20, divider & 0xff); /* Set lower bits */
- ad_write (devc, 16, ad_read (devc, 16) | 0x40); /* Start the timer */
- devc->timer_running = 1;
- restore_flags (flags);
+ ad_write(devc, 21, (divider >> 8) & 0xff); /* Set upper bits */
+ ad_write(devc, 20, divider & 0xff); /* Set lower bits */
+ ad_write(devc, 16, ad_read(devc, 16) | 0x40); /* Start the timer */
+ devc->timer_running = 1;
+ restore_flags(flags);
- return current_interval = (divider * xtal_nsecs + 500) / 1000;
+ return current_interval = (divider * xtal_nsecs + 500) / 1000;
}
static void
-ad1848_tmr_reprogram (int dev)
+ad1848_tmr_reprogram(int dev)
{
/*
* Audio driver has changed sampling rate so that a different xtal
* oscillator was selected. We have to reprogram the timer rate.
*/
- ad1848_tmr_start (dev, current_interval);
- sound_timer_syncinterval (current_interval);
+ ad1848_tmr_start(dev, current_interval);
+ sound_timer_syncinterval(current_interval);
}
static void
-ad1848_tmr_disable (int dev)
+ad1848_tmr_disable(int dev)
{
- unsigned long flags;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
-
- save_flags (flags);
- cli ();
- ad_write (devc, 16, ad_read (devc, 16) & ~0x40);
- devc->timer_running = 0;
- restore_flags (flags);
+ unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ save_flags(flags);
+ cli();
+ ad_write(devc, 16, ad_read(devc, 16) & ~0x40);
+ devc->timer_running = 0;
+ restore_flags(flags);
}
static void
-ad1848_tmr_restart (int dev)
+ad1848_tmr_restart(int dev)
{
- unsigned long flags;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+ unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- if (current_interval == 0)
- return;
+ if (current_interval == 0)
+ return;
- save_flags (flags);
- cli ();
- ad_write (devc, 16, ad_read (devc, 16) | 0x40);
- devc->timer_running = 1;
- restore_flags (flags);
+ save_flags(flags);
+ cli();
+ ad_write(devc, 16, ad_read(devc, 16) | 0x40);
+ devc->timer_running = 1;
+ restore_flags(flags);
}
static struct sound_lowlev_timer ad1848_tmr =
{
- 0,
- 2,
- ad1848_tmr_start,
- ad1848_tmr_disable,
- ad1848_tmr_restart
+ 0,
+ 2,
+ ad1848_tmr_start,
+ ad1848_tmr_disable,
+ ad1848_tmr_restart
};
static int
-ad1848_tmr_install (int dev)
+ad1848_tmr_install(int dev)
{
- if (timer_installed != -1)
- return 0; /* Don't install another timer */
+ if (timer_installed != -1)
+ return 0; /* Don't install another timer */
- timer_installed = ad1848_tmr.dev = dev;
- sound_timer_init (&ad1848_tmr, audio_devs[dev]->name);
+ timer_installed = ad1848_tmr.dev = dev;
+ sound_timer_init(&ad1848_tmr, audio_devs[dev]->name);
- return 1;
+ return 1;
}
#endif
+
+
+EXPORT_SYMBOL(ad1848_detect);
+EXPORT_SYMBOL(ad1848_init);
+EXPORT_SYMBOL(ad1848_unload);
+EXPORT_SYMBOL(adintr);
+EXPORT_SYMBOL(probe_ms_sound);
+EXPORT_SYMBOL(attach_ms_sound);
+EXPORT_SYMBOL(unload_ms_sound);
+
+#ifdef MODULE
+
+MODULE_PARM(io, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(dma, "i");
+MODULE_PARM(dma2, "i");
+MODULE_PARM(type, "i");
+
+int io = -1;
+int irq = -1;
+int dma = -1;
+int dma2 = -1;
+int type = 0;
+
+static int loaded = 0;
+
+struct address_info hw_config;
+
+
+int init_module(void)
+{
+ printk("ad1848/cs4248 codec driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+ if(io!=-1)
+ {
+ if(irq == -1 || dma == -1)
+ {
+ printk(KERN_WARNING "ad1848: must give I/O , IRQ and DMA.\n");
+ return -EINVAL;
+ }
+ hw_config.irq = irq;
+ hw_config.io_base = io;
+ hw_config.dma = dma;
+ hw_config.dma2 = dma2;
+ hw_config.card_subtype = type;
+ if(!probe_ms_sound(&hw_config))
+ return -ENODEV;
+ attach_ms_sound(&hw_config);
+ loaded=1;
+ }
+ SOUND_LOCK;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ SOUND_LOCK_END;
+ if(loaded)
+ unload_ms_sound(&hw_config);
+/* unregister_symtab(&ad1848_syms); */
+}
+
+#else
+
+void
+export_ad1848_syms(void)
+{
+ register_symtab(&ad1848_syms);
+}
+
+#endif
#endif
diff --git a/drivers/sound/adlib_card.c b/drivers/sound/adlib_card.c
index 227f140b6..a8affde7e 100644
--- a/drivers/sound/adlib_card.c
+++ b/drivers/sound/adlib_card.c
@@ -12,39 +12,62 @@
* for more info.
*/
#include <linux/config.h>
+#include <linux/module.h>
#include "sound_config.h"
+#include "soundmodule.h"
-#if defined(CONFIG_YM3812)
+#if defined(CONFIG_YM3812) || defined(MODULE)
-void
-attach_adlib_card (struct address_info *hw_config)
+void attach_adlib_card(struct address_info *hw_config)
{
-
- opl3_init (hw_config->io_base, hw_config->osp);
- request_region (hw_config->io_base, 4, "OPL3/OPL2");
+ hw_config->slots[0] = opl3_init(hw_config->io_base, hw_config->osp);
+ request_region(hw_config->io_base, 4, "OPL3/OPL2");
}
-int
-probe_adlib (struct address_info *hw_config)
+int probe_adlib(struct address_info *hw_config)
{
- if (check_region (hw_config->io_base, 4))
- {
- DDB (printk ("opl3.c: I/O port %x already in use\n",
- hw_config->io_base));
- return 0;
- }
+ if (check_region(hw_config->io_base, 4)) {
+ DDB(printk("opl3.c: I/O port %x already in use\n", hw_config->io_base));
+ return 0;
+ }
+ return opl3_detect(hw_config->io_base, hw_config->osp);
+}
- return opl3_detect (hw_config->io_base, hw_config->osp);
+void unload_adlib(struct address_info *hw_config)
+{
+ release_region(hw_config->io_base, 4);
+ sound_unload_synthdev(hw_config->slots[0]);
}
-void
-unload_adlib (struct address_info *hw_config)
+#ifdef MODULE
+
+int io = -1;
+MODULE_PARM(io, "i");
+
+struct address_info cfg;
+
+int init_module(void)
{
- release_region (hw_config->io_base, 4);
+ if (io == -1) {
+ printk("adlib: must specify I/O address.\n");
+ return -EINVAL;
+ }
+ cfg.io_base = io;
+ if (probe_adlib(&cfg) == 0)
+ return -ENODEV;
+ attach_adlib_card(&cfg);
+ SOUND_LOCK;
+ return 0;
}
+void cleanup_module(void)
+{
+ unload_adlib(&cfg);
+ SOUND_LOCK_END;
+}
#endif
+#endif
diff --git a/drivers/sound/audio.c b/drivers/sound/audio.c
index d1ba581b1..24bbaa0e0 100644
--- a/drivers/sound/audio.c
+++ b/drivers/sound/audio.c
@@ -16,7 +16,7 @@
#include "sound_config.h"
-#ifdef CONFIG_AUDIO
+#if defined(CONFIG_AUDIO) || defined(MODULE)
#include "ulaw.h"
#include "coproc.h"
@@ -30,7 +30,7 @@ static int dev_nblock[MAX_AUDIO_DEV]; /* 1 if in nonblocking mode */
#define AM_NONE 0
#define AM_WRITE OPEN_WRITE
#define AM_READ OPEN_READ
-static int dma_ioctl (int dev, unsigned int cmd, caddr_t arg);
+int dma_ioctl(int dev, unsigned int cmd, caddr_t arg);
static int local_format[MAX_AUDIO_DEV], audio_format[MAX_AUDIO_DEV];
@@ -38,560 +38,546 @@ static int local_conversion[MAX_AUDIO_DEV];
#define CNV_MU_LAW 0x00000001
static int
-set_format (int dev, int fmt)
+set_format(int dev, int fmt)
{
- if (fmt != AFMT_QUERY)
- {
- local_conversion[dev] = 0;
-
- if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */
- if (fmt == AFMT_MU_LAW)
+ if (fmt != AFMT_QUERY)
{
- fmt = AFMT_U8;
- local_conversion[dev] = CNV_MU_LAW;
- }
- else
- fmt = AFMT_U8; /* This is always supported */
-
- audio_format[dev] = audio_devs[dev]->d->set_bits (dev, fmt);
- local_format[dev] = fmt;
- }
- else
- return local_format[dev];
-
- return audio_format[dev];
+ local_conversion[dev] = 0;
+
+ if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */
+ if (fmt == AFMT_MU_LAW)
+ {
+ fmt = AFMT_U8;
+ local_conversion[dev] = CNV_MU_LAW;
+ } else
+ fmt = AFMT_U8; /* This is always supported */
+
+ audio_format[dev] = audio_devs[dev]->d->set_bits(dev, fmt);
+ local_format[dev] = fmt;
+ } else
+ return local_format[dev];
+
+ return local_format[dev];
}
int
-audio_open (int dev, struct fileinfo *file)
+audio_open(int dev, struct fileinfo *file)
{
- int ret;
- int bits;
- int dev_type = dev & 0x0f;
- int mode = file->mode & O_ACCMODE;
-
- dev = dev >> 4;
+ int ret;
+ int bits;
+ int dev_type = dev & 0x0f;
+ int mode = file->mode & O_ACCMODE;
- if (dev_type == SND_DEV_DSP16)
- bits = 16;
- else
- bits = 8;
+ dev = dev >> 4;
- if (dev < 0 || dev >= num_audiodevs)
- return -ENXIO;
+ if (dev_type == SND_DEV_DSP16)
+ bits = 16;
+ else
+ bits = 8;
- if ((ret = DMAbuf_open (dev, mode)) < 0)
- return ret;
+ if (dev < 0 || dev >= num_audiodevs)
+ return -ENXIO;
- if (audio_devs[dev]->coproc)
- if ((ret = audio_devs[dev]->coproc->
- open (audio_devs[dev]->coproc->devc, COPR_PCM)) < 0)
- {
- audio_release (dev, file);
- printk ("Sound: Can't access coprocessor device\n");
+ if ((ret = DMAbuf_open(dev, mode)) < 0)
+ return ret;
- return ret;
- }
+ if (audio_devs[dev]->coproc)
+ if ((ret = audio_devs[dev]->coproc->
+ open(audio_devs[dev]->coproc->devc, COPR_PCM)) < 0)
+ {
+ audio_release(dev, file);
+ printk("Sound: Can't access coprocessor device\n");
- local_conversion[dev] = 0;
+ return ret;
+ }
+ local_conversion[dev] = 0;
- if (dev_type == SND_DEV_AUDIO)
- {
- set_format (dev, AFMT_MU_LAW);
- }
- else
- set_format (dev, bits);
+ if (dev_type == SND_DEV_AUDIO)
+ {
+ set_format(dev, AFMT_MU_LAW);
+ } else
+ set_format(dev, bits);
- audio_mode[dev] = AM_NONE;
- dev_nblock[dev] = 0;
+ audio_mode[dev] = AM_NONE;
+ dev_nblock[dev] = 0;
- return ret;
+ return ret;
}
static void
-sync_output (int dev)
+sync_output(int dev)
{
- int p, i;
- int l;
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
- if (dmap->fragment_size <= 0)
- return;
- dmap->flags |= DMA_POST;
+ int p, i;
+ int l;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
- /* Align the write pointer with fragment boundaries */
- if ((l = dmap->user_counter % dmap->fragment_size) > 0)
- {
- int len;
- unsigned long offs = dmap->user_counter % dmap->bytes_in_use;
+ if (dmap->fragment_size <= 0)
+ return;
+ dmap->flags |= DMA_POST;
- len = dmap->fragment_size - l;
- memset (dmap->raw_buf + offs, dmap->neutral_byte, len);
- DMAbuf_move_wrpointer (dev, len);
- }
+ /* Align the write pointer with fragment boundaries */
+ if ((l = dmap->user_counter % dmap->fragment_size) > 0)
+ {
+ int len;
+ unsigned long offs = dmap->user_counter % dmap->bytes_in_use;
+ len = dmap->fragment_size - l;
+ memset(dmap->raw_buf + offs, dmap->neutral_byte, len);
+ DMAbuf_move_wrpointer(dev, len);
+ }
/*
* Clean all unused buffer fragments.
*/
- p = dmap->qtail;
- dmap->flags |= DMA_POST;
-
- for (i = dmap->qlen + 1; i < dmap->nbufs; i++)
- {
- p = (p + 1) % dmap->nbufs;
- if (((dmap->raw_buf + p * dmap->fragment_size) + dmap->fragment_size) >
- (dmap->raw_buf + dmap->buffsize))
- printk ("audio: Buffer error 2\n");
+ p = dmap->qtail;
+ dmap->flags |= DMA_POST;
- memset (dmap->raw_buf + p * dmap->fragment_size,
- dmap->neutral_byte,
- dmap->fragment_size);
- }
+ for (i = dmap->qlen + 1; i < dmap->nbufs; i++)
+ {
+ p = (p + 1) % dmap->nbufs;
+ if (((dmap->raw_buf + p * dmap->fragment_size) + dmap->fragment_size) >
+ (dmap->raw_buf + dmap->buffsize))
+ printk("audio: Buffer error 2\n");
+
+ memset(dmap->raw_buf + p * dmap->fragment_size,
+ dmap->neutral_byte,
+ dmap->fragment_size);
+ }
- dmap->flags |= DMA_DIRTY;
+ dmap->flags |= DMA_DIRTY;
}
void
-audio_release (int dev, struct fileinfo *file)
+audio_release(int dev, struct fileinfo *file)
{
- int mode;
+ int mode;
- dev = dev >> 4;
- mode = file->mode & O_ACCMODE;
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
- audio_devs[dev]->dmap_out->closing = 1;
- audio_devs[dev]->dmap_in->closing = 1;
+ audio_devs[dev]->dmap_out->closing = 1;
+ audio_devs[dev]->dmap_in->closing = 1;
- sync_output (dev);
+ sync_output(dev);
- if (audio_devs[dev]->coproc)
- audio_devs[dev]->coproc->close (audio_devs[dev]->coproc->devc, COPR_PCM);
- DMAbuf_release (dev, mode);
+ if (audio_devs[dev]->coproc)
+ audio_devs[dev]->coproc->close(audio_devs[dev]->coproc->devc, COPR_PCM);
+ DMAbuf_release(dev, mode);
}
#if defined(NO_INLINE_ASM) || !defined(i386)
static void
-translate_bytes (const unsigned char *table, unsigned char *buff, int n)
+translate_bytes(const unsigned char *table, unsigned char *buff, int n)
{
- unsigned long i;
+ unsigned long i;
- if (n <= 0)
- return;
+ if (n <= 0)
+ return;
- for (i = 0; i < n; ++i)
- buff[i] = table[buff[i]];
+ for (i = 0; i < n; ++i)
+ buff[i] = table[buff[i]];
}
#else
extern inline void
-translate_bytes (const void *table, void *buff, int n)
+translate_bytes(const void *table, void *buff, int n)
{
- if (n > 0)
- {
- __asm__ ("cld\n"
- "1:\tlodsb\n\t"
- "xlatb\n\t"
- "stosb\n\t"
- "loop 1b\n\t":
- : "b" ((long) table), "c" (n), "D" ((long) buff), "S" ((long) buff)
- : "bx", "cx", "di", "si", "ax");
- }
+ if (n > 0)
+ {
+ __asm__("cld\n"
+ "1:\tlodsb\n\t"
+ "xlatb\n\t"
+ "stosb\n\t"
+ "loop 1b\n\t":
+ : "b"((long) table), "c"(n), "D"((long) buff), "S"((long) buff)
+ : "bx", "cx", "di", "si", "ax");
+ }
}
#endif
int
-audio_write (int dev, struct fileinfo *file, const char *buf, int count)
-{
- int c, p, l, buf_size;
- int err;
- char *dma_buf;
-
- dev = dev >> 4;
-
- p = 0;
- c = count;
-
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
- return -EPERM;
-
- if (audio_devs[dev]->flags & DMA_DUPLEX)
- audio_mode[dev] |= AM_WRITE;
- else
- audio_mode[dev] = AM_WRITE;
-
- if (!count) /* Flush output */
- {
- sync_output (dev);
- return 0;
- }
-
- while (c)
- {
- if ((err = DMAbuf_getwrbuffer (dev, &dma_buf, &buf_size, dev_nblock[dev])) < 0)
- {
- /* Handle nonblocking mode */
- if (dev_nblock[dev] && err == -EAGAIN)
- return p; /* No more space. Return # of accepted bytes */
- return err;
- }
-
- l = c;
-
- if (l > buf_size)
- l = buf_size;
-
- if (!audio_devs[dev]->d->copy_user)
- {
- if ((dma_buf + l) >
- (audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->dmap_out->buffsize))
- printk ("audio: Buffer error 3 (%lx,%d), (%lx, %d)\n",
- (long) dma_buf, l,
- (long) audio_devs[dev]->dmap_out->raw_buf,
- (int) audio_devs[dev]->dmap_out->buffsize);
- if (dma_buf < audio_devs[dev]->dmap_out->raw_buf)
- printk ("audio: Buffer error 13\n");
- copy_from_user (dma_buf, &(buf)[p], l);
- }
- else
- audio_devs[dev]->d->copy_user (dev,
- dma_buf, 0, buf, p, l);
-
- if (local_conversion[dev] & CNV_MU_LAW)
- {
- /*
- * This just allows interrupts while the conversion is running
- */
- sti ();
- translate_bytes (ulaw_dsp, (unsigned char *) dma_buf, l);
- }
-
- c -= l;
- p += l;
- DMAbuf_move_wrpointer (dev, l);
-
- }
-
- return count;
-}
-
-int
-audio_read (int dev, struct fileinfo *file, char *buf, int count)
+audio_write(int dev, struct fileinfo *file, const char *buf, int count)
{
- int c, p, l;
- char *dmabuf;
- int buf_no;
-
- dev = dev >> 4;
- p = 0;
- c = count;
-
- if (!(audio_devs[dev]->open_mode & OPEN_READ))
- return -EPERM;
-
- if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
- {
- sync_output (dev);
- }
-
- if (audio_devs[dev]->flags & DMA_DUPLEX)
- audio_mode[dev] |= AM_READ;
- else
- audio_mode[dev] = AM_READ;
+ int c, p, l, buf_size;
+ int err;
+ char *dma_buf;
- while (c)
- {
- if ((buf_no = DMAbuf_getrdbuffer (dev, &dmabuf, &l,
- dev_nblock[dev])) < 0)
- {
- /* Nonblocking mode handling. Return current # of bytes */
+ dev = dev >> 4;
- if (dev_nblock[dev] && buf_no == -EAGAIN)
- return p;
+ p = 0;
+ c = count;
- return buf_no;
- }
-
- if (l > c)
- l = c;
-
- /*
- * Insert any local processing here.
- */
-
- if (local_conversion[dev] & CNV_MU_LAW)
- {
- /*
- * This just allows interrupts while the conversion is running
- */
- sti ();
-
- translate_bytes (dsp_ulaw, (unsigned char *) dmabuf, l);
- }
-
- {
- char *fixit = dmabuf;
+ if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+ return -EPERM;
- copy_to_user (&(buf)[p], fixit, l);
- };
+ if (audio_devs[dev]->flags & DMA_DUPLEX)
+ audio_mode[dev] |= AM_WRITE;
+ else
+ audio_mode[dev] = AM_WRITE;
- DMAbuf_rmchars (dev, buf_no, l);
+ if (!count) /* Flush output */
+ {
+ sync_output(dev);
+ return 0;
+ }
+ while (c)
+ {
+ if ((err = DMAbuf_getwrbuffer(dev, &dma_buf, &buf_size, dev_nblock[dev])) < 0)
+ {
+ /* Handle nonblocking mode */
+ if (dev_nblock[dev] && err == -EAGAIN)
+ return p; /* No more space. Return # of accepted bytes */
+ return err;
+ }
+ l = c;
+
+ if (l > buf_size)
+ l = buf_size;
+
+ if (!audio_devs[dev]->d->copy_user)
+ {
+ if ((dma_buf + l) >
+ (audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->dmap_out->buffsize))
+ {
+ printk("audio: Buffer error 3 (%lx,%d), (%lx, %d)\n", (long) dma_buf, l, (long) audio_devs[dev]->dmap_out->raw_buf, (int) audio_devs[dev]->dmap_out->buffsize);
+ return -EDOM;
+ }
+ if (dma_buf < audio_devs[dev]->dmap_out->raw_buf)
+ {
+ printk("audio: Buffer error 13 (%lx<%lx)\n", (long) dma_buf, (long) audio_devs[dev]->dmap_out->raw_buf);
+ return -EDOM;
+ }
+ copy_from_user(dma_buf, &(buf)[p], l);
+ } else
+ audio_devs[dev]->d->copy_user(dev,
+ dma_buf, 0, buf, p, l);
+
+ if (local_conversion[dev] & CNV_MU_LAW)
+ {
+ /*
+ * This just allows interrupts while the conversion is running
+ */
+ sti();
+ translate_bytes(ulaw_dsp, (unsigned char *) dma_buf, l);
+ }
+ c -= l;
+ p += l;
+ DMAbuf_move_wrpointer(dev, l);
- p += l;
- c -= l;
- }
+ }
- return count - c;
+ return count;
}
int
-audio_ioctl (int dev, struct fileinfo *file_must_not_be_used,
- unsigned int cmd, caddr_t arg)
+audio_read(int dev, struct fileinfo *file, char *buf, int count)
{
- int val;
-
- /* printk("audio_ioctl(%x, %x)\n", (int)cmd, (int)arg); */
-
- dev = dev >> 4;
-
- if (((cmd >> 8) & 0xff) == 'C')
- {
- if (audio_devs[dev]->coproc) /* Coprocessor ioctl */
- return audio_devs[dev]->coproc->ioctl (audio_devs[dev]->coproc->devc, cmd, arg, 0);
- else
- printk ("/dev/dsp%d: No coprocessor for this device\n", dev);
-
- return -ENXIO;
- }
- else
- switch (cmd)
- {
- case SNDCTL_DSP_SYNC:
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
- return 0;
-
- if (audio_devs[dev]->dmap_out->fragment_size == 0)
- return 0;
- sync_output (dev);
- DMAbuf_sync (dev);
- DMAbuf_reset (dev);
- return 0;
- break;
-
- case SNDCTL_DSP_POST:
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
- return 0;
- if (audio_devs[dev]->dmap_out->fragment_size == 0)
- return 0;
- audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY;
- sync_output (dev);
- dma_ioctl (dev, SNDCTL_DSP_POST, (caddr_t) 0);
- return 0;
- break;
-
- case SNDCTL_DSP_RESET:
- audio_mode[dev] = AM_NONE;
- DMAbuf_reset (dev);
- return 0;
- break;
-
- case SNDCTL_DSP_GETFMTS:
- return (*(int *) arg = audio_devs[dev]->format_mask);
- break;
+ int c, p, l;
+ char *dmabuf;
+ int buf_no;
- case SNDCTL_DSP_SETFMT:
- val = *(int *) arg;
- return (*(int *) arg = set_format (dev, val));
+ dev = dev >> 4;
+ p = 0;
+ c = count;
- case SNDCTL_DSP_GETISPACE:
if (!(audio_devs[dev]->open_mode & OPEN_READ))
- return 0;
- if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
- return -EBUSY;
-
- {
- audio_buf_info info;
-
- int err = dma_ioctl (dev, cmd, (caddr_t) & info);
-
- if (err < 0)
- return err;
-
- memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
- return 0;
- }
-
- case SNDCTL_DSP_GETOSPACE:
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
- return -EPERM;
- if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX))
- return -EBUSY;
-
- {
- audio_buf_info info;
+ return -EPERM;
- int err = dma_ioctl (dev, cmd, (caddr_t) & info);
-
- if (err < 0)
- return err;
-
- memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
- return 0;
- }
-
- case SNDCTL_DSP_NONBLOCK:
- dev_nblock[dev] = 1;
- return 0;
- break;
-
- case SNDCTL_DSP_GETCAPS:
- {
- int info = 1; /* Revision level of this ioctl() */
-
- if (audio_devs[dev]->flags & DMA_DUPLEX &&
- audio_devs[dev]->open_mode == OPEN_READWRITE)
- info |= DSP_CAP_DUPLEX;
-
- if (audio_devs[dev]->coproc)
- info |= DSP_CAP_COPROC;
-
- if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */
- info |= DSP_CAP_BATCH;
-
- if (audio_devs[dev]->d->trigger) /* Supports SETTRIGGER */
- info |= DSP_CAP_TRIGGER;
+ if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
+ {
+ sync_output(dev);
+ }
+ if (audio_devs[dev]->flags & DMA_DUPLEX)
+ audio_mode[dev] |= AM_READ;
+ else
+ audio_mode[dev] = AM_READ;
- info |= DSP_CAP_MMAP;
+ while (c)
+ {
+ if ((buf_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l,
+ dev_nblock[dev])) < 0)
+ {
+ /* Nonblocking mode handling. Return current # of bytes */
+
+ if (dev_nblock[dev] && buf_no == -EAGAIN)
+ return p;
+
+ return buf_no;
+ }
+ if (l > c)
+ l = c;
+
+ /*
+ * Insert any local processing here.
+ */
+
+ if (local_conversion[dev] & CNV_MU_LAW)
+ {
+ /*
+ * This just allows interrupts while the conversion is running
+ */
+ sti();
+
+ translate_bytes(dsp_ulaw, (unsigned char *) dmabuf, l);
+ }
+ {
+ char *fixit = dmabuf;
- memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
- return 0;
- }
- break;
+ copy_to_user(&(buf)[p], fixit, l);
+ };
- case SOUND_PCM_WRITE_RATE:
- val = *(int *) arg;
- return (*(int *) arg = audio_devs[dev]->d->set_speed (dev, val));
+ DMAbuf_rmchars(dev, buf_no, l);
- case SOUND_PCM_READ_RATE:
- return (*(int *) arg = audio_devs[dev]->d->set_speed (dev, 0));
+ p += l;
+ c -= l;
+ }
- case SNDCTL_DSP_STEREO:
- {
- int n;
+ return count - c;
+}
- n = *(int *) arg;
- if (n > 1)
- {
- printk ("sound: SNDCTL_DSP_STEREO called with invalid argument %d\n",
- n);
- return -EINVAL;
- }
+int
+audio_ioctl(int dev, struct fileinfo *file_must_not_be_used,
+ unsigned int cmd, caddr_t arg)
+{
+ int val;
- if (n < 0)
- return -EINVAL;
+ /* printk( "audio_ioctl(%x, %x)\n", (int)cmd, (int)arg); */
- return (*(int *) arg = audio_devs[dev]->d->set_channels (dev, n + 1) - 1);
- }
+ dev = dev >> 4;
- case SOUND_PCM_WRITE_CHANNELS:
- val = *(int *) arg;
- return (*(int *) arg = audio_devs[dev]->d->set_channels (dev, val));
+ if (((cmd >> 8) & 0xff) == 'C')
+ {
+ if (audio_devs[dev]->coproc) /* Coprocessor ioctl */
+ return audio_devs[dev]->coproc->ioctl(audio_devs[dev]->coproc->devc, cmd, arg, 0);
+ else
+ printk("/dev/dsp%d: No coprocessor for this device\n", dev);
+
+ return -ENXIO;
+ } else
+ switch (cmd)
+ {
+ case SNDCTL_DSP_SYNC:
+ if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+ return 0;
+
+ if (audio_devs[dev]->dmap_out->fragment_size == 0)
+ return 0;
+ sync_output(dev);
+ DMAbuf_sync(dev);
+ DMAbuf_reset(dev);
+ return 0;
+ break;
+
+ case SNDCTL_DSP_POST:
+ if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+ return 0;
+ if (audio_devs[dev]->dmap_out->fragment_size == 0)
+ return 0;
+ audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY;
+ sync_output(dev);
+ dma_ioctl(dev, SNDCTL_DSP_POST, (caddr_t) 0);
+ return 0;
+ break;
+
+ case SNDCTL_DSP_RESET:
+ audio_mode[dev] = AM_NONE;
+ DMAbuf_reset(dev);
+ return 0;
+ break;
+
+ case SNDCTL_DSP_GETFMTS:
+ return (*(int *) arg = audio_devs[dev]->format_mask);
+ break;
+
+ case SNDCTL_DSP_SETFMT:
+ val = *(int *) arg;
+ return (*(int *) arg = set_format(dev, val));
+
+ case SNDCTL_DSP_GETISPACE:
+ if (!(audio_devs[dev]->open_mode & OPEN_READ))
+ return 0;
+ if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
+ return -EBUSY;
+
+ {
+ audio_buf_info info;
+
+ int err = dma_ioctl(dev, cmd, (caddr_t) & info);
+
+ if (err < 0)
+ return err;
+
+ memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info));
+ return 0;
+ }
+
+ case SNDCTL_DSP_GETOSPACE:
+ if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+ return -EPERM;
+ if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX))
+ return -EBUSY;
+
+ {
+ audio_buf_info info;
+
+ int err = dma_ioctl(dev, cmd, (caddr_t) & info);
+
+ if (err < 0)
+ return err;
+
+ memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info));
+ return 0;
+ }
+
+ case SNDCTL_DSP_NONBLOCK:
+ dev_nblock[dev] = 1;
+ return 0;
+ break;
+
+ case SNDCTL_DSP_GETCAPS:
+ {
+ int info = 1; /* Revision level of this ioctl() */
+
+ if (audio_devs[dev]->flags & DMA_DUPLEX &&
+ audio_devs[dev]->open_mode == OPEN_READWRITE)
+ info |= DSP_CAP_DUPLEX;
+
+ if (audio_devs[dev]->coproc)
+ info |= DSP_CAP_COPROC;
+
+ if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */
+ info |= DSP_CAP_BATCH;
+
+ if (audio_devs[dev]->d->trigger) /* Supports SETTRIGGER */
+ info |= DSP_CAP_TRIGGER;
+
+ info |= DSP_CAP_MMAP;
+
+ memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info));
+ return 0;
+ }
+ break;
+
+ case SOUND_PCM_WRITE_RATE:
+ val = *(int *) arg;
+ return (*(int *) arg = audio_devs[dev]->d->set_speed(dev, val));
+
+ case SOUND_PCM_READ_RATE:
+ return (*(int *) arg = audio_devs[dev]->d->set_speed(dev, 0));
+
+ case SNDCTL_DSP_STEREO:
+ {
+ int n;
+
+ n = *(int *) arg;
+ if (n > 1)
+ {
+ printk("sound: SNDCTL_DSP_STEREO called with invalid argument %d\n", n);
+ return -EINVAL;
+ }
+ if (n < 0)
+ return -EINVAL;
+
+ return (*(int *) arg = audio_devs[dev]->d->set_channels(dev, n + 1) - 1);
+ }
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ val = *(int *) arg;
+ return (*(int *) arg = audio_devs[dev]->d->set_channels(dev, val));
- case SOUND_PCM_READ_CHANNELS:
- return (*(int *) arg = audio_devs[dev]->d->set_channels (dev, 0));
+ case SOUND_PCM_READ_CHANNELS:
+ return (*(int *) arg = audio_devs[dev]->d->set_channels(dev, 0));
- case SOUND_PCM_READ_BITS:
- return (*(int *) arg = audio_devs[dev]->d->set_bits (dev, 0));
+ case SOUND_PCM_READ_BITS:
+ return (*(int *) arg = audio_devs[dev]->d->set_bits(dev, 0));
+
+ case SNDCTL_DSP_SETDUPLEX:
+ if (audio_devs[dev]->open_mode != OPEN_READWRITE)
+ return -EPERM;
+ if (audio_devs[dev]->flags & DMA_DUPLEX)
+ return 0;
+ else
+ return -EIO;
+ break;
- case SNDCTL_DSP_SETDUPLEX:
- if (audio_devs[dev]->open_mode != OPEN_READWRITE)
- return -EPERM;
- if (audio_devs[dev]->flags & DMA_DUPLEX)
- return 0;
- else
- return -EIO;
- break;
-
- case SNDCTL_DSP_PROFILE:
- if (audio_devs[dev]->open_mode & OPEN_WRITE)
- audio_devs[dev]->dmap_out->applic_profile = *(int *) arg;
- if (audio_devs[dev]->open_mode & OPEN_READ)
- audio_devs[dev]->dmap_in->applic_profile = *(int *) arg;
- return 0;
- break;
-
- default:
- return dma_ioctl (dev, cmd, arg);
- }
+ case SNDCTL_DSP_PROFILE:
+ if (audio_devs[dev]->open_mode & OPEN_WRITE)
+ audio_devs[dev]->dmap_out->applic_profile = *(int *) arg;
+ if (audio_devs[dev]->open_mode & OPEN_READ)
+ audio_devs[dev]->dmap_in->applic_profile = *(int *) arg;
+ return 0;
+ break;
+
+ default:
+ return dma_ioctl(dev, cmd, arg);
+ }
}
void
-audio_init_devices (void)
+audio_init_devices(void)
{
- /*
- * NOTE! This routine could be called several times during boot.
- */
+ /*
+ * NOTE! This routine could be called several times during boot.
+ */
}
#endif
void
-reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording)
+reorganize_buffers(int dev, struct dma_buffparms *dmap, int recording)
{
- /*
- * This routine breaks the physical device buffers to logical ones.
- */
-
- struct audio_operations *dsp_dev = audio_devs[dev];
+ /*
+ * This routine breaks the physical device buffers to logical ones.
+ */
- unsigned i, n;
- unsigned sr, nc, sz, bsz;
+ struct audio_operations *dsp_dev = audio_devs[dev];
- if (!dmap->needs_reorg)
- return;
+ unsigned i, n;
+ unsigned sr, nc, sz, bsz;
- sr = dsp_dev->d->set_speed (dev, 0);
- nc = dsp_dev->d->set_channels (dev, 0);
- sz = dsp_dev->d->set_bits (dev, 0);
- dmap->needs_reorg = 0;
+ sr = dsp_dev->d->set_speed(dev, 0);
+ nc = dsp_dev->d->set_channels(dev, 0);
+ sz = dsp_dev->d->set_bits(dev, 0);
- if (sz == 8)
- dmap->neutral_byte = NEUTRAL8;
- else
- dmap->neutral_byte = NEUTRAL16;
+ if (sz == 8)
+ dmap->neutral_byte = NEUTRAL8;
+ else
+ dmap->neutral_byte = NEUTRAL16;
- if (sr < 1 || nc < 1 || sz < 1)
- {
- printk ("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n",
- dev, sr, nc, sz);
- sr = DSP_DEFAULT_SPEED;
- nc = 1;
- sz = 8;
- }
+ if (sr < 1 || nc < 1 || sz < 1)
+ {
+ printk("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);
+ sr = DSP_DEFAULT_SPEED;
+ nc = 1;
+ sz = 8;
+ }
+ sz = sr * nc * sz;
- sz = sr * nc * sz;
+ sz /= 8; /* #bits -> #bytes */
+ dmap->data_rate = sz;
- sz /= 8; /* #bits -> #bytes */
- dmap->data_rate = sz;
+ if (!dmap->needs_reorg)
+ return;
+ dmap->needs_reorg = 0;
- if (dmap->fragment_size == 0)
- { /* Compute the fragment size using the default algorithm */
+ if (dmap->fragment_size == 0)
+ { /* Compute the fragment size using the default algorithm */
- /*
- * Compute a buffer size for time not exceeding 1 second.
- * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds
- * of sound (using the current speed, sample size and #channels).
- */
+ /*
+ * Compute a buffer size for time not exceeding 1 second.
+ * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds
+ * of sound (using the current speed, sample size and #channels).
+ */
- bsz = dmap->buffsize;
- while (bsz > sz)
- bsz /= 2;
+ bsz = dmap->buffsize;
+ while (bsz > sz)
+ bsz /= 2;
- if (bsz == dmap->buffsize)
- bsz /= 2; /* Needs at least 2 buffers */
+ if (bsz == dmap->buffsize)
+ bsz /= 2; /* Needs at least 2 buffers */
/*
* Split the computed fragment to smaller parts. After 3.5a9
@@ -599,434 +585,429 @@ reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording)
* results when recording.
*/
- if (dmap->subdivision == 0) /* Not already set */
- {
- dmap->subdivision = 4; /* Init to the default value */
-
- if ((bsz / dmap->subdivision) > 4096)
- dmap->subdivision *= 2;
- if ((bsz / dmap->subdivision) < 4096)
- dmap->subdivision = 1;
- }
-
- bsz /= dmap->subdivision;
-
- if (bsz < 16)
- bsz = 16; /* Just a sanity check */
-
- dmap->fragment_size = bsz;
- }
- else
- {
- /*
- * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or
- * the buffer size computation has already been done.
- */
- if (dmap->fragment_size > (dmap->buffsize / 2))
- dmap->fragment_size = (dmap->buffsize / 2);
- bsz = dmap->fragment_size;
- }
-
- if (audio_devs[dev]->min_fragment)
- if (bsz < (1 << audio_devs[dev]->min_fragment))
- bsz = 1 << audio_devs[dev]->min_fragment;
- if (audio_devs[dev]->max_fragment)
- if (bsz > (1 << audio_devs[dev]->max_fragment))
- bsz = 1 << audio_devs[dev]->max_fragment;
- bsz &= ~0x07; /* Force size which is multiple of 8 bytes */
+ if (dmap->subdivision == 0) /* Not already set */
+ {
+ dmap->subdivision = 4; /* Init to the default value */
+
+ if ((bsz / dmap->subdivision) > 4096)
+ dmap->subdivision *= 2;
+ if ((bsz / dmap->subdivision) < 4096)
+ dmap->subdivision = 1;
+ }
+ bsz /= dmap->subdivision;
+
+ if (bsz < 16)
+ bsz = 16; /* Just a sanity check */
+
+ dmap->fragment_size = bsz;
+ } else
+ {
+ /*
+ * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or
+ * the buffer size computation has already been done.
+ */
+ if (dmap->fragment_size > (dmap->buffsize / 2))
+ dmap->fragment_size = (dmap->buffsize / 2);
+ bsz = dmap->fragment_size;
+ }
+
+ if (audio_devs[dev]->min_fragment)
+ if (bsz < (1 << audio_devs[dev]->min_fragment))
+ bsz = 1 << audio_devs[dev]->min_fragment;
+ if (audio_devs[dev]->max_fragment)
+ if (bsz > (1 << audio_devs[dev]->max_fragment))
+ bsz = 1 << audio_devs[dev]->max_fragment;
+ bsz &= ~0x07; /* Force size which is multiple of 8 bytes */
#ifdef OS_DMA_ALIGN_CHECK
- OS_DMA_ALIGN_CHECK (bsz);
+ OS_DMA_ALIGN_CHECK(bsz);
#endif
- n = dmap->buffsize / bsz;
- if (n > MAX_SUB_BUFFERS)
- n = MAX_SUB_BUFFERS;
- if (n > dmap->max_fragments)
- n = dmap->max_fragments;
-
- if (n < 2)
- {
- n = 2;
- bsz /= 2;
- }
-
- dmap->nbufs = n;
- dmap->bytes_in_use = n * bsz;
- dmap->fragment_size = bsz;
- dmap->max_byte_counter = (dmap->data_rate * 60 * 60) +
- dmap->bytes_in_use; /* Approximately one hour */
-
- if (dmap->raw_buf)
- {
- memset (dmap->raw_buf,
- dmap->neutral_byte,
- dmap->bytes_in_use);
- }
-
- for (i = 0; i < dmap->nbufs; i++)
- {
- dmap->counts[i] = 0;
- }
-
- dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY;
+ n = dmap->buffsize / bsz;
+ if (n > MAX_SUB_BUFFERS)
+ n = MAX_SUB_BUFFERS;
+ if (n > dmap->max_fragments)
+ n = dmap->max_fragments;
+
+ if (n < 2)
+ {
+ n = 2;
+ bsz /= 2;
+ }
+ dmap->nbufs = n;
+ dmap->bytes_in_use = n * bsz;
+ dmap->fragment_size = bsz;
+ dmap->max_byte_counter = (dmap->data_rate * 60 * 60) +
+ dmap->bytes_in_use; /* Approximately one hour */
+
+ if (dmap->raw_buf)
+ {
+ memset(dmap->raw_buf,
+ dmap->neutral_byte,
+ dmap->bytes_in_use);
+ }
+ for (i = 0; i < dmap->nbufs; i++)
+ {
+ dmap->counts[i] = 0;
+ }
+
+ dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY;
}
static int
-dma_subdivide (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact)
+dma_subdivide(int dev, struct dma_buffparms *dmap, caddr_t arg, int fact)
{
- if (fact == 0)
- {
- fact = dmap->subdivision;
- if (fact == 0)
- fact = 1;
- return (*(int *) arg = fact);
- }
-
- if (dmap->subdivision != 0 ||
- dmap->fragment_size) /* Too late to change */
- return -EINVAL;
-
- if (fact > MAX_REALTIME_FACTOR)
- return -EINVAL;
-
- if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
- return -EINVAL;
-
- dmap->subdivision = fact;
- return (*(int *) arg = fact);
+ if (fact == 0)
+ {
+ fact = dmap->subdivision;
+ if (fact == 0)
+ fact = 1;
+ return (*(int *) arg = fact);
+ }
+ if (dmap->subdivision != 0 ||
+ dmap->fragment_size) /* Too late to change */
+ return -EINVAL;
+
+ if (fact > MAX_REALTIME_FACTOR)
+ return -EINVAL;
+
+ if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
+ return -EINVAL;
+
+ dmap->subdivision = fact;
+ return (*(int *) arg = fact);
}
static int
-dma_set_fragment (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact)
+dma_set_fragment(int dev, struct dma_buffparms *dmap, caddr_t arg, int fact)
{
- int bytes, count;
+ int bytes, count;
- if (fact == 0)
- return -EIO;
+ if (fact == 0)
+ return -EIO;
- if (dmap->subdivision != 0 ||
- dmap->fragment_size) /* Too late to change */
- return -EINVAL;
+ if (dmap->subdivision != 0 ||
+ dmap->fragment_size) /* Too late to change */
+ return -EINVAL;
- bytes = fact & 0xffff;
- count = (fact >> 16) & 0x7fff;
+ bytes = fact & 0xffff;
+ count = (fact >> 16) & 0x7fff;
- if (count == 0)
- count = MAX_SUB_BUFFERS;
- else if (count < MAX_SUB_BUFFERS)
- count++;
+ if (count == 0)
+ count = MAX_SUB_BUFFERS;
+ else if (count < MAX_SUB_BUFFERS)
+ count++;
- if (bytes < 4 || bytes > 17) /* <16 || > 512k */
- return -EINVAL;
+ if (bytes < 4 || bytes > 17) /* <16 || > 512k */
+ return -EINVAL;
- if (count < 2)
- return -EINVAL;
+ if (count < 2)
+ return -EINVAL;
- if (audio_devs[dev]->min_fragment > 0)
- if (bytes < audio_devs[dev]->min_fragment)
- bytes = audio_devs[dev]->min_fragment;
+ if (audio_devs[dev]->min_fragment > 0)
+ if (bytes < audio_devs[dev]->min_fragment)
+ bytes = audio_devs[dev]->min_fragment;
- if (audio_devs[dev]->max_fragment > 0)
- if (bytes > audio_devs[dev]->max_fragment)
- bytes = audio_devs[dev]->max_fragment;
+ if (audio_devs[dev]->max_fragment > 0)
+ if (bytes > audio_devs[dev]->max_fragment)
+ bytes = audio_devs[dev]->max_fragment;
#ifdef OS_DMA_MINBITS
- if (bytes < OS_DMA_MINBITS)
- bytes = OS_DMA_MINBITS;
+ if (bytes < OS_DMA_MINBITS)
+ bytes = OS_DMA_MINBITS;
#endif
- dmap->fragment_size = (1 << bytes);
- dmap->max_fragments = count;
+ dmap->fragment_size = (1 << bytes);
+ dmap->max_fragments = count;
- if (dmap->fragment_size > dmap->buffsize)
- dmap->fragment_size = dmap->buffsize;
+ if (dmap->fragment_size > dmap->buffsize)
+ dmap->fragment_size = dmap->buffsize;
- if (dmap->fragment_size == dmap->buffsize &&
- audio_devs[dev]->flags & DMA_AUTOMODE)
- dmap->fragment_size /= 2; /* Needs at least 2 buffers */
+ if (dmap->fragment_size == dmap->buffsize &&
+ audio_devs[dev]->flags & DMA_AUTOMODE)
+ dmap->fragment_size /= 2; /* Needs at least 2 buffers */
- dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */
- if (arg)
- return (*(int *) arg = bytes | ((count - 1) << 16));
- else
- return 0;
+ dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */
+ if (arg)
+ return (*(int *) arg = bytes | ((count - 1) << 16));
+ else
+ return 0;
}
-static int
-dma_ioctl (int dev, unsigned int cmd, caddr_t arg)
+int
+dma_ioctl(int dev, unsigned int cmd, caddr_t arg)
{
- struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out;
- struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in;
-
- switch (cmd)
- {
-
- case SNDCTL_DSP_SUBDIVIDE:
- {
- int fact;
- int ret = 0;
-
- fact = *(int *) arg;
-
- if (audio_devs[dev]->open_mode & OPEN_WRITE)
- ret = dma_subdivide (dev, dmap_out, arg, fact);
- if (ret < 0)
- return ret;
-
- if (audio_devs[dev]->open_mode != OPEN_WRITE ||
- (audio_devs[dev]->flags & DMA_DUPLEX &&
- audio_devs[dev]->open_mode & OPEN_READ))
- ret = dma_subdivide (dev, dmap_in, arg, fact);
-
- return ret;
- }
- break;
-
- case SNDCTL_DSP_GETISPACE:
- case SNDCTL_DSP_GETOSPACE:
- {
- struct dma_buffparms *dmap = dmap_out;
-
- audio_buf_info *info = (audio_buf_info *) arg;
+ struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out;
+ struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in;
- if (cmd == SNDCTL_DSP_GETISPACE &&
- !(audio_devs[dev]->open_mode & OPEN_READ))
- return -EINVAL;
+ switch (cmd)
+ {
- if (cmd == SNDCTL_DSP_GETOSPACE &&
- !(audio_devs[dev]->open_mode & OPEN_WRITE))
- return -EINVAL;
+ case SNDCTL_DSP_SUBDIVIDE:
+ {
+ int fact;
+ int ret = 0;
- if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
- dmap = dmap_in;
+ fact = *(int *) arg;
- if (dmap->mapping_flags & DMA_MAP_MAPPED)
- return -EINVAL;
+ if (audio_devs[dev]->open_mode & OPEN_WRITE)
+ ret = dma_subdivide(dev, dmap_out, arg, fact);
+ if (ret < 0)
+ return ret;
- if (!(dmap->flags & DMA_ALLOC_DONE))
- reorganize_buffers (dev, dmap, (cmd == SNDCTL_DSP_GETISPACE));
+ if (audio_devs[dev]->open_mode != OPEN_WRITE ||
+ (audio_devs[dev]->flags & DMA_DUPLEX &&
+ audio_devs[dev]->open_mode & OPEN_READ))
+ ret = dma_subdivide(dev, dmap_in, arg, fact);
- info->fragstotal = dmap->nbufs;
+ return ret;
+ }
+ break;
- if (cmd == SNDCTL_DSP_GETISPACE)
- info->fragments = dmap->qlen;
- else
- {
- if (!DMAbuf_space_in_queue (dev))
- info->fragments = 0;
- else
- {
- info->fragments = DMAbuf_space_in_queue (dev);
- if (audio_devs[dev]->d->local_qlen)
+ case SNDCTL_DSP_GETISPACE:
+ case SNDCTL_DSP_GETOSPACE:
{
- int tmp = audio_devs[dev]->d->local_qlen (dev);
-
- if (tmp && info->fragments)
- tmp--; /*
- * This buffer has been counted twice
- */
- info->fragments -= tmp;
+ struct dma_buffparms *dmap = dmap_out;
+
+ audio_buf_info *info = (audio_buf_info *) arg;
+
+ if (cmd == SNDCTL_DSP_GETISPACE &&
+ !(audio_devs[dev]->open_mode & OPEN_READ))
+ return -EINVAL;
+
+ if (cmd == SNDCTL_DSP_GETOSPACE &&
+ !(audio_devs[dev]->open_mode & OPEN_WRITE))
+ return -EINVAL;
+
+ if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
+ dmap = dmap_in;
+
+ if (dmap->mapping_flags & DMA_MAP_MAPPED)
+ return -EINVAL;
+
+ if (!(dmap->flags & DMA_ALLOC_DONE))
+ reorganize_buffers(dev, dmap, (cmd == SNDCTL_DSP_GETISPACE));
+
+ info->fragstotal = dmap->nbufs;
+
+ if (cmd == SNDCTL_DSP_GETISPACE)
+ info->fragments = dmap->qlen;
+ else
+ {
+ if (!DMAbuf_space_in_queue(dev))
+ info->fragments = 0;
+ else
+ {
+ info->fragments = DMAbuf_space_in_queue(dev);
+ if (audio_devs[dev]->d->local_qlen)
+ {
+ int tmp = audio_devs[dev]->d->local_qlen(dev);
+
+ if (tmp && info->fragments)
+ tmp--; /*
+ * This buffer has been counted twice
+ */
+ info->fragments -= tmp;
+ }
+ }
+ }
+
+ if (info->fragments < 0)
+ info->fragments = 0;
+ else if (info->fragments > dmap->nbufs)
+ info->fragments = dmap->nbufs;
+
+ info->fragsize = dmap->fragment_size;
+ info->bytes = info->fragments * dmap->fragment_size;
+
+ if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)
+ info->bytes -= dmap->counts[dmap->qhead];
+ else
+ {
+ info->fragments = info->bytes / dmap->fragment_size;
+ info->bytes -= dmap->user_counter % dmap->fragment_size;
+ }
}
- }
- }
-
- if (info->fragments < 0)
- info->fragments = 0;
- else if (info->fragments > dmap->nbufs)
- info->fragments = dmap->nbufs;
-
- info->fragsize = dmap->fragment_size;
- info->bytes = info->fragments * dmap->fragment_size;
-
- if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)
- info->bytes -= dmap->counts[dmap->qhead];
- }
- return 0;
+ return 0;
- case SNDCTL_DSP_SETTRIGGER:
- {
- unsigned long flags;
+ case SNDCTL_DSP_SETTRIGGER:
+ {
+ unsigned long flags;
+
+ int bits;
+ int changed;
+
+ bits = *(int *) arg;
+ bits &= audio_devs[dev]->open_mode;
+
+ if (audio_devs[dev]->d->trigger == NULL)
+ return -EINVAL;
+
+ if (!(audio_devs[dev]->flags & DMA_DUPLEX))
+ if ((bits & PCM_ENABLE_INPUT) && (bits & PCM_ENABLE_OUTPUT))
+ {
+ printk("Sound: Device doesn't have full duplex capability\n");
+ return -EINVAL;
+ }
+ save_flags(flags);
+ cli();
+ changed = audio_devs[dev]->enable_bits ^ bits;
+
+ if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go)
+ {
+ int err;
+
+ reorganize_buffers(dev, dmap_in, 1);
+
+ if ((err = audio_devs[dev]->d->prepare_for_input(dev,
+ dmap_in->fragment_size, dmap_in->nbufs)) < 0)
+ return -err;
+
+ dmap_in->dma_mode = DMODE_INPUT;
+ audio_devs[dev]->enable_bits = bits;
+ DMAbuf_activate_recording(dev, dmap_in);
+ }
+ if ((changed & bits) & PCM_ENABLE_OUTPUT &&
+ (dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) &&
+ audio_devs[dev]->go)
+ {
+
+ if (!(dmap_out->flags & DMA_ALLOC_DONE))
+ {
+ reorganize_buffers(dev, dmap_out, 0);
+ }
+ dmap_out->dma_mode = DMODE_OUTPUT;
+ ;
+ audio_devs[dev]->enable_bits = bits;
+ dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
+ DMAbuf_launch_output(dev, dmap_out);
+ ;
+ }
+ audio_devs[dev]->enable_bits = bits;
+ if (changed && audio_devs[dev]->d->trigger)
+ {
+ audio_devs[dev]->d->trigger(dev, bits * audio_devs[dev]->go);
+ }
+ restore_flags(flags);
+ }
+ case SNDCTL_DSP_GETTRIGGER:
+ return (*(int *) arg = audio_devs[dev]->enable_bits);
+ break;
- int bits;
- int changed;
+ case SNDCTL_DSP_SETSYNCRO:
- bits = *(int *) arg;
- bits &= audio_devs[dev]->open_mode;
+ if (!audio_devs[dev]->d->trigger)
+ return -EINVAL;
- if (audio_devs[dev]->d->trigger == NULL)
- return -EINVAL;
+ audio_devs[dev]->d->trigger(dev, 0);
+ audio_devs[dev]->go = 0;
+ return 0;
+ break;
- if (!(audio_devs[dev]->flags & DMA_DUPLEX))
- if ((bits & PCM_ENABLE_INPUT) && (bits & PCM_ENABLE_OUTPUT))
- {
- printk ("Sound: Device doesn't have full duplex capability\n");
- return -EINVAL;
- }
+ case SNDCTL_DSP_GETIPTR:
+ {
+ count_info info;
+ unsigned long flags;
+ struct dma_buffparms *dmap = dmap_in;
+
+ if (!(audio_devs[dev]->open_mode & OPEN_READ))
+ return -EINVAL;
+
+ save_flags(flags);
+ cli();
+ info.bytes = dmap->byte_counter;
+ info.ptr = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_INPUT) & ~3;
+ if (info.ptr < dmap->fragment_size && dmap->qtail != 0)
+ info.bytes += dmap->bytes_in_use; /* Pointer wrap not handled yet */
+
+ info.blocks = dmap->qlen;
+ info.bytes += info.ptr;
+ memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info));
+
+ if (dmap->mapping_flags & DMA_MAP_MAPPED)
+ dmap->qlen = 0; /* Reset interrupt counter */
+ restore_flags(flags);
+ return 0;
+ }
+ break;
- save_flags (flags);
- cli ();
- changed = audio_devs[dev]->enable_bits ^ bits;
+ case SNDCTL_DSP_GETOPTR:
+ {
+ count_info info;
+ unsigned long flags;
+ struct dma_buffparms *dmap = dmap_out;
+
+ if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+ return -EINVAL;
+
+ save_flags(flags);
+ cli();
+ info.bytes = dmap->byte_counter;
+ info.ptr = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT) & ~3;
+ if (info.ptr < dmap->fragment_size && dmap->qhead != 0)
+ info.bytes += dmap->bytes_in_use; /* Pointer wrap not handled yet */
+ info.blocks = dmap->qlen;
+ info.bytes += info.ptr;
+ memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info));
+
+ if (dmap->mapping_flags & DMA_MAP_MAPPED)
+ dmap->qlen = 0; /* Reset interrupt counter */
+
+ restore_flags(flags);
+ return 0;
+ }
+ break;
- if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go)
- {
- int err;
- reorganize_buffers (dev, dmap_in, 1);
+ case SNDCTL_DSP_POST:
+ ;
+ if (audio_devs[dev]->dmap_out->qlen > 0)
+ if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE))
+ DMAbuf_launch_output(dev, audio_devs[dev]->dmap_out);
+ ;
+ return 0;
+ break;
- if ((err = audio_devs[dev]->d->prepare_for_input (dev,
- dmap_in->fragment_size, dmap_in->nbufs)) < 0)
- return -err;
+ case SNDCTL_DSP_GETBLKSIZE:
+ {
+ int fragment_size;
+ struct dma_buffparms *dmap = dmap_out;
+
+ if (audio_devs[dev]->open_mode & OPEN_WRITE)
+ reorganize_buffers(dev, dmap_out,
+ (audio_devs[dev]->open_mode == OPEN_READ));
+ if (audio_devs[dev]->open_mode != OPEN_WRITE ||
+ (audio_devs[dev]->flags & DMA_DUPLEX &&
+ audio_devs[dev]->open_mode & OPEN_READ))
+ reorganize_buffers(dev, dmap_in,
+ (audio_devs[dev]->open_mode == OPEN_READ));
+ if (audio_devs[dev]->open_mode == OPEN_READ)
+ dmap = dmap_in;
+ fragment_size = dmap->fragment_size;
+ return (*(int *) arg = fragment_size);
+ }
+ break;
- dmap_in->dma_mode = DMODE_INPUT;
- audio_devs[dev]->enable_bits = bits;
- DMAbuf_activate_recording (dev, dmap_in);
- }
+ case SNDCTL_DSP_SETFRAGMENT:
+ {
+ int fact;
+ int ret;
+ fact = *(int *) arg;
+ ret = dma_set_fragment(dev, dmap_out, arg, fact);
+ if (ret < 0)
+ return ret;
- if ((changed & bits) & PCM_ENABLE_OUTPUT &&
- (dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) &&
- audio_devs[dev]->go)
- {
+ if (audio_devs[dev]->flags & DMA_DUPLEX &&
+ audio_devs[dev]->open_mode & OPEN_READ)
+ ret = dma_set_fragment(dev, dmap_in, arg, fact);
- if (!(dmap_out->flags & DMA_ALLOC_DONE))
- {
- reorganize_buffers (dev, dmap_out, 0);
- }
-
- dmap_out->dma_mode = DMODE_OUTPUT;
- ;
- audio_devs[dev]->enable_bits = bits;
- dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
- DMAbuf_launch_output (dev, dmap_out);
- ;
- }
+ return ret;
+ }
+ break;
- audio_devs[dev]->enable_bits = bits;
- if (changed && audio_devs[dev]->d->trigger)
- {
- audio_devs[dev]->d->trigger (dev, bits * audio_devs[dev]->go);
+ default:
+ return audio_devs[dev]->d->ioctl(dev, cmd, arg);
}
- restore_flags (flags);
- }
- case SNDCTL_DSP_GETTRIGGER:
- return (*(int *) arg = audio_devs[dev]->enable_bits);
- break;
-
- case SNDCTL_DSP_SETSYNCRO:
-
- if (!audio_devs[dev]->d->trigger)
- return -EINVAL;
-
- audio_devs[dev]->d->trigger (dev, 0);
- audio_devs[dev]->go = 0;
- return 0;
- break;
-
- case SNDCTL_DSP_GETIPTR:
- {
- count_info info;
- unsigned long flags;
- struct dma_buffparms *dmap = dmap_in;
-
- if (!(audio_devs[dev]->open_mode & OPEN_READ))
- return -EINVAL;
-
- save_flags (flags);
- cli ();
- info.bytes = dmap->byte_counter;
- info.ptr = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_INPUT) & ~3;
- if (info.ptr < dmap->fragment_size && dmap->qtail != 0)
- info.bytes += dmap->bytes_in_use; /* Pointer wrap not handled yet */
-
- info.blocks = dmap->qlen;
- info.bytes += info.ptr;
- memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
-
- if (dmap->mapping_flags & DMA_MAP_MAPPED)
- dmap->qlen = 0; /* Reset interrupt counter */
- restore_flags (flags);
- return 0;
- }
- break;
-
- case SNDCTL_DSP_GETOPTR:
- {
- count_info info;
- unsigned long flags;
- struct dma_buffparms *dmap = dmap_out;
-
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
- return -EINVAL;
-
- save_flags (flags);
- cli ();
- info.bytes = dmap->byte_counter;
- info.ptr = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT) & ~3;
- if (info.ptr < dmap->fragment_size && dmap->qhead != 0)
- info.bytes += dmap->bytes_in_use; /* Pointer wrap not handled yet */
- info.blocks = dmap->qlen;
- info.bytes += info.ptr;
- memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
-
- if (dmap->mapping_flags & DMA_MAP_MAPPED)
- dmap->qlen = 0; /* Reset interrupt counter */
-
- restore_flags (flags);
- return 0;
- }
- break;
-
-
- case SNDCTL_DSP_POST:
- ;
- if (audio_devs[dev]->dmap_out->qlen > 0)
- if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE))
- DMAbuf_launch_output (dev, audio_devs[dev]->dmap_out);
- ;
- return 0;
- break;
-
- case SNDCTL_DSP_GETBLKSIZE:
- {
- int fragment_size;
- struct dma_buffparms *dmap = dmap_out;
-
- if (audio_devs[dev]->open_mode & OPEN_WRITE)
- reorganize_buffers (dev, dmap_out,
- (audio_devs[dev]->open_mode == OPEN_READ));
- if (audio_devs[dev]->open_mode != OPEN_WRITE ||
- (audio_devs[dev]->flags & DMA_DUPLEX &&
- audio_devs[dev]->open_mode & OPEN_READ))
- reorganize_buffers (dev, dmap_in,
- (audio_devs[dev]->open_mode == OPEN_READ));
- if (audio_devs[dev]->open_mode == OPEN_READ)
- dmap = dmap_in;
- fragment_size = dmap->fragment_size;
- return (*(int *) arg = fragment_size);
- }
- break;
-
- case SNDCTL_DSP_SETFRAGMENT:
- {
- int fact;
- int ret;
-
- fact = *(int *) arg;
- ret = dma_set_fragment (dev, dmap_out, arg, fact);
- if (ret < 0)
- return ret;
-
- if (audio_devs[dev]->flags & DMA_DUPLEX &&
- audio_devs[dev]->open_mode & OPEN_READ)
- ret = dma_set_fragment (dev, dmap_in, arg, fact);
-
- return ret;
- }
- break;
-
- default:
- return audio_devs[dev]->d->ioctl (dev, cmd, arg);
- }
}
diff --git a/drivers/sound/configure.c b/drivers/sound/configure.c
index 6b50a3aa5..aea32b388 100644
--- a/drivers/sound/configure.c
+++ b/drivers/sound/configure.c
@@ -45,18 +45,20 @@
#define OPT_MAD16 12
#define OPT_CS4232 13
#define OPT_MAUI 14
-#define OPT_SPNP 15
-
-#define OPT_HIGHLEVEL 16 /* This must be same than the next one */
-#define OPT_UNUSED1 16
-#define OPT_UNUSED2 17
-#define OPT_AEDSP16 18
-#define OPT_UNUSED3 19
-#define OPT_UNUSED4 20
-#define OPT_UNUSED5 21
-#define OPT_YM3812_AUTO 22
-#define OPT_YM3812 23
-#define OPT_LAST 23 /* Last defined OPT number */
+#define OPT_SPNP 15
+#define OPT_OPL3SA1 16
+#define OPT_SOFTOSS 17
+
+#define OPT_HIGHLEVEL 18 /* This must be same than the next one */
+#define OPT_UNUSED1 18
+#define OPT_UNUSED2 19
+#define OPT_AEDSP16 20
+#define OPT_UNUSED3 21
+#define OPT_UNUSED4 22
+#define OPT_UNUSED5 23
+#define OPT_YM3812_AUTO 24
+#define OPT_YM3812 25
+#define OPT_LAST 25 /* Last defined OPT number */
#define DUMMY_OPTS (B(OPT_YM3812_AUTO))
@@ -64,16 +66,17 @@
B(OPT_MPU401)|B(OPT_PSS)|B(OPT_GUS16)|B(OPT_GUSMAX)| \
B(OPT_MSS)|B(OPT_SSCAPE)|B(OPT_UART6850)|B(OPT_TRIX)| \
B(OPT_MAD16)|B(OPT_CS4232)|B(OPT_MAUI)|B(OPT_ADLIB)| \
- B(OPT_SPNP))
+ B(OPT_SPNP)|B(OPT_OPL3SA1)|B(OPT_SOFTOSS))
#define MPU_DEVS (B(OPT_PSS)|\
B(OPT_CS4232)|B(OPT_SPNP)|B(OPT_MAUI)|B(OPT_SSCAPE))
-#define UART401_DEVS (SBDSP_DEVS|B(OPT_TRIX)|B(OPT_MAD16)|B(OPT_SPNP))
+#define UART401_DEVS (SBDSP_DEVS|B(OPT_TRIX)|B(OPT_MAD16)|B(OPT_SPNP)|\
+ B(OPT_OPL3SA1))
#define NON_AUDIO_CARDS (B(OPT_ADLIB)|B(OPT_MPU401)|B(OPT_UART6850)|B(OPT_MAUI))
#define AUDIO_CARDS (ANY_DEVS & ~NON_AUDIO_CARDS)
#define MIDI_CARDS (ANY_DEVS & ~(B(OPT_ADLIB)|B(OPT_MSS)))
#define AD1848_DEVS (B(OPT_GUS16)|B(OPT_MSS)|B(OPT_PSS)|B(OPT_GUSMAX)|\
B(OPT_SSCAPE)|B(OPT_TRIX)|B(OPT_MAD16)|B(OPT_CS4232)|\
- B(OPT_SPNP))
+ B(OPT_SPNP)|B(OPT_OPL3SA1))
#define SBDSP_DEVS (B(OPT_SB)|B(OPT_SPNP)|B(OPT_MAD16)|B(OPT_TRIX))
#define SEQUENCER_DEVS 0x7fffffff
/*
@@ -84,12 +87,12 @@
typedef struct
{
- unsigned long conditions;
- unsigned long exclusive_options;
- char macro[20];
- int verify;
- int alias;
- int default_answ;
+ unsigned long conditions;
+ unsigned long exclusive_options;
+ char macro[20];
+ int verify;
+ int alias;
+ int default_answ;
}
hw_entry;
@@ -114,200 +117,209 @@ hw_entry hw_table[] =
/*
* 0
*/
- {0, 0, "PAS", 1, 0, 0},
- {0, 0, "SB", 1, 0, 0},
- {0, B (OPT_PAS) | B (OPT_SB), "ADLIB", 1, 0, 0},
-
- {0, 0, "GUS", 1, 0, 0},
- {0, 0, "MPU401", 1, 0, 0},
- {0, 0, "UART6850", 1, 0, 0},
- {0, 0, "PSS", 1, 0, 0},
- {B (OPT_GUS), 0, "GUS16", 1, 0, 0},
- {B (OPT_GUS), B (OPT_GUS16), "GUSMAX", 1, 0, 0},
- {0, 0, "MSS", 1, 0, 0},
- {0, 0, "SSCAPE", 1, 0, 0},
- {0, 0, "TRIX", 1, 0, 0},
- {0, 0, "MAD16", 1, 0, 0},
- {0, 0, "CS4232", 1, 0, 0},
- {0, 0, "MAUI", 1, 0, 0},
- {0, 0, "SPNP", 1, 0, 0},
-
- {B (OPT_SB), B (OPT_PAS), "UNUSED1", 1, 0, 1},
- {B (OPT_SB) | B (OPT_UNUSED1), B (OPT_PAS), "UNUSED2", 1, 0, 1},
- {B (OPT_UNUSED1) | B (OPT_MSS) | B (OPT_MPU401), 0, "AEDSP16", 1, 0, 0},
- {AUDIO_CARDS, 0, "UNUSED3", 1, 0, 1},
- {B (OPT_MPU401) | B (OPT_MAUI), 0, "UNUSED4", 0, 0, 0},
- {MIDI_CARDS, 0, "UNUSED5", 1, 0, 1},
- {B (OPT_ADLIB), 0, "YM3812_AUTO", 0, OPT_YM3812, 0},
- {B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_ADLIB) | B (OPT_MSS) | B (OPT_PSS), B (OPT_YM3812_AUTO), "YM3812", 1, 0, 1}
+ {0, 0, "PAS", 1, 0, 0},
+ {0, 0, "SB", 1, 0, 0},
+ {0, B(OPT_PAS) | B(OPT_SB), "ADLIB", 1, 0, 0},
+
+ {0, 0, "GUS", 1, 0, 0},
+ {0, 0, "MPU401", 1, 0, 0},
+ {0, 0, "UART6850", 1, 0, 0},
+ {0, 0, "PSS", 1, 0, 0},
+ {B(OPT_GUS), 0, "GUS16", 1, 0, 0},
+ {B(OPT_GUS), B(OPT_GUS16), "GUSMAX", 1, 0, 0},
+ {0, 0, "MSS", 1, 0, 0},
+ {0, 0, "SSCAPE", 1, 0, 0},
+ {0, 0, "TRIX", 1, 0, 0},
+ {0, 0, "MAD16", 1, 0, 0},
+ {0, 0, "CS4232", 1, 0, 0},
+ {0, 0, "MAUI", 1, 0, 0},
+ {0, 0, "SPNP", 1, 0, 0},
+ {0, 0, "OPL3SA1", 1, 0, 0},
+ {0, 0, "SOFTOSS", 1, 0, 0},
+
+ {B(OPT_SB), B(OPT_PAS), "UNUSED1", 1, 0, 1},
+ {B(OPT_SB) | B(OPT_UNUSED1), B(OPT_PAS), "UNUSED2", 1, 0, 1},
+ {B(OPT_UNUSED1) | B(OPT_MSS) | B(OPT_MPU401), 0, "AEDSP16", 1, 0, 0},
+ {AUDIO_CARDS, 0, "UNUSED3", 1, 0, 1},
+ {B(OPT_MPU401) | B(OPT_MAUI), 0, "UNUSED4", 0, 0, 0},
+ {MIDI_CARDS, 0, "UNUSED5", 1, 0, 1},
+ {B(OPT_ADLIB), 0, "YM3812_AUTO", 0, OPT_YM3812, 0},
+ {B(OPT_PSS) | B(OPT_SB) | B(OPT_PAS) | B(OPT_ADLIB) | B(OPT_MSS) | B(OPT_PSS), B(OPT_YM3812_AUTO), "YM3812", 1, 0, 1}
};
char *questions[] =
{
- "ProAudioSpectrum 16 support",
- "100%% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support",
- "Generic OPL2/OPL3 FM synthesizer support",
- "Gravis Ultrasound support",
- "MPU-401 support (NOT for SB16)",
- "6850 UART Midi support",
- "PSS (ECHO-ADI2111) support",
- "16 bit sampling option of GUS (_NOT_ GUS MAX)",
- "GUS MAX support",
- "Microsoft Sound System support",
- "Ensoniq SoundScape support",
- "MediaTrix AudioTrix Pro support",
- "Support for MAD16 and/or Mozart based cards",
- "Support for Crystal CS4232 based (PnP) cards",
- "Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers",
- "Support for PnP sound cards (_EXPERIMENTAL_)",
-
- "*** Unused option 1 ***",
- "*** Unused option 2 ***",
- "Audio Excel DSP 16 initialization support",
- "*** Unused option 3 ***",
- "*** Unused option 4 ***",
- "*** Unused option 5 ***",
- "This should not be asked",
- "FM synthesizer (YM3812/OPL-3) support",
- "Is the sky really falling"
+ "ProAudioSpectrum 16 support",
+ "100%% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support",
+ "Generic OPL2/OPL3 FM synthesizer support",
+ "Gravis Ultrasound support",
+ "MPU-401 support (NOT for SB16)",
+ "6850 UART Midi support",
+ "PSS (ECHO-ADI2111) support",
+ "16 bit sampling option of GUS (_NOT_ GUS MAX)",
+ "GUS MAX support",
+ "Microsoft Sound System support",
+ "Ensoniq SoundScape support",
+ "MediaTrix AudioTrix Pro support",
+ "Support for OPTi MAD16 and/or Mozart based cards",
+ "Support for Crystal CS4232 based (PnP) cards",
+ "Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers",
+ "Support for PnP sound cards (_EXPERIMENTAL_)",
+ "Yamaha OPL3-SA1 audio controller",
+ "SoftOSS software wave table engine",
+
+ "*** Unused option 1 ***",
+ "*** Unused option 2 ***",
+ "Audio Excel DSP 16 initialization support",
+ "*** Unused option 3 ***",
+ "*** Unused option 4 ***",
+ "*** Unused option 5 ***",
+ "This should not be asked",
+ "FM synthesizer (YM3812/OPL-3) support",
+ "Is the sky really falling"
};
/* help text for each option */
char *help[] =
{
- "Enable this option only if you have a Pro Audio Spectrum 16,\n"
- "Pro Audio Studio 16, or Logitech SoundMan 16. Don't enable this if\n"
- "you have some other card made by MediaVision or Logitech as\n"
- "they are not PAS16 compatible.\n",
+ "Enable this option only if you have a Pro Audio Spectrum 16,\n"
+ "Pro Audio Studio 16, or Logitech SoundMan 16. Don't enable this if\n"
+ "you have some other card made by MediaVision or Logitech as\n"
+ "they are not PAS16 compatible.\n",
- "Enable this if you have an original Sound Blaster card made by\n"
- "Creative Labs or a 100%% hardware compatible clone. For an\n"
- "unknown card you may want to try this if it claims to be\n"
- "Sound Blaster compatible.\n",
+ "Enable this if you have an original Sound Blaster card made by\n"
+ "Creative Labs or a 100%% hardware compatible clone. For an\n"
+ "unknown card you may want to try this if it claims to be\n"
+ "Sound Blaster compatible.\n",
- "Enable this option if your sound card has a Yamaha OPL2 or OPL3\n"
- "FM synthesizer chip.\n",
+ "Enable this option if your sound card has a Yamaha OPL2 or OPL3\n"
+ "FM synthesizer chip.\n",
- "Enable this option for any type of Gravis Ultrasound card\n"
- "including the GUS or GUS MAX.\n",
+ "Enable this option for any type of Gravis Ultrasound card\n"
+ "including the GUS or GUS MAX.\n",
- "The MPU401 interface is supported by almost all sound cards. However,\n"
- "some natively supported cards have their own driver for\n"
- "MPU401. Enabling the MPU401 option with these cards will cause a\n"
- "conflict. Also enabling MPU401 on a system that doesn't really have a\n"
- "MPU401 could cause some trouble. It's safe to enable this if you have a\n"
- "true MPU401 MIDI interface card.\n",
+"The MPU401 interface is supported by almost all sound cards. However,\n"
+ "some natively supported cards have their own driver for\n"
+ "MPU401. Enabling the MPU401 option with these cards will cause a\n"
+"conflict. Also enabling MPU401 on a system that doesn't really have a\n"
+ "MPU401 could cause some trouble. It's safe to enable this if you have a\n"
+ "true MPU401 MIDI interface card.\n",
- "This option enables support for MIDI interfaces based on the 6850\n"
- "UART chip. This interface is rarely found on sound cards.\n",
+ "This option enables support for MIDI interfaces based on the 6850\n"
+ "UART chip. This interface is rarely found on sound cards.\n",
- "Enable this option if you have an Orchid SW32, Cardinal DSP16 or other\n"
- "sound card based on the PSS chipset (AD1848 codec, ADSP-2115 DSP chip,\n"
- "and Echo ESC614 ASIC CHIP).\n",
+"Enable this option if you have an Orchid SW32, Cardinal DSP16 or other\n"
+"sound card based on the PSS chipset (AD1848 codec, ADSP-2115 DSP chip,\n"
+ "and Echo ESC614 ASIC CHIP).\n",
- "Enable this if you have installed the 16-bit sampling daughtercard on\n"
- "your GUS card. Do not use if you have a GUS MAX as enabling this option\n"
- "disables GUS MAX support.\n",
+"Enable this if you have installed the 16-bit sampling daughtercard on\n"
+ "your GUS card. Do not use if you have a GUS MAX as enabling this option\n"
+ "disables GUS MAX support.\n",
- "Enable this option if you have a Gravis Ultrasound MAX sound\n"
- "card\n",
+ "Enable this option if you have a Gravis Ultrasound MAX sound\n"
+ "card\n",
- "Enable this option if you have the original Windows Sound System\n"
- "card made by Microsoft or the Aztech SG 16 Pro or NX16 Pro.\n",
+ "Enable this option if you have the original Windows Sound System\n"
+ "card made by Microsoft or the Aztech SG 16 Pro or NX16 Pro.\n",
- "Enable this if you have a sound card based on the Ensoniq\n"
- "SoundScape chipset. Such cards are being manufactured by Ensoniq,\n"
- "Spea and Reveal (Reveal makes other cards as well).\n",
+ "Enable this if you have a sound card based on the Ensoniq\n"
+ "SoundScape chipset. Such cards are being manufactured by Ensoniq,\n"
+ "Spea and Reveal (Reveal makes other cards as well).\n",
- "Enable this option if you have the AudioTrix Pro sound card\n"
- "manufactured by MediaTrix.\n",
+ "Enable this option if you have the AudioTrix Pro sound card\n"
+ "manufactured by MediaTrix.\n",
- "Enable this if your card has a Mozart (OAK OTI-601) or MAD16 (OPTi\n"
- "82C928 or 82C929) audio interface chip. These chips are currently\n"
- "quite common so it's possible that many no-name cards have one of\n"
- "them. In addition the MAD16 chip is used in some cards made by known\n"
- "manufacturers such as Turtle Beach (Tropez), Reveal (some models) and\n"
- "Diamond (latest ones).\n",
+ "Enable this if your card has a Mozart (OAK OTI-601) or MAD16 (OPTi\n"
+ "82C928 or 82C929) audio interface chip. These chips are currently\n"
+ "quite common so it's possible that many no-name cards have one of\n"
+ "them. In addition the MAD16 chip is used in some cards made by known\n"
+"manufacturers such as Turtle Beach (Tropez), Reveal (some models) and\n"
+ "Diamond (latest ones).\n",
- "Enable this if you have a card based on the Crystal CS4232 chip set.\n",
+"Enable this if you have a card based on the Crystal CS4232 chip set.\n",
- "Enable this option if you have a Turtle Beach Wave Front, Maui,\n"
- "or Tropez sound card.\n",
+ "Enable this option if you have a Turtle Beach Wave Front, Maui,\n"
+ "or Tropez sound card.\n",
- "Use this option to enable experimental support for cards that\n"
- "use the Plug and Play protocol.\n",
+ "Use this option to enable experimental support for cards that\n"
+ "use the Plug and Play protocol.\n",
- "Enable this option if your card is a Sound Blaster Pro or\n"
- "Sound Blaster 16. It also works with many Sound Blaster Pro clones.\n",
+ "Use this option with Yamaha OPL3-SA1 (YMF701) chip.\n",
- "Enable this if you have a Sound Blaster 16, including the AWE32.\n",
+"SoftOSS is a virtual wave table engine by 4Front Technologies. It can\n"
+ "be used together with any 16 bit stereo soundcard.\n"
- "Enable this if you have an Audio Excel DSP16 card. See the file\n"
- "Readme.aedsp16 for more information.\n",
+ "Enable this option if your card is a Sound Blaster Pro or\n"
+ "Sound Blaster 16. It also works with many Sound Blaster Pro clones.\n",
- "This option enables the A/D and D/A converter (PCM) devices\n"
- "supported by almost all sound cards.\n",
+ "Enable this if you have a Sound Blaster 16, including the AWE32.\n",
- "This should not be asked",
+ "Enable this if you have an Audio Excel DSP16 card. See the file\n"
+ "Readme.aedsp16 for more information.\n",
- "This enables the dev/midixx devices and access to any MIDI ports\n"
- "using /dev/sequencer and /dev/music. This option also affects any\n"
- "MPU401 and/or General MIDI compatible devices.\n",
+ "This option enables the A/D and D/A converter (PCM) devices\n"
+ "supported by almost all sound cards.\n",
- "This should not be asked",
+ "This should not be asked",
- "This enables the Yamaha FM synthesizer chip used on many sound\n"
- "cards.\n",
+ "This enables the dev/midixx devices and access to any MIDI ports\n"
+ "using /dev/sequencer and /dev/music. This option also affects any\n"
+ "MPU401 and/or General MIDI compatible devices.\n",
- "Is the sky really falling"
+ "This should not be asked",
+
+ "This enables the Yamaha FM synthesizer chip used on many sound\n"
+ "cards.\n",
+
+ "Is the sky really falling"
};
struct kludge
{
- char *name;
- int mask;
+ char *name;
+ int mask;
}
extra_options[] =
{
- {
- "MPU_EMU", MPU_DEVS
- }
- ,
- {
- "AD1848", AD1848_DEVS
- }
- ,
- {
- "SBDSP", SBDSP_DEVS
- }
- ,
- {
- "UART401", UART401_DEVS
- }
- ,
- {
- "GUSHW", B (OPT_GUS) | B (OPT_SPNP)
- }
- ,
- {
- "SSCAPEHW", B (OPT_SSCAPE) | B (OPT_SPNP)
- }
- ,
- {
- "SEQUENCER", SEQUENCER_DEVS
- }
- ,
- {
- "AUDIO", AUDIO_CARDS
- }
- ,
- {
- "MIDI", MIDI_CARDS
- }
- ,
- {
- NULL, 0
- }
+ {
+ "MPU_EMU", MPU_DEVS
+ }
+ ,
+ {
+ "AD1848", AD1848_DEVS
+ }
+ ,
+ {
+ "SBDSP", SBDSP_DEVS
+ }
+ ,
+ {
+ "UART401", UART401_DEVS
+ }
+ ,
+ {
+ "GUSHW", B(OPT_GUS) | B(OPT_SPNP)
+ }
+ ,
+ {
+ "SSCAPEHW", B(OPT_SSCAPE) | B(OPT_SPNP)
+ }
+ ,
+ {
+ "SEQUENCER", SEQUENCER_DEVS
+ }
+ ,
+ {
+ "AUDIO", AUDIO_CARDS
+ }
+ ,
+ {
+ "MIDI", MIDI_CARDS
+ }
+ ,
+ {
+ NULL, 0
+ }
};
char *oldconf = "/etc/soundconf";
@@ -320,1330 +332,1338 @@ int sb_dma = 0;
int dump_only = 0;
-void build_defines (void);
+void build_defines(void);
#include "hex2hex.h"
-int bin2hex (char *path, char *target, char *varname);
+int bin2hex(char *path, char *target, char *varname);
int
-can_select_option (int nr)
+can_select_option(int nr)
{
- if (hw_table[nr].conditions)
- if (!(hw_table[nr].conditions & selected_options))
- return 0;
+ if (hw_table[nr].conditions)
+ if (!(hw_table[nr].conditions & selected_options))
+ return 0;
- if (hw_table[nr].exclusive_options)
- if (hw_table[nr].exclusive_options & selected_options)
- return 0;
+ if (hw_table[nr].exclusive_options)
+ if (hw_table[nr].exclusive_options & selected_options)
+ return 0;
- if (DISABLED_OPTIONS & B (nr))
- return 0;
+ if (DISABLED_OPTIONS & B(nr))
+ return 0;
- return 1;
+ return 1;
}
int
-think_positively (char *prompt, int def_answ, char *help)
+think_positively(char *prompt, int def_answ, char *help)
{
- char answ[512];
- int len;
-
-response:
- fprintf (stderr, prompt);
- if (def_answ)
- fprintf (stderr, " [Y/n/?] ");
- else
- fprintf (stderr, " [N/y/?] ");
-
- if ((len = read (0, answ, sizeof (answ))) < 1)
- {
- fprintf (stderr, "\n\nERROR! Cannot read stdin\n");
-
- perror ("stdin");
- printf ("invalid_configuration__run_make_config_again\n");
- exit (-1);
- }
-
- if (len < 2) /*
- * There is an additional LF at the end
- */
- return def_answ;
+ char answ[512];
+ int len;
+
+ response:
+ fprintf(stderr, prompt);
+ if (def_answ)
+ fprintf(stderr, " [Y/n/?] ");
+ else
+ fprintf(stderr, " [N/y/?] ");
- if (answ[0] == '?')
- { /* display help message */
- fprintf (stderr, "\n");
- fprintf (stderr, help);
- fprintf (stderr, "\n");
- goto response;
- }
+ if ((len = read(0, answ, sizeof(answ))) < 1)
+ {
+ fprintf(stderr, "\n\nERROR! Cannot read stdin\n");
- answ[len - 1] = 0;
+ perror("stdin");
+ printf("invalid_configuration__run_make_config_again\n");
+ exit(-1);
+ }
+ if (len < 2) /*
+ * There is an additional LF at the end
+ */
+ return def_answ;
+
+ if (answ[0] == '?')
+ { /* display help message */
+ fprintf(stderr, "\n");
+ fprintf(stderr, help);
+ fprintf(stderr, "\n");
+ goto response;
+ }
+ answ[len - 1] = 0;
- if (!strcmp (answ, "y") || !strcmp (answ, "Y"))
- return 1;
+ if (!strcmp(answ, "y") || !strcmp(answ, "Y"))
+ return 1;
- return 0;
+ return 0;
}
int
-ask_value (char *format, int default_answer)
+ask_value(char *format, int default_answer)
{
- char answ[512];
- int len, num;
+ char answ[512];
+ int len, num;
-play_it_again_Sam:
+ play_it_again_Sam:
- if ((len = read (0, answ, sizeof (answ))) < 1)
- {
- fprintf (stderr, "\n\nERROR! Cannot read stdin\n");
-
- perror ("stdin");
- printf ("invalid_configuration__run_make_config_again\n");
- exit (-1);
- }
+ if ((len = read(0, answ, sizeof(answ))) < 1)
+ {
+ fprintf(stderr, "\n\nERROR! Cannot read stdin\n");
- if (len < 2) /*
+ perror("stdin");
+ printf("invalid_configuration__run_make_config_again\n");
+ exit(-1);
+ }
+ if (len < 2) /*
* There is an additional LF at the end
*/
- return default_answer;
-
- answ[len - 1] = 0;
+ return default_answer;
- if (sscanf (answ, format, &num) != 1)
- {
- fprintf (stderr, "Illegal format. Try again: ");
- goto play_it_again_Sam;
- }
+ answ[len - 1] = 0;
- return num;
+ if (sscanf(answ, format, &num) != 1)
+ {
+ fprintf(stderr, "Illegal format. Try again: ");
+ goto play_it_again_Sam;
+ }
+ return num;
}
#define FMT_HEX 1
#define FMT_INT 2
void
-show_comment (int mask, char *txt)
+show_comment(int mask, char *txt)
{
- int i;
+ int i;
- if (dump_only)
- {
+ if (dump_only)
+ {
- for (i = 0; i < OPT_LAST; i++)
- if (mask == B (i))
+ for (i = 0; i < OPT_LAST; i++)
+ if (mask == B(i))
+ {
+ printf("\n\nif [ \"$CONFIG_%s\" = \"y\" ]; then\n",
+ hw_table[i].macro);
+ printf("comment '%s'\n", txt);
+ printf("fi\n");
+ }
+ } else
{
- printf ("\n\nif [ \"$CONFIG_%s\" = \"y\" ]; then\n",
- hw_table[i].macro);
- printf ("comment '%s'\n", txt);
- printf ("fi\n");
+ if (!(mask & selected_options))
+ return;
+
+ fprintf(stderr, "%s\n", txt);
}
- }
- else
- {
- if (!(mask & selected_options))
- return;
-
- fprintf (stderr, "%s\n", txt);
- }
}
void
-ask_int_choice (int mask, char *macro,
- char *question,
- int format,
- int defa,
- char *choices)
+ask_int_choice(int mask, char *macro,
+ char *question,
+ int format,
+ int defa,
+ char *choices)
{
- int num, i;
+ int num, i;
- if (dump_only)
- {
+ if (dump_only)
+ {
- for (i = 0; i < OPT_LAST; i++)
- if (mask == B (i))
+ for (i = 0; i < OPT_LAST; i++)
+ if (mask == B(i))
+ {
+ unsigned int j;
+
+ for (j = 0; j < strlen(choices); j++)
+ if (choices[j] == '\'')
+ choices[j] = '_';
+
+ printf("\nif [ \"$CONFIG_%s\" = \"y\" ]; then\n",
+ hw_table[i].macro);
+ if (format == FMT_INT)
+ printf("int '%s %s' %s %d\n", question, choices, macro, defa);
+ else
+ printf("hex '%s %s' %s %x\n", question, choices, macro, defa);
+ printf("fi\n");
+ }
+ } else
{
- unsigned int j;
-
- for (j = 0; j < strlen (choices); j++)
- if (choices[j] == '\'')
- choices[j] = '_';
-
- printf ("\nif [ \"$CONFIG_%s\" = \"y\" ]; then\n",
- hw_table[i].macro);
- if (format == FMT_INT)
- printf ("int '%s %s' %s %d\n", question, choices, macro, defa);
- else
- printf ("hex '%s %s' %s %x\n", question, choices, macro, defa);
- printf ("fi\n");
- }
- }
- else
- {
- if (!(mask & selected_options))
- return;
+ if (!(mask & selected_options))
+ return;
- fprintf (stderr, "\n%s\n", question);
- if (strcmp (choices, ""))
- fprintf (stderr, "Possible values are: %s\n", choices);
+ fprintf(stderr, "\n%s\n", question);
+ if (strcmp(choices, ""))
+ fprintf(stderr, "Possible values are: %s\n", choices);
- if (format == FMT_INT)
- {
- if (defa == -1)
- fprintf (stderr, "\t(-1 disables this feature)\n");
- fprintf (stderr, "The default value is %d\n", defa);
- fprintf (stderr, "Enter the value: ");
- num = ask_value ("%d", defa);
- if (num == -1)
- return;
- fprintf (stderr, "%s set to %d.\n", question, num);
- printf ("#define %s %d\n", macro, num);
- }
- else
- {
- if (defa == 0)
- fprintf (stderr, "\t(0 disables this feature)\n");
- fprintf (stderr, "The default value is %x\n", defa);
- fprintf (stderr, "Enter the value: ");
- num = ask_value ("%x", defa);
- if (num == 0)
- return;
- fprintf (stderr, "%s set to %x.\n", question, num);
- printf ("#define %s 0x%x\n", macro, num);
- }
- }
+ if (format == FMT_INT)
+ {
+ if (defa == -1)
+ fprintf(stderr, "\t(-1 disables this feature)\n");
+ fprintf(stderr, "The default value is %d\n", defa);
+ fprintf(stderr, "Enter the value: ");
+ num = ask_value("%d", defa);
+ if (num == -1)
+ return;
+ fprintf(stderr, "%s set to %d.\n", question, num);
+ printf("#define %s %d\n", macro, num);
+ } else
+ {
+ if (defa == 0)
+ fprintf(stderr, "\t(0 disables this feature)\n");
+ fprintf(stderr, "The default value is %x\n", defa);
+ fprintf(stderr, "Enter the value: ");
+ num = ask_value("%x", defa);
+ if (num == 0)
+ return;
+ fprintf(stderr, "%s set to %x.\n", question, num);
+ printf("#define %s 0x%x\n", macro, num);
+ }
+ }
}
void
-rebuild_file (char *line)
+rebuild_file(char *line)
{
- char *method, *next, *old, *var, *p;
+ char *method, *next, *old, *var, *p;
- method = p = line;
+ method = p = line;
- while (*p && *p != ' ')
- p++;
- *p++ = 0;
+ while (*p && *p != ' ')
+ p++;
+ *p++ = 0;
- old = p;
- while (*p && *p != ' ')
- p++;
- *p++ = 0;
+ old = p;
+ while (*p && *p != ' ')
+ p++;
+ *p++ = 0;
- next = p;
- while (*p && *p != ' ')
- p++;
- *p++ = 0;
+ next = p;
+ while (*p && *p != ' ')
+ p++;
+ *p++ = 0;
- var = p;
- while (*p && *p != ' ')
- p++;
- *p++ = 0;
+ var = p;
+ while (*p && *p != ' ')
+ p++;
+ *p++ = 0;
- fprintf (stderr, "Rebuilding file `%s' (%s %s)\n", next, method, old);
+ fprintf(stderr, "Rebuilding file `%s' (%s %s)\n", next, method, old);
- if (strcmp (method, "bin2hex") == 0)
- {
- if (!bin2hex (old, next, var))
- {
- fprintf (stderr, "Rebuild failed\n");
- exit (-1);
- }
- }
- else if (strcmp (method, "hex2hex") == 0)
- {
- if (!hex2hex (old, next, var))
- {
- fprintf (stderr, "Rebuild failed\n");
- exit (-1);
- }
- }
- else
- {
- fprintf (stderr, "Failed to build `%s' - unknown method %s\n",
- next, method);
- exit (-1);
- }
+ if (strcmp(method, "bin2hex") == 0)
+ {
+ if (!bin2hex(old, next, var))
+ {
+ fprintf(stderr, "Rebuild failed\n");
+ exit(-1);
+ }
+ } else if (strcmp(method, "hex2hex") == 0)
+ {
+ if (!hex2hex(old, next, var))
+ {
+ fprintf(stderr, "Rebuild failed\n");
+ exit(-1);
+ }
+ } else
+ {
+ fprintf(stderr, "Failed to build `%s' - unknown method %s\n",
+ next, method);
+ exit(-1);
+ }
}
int
-use_old_config (char *filename)
+use_old_config(char *filename)
{
- char buf[1024];
- int i = 0;
-
- FILE *oldf;
-
- fprintf (stderr, "Copying old configuration from `%s'\n", filename);
+ char buf[1024];
+ int i = 0;
- if ((oldf = fopen (filename, "r")) == NULL)
- {
- fprintf (stderr, "Couldn't open previous configuration file\n");
- perror (filename);
- return 0;
- }
-
- while (fgets (buf, 1024, oldf) != NULL)
- {
- char tmp[100];
-
- if (buf[0] != '#')
- {
- printf ("%s", buf);
+ FILE *oldf;
- strncpy (tmp, buf, 8);
- tmp[8] = 0;
+ fprintf(stderr, "Copying old configuration from `%s'\n", filename);
- if (strcmp (tmp, "/*build ") == 0)
- rebuild_file (&buf[8]);
+ if ((oldf = fopen(filename, "r")) == NULL)
+ {
+ fprintf(stderr, "Couldn't open previous configuration file\n");
+ perror(filename);
+ return 0;
+ }
+ while (fgets(buf, 1024, oldf) != NULL)
+ {
+ char tmp[100];
- continue;
- }
+ if (buf[0] != '#')
+ {
+ printf("%s", buf);
- strncpy (tmp, buf, 8);
- tmp[8] = 0;
+ strncpy(tmp, buf, 8);
+ tmp[8] = 0;
- if (strcmp (tmp, "#define ") == 0)
- {
- char *id = &buf[8];
-
- i = 0;
- while (id[i] && id[i] != ' ' &&
- id[i] != '\t' && id[i] != '\n')
- i++;
-
- strncpy (tmp, id, i);
- tmp[i] = 0;
-
- if (strcmp (tmp, "SELECTED_SOUND_OPTIONS") == 0)
- continue;
-
- if (strcmp (tmp, "KERNEL_SOUNDCARD") == 0)
- continue;
-
- if (strcmp (tmp, "JAZZ_DMA16") == 0) /* Rename it (hack) */
- {
- printf ("#define SB_DMA2 %s\n",
- &buf[18]);
- continue;
- }
-
- if (strcmp (tmp, "SB16_DMA") == 0) /* Rename it (hack) */
- {
- printf ("#define SB_DMA2 %s\n",
- &buf[16]);
- continue;
- }
-
- tmp[8] = 0; /* Truncate the string */
- if (strcmp (tmp, "EXCLUDE_") == 0)
- continue; /* Skip excludes */
-
- strncpy (tmp, id, i);
- tmp[7] = 0; /* Truncate the string */
-
- if (strcmp (tmp, "CONFIG_") == 0)
- {
- strncpy (tmp, &id[7], i - 7);
- tmp[i - 7] = 0;
-
- for (i = 0; i <= OPT_LAST; i++)
- if (strcmp (hw_table[i].macro, tmp) == 0)
- {
- selected_options |= (1 << i);
- break;
- }
- continue;
- }
-
- printf ("%s", buf);
- continue;
- }
+ if (strcmp(tmp, "/*build ") == 0)
+ rebuild_file(&buf[8]);
- if (strcmp (tmp, "#undef ") == 0)
- {
- char *id = &buf[8];
-
- i = 0;
- while (id[i] && id[i] != ' ' &&
- id[i] != '\t' && id[i] != '\n')
- i++;
-
- strncpy (tmp, id, i);
- tmp[7] = 0; /* Truncate the string */
- if (strcmp (tmp, "CONFIG_") == 0)
- continue;
-
- strncpy (tmp, id, i);
-
- tmp[8] = 0; /* Truncate the string */
- if (strcmp (tmp, "EXCLUDE_") != 0)
- continue; /* Not a #undef EXCLUDE_ line */
- strncpy (tmp, &id[8], i - 8);
- tmp[i - 8] = 0;
-
- for (i = 0; i <= OPT_LAST; i++)
- if (strcmp (hw_table[i].macro, tmp) == 0)
- {
- selected_options |= (1 << i);
- break;
- }
- continue;
- }
+ continue;
+ }
+ strncpy(tmp, buf, 8);
+ tmp[8] = 0;
- printf ("%s", buf);
- }
- fclose (oldf);
+ if (strcmp(tmp, "#define ") == 0)
+ {
+ char *id = &buf[8];
+
+ i = 0;
+ while (id[i] && id[i] != ' ' &&
+ id[i] != '\t' && id[i] != '\n')
+ i++;
+
+ strncpy(tmp, id, i);
+ tmp[i] = 0;
+
+ if (strcmp(tmp, "SELECTED_SOUND_OPTIONS") == 0)
+ continue;
+
+ if (strcmp(tmp, "KERNEL_SOUNDCARD") == 0)
+ continue;
+
+ if (strcmp(tmp, "JAZZ_DMA16") == 0) /* Rename it (hack) */
+ {
+ printf("#define SB_DMA2 %s\n",
+ &buf[18]);
+ continue;
+ }
+ if (strcmp(tmp, "SB16_DMA") == 0) /* Rename it (hack) */
+ {
+ printf("#define SB_DMA2 %s\n",
+ &buf[16]);
+ continue;
+ }
+ tmp[8] = 0; /* Truncate the string */
+ if (strcmp(tmp, "EXCLUDE_") == 0)
+ continue; /* Skip excludes */
+
+ strncpy(tmp, id, i);
+ tmp[7] = 0; /* Truncate the string */
+
+ if (strcmp(tmp, "CONFIG_") == 0)
+ {
+ strncpy(tmp, &id[7], i - 7);
+ tmp[i - 7] = 0;
+
+ for (i = 0; i <= OPT_LAST; i++)
+ if (strcmp(hw_table[i].macro, tmp) == 0)
+ {
+ selected_options |= (1 << i);
+ break;
+ }
+ continue;
+ }
+ printf("%s", buf);
+ continue;
+ }
+ if (strcmp(tmp, "#undef ") == 0)
+ {
+ char *id = &buf[8];
+
+ i = 0;
+ while (id[i] && id[i] != ' ' &&
+ id[i] != '\t' && id[i] != '\n')
+ i++;
+
+ strncpy(tmp, id, i);
+ tmp[7] = 0; /* Truncate the string */
+ if (strcmp(tmp, "CONFIG_") == 0)
+ continue;
+
+ strncpy(tmp, id, i);
+
+ tmp[8] = 0; /* Truncate the string */
+ if (strcmp(tmp, "EXCLUDE_") != 0)
+ continue; /* Not a #undef EXCLUDE_ line */
+ strncpy(tmp, &id[8], i - 8);
+ tmp[i - 8] = 0;
+
+ for (i = 0; i <= OPT_LAST; i++)
+ if (strcmp(hw_table[i].macro, tmp) == 0)
+ {
+ selected_options |= (1 << i);
+ break;
+ }
+ continue;
+ }
+ printf("%s", buf);
+ }
+ fclose(oldf);
- for (i = 0; i <= OPT_LAST; i++)
- if (!hw_table[i].alias)
- if (selected_options & B (i))
- printf ("#define CONFIG_%s\n", hw_table[i].macro);
- else
- printf ("#undef CONFIG_%s\n", hw_table[i].macro);
+ for (i = 0; i <= OPT_LAST; i++)
+ if (!hw_table[i].alias)
+ if (selected_options & B(i))
+ printf("#define CONFIG_%s\n", hw_table[i].macro);
+ else
+ printf("#undef CONFIG_%s\n", hw_table[i].macro);
- printf ("\n");
+ printf("\n");
- i = 0;
+ i = 0;
- while (extra_options[i].name != NULL)
- {
- if (selected_options & extra_options[i].mask)
- printf ("#define CONFIG_%s\n", extra_options[i].name);
- else
- printf ("#undef CONFIG_%s\n", extra_options[i].name);
- i++;
- }
+ while (extra_options[i].name != NULL)
+ {
+ if (selected_options & extra_options[i].mask)
+ printf("#define CONFIG_%s\n", extra_options[i].name);
+ else
+ printf("#undef CONFIG_%s\n", extra_options[i].name);
+ i++;
+ }
- printf ("\n");
+ printf("\n");
- printf ("#define SELECTED_SOUND_OPTIONS\t0x%08x\n", selected_options);
- fprintf (stderr, "Old configuration copied.\n");
+ printf("#define SELECTED_SOUND_OPTIONS\t0x%08x\n", selected_options);
+ fprintf(stderr, "Old configuration copied.\n");
- build_defines ();
- old_config_used = 1;
- return 1;
+ build_defines();
+ old_config_used = 1;
+ return 1;
}
void
-build_defines (void)
+build_defines(void)
{
- FILE *optf;
- int i;
+ FILE *optf;
+ int i;
- if ((optf = fopen (".defines", "w")) == NULL)
- {
- perror (".defines");
- exit (-1);
- }
-
-
- for (i = 0; i <= OPT_LAST; i++)
- if (!hw_table[i].alias)
- if (selected_options & B (i))
- fprintf (optf, "CONFIG_%s=y\n", hw_table[i].macro);
+ if ((optf = fopen(".defines", "w")) == NULL)
+ {
+ perror(".defines");
+ exit(-1);
+ }
+ for (i = 0; i <= OPT_LAST; i++)
+ if (!hw_table[i].alias)
+ if (selected_options & B(i))
+ fprintf(optf, "CONFIG_%s=y\n", hw_table[i].macro);
- fprintf (optf, "\n");
+ fprintf(optf, "\n");
- i = 0;
+ i = 0;
- while (extra_options[i].name != NULL)
- {
- if (selected_options & extra_options[i].mask)
- fprintf (optf, "CONFIG_%s=y\n", extra_options[i].name);
- i++;
- }
+ while (extra_options[i].name != NULL)
+ {
+ if (selected_options & extra_options[i].mask)
+ fprintf(optf, "CONFIG_%s=y\n", extra_options[i].name);
+ i++;
+ }
- fprintf (optf, "\n");
- fclose (optf);
+ fprintf(optf, "\n");
+ fclose(optf);
}
void
-ask_parameters (void)
+ask_parameters(void)
{
- int num;
-
- build_defines ();
- /*
- * IRQ and DMA settings
- */
-
- ask_int_choice (B (OPT_AEDSP16), "AEDSP16_BASE",
- "I/O base for Audio Excel DSP 16",
- FMT_HEX,
- 0x220,
- "220 or 240");
-
- ask_int_choice (B (OPT_SB), "SBC_BASE",
- "I/O base for SB",
- FMT_HEX,
- 0x220,
- "Check from manual of the card");
-
- ask_int_choice (B (OPT_SB), "SBC_IRQ",
- "Sound Blaster IRQ",
- FMT_INT,
- 7,
- "Check from manual of the card");
-
- ask_int_choice (B (OPT_SB), "SBC_DMA",
- "Sound Blaster DMA",
- FMT_INT,
- 1,
- "0, 1 or 3");
-
- ask_int_choice (B (OPT_SB), "SB_DMA2",
- "Sound Blaster 16 bit DMA (SB16, Jazz16, SMW)",
- FMT_INT,
- 5,
- "5, 6 or 7 (use 1 for 8 bit cards)");
-
- ask_int_choice (B (OPT_SB), "SB_MPU_BASE",
- "MPU401 I/O base of SB16, Jazz16 and ES1688",
- FMT_HEX,
- 0x330,
- "Check from manual of the card");
-
- show_comment (B (OPT_SB),
- "MPU401 IRQ is only required with Jazz16, SM Wave and ESS1688.");
- show_comment (B (OPT_SB),
- "Enter -1 to the following question if you have something else such as SB16/32.");
-
- ask_int_choice (B (OPT_SB), "SB_MPU_IRQ",
- "SB MPU401 IRQ (Jazz16, SM Wave and ES1688)",
- FMT_INT,
- -1,
- "Check from manual of the card");
-
- ask_int_choice (B (OPT_PAS), "PAS_IRQ",
- "PAS16 IRQ",
- FMT_INT,
- 10,
- "3, 4, 5, 7, 9, 10, 11, 12, 14 or 15");
-
- ask_int_choice (B (OPT_PAS), "PAS_DMA",
- "PAS16 DMA",
- FMT_INT,
- 3,
- "0, 1, 3, 5, 6 or 7");
-
- if (selected_options & B (OPT_PAS))
- {
- if (think_positively ("Enable Joystick port on ProAudioSpectrum", 0,
- "Enable this option if you want to use the joystick port provided\n"
- "on the PAS sound card.\n"))
- printf ("#define PAS_JOYSTICK_ENABLE\n");
-
- if (think_positively ("Enable PAS16 bus clock option", 0,
- "The PAS16 can be noisy with some motherboards. There is a command\n"
- "line switch (:T?) in the DOS driver for PAS16 which solves this.\n"
- "Don't enable this feature unless you have problems and have to use\n"
- "this switch with DOS\n"))
- printf ("#define BROKEN_BUS_CLOCK\n");
-
- if (think_positively ("Disable SB mode of PAS16", 0,
- "You should disable SB emulation of PAS16 if you want to use\n"
- "Another SB compatible card in the same system\n"))
- printf ("#define DISABLE_SB_EMULATION\n");
- }
-
- ask_int_choice (B (OPT_GUS), "GUS_BASE",
- "I/O base for GUS",
- FMT_HEX,
- 0x220,
- "210, 220, 230, 240, 250 or 260");
-
-
- ask_int_choice (B (OPT_GUS), "GUS_IRQ",
- "GUS IRQ",
- FMT_INT,
- 15,
- "3, 5, 7, 9, 11, 12 or 15");
-
- ask_int_choice (B (OPT_GUS), "GUS_DMA",
- "GUS DMA",
- FMT_INT,
- 6,
- "1, 3, 5, 6 or 7");
-
- ask_int_choice (B (OPT_GUS), "GUS_DMA2",
- "Second DMA channel for GUS",
- FMT_INT,
- -1,
- "1, 3, 5, 6 or 7");
-
- ask_int_choice (B (OPT_GUS16), "GUS16_BASE",
- "I/O base for the 16 bit daughtercard of GUS",
- FMT_HEX,
- 0x530,
- "530, 604, E80 or F40");
-
-
- ask_int_choice (B (OPT_GUS16), "GUS16_IRQ",
- "GUS 16 bit daughtercard IRQ",
- FMT_INT,
- 7,
- "3, 4, 5, 7, or 9");
-
- ask_int_choice (B (OPT_GUS16), "GUS16_DMA",
- "GUS DMA",
- FMT_INT,
- 3,
- "0, 1 or 3");
-
- ask_int_choice (B (OPT_MPU401), "MPU_BASE",
- "I/O base for MPU401",
- FMT_HEX,
- 0x330,
- "Check from manual of the card");
-
- ask_int_choice (B (OPT_MPU401), "MPU_IRQ",
- "MPU401 IRQ",
- FMT_INT,
- 9,
- "Check from manual of the card");
-
- if (dump_only)
- show_comment (B (OPT_MAUI),
- "ERROR! You have to use old sound configuration method with Maui.");
-
- ask_int_choice (B (OPT_MAUI), "MAUI_BASE",
- "I/O base for Maui",
- FMT_HEX,
- 0x330,
- "210, 230, 260, 290, 300, 320, 338 or 330");
-
- ask_int_choice (B (OPT_MAUI), "MAUI_IRQ",
- "Maui IRQ",
- FMT_INT,
- 9,
- "5, 9, 12 or 15");
-
- ask_int_choice (B (OPT_UART6850), "U6850_BASE",
- "I/O base for UART 6850 MIDI port",
- FMT_HEX,
- 0,
- "(Unknown)");
-
- ask_int_choice (B (OPT_UART6850), "U6850_IRQ",
- "UART6850 IRQ",
- FMT_INT,
- -1,
- "(Unknown)");
-
- if (dump_only)
- show_comment (B (OPT_PSS),
- "ERROR! You have to use old sound configuration method with PSS cards.");
-
- ask_int_choice (B (OPT_PSS), "PSS_BASE",
- "PSS I/O base",
- FMT_HEX,
- 0x220,
- "220 or 240");
-
- ask_int_choice (B (OPT_PSS), "PSS_MSS_BASE",
- "PSS audio I/O base",
- FMT_HEX,
- 0x530,
- "530, 604, E80 or F40");
-
- ask_int_choice (B (OPT_PSS), "PSS_MSS_IRQ",
- "PSS audio IRQ",
- FMT_INT,
- 11,
- "7, 9, 10 or 11");
-
- ask_int_choice (B (OPT_PSS), "PSS_MSS_DMA",
- "PSS audio DMA",
- FMT_INT,
- 3,
- "0, 1 or 3");
-
- ask_int_choice (B (OPT_PSS), "PSS_MPU_BASE",
- "PSS MIDI I/O base",
- FMT_HEX,
- 0x330,
- "");
-
- ask_int_choice (B (OPT_PSS), "PSS_MPU_IRQ",
- "PSS MIDI IRQ",
- FMT_INT,
- 9,
- "3, 4, 5, 7 or 9");
-
- ask_int_choice (B (OPT_MSS), "MSS_BASE",
- "MSS/WSS I/O base",
- FMT_HEX,
- 0x530,
- "530, 604, E80 or F40");
-
- ask_int_choice (B (OPT_MSS), "MSS_IRQ",
- "MSS/WSS IRQ",
- FMT_INT,
- 11,
- "7, 9, 10 or 11");
-
- ask_int_choice (B (OPT_MSS), "MSS_DMA",
- "MSS/WSS DMA",
- FMT_INT,
- 3,
- "0, 1 or 3");
-
- ask_int_choice (B (OPT_MSS), "MSS_DMA2",
- "MSS/WSS second DMA (if possible)",
- FMT_INT,
- -1,
- "0, 1 or 3");
-
- ask_int_choice (B (OPT_SSCAPE), "SSCAPE_BASE",
- "SoundScape MIDI I/O base",
- FMT_HEX,
- 0x330,
- "320, 330, 340 or 350");
-
- ask_int_choice (B (OPT_SSCAPE), "SSCAPE_IRQ",
- "SoundScape MIDI IRQ",
- FMT_INT,
- 9,
- "");
-
- ask_int_choice (B (OPT_SSCAPE), "SSCAPE_DMA",
- "SoundScape initialization DMA",
- FMT_INT,
- 3,
- "0, 1 or 3");
-
- ask_int_choice (B (OPT_SSCAPE), "SSCAPE_MSS_BASE",
- "SoundScape audio I/O base",
- FMT_HEX,
- 0x534,
- "534, 608, E84 or F44");
-
- ask_int_choice (B (OPT_SSCAPE), "SSCAPE_MSS_IRQ",
- "SoundScape audio IRQ",
- FMT_INT,
- 11,
- "7, 9, 10 or 11");
-
-
- if (selected_options & B (OPT_SSCAPE))
- {
- int reveal_spea;
-
- reveal_spea = think_positively (
- "Is your SoundScape card made/marketed by Reveal or Spea",
- 0,
- "Enable if you have a SoundScape card with the Reveal or\n"
- "Spea name on it.\n");
- if (reveal_spea)
- printf ("#define REVEAL_SPEA\n");
-
- }
-
- if (dump_only)
- show_comment (B (OPT_TRIX),
- "ERROR! You have to use old sound configuration method with AudioTrix.");
-
- ask_int_choice (B (OPT_TRIX), "TRIX_BASE",
- "AudioTrix audio I/O base",
- FMT_HEX,
- 0x530,
- "530, 604, E80 or F40");
-
- ask_int_choice (B (OPT_TRIX), "TRIX_IRQ",
- "AudioTrix audio IRQ",
- FMT_INT,
- 11,
- "7, 9, 10 or 11");
-
- ask_int_choice (B (OPT_TRIX), "TRIX_DMA",
- "AudioTrix audio DMA",
- FMT_INT,
- 0,
- "0, 1 or 3");
-
- ask_int_choice (B (OPT_TRIX), "TRIX_DMA2",
- "AudioTrix second (duplex) DMA",
- FMT_INT,
- 3,
- "0, 1 or 3");
-
- ask_int_choice (B (OPT_TRIX), "TRIX_MPU_BASE",
- "AudioTrix MIDI I/O base",
- FMT_HEX,
- 0x330,
- "330, 370, 3B0 or 3F0");
-
- ask_int_choice (B (OPT_TRIX), "TRIX_MPU_IRQ",
- "AudioTrix MIDI IRQ",
- FMT_INT,
- 9,
- "3, 4, 5, 7 or 9");
-
- ask_int_choice (B (OPT_TRIX), "TRIX_SB_BASE",
- "AudioTrix SB I/O base",
- FMT_HEX,
- 0x220,
- "220, 210, 230, 240, 250, 260 or 270");
-
- ask_int_choice (B (OPT_TRIX), "TRIX_SB_IRQ",
- "AudioTrix SB IRQ",
- FMT_INT,
- 7,
- "3, 4, 5 or 7");
-
- ask_int_choice (B (OPT_TRIX), "TRIX_SB_DMA",
- "AudioTrix SB DMA",
- FMT_INT,
- 1,
- "1 or 3");
-
- ask_int_choice (B (OPT_CS4232), "CS4232_BASE",
- "CS4232 audio I/O base",
- FMT_HEX,
- 0x530,
- "530, 604, E80 or F40");
-
- ask_int_choice (B (OPT_CS4232), "CS4232_IRQ",
- "CS4232 audio IRQ",
- FMT_INT,
- 11,
- "5, 7, 9, 11, 12 or 15");
-
- ask_int_choice (B (OPT_CS4232), "CS4232_DMA",
- "CS4232 audio DMA",
- FMT_INT,
- 0,
- "0, 1 or 3");
-
- ask_int_choice (B (OPT_CS4232), "CS4232_DMA2",
- "CS4232 second (duplex) DMA",
- FMT_INT,
- 3,
- "0, 1 or 3");
-
- ask_int_choice (B (OPT_CS4232), "CS4232_MPU_BASE",
- "CS4232 MIDI I/O base",
- FMT_HEX,
- 0x330,
- "330, 370, 3B0 or 3F0");
-
- ask_int_choice (B (OPT_CS4232), "CS4232_MPU_IRQ",
- "CS4232 MIDI IRQ",
- FMT_INT,
- 9,
- "5, 7, 9, 11, 12 or 15");
-
- ask_int_choice (B (OPT_MAD16), "MAD16_BASE",
- "MAD16 audio I/O base",
- FMT_HEX,
- 0x530,
- "530, 604, E80 or F40");
-
- ask_int_choice (B (OPT_MAD16), "MAD16_IRQ",
- "MAD16 audio IRQ",
- FMT_INT,
- 11,
- "7, 9, 10 or 11");
-
- ask_int_choice (B (OPT_MAD16), "MAD16_DMA",
- "MAD16 audio DMA",
- FMT_INT,
- 3,
- "0, 1 or 3");
-
- ask_int_choice (B (OPT_MAD16), "MAD16_DMA2",
- "MAD16 second (duplex) DMA",
- FMT_INT,
- 0,
- "0, 1 or 3");
-
- ask_int_choice (B (OPT_MAD16), "MAD16_MPU_BASE",
- "MAD16 MIDI I/O base",
- FMT_HEX,
- 0x330,
- "300, 310, 320 or 330 (0 disables)");
-
- ask_int_choice (B (OPT_MAD16), "MAD16_MPU_IRQ",
- "MAD16 MIDI IRQ",
- FMT_INT,
- 9,
- "5, 7, 9 or 10");
+ int num;
+
+ build_defines();
+ /*
+ * IRQ and DMA settings
+ */
+
+ ask_int_choice(B(OPT_AEDSP16), "AEDSP16_BASE",
+ "I/O base for Audio Excel DSP 16",
+ FMT_HEX,
+ 0x220,
+ "220 or 240");
+
+ ask_int_choice(B(OPT_SB), "SBC_BASE",
+ "I/O base for SB",
+ FMT_HEX,
+ 0x220,
+ "Check from manual of the card");
+
+ ask_int_choice(B(OPT_SB), "SBC_IRQ",
+ "Sound Blaster IRQ",
+ FMT_INT,
+ 7,
+ "Check from manual of the card");
+
+ ask_int_choice(B(OPT_SB), "SBC_DMA",
+ "Sound Blaster DMA",
+ FMT_INT,
+ 1,
+ "0, 1 or 3");
+
+ ask_int_choice(B(OPT_SB), "SB_DMA2",
+ "Sound Blaster 16 bit DMA (SB16, Jazz16, SMW)",
+ FMT_INT,
+ 5,
+ "5, 6 or 7 (use 1 for 8 bit cards)");
+
+ ask_int_choice(B(OPT_SB), "SB_MPU_BASE",
+ "MPU401 I/O base of SB16, Jazz16 and ES1688",
+ FMT_HEX,
+ 0x330,
+ "Check from manual of the card");
+
+ show_comment(B(OPT_SB),
+ "MPU401 IRQ is only required with Jazz16, SM Wave and ESS1688.");
+ show_comment(B(OPT_SB),
+ "Enter -1 to the following question if you have something else such as SB16/32.");
+
+ ask_int_choice(B(OPT_SB), "SB_MPU_IRQ",
+ "SB MPU401 IRQ (Jazz16, SM Wave and ES1688)",
+ FMT_INT,
+ -1,
+ "Check from manual of the card");
+
+ ask_int_choice(B(OPT_PAS), "PAS_IRQ",
+ "PAS16 IRQ",
+ FMT_INT,
+ 10,
+ "3, 4, 5, 7, 9, 10, 11, 12, 14 or 15");
+
+ ask_int_choice(B(OPT_PAS), "PAS_DMA",
+ "PAS16 DMA",
+ FMT_INT,
+ 3,
+ "0, 1, 3, 5, 6 or 7");
+
+ if (selected_options & B(OPT_PAS))
+ {
+ if (think_positively("Enable Joystick port on ProAudioSpectrum", 0,
+ "Enable this option if you want to use the joystick port provided\n"
+ "on the PAS sound card.\n"))
+ printf("#define PAS_JOYSTICK_ENABLE\n");
+
+ if (think_positively("Enable PAS16 bus clock option", 0,
+ "The PAS16 can be noisy with some motherboards. There is a command\n"
+ "line switch (:T?) in the DOS driver for PAS16 which solves this.\n"
+ "Don't enable this feature unless you have problems and have to use\n"
+ "this switch with DOS\n"))
+ printf("#define BROKEN_BUS_CLOCK\n");
+
+ if (think_positively("Disable SB mode of PAS16", 0,
+ "You should disable SB emulation of PAS16 if you want to use\n"
+ "Another SB compatible card in the same system\n"))
+ printf("#define DISABLE_SB_EMULATION\n");
+ }
+ ask_int_choice(B(OPT_GUS), "GUS_BASE",
+ "I/O base for GUS",
+ FMT_HEX,
+ 0x220,
+ "210, 220, 230, 240, 250 or 260");
+
+
+ ask_int_choice(B(OPT_GUS), "GUS_IRQ",
+ "GUS IRQ",
+ FMT_INT,
+ 15,
+ "3, 5, 7, 9, 11, 12 or 15");
+
+ ask_int_choice(B(OPT_GUS), "GUS_DMA",
+ "GUS DMA",
+ FMT_INT,
+ 6,
+ "1, 3, 5, 6 or 7");
+
+ ask_int_choice(B(OPT_GUS), "GUS_DMA2",
+ "Second DMA channel for GUS",
+ FMT_INT,
+ -1,
+ "1, 3, 5, 6 or 7");
+
+ ask_int_choice(B(OPT_GUS16), "GUS16_BASE",
+ "I/O base for the 16 bit daughtercard of GUS",
+ FMT_HEX,
+ 0x530,
+ "530, 604, E80 or F40");
+
+
+ ask_int_choice(B(OPT_GUS16), "GUS16_IRQ",
+ "GUS 16 bit daughtercard IRQ",
+ FMT_INT,
+ 7,
+ "3, 4, 5, 7, or 9");
+
+ ask_int_choice(B(OPT_GUS16), "GUS16_DMA",
+ "GUS DMA",
+ FMT_INT,
+ 3,
+ "0, 1 or 3");
+
+ ask_int_choice(B(OPT_MPU401), "MPU_BASE",
+ "I/O base for MPU401",
+ FMT_HEX,
+ 0x330,
+ "Check from manual of the card");
+
+ ask_int_choice(B(OPT_MPU401), "MPU_IRQ",
+ "MPU401 IRQ",
+ FMT_INT,
+ 9,
+ "Check from manual of the card");
+
+ if (dump_only)
+ show_comment(B(OPT_MAUI),
+ "ERROR! You have to use old sound configuration method with Maui.");
+
+ ask_int_choice(B(OPT_MAUI), "MAUI_BASE",
+ "I/O base for Maui",
+ FMT_HEX,
+ 0x330,
+ "210, 230, 260, 290, 300, 320, 338 or 330");
+
+ ask_int_choice(B(OPT_MAUI), "MAUI_IRQ",
+ "Maui IRQ",
+ FMT_INT,
+ 9,
+ "5, 9, 12 or 15");
+
+ ask_int_choice(B(OPT_UART6850), "U6850_BASE",
+ "I/O base for UART 6850 MIDI port",
+ FMT_HEX,
+ 0,
+ "(Unknown)");
+
+ ask_int_choice(B(OPT_UART6850), "U6850_IRQ",
+ "UART6850 IRQ",
+ FMT_INT,
+ -1,
+ "(Unknown)");
+
+ if (dump_only)
+ show_comment(B(OPT_PSS),
+ "ERROR! You have to use old sound configuration method with PSS cards.");
+
+ ask_int_choice(B(OPT_PSS), "PSS_BASE",
+ "PSS I/O base",
+ FMT_HEX,
+ 0x220,
+ "220 or 240");
+
+ ask_int_choice(B(OPT_PSS), "PSS_MSS_BASE",
+ "PSS audio I/O base",
+ FMT_HEX,
+ 0x530,
+ "530, 604, E80 or F40");
+
+ ask_int_choice(B(OPT_PSS), "PSS_MSS_IRQ",
+ "PSS audio IRQ",
+ FMT_INT,
+ 11,
+ "7, 9, 10 or 11");
+
+ ask_int_choice(B(OPT_PSS), "PSS_MSS_DMA",
+ "PSS audio DMA",
+ FMT_INT,
+ 3,
+ "0, 1 or 3");
+
+ ask_int_choice(B(OPT_PSS), "PSS_MPU_BASE",
+ "PSS MIDI I/O base",
+ FMT_HEX,
+ 0x330,
+ "");
+
+ ask_int_choice(B(OPT_PSS), "PSS_MPU_IRQ",
+ "PSS MIDI IRQ",
+ FMT_INT,
+ 9,
+ "3, 4, 5, 7 or 9");
+
+ ask_int_choice(B(OPT_MSS), "MSS_BASE",
+ "MSS/WSS I/O base",
+ FMT_HEX,
+ 0x530,
+ "530, 604, E80 or F40");
+
+ ask_int_choice(B(OPT_MSS), "MSS_IRQ",
+ "MSS/WSS IRQ",
+ FMT_INT,
+ 11,
+ "7, 9, 10 or 11");
+
+ ask_int_choice(B(OPT_MSS), "MSS_DMA",
+ "MSS/WSS DMA",
+ FMT_INT,
+ 3,
+ "0, 1 or 3");
+
+ ask_int_choice(B(OPT_MSS), "MSS_DMA2",
+ "MSS/WSS second DMA (if possible)",
+ FMT_INT,
+ -1,
+ "0, 1 or 3");
+
+ ask_int_choice(B(OPT_SSCAPE), "SSCAPE_BASE",
+ "SoundScape MIDI I/O base",
+ FMT_HEX,
+ 0x330,
+ "320, 330, 340 or 350");
+
+ ask_int_choice(B(OPT_SSCAPE), "SSCAPE_IRQ",
+ "SoundScape MIDI IRQ",
+ FMT_INT,
+ 9,
+ "");
+
+ ask_int_choice(B(OPT_SSCAPE), "SSCAPE_DMA",
+ "SoundScape initialization DMA",
+ FMT_INT,
+ 3,
+ "0, 1 or 3");
+
+ ask_int_choice(B(OPT_SSCAPE), "SSCAPE_MSS_BASE",
+ "SoundScape audio I/O base",
+ FMT_HEX,
+ 0x534,
+ "534, 608, E84 or F44");
+
+ ask_int_choice(B(OPT_SSCAPE), "SSCAPE_MSS_IRQ",
+ "SoundScape audio IRQ",
+ FMT_INT,
+ 11,
+ "7, 9, 10 or 11");
+
+
+ if (selected_options & B(OPT_SSCAPE))
+ {
+ int reveal_spea;
+
+ reveal_spea = think_positively(
+ "Is your SoundScape card made/marketed by Reveal or Spea",
+ 0,
+ "Enable if you have a SoundScape card with the Reveal or\n"
+ "Spea name on it.\n");
+ if (reveal_spea)
+ printf("#define REVEAL_SPEA\n");
+
+ }
+ if (dump_only)
+ show_comment(B(OPT_TRIX),
+ "ERROR! You have to use old sound configuration method with OPL3-SA1.");
+
+ ask_int_choice(B(OPT_TRIX), "TRIX_BASE",
+ "OPL3-SA1 audio I/O base",
+ FMT_HEX,
+ 0x530,
+ "530, 604, E80 or F40");
+
+ ask_int_choice(B(OPT_TRIX), "TRIX_IRQ",
+ "OPL3-SA1 audio IRQ",
+ FMT_INT,
+ 11,
+ "7, 9, 10 or 11");
+
+ ask_int_choice(B(OPT_TRIX), "TRIX_DMA",
+ "OPL3-SA1 audio DMA",
+ FMT_INT,
+ 0,
+ "0, 1 or 3");
+
+ ask_int_choice(B(OPT_TRIX), "TRIX_DMA2",
+ "OPL3-SA1 second (duplex) DMA",
+ FMT_INT,
+ 3,
+ "0, 1 or 3");
+
+ ask_int_choice(B(OPT_TRIX), "TRIX_MPU_BASE",
+ "OPL3-SA1 MIDI I/O base",
+ FMT_HEX,
+ 0x330,
+ "330, 370, 3B0 or 3F0");
+
+ ask_int_choice(B(OPT_TRIX), "TRIX_MPU_IRQ",
+ "OPL3-SA1 MIDI IRQ",
+ FMT_INT,
+ 9,
+ "3, 4, 5, 7 or 9");
+
+ ask_int_choice(B(OPT_TRIX), "TRIX_SB_BASE",
+ "OPL3-SA1 SB I/O base",
+ FMT_HEX,
+ 0x220,
+ "220, 210, 230, 240, 250, 260 or 270");
+
+ ask_int_choice(B(OPT_TRIX), "TRIX_SB_IRQ",
+ "OPL3-SA1 SB IRQ",
+ FMT_INT,
+ 7,
+ "3, 4, 5 or 7");
+
+ ask_int_choice(B(OPT_TRIX), "TRIX_SB_DMA",
+ "OPL3-SA1 SB DMA",
+ FMT_INT,
+ 1,
+ "1 or 3");
+
+
+ ask_int_choice(B(OPT_OPL3SA1), "OPL3SA1_BASE",
+ "OPL3-SA1 audio I/O base",
+ FMT_HEX,
+ 0x530,
+ "530, 604, E80 or F40");
+
+ ask_int_choice(B(OPT_OPL3SA1), "OPL3SA1_IRQ",
+ "OPL3-SA1 audio IRQ",
+ FMT_INT,
+ 11,
+ "7, 9, 10 or 11");
+
+ ask_int_choice(B(OPT_OPL3SA1), "OPL3SA1_DMA",
+ "OPL3-SA1 audio DMA",
+ FMT_INT,
+ 0,
+ "0, 1 or 3");
+
+ ask_int_choice(B(OPT_OPL3SA1), "OPL3SA1_DMA2",
+ "OPL3-SA1 second (duplex) DMA",
+ FMT_INT,
+ 3,
+ "0, 1 or 3");
+
+ ask_int_choice(B(OPT_OPL3SA1), "OPL3SA1_MPU_BASE",
+ "OPL3-SA1 MIDI I/O base",
+ FMT_HEX,
+ 0x330,
+ "330, 370, 3B0 or 3F0");
+
+ ask_int_choice(B(OPT_OPL3SA1), "OPL3SA1_MPU_IRQ",
+ "OPL3-SA1 MIDI IRQ",
+ FMT_INT,
+ 9,
+ "3, 4, 5, 7 or 9");
+
+ ask_int_choice(B(OPT_CS4232), "CS4232_BASE",
+ "CS4232 audio I/O base",
+ FMT_HEX,
+ 0x530,
+ "530, 604, E80 or F40");
+
+ ask_int_choice(B(OPT_CS4232), "CS4232_IRQ",
+ "CS4232 audio IRQ",
+ FMT_INT,
+ 11,
+ "5, 7, 9, 11, 12 or 15");
+
+ ask_int_choice(B(OPT_CS4232), "CS4232_DMA",
+ "CS4232 audio DMA",
+ FMT_INT,
+ 0,
+ "0, 1 or 3");
+
+ ask_int_choice(B(OPT_CS4232), "CS4232_DMA2",
+ "CS4232 second (duplex) DMA",
+ FMT_INT,
+ 3,
+ "0, 1 or 3");
+
+ ask_int_choice(B(OPT_CS4232), "CS4232_MPU_BASE",
+ "CS4232 MIDI I/O base",
+ FMT_HEX,
+ 0x330,
+ "330, 370, 3B0 or 3F0");
+
+ ask_int_choice(B(OPT_CS4232), "CS4232_MPU_IRQ",
+ "CS4232 MIDI IRQ",
+ FMT_INT,
+ 9,
+ "5, 7, 9, 11, 12 or 15");
+
+ ask_int_choice(B(OPT_MAD16), "MAD16_BASE",
+ "MAD16 audio I/O base",
+ FMT_HEX,
+ 0x530,
+ "530, 604, E80 or F40");
+
+ ask_int_choice(B(OPT_MAD16), "MAD16_IRQ",
+ "MAD16 audio IRQ",
+ FMT_INT,
+ 11,
+ "7, 9, 10 or 11");
+
+ ask_int_choice(B(OPT_MAD16), "MAD16_DMA",
+ "MAD16 audio DMA",
+ FMT_INT,
+ 3,
+ "0, 1 or 3");
+
+ ask_int_choice(B(OPT_MAD16), "MAD16_DMA2",
+ "MAD16 second (duplex) DMA",
+ FMT_INT,
+ 0,
+ "0, 1 or 3");
+
+ ask_int_choice(B(OPT_MAD16), "MAD16_MPU_BASE",
+ "MAD16 MIDI I/O base",
+ FMT_HEX,
+ 0x330,
+ "300, 310, 320 or 330 (0 disables)");
+
+ ask_int_choice(B(OPT_MAD16), "MAD16_MPU_IRQ",
+ "MAD16 MIDI IRQ",
+ FMT_INT,
+ 9,
+ "5, 7, 9 or 10");
+ ask_int_choice(B(OPT_SOFTOSS), "SOFTOSS_RATE",
+ "Sampling rate for SoftOSS",
+ FMT_INT,
+ 22050,
+ "8000 to 48000");
+ ask_int_choice(B(OPT_SOFTOSS), "SOFTOSS_VOICES",
+ "Max # of concurrent voices for SoftOSS",
+ FMT_INT,
+ 32,
+ "4 to 32");
}
void
-dump_script (void)
+dump_script(void)
{
- int i;
-
- for (i = 0; i <= OPT_LAST; i++)
- if (!(DUMMY_OPTS & B (i)))
- if (!(DISABLED_OPTIONS & B (i)))
- {
- printf ("bool '%s' CONFIG_%s\n", questions[i], hw_table[i].macro);
- }
-
+ int i;
+
+ for (i = 0; i <= OPT_LAST; i++)
+ if (!(DUMMY_OPTS & B(i)))
+ if (!(DISABLED_OPTIONS & B(i)))
+ {
+ printf("bool '%s' CONFIG_%s\n", questions[i], hw_table[i].macro);
+ }
/*
* Some "hardcoded" options
*/
- dump_only = 1;
- selected_options = 0;
- ask_parameters ();
+ dump_only = 1;
+ selected_options = 0;
+ ask_parameters();
- printf ("#\n$MAKE -C drivers/sound kernelconfig || exit 1\n");
+ printf("#\n$MAKE -C drivers/sound kernelconfig || exit 1\n");
}
void
-dump_fixed_local (void)
+dump_fixed_local(void)
{
- int i = 0;
-
- printf ("/* Computer generated file. Please don't edit! */\n\n");
- printf ("#define KERNEL_COMPATIBLE_CONFIG\n\n");
- printf ("#define SELECTED_SOUND_OPTIONS\t0x%08x\n\n", selected_options);
-
- while (extra_options[i].name != NULL)
- {
- int n = 0, j;
-
- printf ("#if ");
-
- for (j = 0; j < OPT_LAST; j++)
- if (!(DISABLED_OPTIONS & B (j)))
- if (extra_options[i].mask & B (j))
- {
- if (n)
- printf (" || ");
- if (!(n++ % 2))
- printf ("\\\n ");
-
- printf ("defined(CONFIG_%s)", hw_table[j].macro);
- }
-
- printf ("\n");
- printf ("#\tdefine CONFIG_%s\n", extra_options[i].name);
- printf ("#endif\n\n");
- i++;
- }
+ int i = 0;
+
+ printf("/* Computer generated file. Please don't edit! */\n\n");
+ printf("#define KERNEL_COMPATIBLE_CONFIG\n\n");
+ printf("#define SELECTED_SOUND_OPTIONS\t0x%08x\n\n", selected_options);
+
+ while (extra_options[i].name != NULL)
+ {
+ int n = 0, j;
+
+ printf("#if ");
+
+ for (j = 0; j < OPT_LAST; j++)
+ if (!(DISABLED_OPTIONS & B(j)))
+ if (extra_options[i].mask & B(j))
+ {
+ if (n)
+ printf(" || ");
+ if (!(n++ % 2))
+ printf("\\\n ");
+
+ printf("defined(CONFIG_%s)", hw_table[j].macro);
+ }
+ printf("\n");
+ printf("#\tdefine CONFIG_%s\n", extra_options[i].name);
+ printf("#endif\n\n");
+ i++;
+ }
}
void
-dump_fixed_defines (void)
+dump_fixed_defines(void)
{
- int i = 0;
+ int i = 0;
- printf ("# Computer generated file. Please don't edit\n\n");
+ printf("# Computer generated file. Please don't edit\n\n");
- while (extra_options[i].name != NULL)
- {
- int j;
-
- for (j = 0; j < OPT_LAST; j++)
- if (!(DISABLED_OPTIONS & B (j)))
- if (extra_options[i].mask & B (j))
- {
- printf ("ifdef CONFIG_%s\n", hw_table[j].macro);
- printf ("CONFIG_%s=y\n", extra_options[i].name);
- printf ("endif\n\n");
- }
+ while (extra_options[i].name != NULL)
+ {
+ int j;
- i++;
- }
+ for (j = 0; j < OPT_LAST; j++)
+ {
+ if (!(DISABLED_OPTIONS & B(j)))
+ {
+ if (extra_options[i].mask & B(j))
+ {
+ printf("ifdef CONFIG_%s\n", hw_table[j].macro);
+ printf ("ifneq ($(CONFIG_%s),Y)\n", extra_options[i].name);
+ printf("CONFIG_%s=y\n", extra_options[i].name);
+ printf("endif\n");
+ printf("endif\n\n");
+ }
+ }
+ }
+ i++;
+ }
}
int
-main (int argc, char *argv[])
+main(int argc, char *argv[])
{
- int i, full_driver = 1;
- char old_config_file[200];
+ int i, full_driver = 1;
+ char old_config_file[200];
- if (getuid () != 0) /* Not root */
- {
- char *home;
-
- if ((home = getenv ("HOME")) != NULL)
- {
- sprintf (old_config_file, "%s/.soundconf", home);
- oldconf = old_config_file;
- }
- }
-
- if (argc > 1)
- {
- if (strcmp (argv[1], "-o") == 0 &&
- use_old_config (oldconf))
- exit (0);
- else if (strcmp (argv[1], "script") == 0)
- {
- dump_script ();
- exit (0);
- }
- else if (strcmp (argv[1], "fixedlocal") == 0)
- {
- dump_fixed_local ();
- exit (0);
- }
- else if (strcmp (argv[1], "fixeddefines") == 0)
- {
- dump_fixed_defines ();
- exit (0);
- }
- }
+ if (getuid() != 0) /* Not root */
+ {
+ char *home;
- fprintf (stderr, "\nConfiguring Sound Support\n\n");
+ if ((home = getenv("HOME")) != NULL)
+ {
+ sprintf(old_config_file, "%s/.soundconf", home);
+ oldconf = old_config_file;
+ }
+ }
+ if (argc > 1)
+ {
+ if (strcmp(argv[1], "-o") == 0 &&
+ use_old_config(oldconf))
+ exit(0);
+ else if (strcmp(argv[1], "script") == 0)
+ {
+ dump_script();
+ exit(0);
+ } else if (strcmp(argv[1], "fixedlocal") == 0)
+ {
+ dump_fixed_local();
+ exit(0);
+ } else if (strcmp(argv[1], "fixeddefines") == 0)
+ {
+ dump_fixed_defines();
+ exit(0);
+ }
+ }
+ fprintf(stderr, "\nConfiguring Sound Support\n\n");
- if (access (oldconf, R_OK) == 0)
- {
- char str[255];
+ if (access(oldconf, R_OK) == 0)
+ {
+ char str[255];
- sprintf (str, "Old configuration exists in `%s'. Use it", oldconf);
- if (think_positively (str, 1,
- "Enable this option to load the previously saved configuration file\n"
+ sprintf(str, "Old configuration exists in `%s'. Use it", oldconf);
+ if (think_positively(str, 1,
+ "Enable this option to load the previously saved configuration file\n"
"for all of the sound driver parameters.\n"))
- if (use_old_config (oldconf))
- exit (0);
- }
+ if (use_old_config(oldconf))
+ exit(0);
+ }
+ printf("/*\tGenerated by configure. Don't edit!!!!\t*/\n");
+ printf("/*\tMaking changes to this file is not as simple as it may look.\t*/\n\n");
+ printf("/*\tIf you change the CONFIG_ settings in local.h you\t*/\n");
+ printf("/*\t_have_ to edit .defines too.\t*/\n\n");
- printf ("/*\tGenerated by configure. Don't edit!!!!\t*/\n");
- printf ("/*\tMaking changes to this file is not as simple as it may look.\t*/\n\n");
- printf ("/*\tIf you change the CONFIG_ settings in local.h you\t*/\n");
- printf ("/*\t_have_ to edit .defines too.\t*/\n\n");
+ {
+ /*
+ * Partial driver
+ */
+
+ full_driver = 0;
+
+ for (i = 0; i <= OPT_LAST; i++)
+ if (can_select_option(i))
+ {
+ if (!(selected_options & B(i))) /*
+ * Not selected yet
+ */
+ if (!hw_table[i].verify)
+ {
+ if (hw_table[i].alias)
+ selected_options |= B(hw_table[i].alias);
+ else
+ selected_options |= B(i);
+ } else
+ {
+ int def_answ = hw_table[i].default_answ;
+
+ if (think_positively(questions[i], def_answ, help[i]))
+ if (hw_table[i].alias)
+ selected_options |= B(hw_table[i].alias);
+ else
+ selected_options |= B(i);
+ }
+ }
+ }
- {
- /*
- * Partial driver
- */
+ if (selected_options & B(OPT_SB))
+ {
+ if (think_positively(
+ "Support for the SG NX Pro mixer", 0,
+ "Enable this if you want to support the additional mixer functions\n"
+ "provided on Sound Galaxy NX Pro sound cards.\n"))
+ printf("#define __SGNXPRO__\n");
+ }
+ if (selected_options & B(OPT_SB))
+ {
+ if (think_positively("Support for the MV Jazz16 (ProSonic etc.)", 0,
+ "Enable this if you have an MV Jazz16 or ProSonic sound card.\n"))
+ {
+ if (think_positively("Do you have SoundMan Wave", 0,
+ "Enable this option of you have the Logitech SoundMan Wave sound card.\n"))
+ {
+ printf("#define SM_WAVE\n");
+
+ midi0001_again:
+ if (think_positively(
+ "Do you have access to the MIDI0001.BIN file", 1,
+ "The Logitech SoundMan Wave has a microcontroller which must be\n"
+ "initialized before MIDI emulation works. This is possible only if the\n"
+ "microcode file is compiled into the driver.\n"))
+ {
+ char path[512];
+
+ fprintf(stderr,
+ "Enter full name of the MIDI0001.BIN file (pwd is sound): ");
+ scanf("%s", path);
+ fprintf(stderr, "including microcode file %s\n", path);
+
+ if (!bin2hex(path, "smw-midi0001.h", "smw_ucode"))
+ {
+ fprintf(stderr, "Couldn't open file %s\n",
+ path);
+ if (think_positively("Try again with correct path", 1,
+ "The specified file could not be opened. Enter the correct path to the\n"
+ "file.\n"))
+ goto midi0001_again;
+ } else
+ {
+ printf("#define SMW_MIDI0001_INCLUDED\n");
+ printf("/*build bin2hex %s smw-midi0001.h smw_ucode */\n", path);
+ }
+ }
+ }
+ }
+ }
+ if (selected_options & B(OPT_SB))
+ {
+ if (think_positively("Do you have a Logitech SoundMan Games", 0,
+ "The Logitech SoundMan Games supports 44 kHz in stereo while the\n"
+ "standard SB Pro supports just 22 kHz stereo. You have the option of\n"
+ "enabling SM Games mode. However, enable it only if you are sure that\n"
+ "your card is an SM Games. Enabling this feature with a plain old SB\n"
+ "Pro will cause troubles with stereo mode.\n\n"
+ "DANGER! Read the above once again before answering 'y'\n"
+ "Answer 'n' if you are unsure what to do!\n"))
+ printf("#define SM_GAMES\n");
+ }
+ if (selected_options & B(OPT_AEDSP16))
+ {
+ int sel1 = 0;
- full_driver = 0;
+ if (selected_options & B(OPT_SB))
+ {
- for (i = 0; i <= OPT_LAST; i++)
- if (can_select_option (i))
- {
- if (!(selected_options & B (i))) /*
- * Not selected yet
- */
- if (!hw_table[i].verify)
- {
- if (hw_table[i].alias)
- selected_options |= B (hw_table[i].alias);
- else
- selected_options |= B (i);
- }
- else
- {
- int def_answ = hw_table[i].default_answ;
-
- if (think_positively (questions[i], def_answ, help[i]))
- if (hw_table[i].alias)
- selected_options |= B (hw_table[i].alias);
- else
- selected_options |= B (i);
- }
- }
- }
+ if (think_positively(
+ "Do you want support for the Audio Excel Sound Blaster Pro mode",
+ 1,
+ "Enable this option if you want the Audio Excel sound card to operate\n"
+ "in Sound Blaster Pro mode.\n"))
+ {
+ printf("#define AEDSP16_SBPRO\n");
+ sel1 = 1;
+ }
+ }
+ if ((selected_options & B(OPT_MSS)) && (sel1 == 0))
+ {
- if (selected_options & B (OPT_SB))
- {
- if (think_positively (
- "Support for the SG NX Pro mixer", 0,
- "Enable this if you want to support the additional mixer functions\n"
- "provided on Sound Galaxy NX Pro sound cards.\n"))
- printf ("#define __SGNXPRO__\n");
- }
-
- if (selected_options & B (OPT_SB))
- {
- if (think_positively ("Support for the MV Jazz16 (ProSonic etc.)", 0,
- "Enable this if you have an MV Jazz16 or ProSonic sound card.\n"))
- {
- if (think_positively ("Do you have SoundMan Wave", 0,
- "Enable this option of you have the Logitech SoundMan Wave sound card.\n"))
- {
- printf ("#define SM_WAVE\n");
-
- midi0001_again:
- if (think_positively (
- "Do you have access to the MIDI0001.BIN file", 1,
- "The Logitech SoundMan Wave has a microcontroller which must be\n"
- "initialized before MIDI emulation works. This is possible only if the\n"
- "microcode file is compiled into the driver.\n"))
- {
- char path[512];
+ if (think_positively(
+ "Do you want support for the Audio Excel Microsoft Sound System mode",
+ 1,
+ "Enable this option if you want the Audio Excel sound card to operate\n"
+ "in Microsoft Sound System mode.\n"))
+ {
+ printf("#define AEDSP16_MSS\n");
+ sel1 = 1;
+ }
+ }
+ if (sel1 == 0)
+ {
+ printf("invalid_configuration__run_make_config_again\n");
+ fprintf(stderr, "ERROR!!!!!\nYou must select at least one mode when using Audio Excel!\n");
+ exit(-1);
+ }
+ if (selected_options & B(OPT_MPU401))
+ printf("#define AEDSP16_MPU401\n");
+ }
+ if (selected_options & B(OPT_PSS))
+ {
+ genld_again:
+ if (think_positively("Do you wish to include an LD file", 1,
+ "If you want to emulate the Sound Blaster card and you have a DSPxxx.LD\n"
+ "file then you must include the LD in the kernel.\n"))
+ {
+ char path[512];
+
+ fprintf(stderr,
+ "Enter the path to your LD file (pwd is sound): ");
+ scanf("%s", path);
+ fprintf(stderr, "including LD file %s\n", path);
+
+ if (!bin2hex(path, "synth-ld.h", "pss_synth"))
+ {
+ fprintf(stderr, "couldn't open `%s' as the LD file\n", path);
+ if (think_positively("try again with correct path", 1,
+ "The given LD file could not opened.\n"))
+ goto genld_again;
+ } else
+ {
+ printf("#define PSS_HAVE_LD\n");
+ printf("/*build bin2hex %s synth-ld.h pss_synth */\n", path);
+ }
+ } else
+ {
+ FILE *sf = fopen("synth-ld.h", "w");
- fprintf (stderr,
- "Enter full name of the MIDI0001.BIN file (pwd is sound): ");
- scanf ("%s", path);
- fprintf (stderr, "including microcode file %s\n", path);
+ fprintf(sf, "/* automatically generated by configure */\n");
+ fprintf(sf, "unsigned char pss_synth[1];\n"
+ "#define pss_synthLen 0\n");
+ fclose(sf);
+ }
+ }
+ if (selected_options & B(OPT_TRIX))
+ {
+ hex2hex_again:
+
+ if (think_positively("Do you want to include TRXPRO.HEX in your kernel",
+ 1,
+ "The MediaTrix AudioTrix Pro has an on-board microcontroller which\n"
+ "needs to be initialized by downloading the code from the file TRXPRO.HEX\n"
+ "in the DOS driver directory. If you don't have the TRXPRO.HEX file handy\n"
+ "you may skip this step. However, the SB and MPU-401 modes of AudioTrix\n"
+ "Pro will not work without this file!\n"))
+ {
+ char path[512];
- if (!bin2hex (path, "smw-midi0001.h", "smw_ucode"))
+ fprintf(stderr,
+ "Enter the path to your TRXPRO.HEX file (pwd is sound): ");
+ scanf("%s", path);
+ fprintf(stderr, "including HEX file `%s'\n", path);
+
+ if (!hex2hex(path, "trix_boot.h", "trix_boot"))
+ goto hex2hex_again;
+ printf("/*build hex2hex %s trix_boot.h trix_boot */\n", path);
+ printf("#define INCLUDE_TRIX_BOOT\n");
+ }
+ }
+ if (selected_options & B(OPT_MSS))
+ {
+ if (think_positively("Support for builtin sound of Compaq Deskpro XL", 0,
+ "Enable this if you have Compaq Deskpro XL.\n"))
{
- fprintf (stderr, "Couldn't open file %s\n",
- path);
- if (think_positively ("Try again with correct path", 1,
- "The specified file could not be opened. Enter the correct path to the\n"
- "file.\n"))
- goto midi0001_again;
+ printf("#define DESKPROXL\n");
}
- else
+ }
+ if (selected_options & B(OPT_MAUI))
+ {
+ oswf_again:
+ if (think_positively(
+ "Do you have access to the OSWF.MOT file", 1,
+ "TB Maui and Tropez have a microcontroller which needs to be initialized\n"
+ "prior use. OSWF.MOT is a file distributed with card's DOS/Windows drivers\n"
+ "which is required during initialization\n"))
{
- printf ("#define SMW_MIDI0001_INCLUDED\n");
- printf ("/*build bin2hex %s smw-midi0001.h smw_ucode */\n", path);
+ char path[512];
+
+ fprintf(stderr,
+ "Enter full name of the OSWF.MOT file (pwd is sound): ");
+ scanf("%s", path);
+ fprintf(stderr, "including microcode file %s\n", path);
+
+ if (!bin2hex(path, "maui_boot.h", "maui_os"))
+ {
+ fprintf(stderr, "Couldn't open file %s\n",
+ path);
+ if (think_positively("Try again with correct path", 1,
+ "The specified file could not be opened. Enter the correct path to the\n"
+ "file.\n"))
+ goto oswf_again;
+ } else
+ {
+ printf("#define HAVE_MAUI_BOOT\n");
+ printf("/*build bin2hex %s maui_boot.h maui_os */\n", path);
+ }
}
- }
- }
- }
- }
-
- if (selected_options & B (OPT_SB))
- {
- if (think_positively ("Do you have a Logitech SoundMan Games", 0,
- "The Logitech SoundMan Games supports 44 kHz in stereo while the\n"
- "standard SB Pro supports just 22 kHz stereo. You have the option of\n"
- "enabling SM Games mode. However, enable it only if you are sure that\n"
- "your card is an SM Games. Enabling this feature with a plain old SB\n"
- "Pro will cause troubles with stereo mode.\n\n"
- "DANGER! Read the above once again before answering 'y'\n"
- "Answer 'n' if you are unsure what to do!\n"))
- printf ("#define SM_GAMES\n");
- }
-
- if (selected_options & B (OPT_AEDSP16))
- {
- int sel1 = 0;
-
- if (selected_options & B (OPT_SB))
- {
+ }
+ if (!(selected_options & ANY_DEVS))
+ {
+ printf("invalid_configuration__run_make_config_again\n");
+ fprintf(stderr, "\n*** This combination is useless. Sound driver disabled!!! ***\n*** You need to enable support for at least one device ***\n\n");
+ exit(0);
+ }
+ for (i = 0; i <= OPT_LAST; i++)
+ if (!hw_table[i].alias)
+ if (selected_options & B(i))
+ printf("#define CONFIG_%s\n", hw_table[i].macro);
+ else
+ printf("#undef CONFIG_%s\n", hw_table[i].macro);
- if (think_positively (
- "Do you want support for the Audio Excel Sound Blaster Pro mode",
- 1,
- "Enable this option if you want the Audio Excel sound card to operate\n"
- "in Sound Blaster Pro mode.\n"))
- {
- printf ("#define AEDSP16_SBPRO\n");
- sel1 = 1;
- }
- }
+ printf("\n");
- if ((selected_options & B (OPT_MSS)) && (sel1 == 0))
- {
+ i = 0;
- if (think_positively (
- "Do you want support for the Audio Excel Microsoft Sound System mode",
- 1,
- "Enable this option if you want the Audio Excel sound card to operate\n"
- "in Microsoft Sound System mode.\n"))
- {
- printf ("#define AEDSP16_MSS\n");
- sel1 = 1;
- }
- }
+ while (extra_options[i].name != NULL)
+ {
+ if (selected_options & extra_options[i].mask)
+ printf("#define CONFIG_%s\n", extra_options[i].name);
+ else
+ printf("#undef CONFIG_%s\n", extra_options[i].name);
+ i++;
+ }
- if (sel1 == 0)
- {
- printf ("invalid_configuration__run_make_config_again\n");
- fprintf (stderr, "ERROR!!!!!\nYou must select at least one mode when using Audio Excel!\n");
- exit (-1);
- }
- if (selected_options & B (OPT_MPU401))
- printf ("#define AEDSP16_MPU401\n");
- }
-
- if (selected_options & B (OPT_PSS))
- {
- genld_again:
- if (think_positively ("Do you wish to include an LD file", 1,
- "If you want to emulate the Sound Blaster card and you have a DSPxxx.LD\n"
- "file then you must include the LD in the kernel.\n"))
- {
- char path[512];
-
- fprintf (stderr,
- "Enter the path to your LD file (pwd is sound): ");
- scanf ("%s", path);
- fprintf (stderr, "including LD file %s\n", path);
-
- if (!bin2hex (path, "synth-ld.h", "pss_synth"))
- {
- fprintf (stderr, "couldn't open `%s' as the LD file\n", path);
- if (think_positively ("try again with correct path", 1,
- "The given LD file could not opened.\n"))
- goto genld_again;
- }
- else
- {
- printf ("#define PSS_HAVE_LD\n");
- printf ("/*build bin2hex %s synth-ld.h pss_synth */\n", path);
- }
- }
- else
- {
- FILE *sf = fopen ("synth-ld.h", "w");
+ printf("\n");
- fprintf (sf, "/* automatically generated by configure */\n");
- fprintf (sf, "unsigned char pss_synth[1];\n"
- "#define pss_synthLen 0\n");
- fclose (sf);
- }
- }
-
- if (selected_options & B (OPT_TRIX))
- {
- hex2hex_again:
-
- if (think_positively ("Do you want to include TRXPRO.HEX in your kernel",
- 1,
- "The MediaTrix AudioTrix Pro has an on-board microcontroller which\n"
- "needs to be initialized by downloading the code from the file TRXPRO.HEX\n"
- "in the DOS driver directory. If you don't have the TRXPRO.HEX file handy\n"
- "you may skip this step. However, the SB and MPU-401 modes of AudioTrix\n"
- "Pro will not work without this file!\n"))
- {
- char path[512];
+ ask_parameters();
- fprintf (stderr,
- "Enter the path to your TRXPRO.HEX file (pwd is sound): ");
- scanf ("%s", path);
- fprintf (stderr, "including HEX file `%s'\n", path);
+ printf("#define SELECTED_SOUND_OPTIONS\t0x%08lx\n", selected_options);
+ fprintf(stderr, "\nThe sound driver is now configured.\n");
- if (!hex2hex (path, "trix_boot.h", "trix_boot"))
- goto hex2hex_again;
- printf ("/*build hex2hex %s trix_boot.h trix_boot */\n", path);
- printf ("#define INCLUDE_TRIX_BOOT\n");
- }
- }
+ if (!old_config_used)
+ {
+ char str[255];
- if (selected_options & B (OPT_MSS))
- {
- if (think_positively ("Support for builtin sound of Compaq Deskpro XL", 0,
- "Enable this if you have Compaq Deskpro XL.\n"))
- {
- printf ("#define DESKPROXL\n");
- }
- }
-
- if (selected_options & B (OPT_MAUI))
- {
- oswf_again:
- if (think_positively (
- "Do you have access to the OSWF.MOT file", 1,
- "TB Maui and Tropez have a microcontroller which needs to be initialized\n"
- "prior use. OSWF.MOT is a file distributed with card's DOS/Windows drivers\n"
- "which is required during initialization\n"))
- {
- char path[512];
-
- fprintf (stderr,
- "Enter full name of the OSWF.MOT file (pwd is sound): ");
- scanf ("%s", path);
- fprintf (stderr, "including microcode file %s\n", path);
-
- if (!bin2hex (path, "maui_boot.h", "maui_os"))
- {
- fprintf (stderr, "Couldn't open file %s\n",
- path);
- if (think_positively ("Try again with correct path", 1,
- "The specified file could not be opened. Enter the correct path to the\n"
- "file.\n"))
- goto oswf_again;
- }
- else
- {
- printf ("#define HAVE_MAUI_BOOT\n");
- printf ("/*build bin2hex %s maui_boot.h maui_os */\n", path);
- }
- }
- }
-
- if (!(selected_options & ANY_DEVS))
- {
- printf ("invalid_configuration__run_make_config_again\n");
- fprintf (stderr, "\n*** This combination is useless. Sound driver disabled!!! ***\n*** You need to enable support for at least one device ***\n\n");
- exit (0);
- }
-
- for (i = 0; i <= OPT_LAST; i++)
- if (!hw_table[i].alias)
- if (selected_options & B (i))
- printf ("#define CONFIG_%s\n", hw_table[i].macro);
- else
- printf ("#undef CONFIG_%s\n", hw_table[i].macro);
-
- printf ("\n");
-
- i = 0;
-
- while (extra_options[i].name != NULL)
- {
- if (selected_options & extra_options[i].mask)
- printf ("#define CONFIG_%s\n", extra_options[i].name);
- else
- printf ("#undef CONFIG_%s\n", extra_options[i].name);
- i++;
- }
-
- printf ("\n");
-
- ask_parameters ();
-
- printf ("#define SELECTED_SOUND_OPTIONS\t0x%08lx\n", selected_options);
- fprintf (stderr, "\nThe sound driver is now configured.\n");
-
- if (!old_config_used)
- {
- char str[255];
-
- sprintf (str, "Save copy of this configuration to `%s'", oldconf);
- if (think_positively (str, 1,
- "If you enable this option then the sound driver configuration is\n"
- "saved to a file. If you later need to recompile the kernel you have\n"
- "the option of using the saved configuration.\n"))
- {
- char cmd[200];
+ sprintf(str, "Save copy of this configuration to `%s'", oldconf);
+ if (think_positively(str, 1,
+ "If you enable this option then the sound driver configuration is\n"
+ "saved to a file. If you later need to recompile the kernel you have\n"
+ "the option of using the saved configuration.\n"))
+ {
+ char cmd[200];
- sprintf (cmd, "cp local.h %s", oldconf);
+ sprintf(cmd, "cp local.h %s", oldconf);
- fclose (stdout);
- if (system (cmd) != 0)
- perror (cmd);
- }
- }
- exit (0);
+ fclose(stdout);
+ if (system(cmd) != 0)
+ perror(cmd);
+ }
+ }
+ exit(0);
}
int
-bin2hex (char *path, char *target, char *varname)
+bin2hex(char *path, char *target, char *varname)
{
- int fd;
- int count;
- char c;
- int i = 0;
-
- if ((fd = open (path, 0)) > 0)
- {
- FILE *sf = fopen (target, "w");
-
- fprintf (sf, "/* automatically generated by configure */\n");
- fprintf (sf, "static unsigned char %s[] = {\n", varname);
- while (1)
- {
- count = read (fd, &c, 1);
- if (count == 0)
- break;
- if (i != 0 && (i % 10) == 0)
- fprintf (sf, "\n");
- fprintf (sf, "0x%02lx,", c & 0xFFL);
- i++;
- }
- fprintf (sf, "};\n"
- "#define %sLen %d\n", varname, i);
- fclose (sf);
- close (fd);
- return 1;
- }
-
- return 0;
+ int fd;
+ int count;
+ char c;
+ int i = 0;
+
+ if ((fd = open(path, 0)) > 0)
+ {
+ FILE *sf = fopen(target, "w");
+
+ fprintf(sf, "/* automatically generated by configure */\n");
+ fprintf(sf, "static unsigned char %s[] = {\n", varname);
+ while (1)
+ {
+ count = read(fd, &c, 1);
+ if (count == 0)
+ break;
+ if (i != 0 && (i % 10) == 0)
+ fprintf(sf, "\n");
+ fprintf(sf, "0x%02lx,", c & 0xFFL);
+ i++;
+ }
+ fprintf(sf, "};\n"
+ "#define %sLen %d\n", varname, i);
+ fclose(sf);
+ close(fd);
+ return 1;
+ }
+ return 0;
}
diff --git a/drivers/sound/cs4232.c b/drivers/sound/cs4232.c
index 1946cd427..eb2a5eb05 100644
--- a/drivers/sound/cs4232.c
+++ b/drivers/sound/cs4232.c
@@ -21,19 +21,20 @@
* for more info.
*/
#include <linux/config.h>
-
+#include <linux/module.h>
#include "sound_config.h"
+#include "soundmodule.h"
-#if defined(CONFIG_CS4232)
+#if defined(CONFIG_CS4232) || defined (MODULE)
#define KEY_PORT 0x279 /* Same as LPT1 status port */
#define CSN_NUM 0x99 /* Just a random number */
-static void
-CS_OUT (unsigned char a)
+static void
+CS_OUT(unsigned char a)
{
- outb ((a), KEY_PORT);
+ outb((a), KEY_PORT);
}
#define CS_OUT2(a, b) {CS_OUT(a);CS_OUT(b);}
#define CS_OUT3(a, b, c) {CS_OUT(a);CS_OUT(b);CS_OUT(c);}
@@ -42,55 +43,54 @@ static int mpu_base = 0, mpu_irq = 0;
static int mpu_detected = 0;
int
-probe_cs4232_mpu (struct address_info *hw_config)
+probe_cs4232_mpu(struct address_info *hw_config)
{
/*
* Just write down the config values.
*/
- mpu_base = hw_config->io_base;
- mpu_irq = hw_config->irq;
+ mpu_base = hw_config->io_base;
+ mpu_irq = hw_config->irq;
- return 1;
+ return 1;
}
void
-attach_cs4232_mpu (struct address_info *hw_config)
+attach_cs4232_mpu(struct address_info *hw_config)
{
}
static unsigned char crystal_key[] = /* A 32 byte magic key sequence */
{
- 0x96, 0x35, 0x9a, 0xcd, 0xe6, 0xf3, 0x79, 0xbc,
- 0x5e, 0xaf, 0x57, 0x2b, 0x15, 0x8a, 0xc5, 0xe2,
- 0xf1, 0xf8, 0x7c, 0x3e, 0x9f, 0x4f, 0x27, 0x13,
- 0x09, 0x84, 0x42, 0xa1, 0xd0, 0x68, 0x34, 0x1a
+ 0x96, 0x35, 0x9a, 0xcd, 0xe6, 0xf3, 0x79, 0xbc,
+ 0x5e, 0xaf, 0x57, 0x2b, 0x15, 0x8a, 0xc5, 0xe2,
+ 0xf1, 0xf8, 0x7c, 0x3e, 0x9f, 0x4f, 0x27, 0x13,
+ 0x09, 0x84, 0x42, 0xa1, 0xd0, 0x68, 0x34, 0x1a
};
int
-probe_cs4232 (struct address_info *hw_config)
+probe_cs4232(struct address_info *hw_config)
{
- int i, n;
- int base = hw_config->io_base, irq = hw_config->irq;
- int dma1 = hw_config->dma, dma2 = hw_config->dma2;
+ int i, n;
+ int base = hw_config->io_base, irq = hw_config->irq;
+ int dma1 = hw_config->dma, dma2 = hw_config->dma2;
- static struct wait_queue *cs_sleeper = NULL;
- static volatile struct snd_wait cs_sleep_flag =
- {0};
+ static struct wait_queue *cs_sleeper = NULL;
+ static volatile struct snd_wait cs_sleep_flag =
+ {0};
/*
* Verify that the I/O port range is free.
*/
- if (check_region (base, 4))
- {
- printk ("cs4232.c: I/O port 0x%03x not free\n", base);
- return 0;
- }
-
- if (ad1848_detect (hw_config->io_base, NULL, hw_config->osp))
- return 1; /* The card is already active */
+ if (check_region(base, 4))
+ {
+ printk("cs4232.c: I/O port 0x%03x not free\n", base);
+ return 0;
+ }
+ if (ad1848_detect(hw_config->io_base, NULL, hw_config->osp))
+ return 1; /* The card is already active */
/*
* This version of the driver doesn't use the PnP method when configuring
@@ -105,241 +105,287 @@ probe_cs4232 (struct address_info *hw_config)
* first time.
*/
- for (n = 0; n < 4; n++)
- {
- cs_sleep_flag.opts = WK_NONE;
+ for (n = 0; n < 4; n++)
+ {
+ cs_sleep_flag.opts = WK_NONE;
/*
* Wake up the card by sending a 32 byte Crystal key to the key port.
*/
- for (i = 0; i < 32; i++)
- CS_OUT (crystal_key[i]);
-
-
- {
- unsigned long tlimit;
-
- if (HZ / 10)
- current->timeout = tlimit = jiffies + (HZ / 10);
- else
- tlimit = (unsigned long) -1;
- cs_sleep_flag.opts = WK_SLEEP;
- interruptible_sleep_on (&cs_sleeper);
- if (!(cs_sleep_flag.opts & WK_WAKEUP))
- {
- if (jiffies >= tlimit)
- cs_sleep_flag.opts |= WK_TIMEOUT;
- }
- cs_sleep_flag.opts &= ~WK_SLEEP;
- }; /* Delay */
+ for (i = 0; i < 32; i++)
+ CS_OUT(crystal_key[i]);
+
+
+ {
+ unsigned long tlimit;
+
+ if (HZ / 10)
+ current->timeout = tlimit = jiffies + (HZ / 10);
+ else
+ tlimit = (unsigned long) -1;
+ cs_sleep_flag.opts = WK_SLEEP;
+ interruptible_sleep_on(&cs_sleeper);
+ if (!(cs_sleep_flag.opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ cs_sleep_flag.opts |= WK_TIMEOUT;
+ }
+ cs_sleep_flag.opts &= ~WK_SLEEP;
+ }; /* Delay */
/*
* Now set the CSN (Card Select Number).
*/
- CS_OUT2 (0x06, CSN_NUM);
+ CS_OUT2(0x06, CSN_NUM);
/*
* Then set some config bytes. First logical device 0
*/
- CS_OUT2 (0x15, 0x00); /* Select logical device 0 (WSS/SB/FM) */
- CS_OUT3 (0x47, (base >> 8) & 0xff, base & 0xff); /* WSS base */
+ CS_OUT2(0x15, 0x00); /* Select logical device 0 (WSS/SB/FM) */
+ CS_OUT3(0x47, (base >> 8) & 0xff, base & 0xff); /* WSS base */
- if (check_region (0x388, 4)) /* Not free */
- CS_OUT3 (0x48, 0x00, 0x00) /* FM base off */
- else
- CS_OUT3 (0x48, 0x03, 0x88); /* FM base 0x388 */
+ if (check_region(0x388, 4)) /* Not free */
+ CS_OUT3(0x48, 0x00, 0x00) /* FM base off */
+ else
+ CS_OUT3(0x48, 0x03, 0x88); /* FM base 0x388 */
- CS_OUT3 (0x42, 0x00, 0x00); /* SB base off */
- CS_OUT2 (0x22, irq); /* SB+WSS IRQ */
- CS_OUT2 (0x2a, dma1); /* SB+WSS DMA */
+ CS_OUT3(0x42, 0x00, 0x00); /* SB base off */
+ CS_OUT2(0x22, irq); /* SB+WSS IRQ */
+ CS_OUT2(0x2a, dma1); /* SB+WSS DMA */
- if (dma2 != -1)
- CS_OUT2 (0x25, dma2) /* WSS DMA2 */
- else
- CS_OUT2 (0x25, 4); /* No WSS DMA2 */
+ if (dma2 != -1)
+ CS_OUT2(0x25, dma2) /* WSS DMA2 */
+ else
+ CS_OUT2(0x25, 4); /* No WSS DMA2 */
- CS_OUT2 (0x33, 0x01); /* Activate logical dev 0 */
+ CS_OUT2(0x33, 0x01); /* Activate logical dev 0 */
- {
- unsigned long tlimit;
+ {
+ unsigned long tlimit;
- if (HZ / 10)
- current->timeout = tlimit = jiffies + (HZ / 10);
- else
- tlimit = (unsigned long) -1;
- cs_sleep_flag.opts = WK_SLEEP;
- interruptible_sleep_on (&cs_sleeper);
- if (!(cs_sleep_flag.opts & WK_WAKEUP))
- {
- if (jiffies >= tlimit)
- cs_sleep_flag.opts |= WK_TIMEOUT;
- }
- cs_sleep_flag.opts &= ~WK_SLEEP;
- }; /* Delay */
+ if (HZ / 10)
+ current->timeout = tlimit = jiffies + (HZ / 10);
+ else
+ tlimit = (unsigned long) -1;
+ cs_sleep_flag.opts = WK_SLEEP;
+ interruptible_sleep_on(&cs_sleeper);
+ if (!(cs_sleep_flag.opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ cs_sleep_flag.opts |= WK_TIMEOUT;
+ }
+ cs_sleep_flag.opts &= ~WK_SLEEP;
+ }; /* Delay */
/*
* Initialize logical device 3 (MPU)
*/
#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
- if (mpu_base != 0 && mpu_irq != 0)
- {
- CS_OUT2 (0x15, 0x03); /* Select logical device 3 (MPU) */
- CS_OUT3 (0x47, (mpu_base >> 8) & 0xff, mpu_base & 0xff); /* MPU base */
- CS_OUT2 (0x22, mpu_irq); /* MPU IRQ */
- CS_OUT2 (0x33, 0x01); /* Activate logical dev 3 */
- }
+ if (mpu_base != 0 && mpu_irq != 0)
+ {
+ CS_OUT2(0x15, 0x03); /* Select logical device 3 (MPU) */
+ CS_OUT3(0x47, (mpu_base >> 8) & 0xff, mpu_base & 0xff); /* MPU base */
+ CS_OUT2(0x22, mpu_irq); /* MPU IRQ */
+ CS_OUT2(0x33, 0x01); /* Activate logical dev 3 */
+ }
#endif
/*
* Finally activate the chip
*/
- CS_OUT (0x79);
-
-
- {
- unsigned long tlimit;
-
- if (HZ / 5)
- current->timeout = tlimit = jiffies + (HZ / 5);
- else
- tlimit = (unsigned long) -1;
- cs_sleep_flag.opts = WK_SLEEP;
- interruptible_sleep_on (&cs_sleeper);
- if (!(cs_sleep_flag.opts & WK_WAKEUP))
- {
- if (jiffies >= tlimit)
- cs_sleep_flag.opts |= WK_TIMEOUT;
- }
- cs_sleep_flag.opts &= ~WK_SLEEP;
- }; /* Delay */
+ CS_OUT(0x79);
+
+
+ {
+ unsigned long tlimit;
+
+ if (HZ / 5)
+ current->timeout = tlimit = jiffies + (HZ / 5);
+ else
+ tlimit = (unsigned long) -1;
+ cs_sleep_flag.opts = WK_SLEEP;
+ interruptible_sleep_on(&cs_sleeper);
+ if (!(cs_sleep_flag.opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ cs_sleep_flag.opts |= WK_TIMEOUT;
+ }
+ cs_sleep_flag.opts &= ~WK_SLEEP;
+ }; /* Delay */
/*
* Then try to detect the codec part of the chip
*/
- if (ad1848_detect (hw_config->io_base, NULL, hw_config->osp))
- return 1;
-
+ if (ad1848_detect(hw_config->io_base, NULL, hw_config->osp))
+ return 1;
+
+
+ {
+ unsigned long tlimit;
+
+ if (HZ)
+ current->timeout = tlimit = jiffies + (HZ);
+ else
+ tlimit = (unsigned long) -1;
+ cs_sleep_flag.opts = WK_SLEEP;
+ interruptible_sleep_on(&cs_sleeper);
+ if (!(cs_sleep_flag.opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ cs_sleep_flag.opts |= WK_TIMEOUT;
+ }
+ cs_sleep_flag.opts &= ~WK_SLEEP;
+ }; /* Longer delay */
+ }
- {
- unsigned long tlimit;
+ return 0;
+}
- if (HZ)
- current->timeout = tlimit = jiffies + (HZ);
- else
- tlimit = (unsigned long) -1;
- cs_sleep_flag.opts = WK_SLEEP;
- interruptible_sleep_on (&cs_sleeper);
- if (!(cs_sleep_flag.opts & WK_WAKEUP))
+void
+attach_cs4232(struct address_info *hw_config)
+{
+ int base = hw_config->io_base, irq = hw_config->irq;
+ int dma1 = hw_config->dma, dma2 = hw_config->dma2;
+ int old_num_mixers = num_mixers;
+
+ if (dma2 == -1)
+ dma2 = dma1;
+
+ hw_config->slots[0] = ad1848_init("Crystal audio controller", base,
+ irq,
+ dma1, /* Playback DMA */
+ dma2, /* Capture DMA */
+ 0,
+ hw_config->osp);
+
+ if (num_mixers > old_num_mixers)
+ { /* Assume the mixer map is as suggested in the CS4232 databook */
+ AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE);
+ AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD);
+ AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* FM synth */
+ }
+#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
+ if (mpu_base != 0 && mpu_irq != 0)
{
- if (jiffies >= tlimit)
- cs_sleep_flag.opts |= WK_TIMEOUT;
+ static struct address_info hw_config2 =
+ {0}; /* Ensure it's initialized */
+
+ hw_config2.io_base = mpu_base;
+ hw_config2.irq = mpu_irq;
+ hw_config2.dma = -1;
+ hw_config2.dma2 = -1;
+ hw_config2.always_detect = 0;
+ hw_config2.name = NULL;
+ hw_config2.driver_use_1 = 0;
+ hw_config2.driver_use_2 = 0;
+ hw_config2.card_subtype = 0;
+
+ if (probe_uart401(&hw_config2))
+ {
+ mpu_detected = 1;
+ attach_uart401(&hw_config2);
+ } else
+ {
+ mpu_base = mpu_irq = 0;
+ }
+ hw_config->slots[1] = hw_config2.slots[1];
}
- cs_sleep_flag.opts &= ~WK_SLEEP;
- }; /* Longer delay */
- }
-
- return 0;
+#endif
}
void
-attach_cs4232 (struct address_info *hw_config)
+unload_cs4232(struct address_info *hw_config)
{
- int base = hw_config->io_base, irq = hw_config->irq;
- int dma1 = hw_config->dma, dma2 = hw_config->dma2;
- int old_num_mixers = num_mixers;
-
- if (dma2 == -1)
- dma2 = dma1;
-
- ad1848_init ("Crystal audio controller", base,
- irq,
- dma1, /* Playback DMA */
- dma2, /* Capture DMA */
- 0,
- hw_config->osp);
-
- if (num_mixers > old_num_mixers)
- { /* Assume the mixer map is as suggested in the CS4232 databook */
- AD1848_REROUTE (SOUND_MIXER_LINE1, SOUND_MIXER_LINE);
- AD1848_REROUTE (SOUND_MIXER_LINE2, SOUND_MIXER_CD);
- AD1848_REROUTE (SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* FM synth */
- }
-
+ int base = hw_config->io_base, irq = hw_config->irq;
+ int dma1 = hw_config->dma, dma2 = hw_config->dma2;
+
+ if (dma2 == -1)
+ dma2 = dma1;
+
+ ad1848_unload(base,
+ irq,
+ dma1, /* Playback DMA */
+ dma2, /* Capture DMA */
+ 0);
+ sound_unload_audiodev(hw_config->slots[0]);
#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
- if (mpu_base != 0 && mpu_irq != 0)
- {
- static struct address_info hw_config2 =
- {0}; /* Ensure it's initialized */
-
- hw_config2.io_base = mpu_base;
- hw_config2.irq = mpu_irq;
- hw_config2.dma = -1;
- hw_config2.dma2 = -1;
- hw_config2.always_detect = 0;
- hw_config2.name = NULL;
- hw_config2.driver_use_1 = 0;
- hw_config2.driver_use_2 = 0;
- hw_config2.card_subtype = 0;
-
- if (probe_uart401 (&hw_config2))
- {
- mpu_detected = 1;
- attach_uart401 (&hw_config2);
- }
- else
- {
- mpu_base = mpu_irq = 0;
- }
- }
+ if (mpu_base != 0 && mpu_irq != 0 && mpu_detected)
+ {
+ static struct address_info hw_config2 =
+ {0}; /* Ensure it's initialized */
+
+ hw_config2.io_base = mpu_base;
+ hw_config2.irq = mpu_irq;
+ hw_config2.dma = -1;
+ hw_config2.dma2 = -1;
+ hw_config2.always_detect = 0;
+ hw_config2.name = NULL;
+ hw_config2.driver_use_1 = 0;
+ hw_config2.driver_use_2 = 0;
+ hw_config2.card_subtype = 0;
+ hw_config2.slots[1] = hw_config->slots[1];
+
+ unload_uart401(&hw_config2);
+ }
#endif
}
void
-unload_cs4232 (struct address_info *hw_config)
+unload_cs4232_mpu(struct address_info *hw_config)
{
- int base = hw_config->io_base, irq = hw_config->irq;
- int dma1 = hw_config->dma, dma2 = hw_config->dma2;
+ /* Not required. Handled by cs4232_unload */
+}
- if (dma2 == -1)
- dma2 = dma1;
+#ifdef MODULE
- ad1848_unload (base,
- irq,
- dma1, /* Playback DMA */
- dma2, /* Capture DMA */
- 0);
+int io = -1;
+int irq = -1;
+int dma = -1;
+int dma2 = -1;
-#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
- if (mpu_base != 0 && mpu_irq != 0 && mpu_detected)
- {
- static struct address_info hw_config2 =
- {0}; /* Ensure it's initialized */
-
- hw_config2.io_base = mpu_base;
- hw_config2.irq = mpu_irq;
- hw_config2.dma = -1;
- hw_config2.dma2 = -1;
- hw_config2.always_detect = 0;
- hw_config2.name = NULL;
- hw_config2.driver_use_1 = 0;
- hw_config2.driver_use_2 = 0;
- hw_config2.card_subtype = 0;
-
- unload_uart401 (&hw_config2);
- }
-#endif
+struct address_info cfg;
+
+/*
+ * Install a CS4232 based card. Need to have ad1848 and mpu401
+ * loaded ready.
+ */
+
+int
+init_module(void)
+{
+ if (io == -1 || irq == -1 || dma == -1 || dma2 == -1)
+ {
+ printk(KERN_ERR "cs4232: dma, dma2, irq and io must be set.\n");
+ return -EINVAL;
+ }
+ cfg.io_base = io;
+ cfg.irq = irq;
+ cfg.dma = dma;
+ cfg.dma2 = dma2;
+
+ if (probe_cs4232(&cfg) == 0)
+ return -ENODEV;
+
+ probe_cs4232_mpu(&cfg); /* Bug always returns 0 not OK -- AC */
+
+ attach_cs4232(&cfg);
+ attach_cs4232_mpu(&cfg);
+ SOUND_LOCK;
+ return 0;
}
void
-unload_cs4232_mpu (struct address_info *hw_config)
+cleanup_module(void)
{
- /* Not required. Handled by cs4232_unload */
+ unload_cs4232_mpu(&cfg);
+ unload_cs4232(&cfg);
+ SOUND_LOCK_END;
}
-
+#endif
#endif
diff --git a/drivers/sound/dev_table.c b/drivers/sound/dev_table.c
index 10daa50ad..1a1f230c2 100644
--- a/drivers/sound/dev_table.c
+++ b/drivers/sound/dev_table.c
@@ -16,537 +16,646 @@
#define _DEV_TABLE_C_
#include "sound_config.h"
int sb_be_quiet = 0;
+int softoss_dev = 0;
int sound_started = 0;
-int sndtable_get_cardcount (void);
+int sndtable_get_cardcount(void);
int
-snd_find_driver (int type)
+snd_find_driver(int type)
{
- int i, n = num_sound_drivers;
+ int i, n = num_sound_drivers;
- for (i = 0; i < n; i++)
- if (sound_drivers[i].card_type == type)
- return i;
+ for (i = 0; i < n; i++)
+ if (sound_drivers[i].card_type == type)
+ return i;
- return -1;
+ return -1;
}
static void
-start_services (void)
+start_services(void)
{
- int soundcards_installed;
+ int soundcards_installed;
- if (!(soundcards_installed = sndtable_get_cardcount ()))
- return; /* No cards detected */
+#ifdef FIXME
+ if (!(soundcards_installed = sndtable_get_cardcount()))
+ return; /* No cards detected */
+#endif
#ifdef CONFIG_AUDIO
- if (num_audiodevs) /* Audio devices present */
- {
- int dev;
+ if (num_audiodevs) /* Audio devices present */
+ {
+ int dev;
- for (dev = 0; dev < num_audiodevs; dev++)
- {
- }
- audio_init_devices ();
- }
+ for (dev = 0; dev < num_audiodevs; dev++)
+ {
+ }
+ audio_init_devices();
+ }
#endif
- return;
+ return;
}
static void
-start_cards (void)
+start_cards(void)
{
- int i, n = num_sound_cards;
- int drv;
+ int i, n = num_sound_cards;
+ int drv;
- sound_started = 1;
- if (trace_init)
- printk ("Sound initialization started\n");
+ sound_started = 1;
+ if (trace_init)
+ printk("Sound initialization started\n");
#ifdef CONFIG_LOWLEVEL_SOUND
- {
- extern void sound_preinit_lowlevel_drivers (void);
+ {
+ extern void sound_preinit_lowlevel_drivers(void);
- sound_preinit_lowlevel_drivers ();
- }
+#ifdef FIXME
+ sound_preinit_lowlevel_drivers();
+#endif
+ }
#endif
/*
* Check the number of cards actually defined in the table
*/
- for (i = 0; i < n && snd_installed_cards[i].card_type; i++)
- num_sound_cards = i + 1;
-
- for (i = 0; i < n && snd_installed_cards[i].card_type; i++)
- if (snd_installed_cards[i].enabled)
- {
- snd_installed_cards[i].for_driver_use = NULL;
-
- if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) == -1)
- {
- snd_installed_cards[i].enabled = 0; /*
- * Mark as not detected
- */
- continue;
- }
-
- snd_installed_cards[i].config.card_subtype =
- sound_drivers[drv].card_subtype;
-
- if (sound_drivers[drv].probe (&snd_installed_cards[i].config))
- {
-
- sound_drivers[drv].attach (&snd_installed_cards[i].config);
-
- }
- else
- snd_installed_cards[i].enabled = 0; /*
- * Mark as not detected
- */
- }
+ for (i = 0; i < n && snd_installed_cards[i].card_type; i++)
+ num_sound_cards = i + 1;
+ for (i = 0; i < n && snd_installed_cards[i].card_type; i++)
+ if (snd_installed_cards[i].enabled)
+ {
+ snd_installed_cards[i].for_driver_use = NULL;
+
+ if ((drv = snd_find_driver(snd_installed_cards[i].card_type)) == -1)
+ {
+ snd_installed_cards[i].enabled = 0; /*
+ * Mark as not detected
+ */
+ continue;
+ }
+ snd_installed_cards[i].config.card_subtype =
+ sound_drivers[drv].card_subtype;
+
+ if (sound_drivers[drv].probe(&snd_installed_cards[i].config))
+ {
+
+ sound_drivers[drv].attach(&snd_installed_cards[i].config);
+
+ } else
+ snd_installed_cards[i].enabled = 0; /*
+ * Mark as not detected
+ */
+ }
#ifdef CONFIG_LOWLEVEL_SOUND
- {
- extern void sound_init_lowlevel_drivers (void);
+ {
+ extern void sound_init_lowlevel_drivers(void);
- sound_init_lowlevel_drivers ();
- }
+ sound_init_lowlevel_drivers();
+ }
#endif
- if (trace_init)
- printk ("Sound initialization complete\n");
+ if (trace_init)
+ printk("Sound initialization complete\n");
}
void
-sndtable_init (void)
+sndtable_init(void)
{
- start_cards ();
+ start_cards();
}
void
-sound_unload_drivers (void)
+sound_unload_drivers(void)
{
- int i, n = num_sound_cards;
- int drv;
+ int i, n = num_sound_cards;
+ int drv;
- if (!sound_started)
- return;
+ if (!sound_started)
+ return;
- if (trace_init)
- printk ("Sound unload started\n");
+ if (trace_init)
+ printk("Sound unload started\n");
- for (i = 0; i < n && snd_installed_cards[i].card_type; i++)
- if (snd_installed_cards[i].enabled)
- {
- if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) != -1)
- {
- if (sound_drivers[drv].unload)
- {
- sound_drivers[drv].unload (&snd_installed_cards[i].config);
- snd_installed_cards[i].enabled = 0;
- }
- }
- }
+ for (i = 0; i < n && snd_installed_cards[i].card_type; i++)
+ if (snd_installed_cards[i].enabled)
+ {
+ if ((drv = snd_find_driver(snd_installed_cards[i].card_type)) != -1)
+ {
+ if (sound_drivers[drv].unload)
+ {
+ sound_drivers[drv].unload(&snd_installed_cards[i].config);
+ snd_installed_cards[i].enabled = 0;
+ }
+ }
+ }
+ for (i=0;i<num_audiodevs;i++)
+ DMAbuf_deinit(i);
- if (trace_init)
- printk ("Sound unload complete\n");
+ if (trace_init)
+ printk("Sound unload complete\n");
}
void
-sound_unload_driver (int type)
+sound_unload_driver(int type)
{
- int i, drv = -1, n = num_sound_cards;
+ int i, drv = -1, n = num_sound_cards;
- unsigned long flags;
+ DEB(printk("unload driver %d: ", type));
- DEB (printk ("unload driver %d: ", type));
-
- for (i = 0; i < n && snd_installed_cards[i].card_type; i++)
- if (snd_installed_cards[i].card_type == type)
- {
- if (snd_installed_cards[i].enabled)
- {
- if ((drv = snd_find_driver (type)) != -1)
- {
- DEB (printk (" card %d", i));
- if (sound_drivers[drv].unload)
+ for (i = 0; i < n && snd_installed_cards[i].card_type; i++)
+ if (snd_installed_cards[i].card_type == type)
{
- sound_drivers[drv].unload (&snd_installed_cards[i].config);
- snd_installed_cards[i].enabled = 0;
+ if (snd_installed_cards[i].enabled)
+ {
+ if ((drv = snd_find_driver(type)) != -1)
+ {
+ DEB(printk(" card %d", i));
+ if (sound_drivers[drv].unload)
+ {
+ sound_drivers[drv].unload(&snd_installed_cards[i].config);
+ snd_installed_cards[i].enabled = 0;
+ }
+ }
+ }
}
- }
+ DEB(printk("\n"));
+}
+
+
+int
+sndtable_probe(int unit, struct address_info *hw_config)
+{
+ int sel = -1;
+
+ DEB(printk("sndtable_probe(%d)\n", unit));
+
+ if (!unit)
+ return 1;
+
+
+ if (sel == -1 && num_sound_cards < max_sound_cards)
+ {
+ int i;
+
+ i = sel = (num_sound_cards++);
+
+ snd_installed_cards[sel].card_type = unit;
+ snd_installed_cards[sel].enabled = 1;
}
- }
- DEB (printk ("\n"));
+ if (sel != -1)
+ {
+ int drv;
+
+ snd_installed_cards[sel].for_driver_use = NULL;
+ snd_installed_cards[sel].config.io_base = hw_config->io_base;
+ snd_installed_cards[sel].config.irq = hw_config->irq;
+ snd_installed_cards[sel].config.dma = hw_config->dma;
+ snd_installed_cards[sel].config.dma2 = hw_config->dma2;
+ snd_installed_cards[sel].config.name = hw_config->name;
+ snd_installed_cards[sel].config.always_detect = hw_config->always_detect;
+ snd_installed_cards[sel].config.driver_use_1 = hw_config->driver_use_1;
+ snd_installed_cards[sel].config.driver_use_2 = hw_config->driver_use_2;
+ snd_installed_cards[sel].config.card_subtype = hw_config->card_subtype;
+
+ if ((drv = snd_find_driver(snd_installed_cards[sel].card_type)) == -1)
+ {
+ snd_installed_cards[sel].enabled = 0;
+ DEB(printk("Failed to find driver\n"));
+ return 0;
+ }
+ DEB(printk("Driver name '%s'\n", sound_drivers[drv].name));
+
+ hw_config->card_subtype =
+ snd_installed_cards[sel].config.card_subtype =
+ sound_drivers[drv].card_subtype;
+
+ if (sound_drivers[drv].probe(hw_config))
+ {
+ DEB(printk("Hardware probed OK\n"));
+ return 1;
+ }
+ DEB(printk("Failed to find hardware\n"));
+ snd_installed_cards[sel].enabled = 0; /*
+ * Mark as not detected
+ */
+ return 0;
+ }
+ return 0;
+}
- save_flags (flags);
- cli ();
- restore_flags (flags);
+int
+sndtable_init_card(int unit, struct address_info *hw_config)
+{
+ int i, n = num_sound_cards;
+
+ DEB(printk("sndtable_init_card(%d) entered\n", unit));
+
+ if (!unit)
+ {
+ sndtable_init();
+ return 1;
+ }
+ for (i = 0; i < n && snd_installed_cards[i].card_type; i++)
+ if (snd_installed_cards[i].card_type == unit)
+ {
+ int drv;
+
+ snd_installed_cards[i].config.io_base = hw_config->io_base;
+ snd_installed_cards[i].config.irq = hw_config->irq;
+ snd_installed_cards[i].config.dma = hw_config->dma;
+ snd_installed_cards[i].config.dma2 = hw_config->dma2;
+ snd_installed_cards[i].config.name = hw_config->name;
+ snd_installed_cards[i].config.always_detect = hw_config->always_detect;
+ snd_installed_cards[i].config.driver_use_1 = hw_config->driver_use_1;
+ snd_installed_cards[i].config.driver_use_2 = hw_config->driver_use_2;
+ snd_installed_cards[i].config.card_subtype = hw_config->card_subtype;
+
+ if ((drv = snd_find_driver(snd_installed_cards[i].card_type)) == -1)
+ snd_installed_cards[i].enabled = 0; /*
+ * Mark as not detected
+ */
+ else
+ {
+
+ DEB(printk("Located card - calling attach routine\n"));
+ sound_drivers[drv].attach(hw_config);
+
+ DEB(printk("attach routine finished\n"));
+ }
+ start_services();
+ return 1;
+ }
+ DEB(printk("sndtable_init_card: No card defined with type=%d, num cards: %d\n", unit, num_sound_cards));
+ return 0;
}
+int
+sndtable_get_cardcount(void)
+{
+ return num_audiodevs + num_mixers + num_synths + num_midis;
+}
int
-sndtable_probe (int unit, struct address_info *hw_config)
+sndtable_identify_card(char *name)
{
- int sel = -1;
+ int i, n = num_sound_drivers;
- DEB (printk ("sndtable_probe(%d)\n", unit));
+ if (name == NULL)
+ return 0;
- if (!unit)
- return 1;
+ for (i = 0; i < n; i++)
+ if (sound_drivers[i].driver_id != NULL)
+ {
+ char *id = sound_drivers[i].driver_id;
+ int j;
+ for (j = 0; j < 80 && name[j] == id[j]; j++)
+ if (id[j] == 0 && name[j] == 0) /* Match */
+ return sound_drivers[i].card_type;
+ }
+ return 0;
+}
- if (sel == -1 && num_sound_cards < max_sound_cards)
- {
- int i;
+void
+sound_setup(char *str, int *ints)
+{
+ int i, n = num_sound_cards;
- i = sel = (num_sound_cards++);
+ /*
+ * First disable all drivers
+ */
- snd_installed_cards[sel].card_type = unit;
- snd_installed_cards[sel].enabled = 1;
- }
+ for (i = 0; i < n && snd_installed_cards[i].card_type; i++)
+ snd_installed_cards[i].enabled = 0;
- if (sel != -1)
- {
- int drv;
+ if (ints[0] == 0 || ints[1] == 0)
+ return;
+ /*
+ * Then enable them one by time
+ */
- snd_installed_cards[sel].for_driver_use = NULL;
- snd_installed_cards[sel].config.io_base = hw_config->io_base;
- snd_installed_cards[sel].config.irq = hw_config->irq;
- snd_installed_cards[sel].config.dma = hw_config->dma;
- snd_installed_cards[sel].config.dma2 = hw_config->dma2;
- snd_installed_cards[sel].config.name = hw_config->name;
- snd_installed_cards[sel].config.always_detect = hw_config->always_detect;
- snd_installed_cards[sel].config.driver_use_1 = hw_config->driver_use_1;
- snd_installed_cards[sel].config.driver_use_2 = hw_config->driver_use_2;
- snd_installed_cards[sel].config.card_subtype = hw_config->card_subtype;
+ for (i = 1; i <= ints[0]; i++)
+ {
+ int card_type, ioaddr, irq, dma, dma2,
+ ptr, j;
+ unsigned int val;
+
+ val = (unsigned int) ints[i];
+
+ card_type = (val & 0x0ff00000) >> 20;
+
+ if (card_type > 127)
+ {
+ /*
+ * Add any future extensions here
+ */
+ return;
+ }
+ ioaddr = (val & 0x000fff00) >> 8;
+ irq = (val & 0x000000f0) >> 4;
+ dma = (val & 0x0000000f);
+ dma2 = (val & 0xf0000000) >> 28;
+
+ ptr = -1;
+ for (j = 0; j < n && ptr == -1; j++)
+ if (snd_installed_cards[j].card_type == card_type &&
+ !snd_installed_cards[j].enabled) /*
+ * Not already found
+ */
+ ptr = j;
+
+ if (ptr == -1)
+ printk("Sound: Invalid setup parameter 0x%08x\n", val);
+ else
+ {
+ snd_installed_cards[ptr].enabled = 1;
+ snd_installed_cards[ptr].config.io_base = ioaddr;
+ snd_installed_cards[ptr].config.irq = irq;
+ snd_installed_cards[ptr].config.dma = dma;
+ snd_installed_cards[ptr].config.dma2 = dma2;
+ snd_installed_cards[ptr].config.name = NULL;
+ snd_installed_cards[ptr].config.always_detect = 0;
+ snd_installed_cards[ptr].config.driver_use_1 = 0;
+ snd_installed_cards[ptr].config.driver_use_2 = 0;
+ snd_installed_cards[ptr].config.card_subtype = 0;
+ }
+ }
+}
- if ((drv = snd_find_driver (snd_installed_cards[sel].card_type)) == -1)
- {
- snd_installed_cards[sel].enabled = 0;
- DEB (printk ("Failed to find driver\n"));
- return 0;
- }
- DEB (printk ("Driver name '%s'\n", sound_drivers[drv].name));
- hw_config->card_subtype =
- snd_installed_cards[sel].config.card_subtype =
- sound_drivers[drv].card_subtype;
+struct address_info
+ *
+sound_getconf(int card_type)
+{
+ int j, ptr;
+ int n = num_sound_cards;
- if (sound_drivers[drv].probe (hw_config))
- {
- DEB (printk ("Hardware probed OK\n"));
- return 1;
- }
+ ptr = -1;
+ for (j = 0; j < n && ptr == -1 && snd_installed_cards[j].card_type; j++)
+ if (snd_installed_cards[j].card_type == card_type)
+ ptr = j;
- DEB (printk ("Failed to find hardware\n"));
- snd_installed_cards[sel].enabled = 0; /*
- * Mark as not detected
- */
- return 0;
- }
+ if (ptr == -1)
+ return (struct address_info *) NULL;
- return 0;
+ return &snd_installed_cards[ptr].config;
}
+
int
-sndtable_init_card (int unit, struct address_info *hw_config)
+sound_install_audiodrv(int vers,
+ char *name,
+ struct audio_driver *driver,
+ int driver_size,
+ int flags,
+ unsigned int format_mask,
+ void *devc,
+ int dma1,
+ int dma2)
{
- int i, n = num_sound_cards;
+#ifdef CONFIG_AUDIO
+ struct audio_driver *d;
+ struct audio_operations *op;
+ int l, num;
- DEB (printk ("sndtable_init_card(%d) entered\n", unit));
+ if (vers != AUDIO_DRIVER_VERSION ||
+ driver_size > sizeof(struct audio_driver))
+ {
+ printk(KERN_ERR "Sound: Incompatible audio driver for %s\n", name);
+ return -(EINVAL);
+ }
+ num = sound_alloc_audiodev();
- if (!unit)
- {
- sndtable_init ();
- return 1;
- }
+ if (num == -1)
+ {
+ printk(KERN_ERR "sound: Too many audio drivers\n");
+ return -(EBUSY);
+ }
+ d = (struct audio_driver *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_driver)));
+ sound_mem_sizes[sound_nblocks] = sizeof(struct audio_driver);
- for (i = 0; i < n && snd_installed_cards[i].card_type; i++)
- if (snd_installed_cards[i].card_type == unit)
- {
- int drv;
+ if (sound_nblocks < 1024)
+ sound_nblocks++;;
- snd_installed_cards[i].config.io_base = hw_config->io_base;
- snd_installed_cards[i].config.irq = hw_config->irq;
- snd_installed_cards[i].config.dma = hw_config->dma;
- snd_installed_cards[i].config.dma2 = hw_config->dma2;
- snd_installed_cards[i].config.name = hw_config->name;
- snd_installed_cards[i].config.always_detect = hw_config->always_detect;
- snd_installed_cards[i].config.driver_use_1 = hw_config->driver_use_1;
- snd_installed_cards[i].config.driver_use_2 = hw_config->driver_use_2;
- snd_installed_cards[i].config.card_subtype = hw_config->card_subtype;
-
- if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) == -1)
- snd_installed_cards[i].enabled = 0; /*
- * Mark as not detected
- */
- else
+ op = (struct audio_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_operations)));
+ sound_mem_sizes[sound_nblocks] = sizeof(struct audio_operations);
+
+ if (sound_nblocks < 1024)
+ sound_nblocks++;;
+ if (d == NULL || op == NULL)
{
+ printk(KERN_ERR "Sound: Can't allocate driver for (%s)\n", name);
+ sound_unload_audiodev(num);
+ return -(ENOMEM);
+ }
+ memset((char *) op, 0, sizeof(struct audio_operations));
+ if (driver_size < sizeof(struct audio_driver))
+ memset((char *) d, 0, sizeof(struct audio_driver));
- DEB (printk ("Located card - calling attach routine\n"));
- sound_drivers[drv].attach (hw_config);
+ memcpy((char *) d, (char *) driver, driver_size);
- DEB (printk ("attach routine finished\n"));
- }
- start_services ();
- return 1;
- }
+ op->d = d;
- DEB (printk ("sndtable_init_card: No card defined with type=%d, num cards: %d\n",
- unit, num_sound_cards));
- return 0;
-}
+ l = strlen(name) + 1;
+ if (l > sizeof(op->name))
+ l = sizeof(op->name);
+ strncpy(op->name, name, l);
+ op->name[l - 1] = 0;
+ op->flags = flags;
+ op->format_mask = format_mask;
+ op->devc = devc;
-int
-sndtable_get_cardcount (void)
-{
- return num_audiodevs + num_mixers + num_synths + num_midis;
+/*
+ * Hardcoded defaults
+ */
+ audio_devs[num] = op;
+
+ DMAbuf_init(num, dma1, dma2);
+
+ audio_init_devices();
+ return num;
+#else
+ return -EINVAL;
+#endif
}
int
-sndtable_identify_card (char *name)
+sound_install_mixer(int vers,
+ char *name,
+ struct mixer_operations *driver,
+ int driver_size,
+ void *devc)
{
- int i, n = num_sound_drivers;
+ struct mixer_operations *op;
+ int l;
- if (name == NULL)
- return 0;
+ int n = sound_alloc_mixerdev();
- for (i = 0; i < n; i++)
- if (sound_drivers[i].driver_id != NULL)
- {
- char *id = sound_drivers[i].driver_id;
- int j;
+ if (n == -1)
+ {
+ printk(KERN_ERR "Sound: Too many mixer drivers\n");
+ return -(EBUSY);
+ }
+ if (vers != MIXER_DRIVER_VERSION ||
+ driver_size > sizeof(struct mixer_operations))
+ {
+ printk(KERN_ERR "Sound: Incompatible mixer driver for %s\n", name);
+ return -(EINVAL);
+ }
+ op = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct mixer_operations)));
+ sound_mem_sizes[sound_nblocks] = sizeof(struct mixer_operations);
- for (j = 0; j < 80 && name[j] == id[j]; j++)
- if (id[j] == 0 && name[j] == 0) /* Match */
- return sound_drivers[i].card_type;
- }
+ if (sound_nblocks < 1024)
+ sound_nblocks++;;
+ if (op == NULL)
+ {
+ printk(KERN_ERR "Sound: Can't allocate mixer driver for (%s)\n", name);
+ return -(ENOMEM);
+ }
+ memset((char *) op, 0, sizeof(struct mixer_operations));
- return 0;
+ memcpy((char *) op, (char *) driver, driver_size);
+
+ l = strlen(name) + 1;
+ if (l > sizeof(op->name))
+ l = sizeof(op->name);
+ strncpy(op->name, name, l);
+ op->name[l - 1] = 0;
+ op->devc = devc;
+
+ mixer_devs[n] = op;
+ return n;
}
void
-sound_setup (char *str, int *ints)
+sound_unload_audiodev(int dev)
{
- int i, n = num_sound_cards;
-
- /*
- * First disable all drivers
- */
-
- for (i = 0; i < n && snd_installed_cards[i].card_type; i++)
- snd_installed_cards[i].enabled = 0;
-
- if (ints[0] == 0 || ints[1] == 0)
- return;
- /*
- * Then enable them one by time
- */
+ if (dev != -1)
+ audio_devs[dev] = NULL;
+}
- for (i = 1; i <= ints[0]; i++)
- {
- int card_type, ioaddr, irq, dma, dma2, ptr, j;
- unsigned int val;
+int
+sound_alloc_audiodev(void)
+{
+ int i;
- val = (unsigned int) ints[i];
+ for (i = 0; i < MAX_AUDIO_DEV; i++)
+ {
+ if (audio_devs[i] == NULL)
+ {
+ if (i >= num_audiodevs)
+ num_audiodevs = i + 1;
+ return i;
+ }
+ }
+ return -1;
+}
- card_type = (val & 0x0ff00000) >> 20;
+int
+sound_alloc_mididev(void)
+{
+ int i;
- if (card_type > 127)
- {
- /*
- * Add any future extensions here
- */
- return;
- }
+ for (i = 0; i < MAX_MIDI_DEV; i++)
+ {
+ if (midi_devs[i] == NULL)
+ {
+ if (i >= num_midis)
+ num_midis++;
+ return i;
+ }
+ }
- ioaddr = (val & 0x000fff00) >> 8;
- irq = (val & 0x000000f0) >> 4;
- dma = (val & 0x0000000f);
- dma2 = (val & 0xf0000000) >> 28;
-
- ptr = -1;
- for (j = 0; j < n && ptr == -1; j++)
- if (snd_installed_cards[j].card_type == card_type &&
- !snd_installed_cards[j].enabled) /*
- * Not already found
- */
- ptr = j;
-
- if (ptr == -1)
- printk ("Sound: Invalid setup parameter 0x%08x\n", val);
- else
- {
- snd_installed_cards[ptr].enabled = 1;
- snd_installed_cards[ptr].config.io_base = ioaddr;
- snd_installed_cards[ptr].config.irq = irq;
- snd_installed_cards[ptr].config.dma = dma;
- snd_installed_cards[ptr].config.dma2 = dma2;
- snd_installed_cards[ptr].config.name = NULL;
- snd_installed_cards[ptr].config.always_detect = 0;
- snd_installed_cards[ptr].config.driver_use_1 = 0;
- snd_installed_cards[ptr].config.driver_use_2 = 0;
- snd_installed_cards[ptr].config.card_subtype = 0;
- }
- }
+ return -1;
}
-
-struct address_info
- *
-sound_getconf (int card_type)
+int
+sound_alloc_synthdev(void)
{
- int j, ptr;
- int n = num_sound_cards;
+ int i;
- ptr = -1;
- for (j = 0; j < n && ptr == -1 && snd_installed_cards[j].card_type; j++)
- if (snd_installed_cards[j].card_type == card_type)
- ptr = j;
-
- if (ptr == -1)
- return (struct address_info *) NULL;
-
- return &snd_installed_cards[ptr].config;
+ for (i = 0; i < MAX_SYNTH_DEV; i++)
+ {
+ if (synth_devs[i] == NULL)
+ {
+ if (i >= num_synths)
+ num_synths++;
+ return i;
+ }
+ }
+ return -1;
}
+int
+sound_alloc_mixerdev(void)
+{
+ int i;
+ for (i = 0; i < MAX_MIXER_DEV; i++)
+ {
+ if (mixer_devs[i] == NULL)
+ {
+ if (i >= num_mixers)
+ num_mixers++;
+ return i;
+ }
+ }
+ return -1;
+}
int
-sound_install_audiodrv (int vers,
- char *name,
- struct audio_driver *driver,
- int driver_size,
- int flags,
- unsigned int format_mask,
- void *devc,
- int dma1,
- int dma2)
+sound_alloc_timerdev(void)
{
-#ifdef CONFIG_AUDIO
- struct audio_driver *d;
- struct audio_operations *op;
- int l, num;
-
- if (num_audiodevs >= MAX_AUDIO_DEV)
- {
- printk ("Sound: Too many audio drivers\n");
- return -EIO;
- }
-
- if (vers != AUDIO_DRIVER_VERSION ||
- driver_size > sizeof (struct audio_driver))
- {
- printk ("Sound: Incompatible audio driver for %s\n", name);
- return -EIO;
- }
-
-
- d = (struct audio_driver *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct audio_driver)));
- sound_mem_sizes[sound_nblocks] = sizeof (struct audio_driver);
-
- if (sound_nblocks < 1024)
- sound_nblocks++;;
-
- op = (struct audio_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct audio_operations)));
- sound_mem_sizes[sound_nblocks] = sizeof (struct audio_operations);
-
- if (sound_nblocks < 1024)
- sound_nblocks++;;
- if (d == NULL || op == NULL)
- {
- printk ("Sound: Can't allocate driver for (%s)\n", name);
- return -ENOSPC;
- }
-
- memset ((char *) op, 0, sizeof (struct audio_operations));
- if (driver_size < sizeof (struct audio_driver))
- memset ((char *) d, 0, sizeof (struct audio_driver));
-
- memcpy ((char *) d, (char *) driver, driver_size);
-
- op->d = d;
-
- l = strlen (name) + 1;
- if (l > sizeof (op->name))
- l = sizeof (op->name);
- strncpy (op->name, name, l);
- op->name[l - 1] = 0;
- op->flags = flags;
- op->format_mask = format_mask;
- op->devc = devc;
+ int i;
-/*
- * Hardcoded defaults
- */
- audio_devs[num_audiodevs] = op;
- num = num_audiodevs++;
+ for (i = 0; i < MAX_TIMER_DEV; i++)
+ {
+ if (sound_timer_devs[i] == NULL)
+ {
+ if (i >= num_sound_timers)
+ num_sound_timers++;
+ return i;
+ }
+ }
+ return -1;
+}
- DMAbuf_init (num, dma1, dma2);
+void
+sound_unload_mixerdev(int dev)
+{
+ if (dev != -1)
+ mixer_devs[dev] = NULL;
+}
- audio_init_devices ();
- return num;
-#else
- return -EINVAL;
+void
+sound_unload_mididev(int dev)
+{
+#ifdef CONFIG_MIDI
+ if (dev != -1)
+ midi_devs[dev] = NULL;
#endif
}
-int
-sound_install_mixer (int vers,
- char *name,
- struct mixer_operations *driver,
- int driver_size,
- void *devc)
+void
+sound_unload_synthdev(int dev)
+{
+ if (dev != -1)
+ synth_devs[dev] = NULL;
+}
+
+void
+sound_unload_timerdev(int dev)
{
- struct mixer_operations *op;
- int l;
-
- if (num_mixers >= MAX_MIXER_DEV)
- {
- printk ("Sound: Too many mixer drivers\n");
- return -EIO;
- }
-
- if (vers != MIXER_DRIVER_VERSION ||
- driver_size > sizeof (struct mixer_operations))
- {
- printk ("Sound: Incompatible mixer driver for %s\n", name);
- return -EIO;
- }
-
-
- op = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct mixer_operations)));
- sound_mem_sizes[sound_nblocks] = sizeof (struct mixer_operations);
-
- if (sound_nblocks < 1024)
- sound_nblocks++;;
- if (op == NULL)
- {
- printk ("Sound: Can't allocate mixer driver for (%s)\n", name);
- return -ENOSPC;
- }
-
- memset ((char *) op, 0, sizeof (struct mixer_operations));
-
- memcpy ((char *) op, (char *) driver, driver_size);
-
- l = strlen (name) + 1;
- if (l > sizeof (op->name))
- l = sizeof (op->name);
- strncpy (op->name, name, l);
- op->name[l - 1] = 0;
- op->devc = devc;
-
- mixer_devs[num_mixers] = op;
- return num_mixers++;
+ if (dev != -1)
+ sound_timer_devs[dev] = NULL;
}
diff --git a/drivers/sound/dev_table.h b/drivers/sound/dev_table.h
index a7e4026e8..a146614c8 100644
--- a/drivers/sound/dev_table.h
+++ b/drivers/sound/dev_table.h
@@ -15,7 +15,6 @@
#ifndef _DEV_TABLE_H_
#define _DEV_TABLE_H_
-#include <linux/config.h>
/*
* Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h)
@@ -23,6 +22,23 @@
*/
#define SNDCARD_DESKPROXL 27 /* Compaq Deskpro XL */
#define SNDCARD_SBPNP 29
+#define SNDCARD_OPL3SA1 38
+#define SNDCARD_OPL3SA1_SB 39
+#define SNDCARD_OPL3SA1_MPU 40
+#define SNDCARD_SOFTOSS 36
+
+void attach_opl3sa_wss (struct address_info *hw_config);
+int probe_opl3sa_wss (struct address_info *hw_config);
+void attach_opl3sa_sb (struct address_info *hw_config);
+int probe_opl3sa_sb (struct address_info *hw_config);
+void attach_opl3sa_mpu (struct address_info *hw_config);
+int probe_opl3sa_mpu (struct address_info *hw_config);
+void unload_opl3sa_wss(struct address_info *hw_info);
+void unload_opl3sa_sb(struct address_info *hw_info);
+void unload_opl3sa_mpu(struct address_info *hw_info);
+void attach_softsyn_card (struct address_info *hw_config);
+int probe_softsyn (struct address_info *hw_config);
+void unload_softsyn (struct address_info *hw_config);
/*
* NOTE! NOTE! NOTE! NOTE!
@@ -125,6 +141,10 @@ struct dma_buffparms {
OS_DMA_PARMS
#endif
int applic_profile; /* Application profile (APF_*) */
+ /* Interrupt callback stuff */
+ void (*audio_callback) (int dev, int parm);
+ int callback_parm;
+
int buf_flags[MAX_SUB_BUFFERS];
#define BUFF_EOF 0x00000001 /* Increment eof count */
#define BUFF_DIRTY 0x00000002 /* Buffer written */
@@ -318,12 +338,13 @@ struct sound_timer_operations {
*/
struct driver_info sound_drivers[] = {
-#ifdef CONFIG_PSS
+#if defined(CONFIG_PSS) && !defined(CONFIG_PSS_MODULE)
{"PSS", 0, SNDCARD_PSS, "Echo Personal Sound System PSS (ESC614)", attach_pss, probe_pss, unload_pss},
{"PSSMPU", 0, SNDCARD_PSS_MPU, "PSS-MPU", attach_pss_mpu, probe_pss_mpu, unload_pss_mpu},
{"PSSMSS", 0, SNDCARD_PSS_MSS, "PSS-MSS", attach_pss_mss, probe_pss_mss, unload_pss_mss},
#endif
+#if defined(CONFIG_GUS) && !defined(CONFIG_GUS_MODULE)
#ifdef CONFIG_GUS16
{"GUS16", 0, SNDCARD_GUS16, "Ultrasound 16-bit opt.", attach_gus_db16, probe_gus_db16, unload_gus_db16},
#endif
@@ -331,8 +352,9 @@ struct sound_timer_operations {
{"GUS", 0, SNDCARD_GUS, "Gravis Ultrasound", attach_gus_card, probe_gus, unload_gus},
{"GUSPNP", 1, SNDCARD_GUSPNP, "GUS PnP", attach_gus_card, probe_gus, unload_gus},
#endif
+#endif
-#ifdef CONFIG_MSS
+#if defined(CONFIG_MSS) && !defined(CONFIG_MSS_MODULE)
{"MSS", 0, SNDCARD_MSS, "MS Sound System", attach_ms_sound, probe_ms_sound, unload_ms_sound},
/* Compaq Deskpro XL */
{"DESKPROXL", 2, SNDCARD_DESKPROXL, "Compaq Deskpro XL", attach_ms_sound, probe_ms_sound, unload_ms_sound},
@@ -345,30 +367,30 @@ struct sound_timer_operations {
{"CS4232", 0, SNDCARD_CS4232, "CS4232", attach_cs4232, probe_cs4232, unload_cs4232},
{"CS4232MPU", 0, SNDCARD_CS4232_MPU, "CS4232 MIDI", attach_cs4232_mpu, probe_cs4232_mpu, unload_cs4232_mpu},
#endif
-#if defined(CONFIG_YM3812)
+#if defined(CONFIG_YM3812) && !defined(CONFIG_YM3812_MODULE)
{"OPL3", 0, SNDCARD_ADLIB, "OPL-2/OPL-3 FM", attach_adlib_card, probe_adlib, unload_adlib},
#endif
-#ifdef CONFIG_PAS
+#if defined(CONFIG_PAS) && !defined(CONFIG_PAS_MODULE)
{"PAS16", 0, SNDCARD_PAS, "ProAudioSpectrum", attach_pas_card, probe_pas, unload_pas},
#endif
-#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI)
+#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) && !defined(CONFIG_MPU401_MODULE)
{"MPU401", 0, SNDCARD_MPU401,"Roland MPU-401", attach_mpu401, probe_mpu401, unload_mpu401},
#endif
-#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
+#if defined(CONFIG_UART401) && defined(CONFIG_MIDI) && !defined(CONFIG_UART401_MODULE)
{"UART401", 0, SNDCARD_UART401,"MPU-401 (UART)",
attach_uart401, probe_uart401, unload_uart401},
#endif
-#if defined(CONFIG_MAUI)
+#if defined(CONFIG_MAUI) && !defined(CONFIG_MAUI_MODULE)
{"MAUI", 0, SNDCARD_MAUI,"TB Maui", attach_maui, probe_maui, unload_maui},
#endif
-#if defined(CONFIG_UART6850) && defined(CONFIG_MIDI)
+#if defined(CONFIG_UART6850) && defined(CONFIG_MIDI) && !defined(CONFIG_UART6850_MODULE)
{"MIDI6850", 0, SNDCARD_UART6850,"6860 UART Midi", attach_uart6850, probe_uart6850, unload_uart6850},
#endif
-#ifdef CONFIG_SBDSP
+#if defined(CONFIG_SBDSP) && !defined(CONFIG_SBDSP_MODULE)
{"SBLAST", 0, SNDCARD_SB, "Sound Blaster", attach_sb_card, probe_sb, unload_sb},
{"SBPNP", 6, SNDCARD_SBPNP, "Sound Blaster PnP", attach_sb_card, probe_sb, unload_sb},
@@ -381,12 +403,23 @@ struct sound_timer_operations {
{"SSCAPE", 0, SNDCARD_SSCAPE, "Ensoniq SoundScape", attach_sscape, probe_sscape, unload_sscape},
{"SSCAPEMSS", 0, SNDCARD_SSCAPE_MSS, "MS Sound System (SoundScape)", attach_ss_ms_sound, probe_ss_ms_sound, unload_ss_ms_sound},
#endif
-#ifdef CONFIG_TRIX
+
+#ifdef CONFIG_OPL3SA1
+ {"OPL3SA", 0, SNDCARD_OPL3SA1, "Yamaha OPL3-SA", attach_opl3sa_wss, probe_opl3sa_wss, unload_opl3sa_wss},
+/* {"OPL3SASB", 0, SNDCARD_OPL3SA1_SB, "OPL3-SA (SB mode)", attach_opl3sa_sb, probe_opl3sa_sb, unload_opl3sa_sb}, */
+ {"OPL3SAMPU", 0, SNDCARD_OPL3SA1_MPU, "OPL3-SA MIDI", attach_opl3sa_mpu, probe_opl3sa_mpu, unload_opl3sa_mpu},
+#endif
+
+#if defined (CONFIG_TRIX) && !defined(CONFIG_TRIX_MODULE)
{"TRXPRO", 0, SNDCARD_TRXPRO, "MediaTrix AudioTrix Pro", attach_trix_wss, probe_trix_wss, unload_trix_wss},
{"TRXPROSB", 0, SNDCARD_TRXPRO_SB, "AudioTrix (SB mode)", attach_trix_sb, probe_trix_sb, unload_trix_sb},
{"TRXPROMPU", 0, SNDCARD_TRXPRO_MPU, "AudioTrix MIDI", attach_trix_mpu, probe_trix_mpu, unload_trix_mpu},
#endif
+#if defined(CONFIG_SOFTOSS) && !defined(CONFIG_SOFTOSS_MODULE)
+ {"SOFTSYN", 0, SNDCARD_SOFTOSS, "SoftOSS Virtual Wave Table",
+ attach_softsyn_card, probe_softsyn, unload_softsyn},
+#endif
@@ -427,6 +460,18 @@ struct sound_timer_operations {
{SNDCARD_TRXPRO_MPU, {TRIX_MPU_BASE, TRIX_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE},
# endif
#endif
+
+#ifdef CONFIG_OPL3SA1
+ {SNDCARD_OPL3SA1, {OPL3SA1_BASE, OPL3SA1_IRQ, OPL3SA1_DMA, OPL3SA1_DMA2}, SND_DEFAULT_ENABLE},
+# ifdef OPL3SA1_MPU_BASE
+ {SNDCARD_OPL3SA1_MPU, {OPL3SA1_MPU_BASE, OPL3SA1_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE},
+# endif
+#endif
+
+#ifdef CONFIG_SOFTOSS
+ {SNDCARD_SOFTOSS, {0, 0, -1, -1}, SND_DEFAULT_ENABLE},
+#endif
+
#ifdef CONFIG_SSCAPE
{SNDCARD_SSCAPE, {SSCAPE_BASE, SSCAPE_IRQ, SSCAPE_DMA, -1}, SND_DEFAULT_ENABLE},
{SNDCARD_SSCAPE_MSS, {SSCAPE_MSS_BASE, SSCAPE_MSS_IRQ, SSCAPE_DMA, -1}, SND_DEFAULT_ENABLE},
@@ -518,7 +563,7 @@ struct sound_timer_operations {
{SNDCARD_GUS, {GUS_BASE, GUS_IRQ, GUS_DMA, GUS_DMA2}, SND_DEFAULT_ENABLE},
#endif
-#ifdef CONFIG_YM3812
+#if defined(CONFIG_YM3812)
{SNDCARD_ADLIB, {FM_MONO, 0, 0, -1}, SND_DEFAULT_ENABLE},
#endif
{0, {0}, 0}
@@ -595,4 +640,15 @@ int sound_install_mixer(int vers,
int driver_size,
void *devc);
+void sound_unload_audiodev(int dev);
+void sound_unload_mixerdev(int dev);
+void sound_unload_mididev(int dev);
+void sound_unload_synthdev(int dev);
+void sound_unload_timerdev(int dev);
+int sound_alloc_audiodev(void);
+int sound_alloc_mixerdev(void);
+int sound_alloc_timerdev(void);
+int sound_alloc_synthdev(void);
+int sound_alloc_mididev(void);
#endif /* _DEV_TABLE_H_ */
+
diff --git a/drivers/sound/dmabuf.c b/drivers/sound/dmabuf.c
index 54523d88f..609831377 100644
--- a/drivers/sound/dmabuf.c
+++ b/drivers/sound/dmabuf.c
@@ -22,12 +22,12 @@ static struct wait_queue *in_sleeper[MAX_AUDIO_DEV] =
{NULL};
static volatile struct snd_wait in_sleep_flag[MAX_AUDIO_DEV] =
{
- {0}};
+ {0}};
static struct wait_queue *out_sleeper[MAX_AUDIO_DEV] =
{NULL};
static volatile struct snd_wait out_sleep_flag[MAX_AUDIO_DEV] =
{
- {0}};
+ {0}};
static int ndmaps = 0;
@@ -35,721 +35,700 @@ static int ndmaps = 0;
static struct dma_buffparms dmaps[MAX_DMAP] =
{
- {0}};
+ {0}};
-static void dma_reset_output (int dev);
-static void dma_reset_input (int dev);
-static int local_start_dma (int dev, unsigned long physaddr, int count, int dma_mode);
+static void dma_reset_output(int dev);
+static void dma_reset_input(int dev);
+static int local_start_dma(int dev, unsigned long physaddr, int count, int dma_mode);
static void
-dma_init_buffers (int dev, struct dma_buffparms *dmap)
+dma_init_buffers(int dev, struct dma_buffparms *dmap)
{
- dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
- dmap->byte_counter = 0;
- dmap->max_byte_counter = 8000 * 60 * 60;
- dmap->bytes_in_use = dmap->buffsize;
-
- dmap->dma_mode = DMODE_NONE;
- dmap->mapping_flags = 0;
- dmap->neutral_byte = 0x80;
- dmap->data_rate = 8000;
- dmap->cfrag = -1;
- dmap->closing = 0;
- dmap->nbufs = 1;
- dmap->flags = DMA_BUSY; /* Other flags off */
+ dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+ dmap->byte_counter = 0;
+ dmap->max_byte_counter = 8000 * 60 * 60;
+ dmap->bytes_in_use = dmap->buffsize;
+
+ dmap->dma_mode = DMODE_NONE;
+ dmap->mapping_flags = 0;
+ dmap->neutral_byte = 0x80;
+ dmap->data_rate = 8000;
+ dmap->cfrag = -1;
+ dmap->closing = 0;
+ dmap->nbufs = 1;
+ dmap->flags = DMA_BUSY; /* Other flags off */
}
static int
-open_dmap (int dev, int mode, struct dma_buffparms *dmap, int chan)
+open_dmap(int dev, int mode, struct dma_buffparms *dmap, int chan)
{
- if (dmap->flags & DMA_BUSY)
- return -EBUSY;
-
- {
- int err;
-
- if ((err = sound_alloc_dmap (dev, dmap, chan)) < 0)
- return err;
- }
-
- if (dmap->raw_buf == NULL)
- {
- printk ("Sound: DMA buffers not available\n");
- return -ENOSPC; /* Memory allocation failed during boot */
- }
-
- if (sound_open_dma (chan, audio_devs[dev]->name))
- {
- printk ("Unable to grab(2) DMA%d for the audio driver\n", chan);
- return -EBUSY;
- }
-
- dma_init_buffers (dev, dmap);
- dmap->open_mode = mode;
- dmap->subdivision = dmap->underrun_count = 0;
- dmap->fragment_size = 0;
- dmap->max_fragments = 65536; /* Just a large value */
- dmap->byte_counter = 0;
- dmap->max_byte_counter = 8000 * 60 * 60;
- dmap->applic_profile = APF_NORMAL;
- dmap->needs_reorg = 1;
-
-
- if (dmap->dma_mode & DMODE_OUTPUT)
- {
- out_sleep_flag[dev].opts = WK_NONE;
- }
- else
- {
- in_sleep_flag[dev].opts = WK_NONE;
- }
-
- return 0;
+ if (dmap->flags & DMA_BUSY)
+ return -EBUSY;
+
+ {
+ int err;
+
+ if ((err = sound_alloc_dmap(dev, dmap, chan)) < 0)
+ return err;
+ }
+
+ if (dmap->raw_buf == NULL)
+ {
+ printk("Sound: DMA buffers not available\n");
+ return -ENOSPC; /* Memory allocation failed during boot */
+ }
+ if (sound_open_dma(chan, audio_devs[dev]->name))
+ {
+ printk("Unable to grab(2) DMA%d for the audio driver\n", chan);
+ return -EBUSY;
+ }
+ dma_init_buffers(dev, dmap);
+ dmap->open_mode = mode;
+ dmap->subdivision = dmap->underrun_count = 0;
+ dmap->fragment_size = 0;
+ dmap->max_fragments = 65536; /* Just a large value */
+ dmap->byte_counter = 0;
+ dmap->max_byte_counter = 8000 * 60 * 60;
+ dmap->applic_profile = APF_NORMAL;
+ dmap->needs_reorg = 1;
+ dmap->audio_callback = NULL;
+ dmap->callback_parm = 0;
+
+
+ if (dmap->dma_mode & DMODE_OUTPUT)
+ {
+ out_sleep_flag[dev].opts = WK_NONE;
+ } else
+ {
+ in_sleep_flag[dev].opts = WK_NONE;
+ }
+
+ return 0;
}
static void
-close_dmap (int dev, struct dma_buffparms *dmap, int chan)
+close_dmap(int dev, struct dma_buffparms *dmap, int chan)
{
- sound_close_dma (chan);
+ sound_close_dma(chan);
- if (dmap->flags & DMA_BUSY)
- dmap->dma_mode = DMODE_NONE;
- dmap->flags &= ~DMA_BUSY;
+ if (dmap->flags & DMA_BUSY)
+ dmap->dma_mode = DMODE_NONE;
+ dmap->flags &= ~DMA_BUSY;
- disable_dma (dmap->dma);
+ disable_dma(dmap->dma);
}
static unsigned int
-default_set_bits (int dev, unsigned int bits)
+default_set_bits(int dev, unsigned int bits)
{
- return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SETFMT, (caddr_t) & bits);
+ return audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SETFMT, (caddr_t) & bits);
}
static int
-default_set_speed (int dev, int speed)
+default_set_speed(int dev, int speed)
{
- return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SPEED, (caddr_t) & speed);
+ return audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SPEED, (caddr_t) & speed);
}
static short
-default_set_channels (int dev, short channels)
+default_set_channels(int dev, short channels)
{
- int c = channels;
+ int c = channels;
- return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_CHANNELS, (caddr_t) & c);
+ return audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_CHANNELS, (caddr_t) & c);
}
static void
-check_driver (struct audio_driver *d)
+check_driver(struct audio_driver *d)
{
- if (d->set_speed == NULL)
- d->set_speed = default_set_speed;
- if (d->set_bits == NULL)
- d->set_bits = default_set_bits;
- if (d->set_channels == NULL)
- d->set_channels = default_set_channels;
+ if (d->set_speed == NULL)
+ d->set_speed = default_set_speed;
+ if (d->set_bits == NULL)
+ d->set_bits = default_set_bits;
+ if (d->set_channels == NULL)
+ d->set_channels = default_set_channels;
}
int
-DMAbuf_open (int dev, int mode)
+DMAbuf_open(int dev, int mode)
{
- int retval;
- struct dma_buffparms *dmap_in = NULL;
- struct dma_buffparms *dmap_out = NULL;
-
- if (dev >= num_audiodevs)
- {
- return -ENXIO;
- }
-
- if (!audio_devs[dev])
- {
- return -ENXIO;
- }
-
- if (!(audio_devs[dev]->flags & DMA_DUPLEX))
- {
- audio_devs[dev]->dmap_in = audio_devs[dev]->dmap_out;
- audio_devs[dev]->dmap_in->dma = audio_devs[dev]->dmap_out->dma;
- }
-
- check_driver (audio_devs[dev]->d);
-
- if ((retval = audio_devs[dev]->d->open (dev, mode)) < 0)
- return retval;
-
- dmap_out = audio_devs[dev]->dmap_out;
- dmap_in = audio_devs[dev]->dmap_in;
-
- if (dmap_in == dmap_out)
- audio_devs[dev]->flags &= ~DMA_DUPLEX;
+ int retval;
+ struct dma_buffparms *dmap_in = NULL;
+ struct dma_buffparms *dmap_out = NULL;
- if (mode & OPEN_WRITE)
- {
- if ((retval = open_dmap (dev, mode, dmap_out, audio_devs[dev]->dmap_out->dma)) < 0)
- {
- audio_devs[dev]->d->close (dev);
- return retval;
- }
- }
-
- audio_devs[dev]->enable_bits = mode;
+ if (dev >= num_audiodevs || audio_devs[dev] == NULL)
+ {
+ return -ENXIO;
+ }
+ if (!audio_devs[dev])
+ {
+ return -ENXIO;
+ }
+ if (!(audio_devs[dev]->flags & DMA_DUPLEX))
+ {
+ audio_devs[dev]->dmap_in = audio_devs[dev]->dmap_out;
+ audio_devs[dev]->dmap_in->dma = audio_devs[dev]->dmap_out->dma;
+ }
+ check_driver(audio_devs[dev]->d);
- if (mode == OPEN_READ || (mode != OPEN_WRITE &&
- audio_devs[dev]->flags & DMA_DUPLEX))
- {
- if ((retval = open_dmap (dev, mode, dmap_in, audio_devs[dev]->dmap_in->dma)) < 0)
- {
- audio_devs[dev]->d->close (dev);
+ if ((retval = audio_devs[dev]->d->open(dev, mode)) < 0)
+ return retval;
- if (mode & OPEN_WRITE)
- {
- close_dmap (dev, dmap_out, audio_devs[dev]->dmap_out->dma);
- }
+ dmap_out = audio_devs[dev]->dmap_out;
+ dmap_in = audio_devs[dev]->dmap_in;
- return retval;
- }
- }
+ if (dmap_in == dmap_out)
+ audio_devs[dev]->flags &= ~DMA_DUPLEX;
- audio_devs[dev]->open_mode = mode;
- audio_devs[dev]->go = 1;
+ if (mode & OPEN_WRITE)
+ {
+ if ((retval = open_dmap(dev, mode, dmap_out, audio_devs[dev]->dmap_out->dma)) < 0)
+ {
+ audio_devs[dev]->d->close(dev);
+ return retval;
+ }
+ }
+ audio_devs[dev]->enable_bits = mode;
- if (mode & OPEN_READ)
- in_sleep_flag[dev].opts = WK_NONE;
+ if (mode == OPEN_READ || (mode != OPEN_WRITE &&
+ audio_devs[dev]->flags & DMA_DUPLEX))
+ {
+ if ((retval = open_dmap(dev, mode, dmap_in, audio_devs[dev]->dmap_in->dma)) < 0)
+ {
+ audio_devs[dev]->d->close(dev);
+
+ if (mode & OPEN_WRITE)
+ {
+ close_dmap(dev, dmap_out, audio_devs[dev]->dmap_out->dma);
+ }
+ return retval;
+ }
+ }
+ audio_devs[dev]->open_mode = mode;
+ audio_devs[dev]->go = 1;
- if (mode & OPEN_WRITE)
- out_sleep_flag[dev].opts = WK_NONE;
+ if (mode & OPEN_READ)
+ in_sleep_flag[dev].opts = WK_NONE;
- audio_devs[dev]->d->set_bits (dev, 8);
- audio_devs[dev]->d->set_channels (dev, 1);
- audio_devs[dev]->d->set_speed (dev, DSP_DEFAULT_SPEED);
+ if (mode & OPEN_WRITE)
+ out_sleep_flag[dev].opts = WK_NONE;
- if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)
- {
- memset (audio_devs[dev]->dmap_out->raw_buf,
- audio_devs[dev]->dmap_out->neutral_byte,
- audio_devs[dev]->dmap_out->bytes_in_use);
- }
+ audio_devs[dev]->d->set_bits(dev, 8);
+ audio_devs[dev]->d->set_channels(dev, 1);
+ audio_devs[dev]->d->set_speed(dev, DSP_DEFAULT_SPEED);
- return 0;
+ if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)
+ {
+ memset(audio_devs[dev]->dmap_out->raw_buf,
+ audio_devs[dev]->dmap_out->neutral_byte,
+ audio_devs[dev]->dmap_out->bytes_in_use);
+ }
+ return 0;
}
void
-DMAbuf_reset (int dev)
+DMAbuf_reset(int dev)
{
- if (audio_devs[dev]->open_mode & OPEN_WRITE)
- dma_reset_output (dev);
+ if (audio_devs[dev]->open_mode & OPEN_WRITE)
+ dma_reset_output(dev);
- if (audio_devs[dev]->open_mode & OPEN_READ)
- dma_reset_input (dev);
+ if (audio_devs[dev]->open_mode & OPEN_READ)
+ dma_reset_input(dev);
}
static void
-dma_reset_output (int dev)
+dma_reset_output(int dev)
{
- unsigned long flags;
- int tmout;
+ unsigned long flags;
+ int tmout;
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
- if (!(dmap->flags & DMA_STARTED)) /* DMA is not active */
- return;
+ if (!(dmap->flags & DMA_STARTED)) /* DMA is not active */
+ return;
/*
* First wait until the current fragment has been played completely
*/
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- tmout =
- (dmap->fragment_size * HZ) / dmap->data_rate;
+ tmout =
+ (dmap->fragment_size * HZ) / dmap->data_rate;
- tmout += HZ / 10; /* Some safety distance */
+ tmout += HZ / 5; /* Some safety distance */
- if (tmout < (HZ / 2))
- tmout = HZ / 2;
- if (tmout > 20 * HZ)
- tmout = 20 * HZ;
+ if (tmout < (HZ / 2))
+ tmout = HZ / 2;
+ if (tmout > 20 * HZ)
+ tmout = 20 * HZ;
- audio_devs[dev]->dmap_out->flags |= DMA_SYNCING;
+ audio_devs[dev]->dmap_out->flags |= DMA_SYNCING;
- audio_devs[dev]->dmap_out->underrun_count = 0;
- if (!(current->signal & ~current->blocked)
- && audio_devs[dev]->dmap_out->qlen
- && audio_devs[dev]->dmap_out->underrun_count == 0)
- {
-
- {
- unsigned long tlimit;
-
- if (tmout)
- current->timeout = tlimit = jiffies + (tmout);
- else
- tlimit = (unsigned long) -1;
- out_sleep_flag[dev].opts = WK_SLEEP;
- interruptible_sleep_on (&out_sleeper[dev]);
- if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
+ audio_devs[dev]->dmap_out->underrun_count = 0;
+ if (!(current->signal & ~current->blocked)
+ && audio_devs[dev]->dmap_out->qlen
+ && audio_devs[dev]->dmap_out->underrun_count == 0)
{
- if (jiffies >= tlimit)
- out_sleep_flag[dev].opts |= WK_TIMEOUT;
+
+ {
+ unsigned long tlimit;
+
+ if (tmout)
+ current->timeout = tlimit = jiffies + (tmout);
+ else
+ tlimit = (unsigned long) -1;
+ out_sleep_flag[dev].opts = WK_SLEEP;
+ interruptible_sleep_on(&out_sleeper[dev]);
+ if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ out_sleep_flag[dev].opts |= WK_TIMEOUT;
+ }
+ out_sleep_flag[dev].opts &= ~WK_SLEEP;
+ };
}
- out_sleep_flag[dev].opts &= ~WK_SLEEP;
- };
- }
- audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
+ audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
/*
* Finally shut the device off
*/
- if (!(audio_devs[dev]->flags & DMA_DUPLEX) ||
- !audio_devs[dev]->d->halt_output)
- audio_devs[dev]->d->halt_io (dev);
- else
- audio_devs[dev]->d->halt_output (dev);
- audio_devs[dev]->dmap_out->flags &= ~DMA_STARTED;
- restore_flags (flags);
-
- clear_dma_ff (dmap->dma);
- disable_dma (dmap->dma);
- dmap->byte_counter = 0;
- reorganize_buffers (dev, audio_devs[dev]->dmap_out, 0);
- dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+ if (!(audio_devs[dev]->flags & DMA_DUPLEX) ||
+ !audio_devs[dev]->d->halt_output)
+ audio_devs[dev]->d->halt_io(dev);
+ else
+ audio_devs[dev]->d->halt_output(dev);
+ audio_devs[dev]->dmap_out->flags &= ~DMA_STARTED;
+ restore_flags(flags);
+
+ clear_dma_ff(dmap->dma);
+ disable_dma(dmap->dma);
+ dmap->byte_counter = 0;
+ reorganize_buffers(dev, audio_devs[dev]->dmap_out, 0);
+ dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
}
static void
-dma_reset_input (int dev)
+dma_reset_input(int dev)
{
- unsigned long flags;
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
-
- save_flags (flags);
- cli ();
- if (!(audio_devs[dev]->flags & DMA_DUPLEX) ||
- !audio_devs[dev]->d->halt_input)
- audio_devs[dev]->d->halt_io (dev);
- else
- audio_devs[dev]->d->halt_input (dev);
- audio_devs[dev]->dmap_in->flags &= ~DMA_STARTED;
- restore_flags (flags);
-
- dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
- dmap->byte_counter = 0;
- reorganize_buffers (dev, audio_devs[dev]->dmap_in, 1);
+ unsigned long flags;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
+
+ save_flags(flags);
+ cli();
+ if (!(audio_devs[dev]->flags & DMA_DUPLEX) ||
+ !audio_devs[dev]->d->halt_input)
+ audio_devs[dev]->d->halt_io(dev);
+ else
+ audio_devs[dev]->d->halt_input(dev);
+ audio_devs[dev]->dmap_in->flags &= ~DMA_STARTED;
+ restore_flags(flags);
+
+ dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+ dmap->byte_counter = 0;
+ reorganize_buffers(dev, audio_devs[dev]->dmap_in, 1);
}
void
-DMAbuf_launch_output (int dev, struct dma_buffparms *dmap)
+DMAbuf_launch_output(int dev, struct dma_buffparms *dmap)
{
- if (!((audio_devs[dev]->enable_bits * audio_devs[dev]->go) & PCM_ENABLE_OUTPUT))
- return; /* Don't start DMA yet */
+ if (!((audio_devs[dev]->enable_bits * audio_devs[dev]->go) & PCM_ENABLE_OUTPUT))
+ return; /* Don't start DMA yet */
- dmap->dma_mode = DMODE_OUTPUT;
+ dmap->dma_mode = DMODE_OUTPUT;
- if (!(dmap->flags & DMA_ACTIVE) || !(audio_devs[dev]->flags & DMA_AUTOMODE) || dmap->flags & DMA_NODMA)
- {
- if (!(dmap->flags & DMA_STARTED))
- {
- reorganize_buffers (dev, dmap, 0);
-
- if (audio_devs[dev]->d->prepare_for_output (dev,
- dmap->fragment_size, dmap->nbufs))
- return;
-
- if (!(dmap->flags & DMA_NODMA))
- {
- local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
- DMA_MODE_WRITE);
- }
- dmap->flags |= DMA_STARTED;
- }
- if (dmap->counts[dmap->qhead] == 0)
- dmap->counts[dmap->qhead] = dmap->fragment_size;
-
- dmap->dma_mode = DMODE_OUTPUT;
- audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys +
- dmap->qhead * dmap->fragment_size,
- dmap->counts[dmap->qhead], 1);
- if (audio_devs[dev]->d->trigger)
- audio_devs[dev]->d->trigger (dev,
- audio_devs[dev]->enable_bits * audio_devs[dev]->go);
- }
- dmap->flags |= DMA_ACTIVE;
+ if (!(dmap->flags & DMA_ACTIVE) || !(audio_devs[dev]->flags & DMA_AUTOMODE) || dmap->flags & DMA_NODMA)
+ {
+ if (!(dmap->flags & DMA_STARTED))
+ {
+ reorganize_buffers(dev, dmap, 0);
+
+ if (audio_devs[dev]->d->prepare_for_output(dev,
+ dmap->fragment_size, dmap->nbufs))
+ return;
+
+ if (!(dmap->flags & DMA_NODMA))
+ {
+ local_start_dma(dev, dmap->raw_buf_phys, dmap->bytes_in_use,
+ DMA_MODE_WRITE);
+ }
+ dmap->flags |= DMA_STARTED;
+ }
+ if (dmap->counts[dmap->qhead] == 0)
+ dmap->counts[dmap->qhead] = dmap->fragment_size;
+
+ dmap->dma_mode = DMODE_OUTPUT;
+ audio_devs[dev]->d->output_block(dev, dmap->raw_buf_phys +
+ dmap->qhead * dmap->fragment_size,
+ dmap->counts[dmap->qhead], 1);
+ if (audio_devs[dev]->d->trigger)
+ audio_devs[dev]->d->trigger(dev,
+ audio_devs[dev]->enable_bits * audio_devs[dev]->go);
+ }
+ dmap->flags |= DMA_ACTIVE;
}
int
-DMAbuf_sync (int dev)
+DMAbuf_sync(int dev)
{
- unsigned long flags;
- int tmout, n = 0;
-
- if (!audio_devs[dev]->go && (!audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
- return 0;
-
- if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)
- {
-
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
- save_flags (flags);
- cli ();
-
- tmout =
- (dmap->fragment_size * HZ) / dmap->data_rate;
-
- tmout += HZ / 10; /* Some safety distance */
-
- if (tmout < (HZ / 2))
- tmout = HZ / 2;
- if (tmout > 20 * HZ)
- tmout = 20 * HZ;
-
- ;
- if (dmap->qlen > 0)
- if (!(dmap->flags & DMA_ACTIVE))
- DMAbuf_launch_output (dev, dmap);
- ;
+ unsigned long flags;
+ int tmout, n = 0;
- audio_devs[dev]->dmap_out->flags |= DMA_SYNCING;
-
- audio_devs[dev]->dmap_out->underrun_count = 0;
- while (!(current->signal & ~current->blocked)
- && n++ <= audio_devs[dev]->dmap_out->nbufs
- && audio_devs[dev]->dmap_out->qlen
- && audio_devs[dev]->dmap_out->underrun_count == 0)
- {
+ if (!audio_devs[dev]->go && (!audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
+ return 0;
+ if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)
{
- unsigned long tlimit;
-
- if (tmout)
- current->timeout = tlimit = jiffies + (tmout);
- else
- tlimit = (unsigned long) -1;
- out_sleep_flag[dev].opts = WK_SLEEP;
- interruptible_sleep_on (&out_sleeper[dev]);
- if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
- {
- if (jiffies >= tlimit)
- out_sleep_flag[dev].opts |= WK_TIMEOUT;
- }
- out_sleep_flag[dev].opts &= ~WK_SLEEP;
- };
- if ((out_sleep_flag[dev].opts & WK_TIMEOUT))
- {
- audio_devs[dev]->dmap_out->flags &= ~DMA_SYNCING;
- restore_flags (flags);
- return audio_devs[dev]->dmap_out->qlen;
- }
- }
- audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
- restore_flags (flags);
- /*
- * Some devices such as GUS have huge amount of on board RAM for the
- * audio data. We have to wait until the device has finished playing.
- */
-
- save_flags (flags);
- cli ();
- if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */
- {
- while (!((current->signal & ~current->blocked))
- && audio_devs[dev]->d->local_qlen (dev))
- {
-
- {
- unsigned long tlimit;
- if (tmout)
- current->timeout = tlimit = jiffies + (tmout);
- else
- tlimit = (unsigned long) -1;
- out_sleep_flag[dev].opts = WK_SLEEP;
- interruptible_sleep_on (&out_sleeper[dev]);
- if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
- {
- if (jiffies >= tlimit)
- out_sleep_flag[dev].opts |= WK_TIMEOUT;
- }
- out_sleep_flag[dev].opts &= ~WK_SLEEP;
- };
- }
- }
- restore_flags (flags);
- }
- audio_devs[dev]->dmap_out->dma_mode = DMODE_NONE;
- return audio_devs[dev]->dmap_out->qlen;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+
+ save_flags(flags);
+ cli();
+
+ tmout =
+ (dmap->fragment_size * HZ) / dmap->data_rate;
+
+ tmout += HZ / 5; /* Some safety distance */
+
+ if (tmout < (HZ / 2))
+ tmout = HZ / 2;
+ if (tmout > 20 * HZ)
+ tmout = 20 * HZ;
+
+ ;
+ if (dmap->qlen > 0)
+ if (!(dmap->flags & DMA_ACTIVE))
+ DMAbuf_launch_output(dev, dmap);
+ ;
+
+ audio_devs[dev]->dmap_out->flags |= DMA_SYNCING;
+
+ audio_devs[dev]->dmap_out->underrun_count = 0;
+ while (!(current->signal & ~current->blocked)
+ && n++ <= audio_devs[dev]->dmap_out->nbufs
+ && audio_devs[dev]->dmap_out->qlen
+ && audio_devs[dev]->dmap_out->underrun_count == 0)
+ {
+
+ {
+ unsigned long tlimit;
+
+ if (tmout)
+ current->timeout = tlimit = jiffies + (tmout);
+ else
+ tlimit = (unsigned long) -1;
+ out_sleep_flag[dev].opts = WK_SLEEP;
+ interruptible_sleep_on(&out_sleeper[dev]);
+ if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ out_sleep_flag[dev].opts |= WK_TIMEOUT;
+ }
+ out_sleep_flag[dev].opts &= ~WK_SLEEP;
+ };
+ if ((out_sleep_flag[dev].opts & WK_TIMEOUT))
+ {
+ audio_devs[dev]->dmap_out->flags &= ~DMA_SYNCING;
+ restore_flags(flags);
+ return audio_devs[dev]->dmap_out->qlen;
+ }
+ }
+ audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
+ restore_flags(flags);
+ /*
+ * Some devices such as GUS have huge amount of on board RAM for the
+ * audio data. We have to wait until the device has finished playing.
+ */
+
+ save_flags(flags);
+ cli();
+ if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */
+ {
+ while (!((current->signal & ~current->blocked))
+ && audio_devs[dev]->d->local_qlen(dev))
+ {
+
+ {
+ unsigned long tlimit;
+
+ if (tmout)
+ current->timeout = tlimit = jiffies + (tmout);
+ else
+ tlimit = (unsigned long) -1;
+ out_sleep_flag[dev].opts = WK_SLEEP;
+ interruptible_sleep_on(&out_sleeper[dev]);
+ if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ out_sleep_flag[dev].opts |= WK_TIMEOUT;
+ }
+ out_sleep_flag[dev].opts &= ~WK_SLEEP;
+ };
+ }
+ }
+ restore_flags(flags);
+ }
+ audio_devs[dev]->dmap_out->dma_mode = DMODE_NONE;
+ return audio_devs[dev]->dmap_out->qlen;
}
int
-DMAbuf_release (int dev, int mode)
+DMAbuf_release(int dev, int mode)
{
- unsigned long flags;
-
- if (audio_devs[dev]->open_mode & OPEN_WRITE)
- audio_devs[dev]->dmap_out->closing = 1;
- if (audio_devs[dev]->open_mode & OPEN_READ)
- audio_devs[dev]->dmap_in->closing = 1;
-
- if (audio_devs[dev]->open_mode & OPEN_WRITE)
- if (!(audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED))
- if (!((current->signal & ~current->blocked))
- && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT))
- {
- DMAbuf_sync (dev);
- }
-
- if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)
- {
- memset (audio_devs[dev]->dmap_out->raw_buf,
- audio_devs[dev]->dmap_out->neutral_byte,
- audio_devs[dev]->dmap_out->bytes_in_use);
- }
-
- save_flags (flags);
- cli ();
+ unsigned long flags;
+
+ if (audio_devs[dev]->open_mode & OPEN_WRITE)
+ audio_devs[dev]->dmap_out->closing = 1;
+ if (audio_devs[dev]->open_mode & OPEN_READ)
+ audio_devs[dev]->dmap_in->closing = 1;
+
+ if (audio_devs[dev]->open_mode & OPEN_WRITE)
+ if (!(audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED))
+ if (!((current->signal & ~current->blocked))
+ && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT))
+ {
+ DMAbuf_sync(dev);
+ }
+ if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)
+ {
+ memset(audio_devs[dev]->dmap_out->raw_buf,
+ audio_devs[dev]->dmap_out->neutral_byte,
+ audio_devs[dev]->dmap_out->bytes_in_use);
+ }
+ save_flags(flags);
+ cli();
- DMAbuf_reset (dev);
- audio_devs[dev]->d->close (dev);
+ DMAbuf_reset(dev);
+ audio_devs[dev]->d->close(dev);
- if (audio_devs[dev]->open_mode & OPEN_WRITE)
- close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
+ if (audio_devs[dev]->open_mode & OPEN_WRITE)
+ close_dmap(dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
- if (audio_devs[dev]->open_mode == OPEN_READ ||
- (audio_devs[dev]->open_mode != OPEN_WRITE &&
- audio_devs[dev]->flags & DMA_DUPLEX))
- close_dmap (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmap_in->dma);
- audio_devs[dev]->open_mode = 0;
+ if (audio_devs[dev]->open_mode == OPEN_READ ||
+ (audio_devs[dev]->open_mode != OPEN_WRITE &&
+ audio_devs[dev]->flags & DMA_DUPLEX))
+ close_dmap(dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmap_in->dma);
+ audio_devs[dev]->open_mode = 0;
- restore_flags (flags);
+ restore_flags(flags);
- return 0;
+ return 0;
}
int
-DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap)
+DMAbuf_activate_recording(int dev, struct dma_buffparms *dmap)
{
- if (!(audio_devs[dev]->open_mode & OPEN_READ))
- return 0;
-
- if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT))
- return 0;
-
- if (dmap->dma_mode == DMODE_OUTPUT) /* Direction change */
- {
- DMAbuf_sync (dev);
- DMAbuf_reset (dev);
- dmap->dma_mode = DMODE_NONE;
- }
-
- if (!dmap->dma_mode)
- {
- int err;
-
- reorganize_buffers (dev, dmap, 1);
- if ((err = audio_devs[dev]->d->prepare_for_input (dev,
- dmap->fragment_size, dmap->nbufs)) < 0)
- {
- return err;
- }
- dmap->dma_mode = DMODE_INPUT;
- }
-
- if (!(dmap->flags & DMA_ACTIVE))
- {
- if (dmap->needs_reorg)
- reorganize_buffers (dev, dmap, 0);
- local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
- DMA_MODE_READ);
- audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys +
- dmap->qtail * dmap->fragment_size,
- dmap->fragment_size, 0);
- dmap->flags |= DMA_ACTIVE;
- if (audio_devs[dev]->d->trigger)
- audio_devs[dev]->d->trigger (dev,
- audio_devs[dev]->enable_bits * audio_devs[dev]->go);
- }
- return 0;
-}
+ if (!(audio_devs[dev]->open_mode & OPEN_READ))
+ return 0;
-int
-DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock)
-{
- unsigned long flags;
- int err = 0, n = 0;
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
-
- if (!(audio_devs[dev]->open_mode & OPEN_READ))
- return -EIO;
- if (dmap->needs_reorg)
- reorganize_buffers (dev, dmap, 0);
-
- save_flags (flags);
- cli ();
- if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)
- {
- printk ("Sound: Can't read from mmapped device (1)\n");
- return -EINVAL;
- }
- else
- while (dmap->qlen <= 0 && n++ < 10)
- {
- int tmout;
+ if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT))
+ return 0;
- if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT) ||
- !audio_devs[dev]->go)
+ if (dmap->dma_mode == DMODE_OUTPUT) /* Direction change */
{
- restore_flags (flags);
- return -EAGAIN;
+ DMAbuf_sync(dev);
+ DMAbuf_reset(dev);
+ dmap->dma_mode = DMODE_NONE;
}
-
- if ((err = DMAbuf_activate_recording (dev, dmap)) < 0)
+ if (!dmap->dma_mode)
{
- restore_flags (flags);
- return err;
+ int err;
+
+ reorganize_buffers(dev, dmap, 1);
+ if ((err = audio_devs[dev]->d->prepare_for_input(dev,
+ dmap->fragment_size, dmap->nbufs)) < 0)
+ {
+ return err;
+ }
+ dmap->dma_mode = DMODE_INPUT;
}
-
- /* Wait for the next block */
-
- if (dontblock)
+ if (!(dmap->flags & DMA_ACTIVE))
{
- restore_flags (flags);
- return -EAGAIN;
+ if (dmap->needs_reorg)
+ reorganize_buffers(dev, dmap, 0);
+ local_start_dma(dev, dmap->raw_buf_phys, dmap->bytes_in_use,
+ DMA_MODE_READ);
+ audio_devs[dev]->d->start_input(dev, dmap->raw_buf_phys +
+ dmap->qtail * dmap->fragment_size,
+ dmap->fragment_size, 0);
+ dmap->flags |= DMA_ACTIVE;
+ if (audio_devs[dev]->d->trigger)
+ audio_devs[dev]->d->trigger(dev,
+ audio_devs[dev]->enable_bits * audio_devs[dev]->go);
}
+ return 0;
+}
- if (!audio_devs[dev]->go)
- tmout = 0;
- else
+int
+DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock)
+{
+ unsigned long flags;
+ int err = 0, n = 0;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
+
+ if (!(audio_devs[dev]->open_mode & OPEN_READ))
+ return -EIO;
+ if (dmap->needs_reorg)
+ reorganize_buffers(dev, dmap, 0);
+
+ save_flags(flags);
+ cli();
+ if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)
{
- tmout =
- (dmap->fragment_size * HZ) / dmap->data_rate;
-
- tmout += HZ / 10; /* Some safety distance */
-
- if (tmout < (HZ / 2))
- tmout = HZ / 2;
- if (tmout > 20 * HZ)
- tmout = 20 * HZ;
- }
-
+ printk("Sound: Can't read from mmapped device (1)\n");
+ restore_flags(flags);
+ return -EINVAL;
+ } else
+ while (dmap->qlen <= 0 && n++ < 10)
+ {
+ int tmout;
+
+ if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT) ||
+ !audio_devs[dev]->go)
+ {
+ restore_flags(flags);
+ return -EAGAIN;
+ }
+ if ((err = DMAbuf_activate_recording(dev, dmap)) < 0)
+ {
+ restore_flags(flags);
+ return err;
+ }
+ /* Wait for the next block */
+
+ if (dontblock)
+ {
+ restore_flags(flags);
+ return -EAGAIN;
+ }
+ if (!audio_devs[dev]->go)
+ tmout = 0;
+ else
+ {
+ tmout =
+ (dmap->fragment_size * HZ) / dmap->data_rate;
+
+ tmout += HZ / 5; /* Some safety distance */
+
+ if (tmout < (HZ / 2))
+ tmout = HZ / 2;
+ if (tmout > 20 * HZ)
+ tmout = 20 * HZ;
+ }
+
+
+ {
+ unsigned long tlimit;
+
+ if (tmout)
+ current->timeout = tlimit = jiffies + (tmout);
+ else
+ tlimit = (unsigned long) -1;
+ in_sleep_flag[dev].opts = WK_SLEEP;
+ interruptible_sleep_on(&in_sleeper[dev]);
+ if (!(in_sleep_flag[dev].opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ in_sleep_flag[dev].opts |= WK_TIMEOUT;
+ }
+ in_sleep_flag[dev].opts &= ~WK_SLEEP;
+ };
+ if ((in_sleep_flag[dev].opts & WK_TIMEOUT))
+ {
+ err = -EIO;
+ printk("Sound: DMA (input) timed out - IRQ/DRQ config error?\n");
+ dma_reset_input(dev);
+ ;
+ } else
+ err = -EINTR;
+ }
+ restore_flags(flags);
- {
- unsigned long tlimit;
-
- if (tmout)
- current->timeout = tlimit = jiffies + (tmout);
- else
- tlimit = (unsigned long) -1;
- in_sleep_flag[dev].opts = WK_SLEEP;
- interruptible_sleep_on (&in_sleeper[dev]);
- if (!(in_sleep_flag[dev].opts & WK_WAKEUP))
- {
- if (jiffies >= tlimit)
- in_sleep_flag[dev].opts |= WK_TIMEOUT;
- }
- in_sleep_flag[dev].opts &= ~WK_SLEEP;
- };
- if ((in_sleep_flag[dev].opts & WK_TIMEOUT))
+ if (dmap->qlen <= 0)
{
- err = -EIO;
- printk ("Sound: DMA (input) timed out - IRQ/DRQ config error?\n");
- dma_reset_input (dev);
- ;
+ if (err == 0)
+ err = -EINTR;
+ return err;
}
- else
- err = -EINTR;
- }
- restore_flags (flags);
+ *buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]];
+ *len = dmap->fragment_size - dmap->counts[dmap->qhead];
- if (dmap->qlen <= 0)
- {
- if (err == 0)
- err = -EINTR;
- return err;
- }
-
- *buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]];
- *len = dmap->fragment_size - dmap->counts[dmap->qhead];
-
- return dmap->qhead;
+ return dmap->qhead;
}
int
-DMAbuf_rmchars (int dev, int buff_no, int c)
+DMAbuf_rmchars(int dev, int buff_no, int c)
{
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
-
- int p = dmap->counts[dmap->qhead] + c;
-
- if (dmap->mapping_flags & DMA_MAP_MAPPED)
- {
- printk ("Sound: Can't read from mmapped device (2)\n");
- return -EINVAL;
- }
- else if (dmap->qlen <= 0)
- return -EIO;
- else if (p >= dmap->fragment_size)
- { /* This buffer is completely empty */
- dmap->counts[dmap->qhead] = 0;
- dmap->qlen--;
- dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
- }
- else
- dmap->counts[dmap->qhead] = p;
-
- return 0;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
+
+ int p = dmap->counts[dmap->qhead] + c;
+
+ if (dmap->mapping_flags & DMA_MAP_MAPPED)
+ {
+ printk("Sound: Can't read from mmapped device (2)\n");
+ return -EINVAL;
+ } else if (dmap->qlen <= 0)
+ return -EIO;
+ else if (p >= dmap->fragment_size)
+ { /* This buffer is completely empty */
+ dmap->counts[dmap->qhead] = 0;
+ dmap->qlen--;
+ dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+ } else
+ dmap->counts[dmap->qhead] = p;
+
+ return 0;
}
int
-DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap, int direction)
+DMAbuf_get_buffer_pointer(int dev, struct dma_buffparms *dmap, int direction)
{
/*
* Try to approximate the active byte position of the DMA pointer within the
* buffer area as well as possible.
*/
- int pos;
- unsigned long flags;
-
- save_flags (flags);
- cli ();
- if (!(dmap->flags & DMA_ACTIVE))
- pos = 0;
- else
- {
- int chan = dmap->dma;
-
- clear_dma_ff (chan);
- disable_dma (dmap->dma);
- pos = get_dma_residue (chan);
- pos = dmap->bytes_in_use - pos;
-
- if (!(dmap->mapping_flags & DMA_MAP_MAPPED))
- if (direction == DMODE_OUTPUT)
- {
- if (dmap->qhead == 0)
- if (pos > dmap->fragment_size)
+ int pos;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (!(dmap->flags & DMA_ACTIVE))
pos = 0;
- }
else
{
- if (dmap->qtail == 0)
- if (pos > dmap->fragment_size)
- pos = 0;
+ int chan = dmap->dma;
+
+ clear_dma_ff(chan);
+ disable_dma(dmap->dma);
+ pos = get_dma_residue(chan);
+ pos = dmap->bytes_in_use - pos;
+
+ if (!(dmap->mapping_flags & DMA_MAP_MAPPED))
+ if (direction == DMODE_OUTPUT)
+ {
+ if (dmap->qhead == 0)
+ if (pos > dmap->fragment_size)
+ pos = 0;
+ } else
+ {
+ if (dmap->qtail == 0)
+ if (pos > dmap->fragment_size)
+ pos = 0;
+ }
+ if (pos < 0)
+ pos = 0;
+ if (pos >= dmap->bytes_in_use)
+ pos = 0;
+ enable_dma(dmap->dma);
}
+ restore_flags(flags);
+ /* printk( "%04x ", pos); */
- if (pos < 0)
- pos = 0;
- if (pos >= dmap->bytes_in_use)
- pos = 0;
- enable_dma (dmap->dma);
- }
- restore_flags (flags);
- /* printk ("%04x ", pos); */
-
- return pos;
+ return pos;
}
/*
@@ -757,824 +736,816 @@ DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap, int direction)
* one or more audio devices at desired moment.
*/
static void
-DMAbuf_start_device (int dev)
+DMAbuf_start_device(int dev)
{
- if (audio_devs[dev]->open_mode != 0)
- if (!audio_devs[dev]->go)
- {
- /* OK to start the device */
- audio_devs[dev]->go = 1;
+ if (audio_devs[dev]->open_mode != 0)
+ if (!audio_devs[dev]->go)
+ {
+ /* OK to start the device */
+ audio_devs[dev]->go = 1;
- if (audio_devs[dev]->d->trigger)
- audio_devs[dev]->d->trigger (dev,
- audio_devs[dev]->enable_bits * audio_devs[dev]->go);
- }
+ if (audio_devs[dev]->d->trigger)
+ audio_devs[dev]->d->trigger(dev,
+ audio_devs[dev]->enable_bits * audio_devs[dev]->go);
+ }
}
void
-DMAbuf_start_devices (unsigned int devmask)
+DMAbuf_start_devices(unsigned int devmask)
{
- int dev;
+ int dev;
- for (dev = 0; dev < num_audiodevs; dev++)
- if (devmask & (1 << dev))
- DMAbuf_start_device (dev);
+ for (dev = 0; dev < num_audiodevs; dev++)
+ if ((devmask & (1 << dev)) && audio_devs[dev] != NULL)
+ DMAbuf_start_device(dev);
}
int
-DMAbuf_space_in_queue (int dev)
+DMAbuf_space_in_queue(int dev)
{
- int len, max, tmp;
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+ int len, max, tmp;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
- int lim = dmap->nbufs;
+ int lim = dmap->nbufs;
- if (lim < 2)
- lim = 2;
+ if (lim < 2)
+ lim = 2;
- if (dmap->qlen >= lim) /* No space at all */
- return 0;
+ if (dmap->qlen >= lim) /* No space at all */
+ return 0;
- /*
- * Verify that there are no more pending buffers than the limit
- * defined by the process.
- */
+ /*
+ * Verify that there are no more pending buffers than the limit
+ * defined by the process.
+ */
- max = dmap->max_fragments;
- if (max > dmap->nbufs)
- max = dmap->nbufs;
- len = dmap->qlen;
+ max = dmap->max_fragments;
+ if (max > lim)
+ max = lim;
+ len = dmap->qlen;
- if (audio_devs[dev]->d->local_qlen)
- {
- tmp = audio_devs[dev]->d->local_qlen (dev);
- if (tmp && len)
- tmp--; /*
- * This buffer has been counted twice
- */
- len += tmp;
- }
+ if (audio_devs[dev]->d->local_qlen)
+ {
+ tmp = audio_devs[dev]->d->local_qlen(dev);
+ if (tmp && len)
+ tmp--; /*
+ * This buffer has been counted twice
+ */
+ len += tmp;
+ }
+ if (dmap->byte_counter % dmap->fragment_size) /* There is a partial fragment */
+ len = len + 1;
- if (len >= max)
- return 0;
- return max - len;
+ if (len >= max)
+ return 0;
+ return max - len;
}
static int
-output_sleep (int dev, int dontblock)
+output_sleep(int dev, int dontblock)
{
- int tmout;
- int err = 0;
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
- if (dontblock)
- {
- return -EAGAIN;
- }
-
- if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
- {
- return -EAGAIN;
- }
-
- /*
- * Wait for free space
- */
- if (!audio_devs[dev]->go || dmap->flags & DMA_NOTIMEOUT)
- tmout = 0;
- else
- {
- tmout =
- (dmap->fragment_size * HZ) / dmap->data_rate;
-
- tmout += HZ / 10; /* Some safety distance */
-
- if (tmout < (HZ / 2))
- tmout = HZ / 2;
- if (tmout > 20 * HZ)
- tmout = 20 * HZ;
- }
-
- if ((current->signal & ~current->blocked))
- return -EIO;
-
-
- {
- unsigned long tlimit;
-
- if (tmout)
- current->timeout = tlimit = jiffies + (tmout);
- else
- tlimit = (unsigned long) -1;
- out_sleep_flag[dev].opts = WK_SLEEP;
- interruptible_sleep_on (&out_sleeper[dev]);
- if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
- {
- if (jiffies >= tlimit)
- out_sleep_flag[dev].opts |= WK_TIMEOUT;
- }
- out_sleep_flag[dev].opts &= ~WK_SLEEP;
- };
- if ((out_sleep_flag[dev].opts & WK_TIMEOUT))
- {
- printk ("Sound: DMA (output) timed out - IRQ/DRQ config error?\n");
- ;
- dma_reset_output (dev);
- }
- else if ((current->signal & ~current->blocked))
- {
- err = -EINTR;
- }
-
- return err;
+ int tmout;
+ int err = 0;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+
+ if (dontblock)
+ {
+ return -EAGAIN;
+ }
+ if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
+ {
+ return -EAGAIN;
+ }
+ /*
+ * Wait for free space
+ */
+ if (!audio_devs[dev]->go || dmap->flags & DMA_NOTIMEOUT)
+ tmout = 0;
+ else
+ {
+ tmout =
+ (dmap->fragment_size * HZ) / dmap->data_rate;
+
+ tmout += HZ / 5; /* Some safety distance */
+
+ if (tmout < (HZ / 2))
+ tmout = HZ / 2;
+ if (tmout > 20 * HZ)
+ tmout = 20 * HZ;
+ }
+
+ if ((current->signal & ~current->blocked))
+ return -EIO;
+
+
+ {
+ unsigned long tlimit;
+
+ if (tmout)
+ current->timeout = tlimit = jiffies + (tmout);
+ else
+ tlimit = (unsigned long) -1;
+ out_sleep_flag[dev].opts = WK_SLEEP;
+ interruptible_sleep_on(&out_sleeper[dev]);
+ if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ out_sleep_flag[dev].opts |= WK_TIMEOUT;
+ }
+ out_sleep_flag[dev].opts &= ~WK_SLEEP;
+ };
+ if ((out_sleep_flag[dev].opts & WK_TIMEOUT))
+ {
+ printk("Sound: DMA (output) timed out - IRQ/DRQ config error?\n");
+ ;
+ dma_reset_output(dev);
+ } else if ((current->signal & ~current->blocked))
+ {
+ err = -EINTR;
+ }
+ return err;
}
static int
-find_output_space (int dev, char **buf, int *size)
+find_output_space(int dev, char **buf, int *size)
{
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
- unsigned long flags;
- unsigned long offs, active_offs;
- long len;
- int maxfrags;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+ unsigned long flags;
+ unsigned long active_offs;
+ long len, offs;
+ int maxfrags;
+ int occupied_bytes = (dmap->user_counter % dmap->fragment_size);
- if (!(maxfrags = DMAbuf_space_in_queue (dev)))
- {
- return 0;
- }
+ *buf = dmap->raw_buf;
- save_flags (flags);
- cli ();
+ if (!(maxfrags = DMAbuf_space_in_queue(dev)) && !occupied_bytes)
+ {
+ return 0;
+ }
+ save_flags(flags);
+ cli();
#ifdef BE_CONSERVATIVE
- active_offs = dmap->byte_counter + dmap->qhead * dmap->fragment_size;
+ active_offs = dmap->byte_counter + dmap->qhead * dmap->fragment_size;
#else
- active_offs = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT);
- /* Check for pointer wrapping situation */
- if (active_offs < 0 || active_offs >= dmap->bytes_in_use)
- active_offs = 0;
- active_offs += dmap->byte_counter;
+ active_offs = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT);
+ /* Check for pointer wrapping situation */
+ if (active_offs < 0 || active_offs >= dmap->bytes_in_use)
+ active_offs = 0;
+ active_offs += dmap->byte_counter;
#endif
- offs = (dmap->user_counter % dmap->bytes_in_use) & ~3;
- *buf = dmap->raw_buf + offs;
-
- len = active_offs + dmap->bytes_in_use - dmap->user_counter; /* Number of unused bytes in buffer */
-
- if ((offs + len) > dmap->bytes_in_use)
- len = dmap->bytes_in_use - offs;
-
- if (len < 0)
- {
- restore_flags (flags);
- return 0;
- }
+ offs = (dmap->user_counter % dmap->bytes_in_use) & ~3;
+ if (offs < 0 || offs >= dmap->bytes_in_use)
+ {
+ printk("OSS: Got unexpected offs %ld. Giving up.\n", offs);
+ printk("Counter = %ld, bytes=%d\n", dmap->user_counter, dmap->bytes_in_use);
+ return 0;
+ }
+ *buf = dmap->raw_buf + offs;
- if (len > maxfrags * dmap->fragment_size)
- len = maxfrags * dmap->fragment_size;
+ len = active_offs + dmap->bytes_in_use - dmap->user_counter; /* Number of unused bytes in buffer */
- *size = len & ~3;
+ if ((offs + len) > dmap->bytes_in_use)
+ {
+ len = dmap->bytes_in_use - offs;
+ }
+ if (len < 0)
+ {
+ restore_flags(flags);
+ return 0;
+ }
+ if (len > ((maxfrags * dmap->fragment_size) - occupied_bytes))
+ {
+ len = (maxfrags * dmap->fragment_size) - occupied_bytes;
+ }
+ *size = len & ~3;
- restore_flags (flags);
- return (len > 0);
+ restore_flags(flags);
+ return (len > 0);
}
int
-DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
+DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock)
{
- unsigned long flags;
- int err = -EIO;
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
- if (dmap->needs_reorg)
- reorganize_buffers (dev, dmap, 0);
+ unsigned long flags;
+ int err = -EIO;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
- if (dmap->mapping_flags & DMA_MAP_MAPPED)
- {
- printk ("Sound: Can't write to mmapped device (3)\n");
- return -EINVAL;
- }
+ if (dmap->needs_reorg)
+ reorganize_buffers(dev, dmap, 0);
- if (dmap->dma_mode == DMODE_INPUT) /* Direction change */
- {
- DMAbuf_reset (dev);
- dmap->dma_mode = DMODE_NONE;
- }
-
- dmap->dma_mode = DMODE_OUTPUT;
+ if (dmap->mapping_flags & DMA_MAP_MAPPED)
+ {
+ printk("Sound: Can't write to mmapped device (3)\n");
+ return -EINVAL;
+ }
+ if (dmap->dma_mode == DMODE_INPUT) /* Direction change */
+ {
+ DMAbuf_reset(dev);
+ dmap->dma_mode = DMODE_NONE;
+ }
+ dmap->dma_mode = DMODE_OUTPUT;
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- while (!find_output_space (dev, buf, size))
- {
- if ((err = output_sleep (dev, dontblock)) < 0)
- {
- restore_flags (flags);
- return err;
- }
- }
+ while (find_output_space(dev, buf, size) <= 0)
+ {
+ if ((err = output_sleep(dev, dontblock)) < 0)
+ {
+ restore_flags(flags);
+ return err;
+ }
+ }
- restore_flags (flags);
+ restore_flags(flags);
- return 0;
+ return 0;
}
int
-DMAbuf_move_wrpointer (int dev, int l)
+DMAbuf_move_wrpointer(int dev, int l)
{
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
- unsigned long ptr = (dmap->user_counter / dmap->fragment_size)
- * dmap->fragment_size;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+ unsigned long ptr = (dmap->user_counter / dmap->fragment_size)
+ * dmap->fragment_size;
- unsigned long end_ptr, p;
- int post = (dmap->flags & DMA_POST);
+ unsigned long end_ptr, p;
+ int post = (dmap->flags & DMA_POST);
- ;
+ ;
- dmap->flags &= ~DMA_POST;
+ dmap->flags &= ~DMA_POST;
- dmap->cfrag = -1;
+ dmap->cfrag = -1;
- dmap->user_counter += l;
- dmap->flags |= DMA_DIRTY;
+ dmap->user_counter += l;
+ dmap->flags |= DMA_DIRTY;
- if (dmap->user_counter >= dmap->max_byte_counter)
- { /* Wrap the byte counters */
- long decr = dmap->user_counter;
+ if (dmap->user_counter >= dmap->max_byte_counter)
+ { /* Wrap the byte counters */
+ long decr = dmap->user_counter;
- dmap->user_counter = (dmap->user_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
- decr -= dmap->user_counter;
- dmap->byte_counter -= decr;
- }
-
- end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size;
+ dmap->user_counter = (dmap->user_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
+ decr -= dmap->user_counter;
+ dmap->byte_counter -= decr;
+ }
+ end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size;
- p = (dmap->user_counter - 1) % dmap->bytes_in_use;
- dmap->neutral_byte = dmap->raw_buf[p];
+ p = (dmap->user_counter - 1) % dmap->bytes_in_use;
+ dmap->neutral_byte = dmap->raw_buf[p];
- /* Update the fragment based bookkeeping too */
- while (ptr < end_ptr)
- {
- dmap->counts[dmap->qtail] = dmap->fragment_size;
- dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
- dmap->qlen++;
- ptr += dmap->fragment_size;
- }
+ /* Update the fragment based bookkeeping too */
+ while (ptr < end_ptr)
+ {
+ dmap->counts[dmap->qtail] = dmap->fragment_size;
+ dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+ dmap->qlen++;
+ ptr += dmap->fragment_size;
+ }
- dmap->counts[dmap->qtail] = dmap->user_counter - ptr;
+ dmap->counts[dmap->qtail] = dmap->user_counter - ptr;
/*
* Let the low level driver to perform some postprocessing to
* the written data.
*/
- if (audio_devs[dev]->d->postprocess_write)
- audio_devs[dev]->d->postprocess_write (dev);
-
- if (!(dmap->flags & DMA_ACTIVE))
- if (dmap->qlen > 1 ||
- (dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1)))
- {
- DMAbuf_launch_output (dev, dmap);
- }
-
- ;
- return 0;
+ if (audio_devs[dev]->d->postprocess_write)
+ audio_devs[dev]->d->postprocess_write(dev);
+
+ if (!(dmap->flags & DMA_ACTIVE))
+ if (dmap->qlen > 1 ||
+ (dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1)))
+ {
+ DMAbuf_launch_output(dev, dmap);
+ };
+ return 0;
}
int
-DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
+DMAbuf_start_dma(int dev, unsigned long physaddr, int count, int dma_mode)
{
- int chan;
- struct dma_buffparms *dmap;
-
- if (dma_mode == DMA_MODE_WRITE)
- {
- chan = audio_devs[dev]->dmap_out->dma;
- dmap = audio_devs[dev]->dmap_out;
- }
- else
- {
- chan = audio_devs[dev]->dmap_in->dma;
- dmap = audio_devs[dev]->dmap_in;
- }
-
- if (dmap->raw_buf == NULL)
- {
- printk ("sound: DMA buffer(1) == NULL\n");
- printk ("Device %d, chn=%s\n", dev,
- (dmap == audio_devs[dev]->dmap_out) ? "out" : "in");
- return 0;
- }
-
- if (chan < 0)
- return 0;
-
- sound_start_dma (dev, dmap, chan, physaddr, count, dma_mode, 0);
-
- return count;
+ int chan;
+ struct dma_buffparms *dmap;
+
+ if (dma_mode == DMA_MODE_WRITE)
+ {
+ chan = audio_devs[dev]->dmap_out->dma;
+ dmap = audio_devs[dev]->dmap_out;
+ } else
+ {
+ chan = audio_devs[dev]->dmap_in->dma;
+ dmap = audio_devs[dev]->dmap_in;
+ }
+
+ if (dmap->raw_buf == NULL)
+ {
+ printk("sound: DMA buffer(1) == NULL\n");
+ printk("Device %d, chn=%s\n", dev, (dmap == audio_devs[dev]->dmap_out) ? "out" : "in");
+ return 0;
+ }
+ if (chan < 0)
+ return 0;
+
+ sound_start_dma(dev, dmap, chan, physaddr, count, dma_mode, 0);
+
+ return count;
}
static int
-local_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
+local_start_dma(int dev, unsigned long physaddr, int count, int dma_mode)
{
- int chan;
- struct dma_buffparms *dmap;
-
- if (dma_mode == DMA_MODE_WRITE)
- {
- chan = audio_devs[dev]->dmap_out->dma;
- dmap = audio_devs[dev]->dmap_out;
- }
- else
- {
- chan = audio_devs[dev]->dmap_in->dma;
- dmap = audio_devs[dev]->dmap_in;
- }
-
- if (dmap->raw_buf == NULL)
- {
- printk ("sound: DMA buffer(2) == NULL\n");
- printk ("Device %d, chn=%s\n", dev,
- (dmap == audio_devs[dev]->dmap_out) ? "out" : "in");
- return 0;
- }
-
- if (dmap->flags & DMA_NODMA)
- {
- return 1;
- }
-
- if (chan < 0)
- return 0;
-
- sound_start_dma (dev, dmap, chan, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode, 1);
- dmap->flags |= DMA_STARTED;
-
- return count;
+ int chan;
+ struct dma_buffparms *dmap;
+
+ if (dma_mode == DMA_MODE_WRITE)
+ {
+ chan = audio_devs[dev]->dmap_out->dma;
+ dmap = audio_devs[dev]->dmap_out;
+ } else
+ {
+ chan = audio_devs[dev]->dmap_in->dma;
+ dmap = audio_devs[dev]->dmap_in;
+ }
+
+ if (dmap->raw_buf == NULL)
+ {
+ printk("sound: DMA buffer(2) == NULL\n");
+ printk("Device %d, chn=%s\n", dev, (dmap == audio_devs[dev]->dmap_out) ? "out" : "in");
+ return 0;
+ }
+ if (dmap->flags & DMA_NODMA)
+ {
+ return 1;
+ }
+ if (chan < 0)
+ return 0;
+
+ sound_start_dma(dev, dmap, chan, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode, 1);
+ dmap->flags |= DMA_STARTED;
+
+ return count;
}
static void
-finish_output_interrupt (int dev, struct dma_buffparms *dmap)
+finish_output_interrupt(int dev, struct dma_buffparms *dmap)
{
- unsigned long flags;
-
- save_flags (flags);
- cli ();
- if ((out_sleep_flag[dev].opts & WK_SLEEP))
- {
- {
- out_sleep_flag[dev].opts = WK_WAKEUP;
- wake_up (&out_sleeper[dev]);
- };
- }
- restore_flags (flags);
+ unsigned long flags;
+
+ if (dmap->audio_callback != NULL)
+ dmap->audio_callback(dev, dmap->callback_parm);
+
+ save_flags(flags);
+ cli();
+ if ((out_sleep_flag[dev].opts & WK_SLEEP))
+ {
+ {
+ out_sleep_flag[dev].opts = WK_WAKEUP;
+ wake_up(&out_sleeper[dev]);
+ };
+ }
+ restore_flags(flags);
}
static void
-do_outputintr (int dev, int dummy)
+do_outputintr(int dev, int dummy)
{
- unsigned long flags;
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
- int this_fragment;
+ unsigned long flags;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+ int this_fragment;
#ifdef OS_DMA_INTR
- if (audio_devs[dev]->dmap_out->dma >= 0)
- sound_dma_intr (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
+ if (audio_devs[dev]->dmap_out->dma >= 0)
+ sound_dma_intr(dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
#endif
- if (dmap->raw_buf == NULL)
- {
- printk ("Sound: Fatal error. Audio interrupt (%d) after freeing buffers.\n", dev);
- return;
- }
-
- if (dmap->mapping_flags & DMA_MAP_MAPPED) /* Virtual memory mapped access */
- {
- /* mmapped access */
- dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
- if (dmap->qhead == 0) /* Wrapped */
- {
- dmap->byte_counter += dmap->bytes_in_use;
- if (dmap->byte_counter >= dmap->max_byte_counter) /* Overflow */
- {
- long decr = dmap->byte_counter;
-
- dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
- decr -= dmap->byte_counter;
- dmap->user_counter -= decr;
- }
- }
-
- dmap->qlen++; /* Yes increment it (don't decrement) */
- if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
- dmap->flags &= ~DMA_ACTIVE;
- dmap->counts[dmap->qhead] = dmap->fragment_size;
-
- DMAbuf_launch_output (dev, dmap);
- finish_output_interrupt (dev, dmap);
- return;
- }
-
- save_flags (flags);
- cli ();
-
- dmap->qlen--;
- this_fragment = dmap->qhead;
- dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
-
- if (dmap->qhead == 0) /* Wrapped */
- {
- dmap->byte_counter += dmap->bytes_in_use;
- if (dmap->byte_counter >= dmap->max_byte_counter) /* Overflow */
- {
- long decr = dmap->byte_counter;
-
- dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
- decr -= dmap->byte_counter;
- dmap->user_counter -= decr;
- }
- }
+ if (dmap->raw_buf == NULL)
+ {
+ printk("Sound: Fatal error. Audio interrupt (%d) after freeing buffers.\n", dev);
+ return;
+ }
+ if (dmap->mapping_flags & DMA_MAP_MAPPED) /* Virtual memory mapped access */
+ {
+ /* mmapped access */
+ dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+ if (dmap->qhead == 0) /* Wrapped */
+ {
+ dmap->byte_counter += dmap->bytes_in_use;
+ if (dmap->byte_counter >= dmap->max_byte_counter) /* Overflow */
+ {
+ long decr = dmap->byte_counter;
+
+ dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
+ decr -= dmap->byte_counter;
+ dmap->user_counter -= decr;
+ }
+ }
+ dmap->qlen++; /* Yes increment it (don't decrement) */
+ if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
+ dmap->flags &= ~DMA_ACTIVE;
+ dmap->counts[dmap->qhead] = dmap->fragment_size;
+
+ DMAbuf_launch_output(dev, dmap);
+ finish_output_interrupt(dev, dmap);
+ return;
+ }
+ save_flags(flags);
+ cli();
- if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
- dmap->flags &= ~DMA_ACTIVE;
+ dmap->qlen--;
+ this_fragment = dmap->qhead;
+ dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
- while (dmap->qlen < 0)
- {
- dmap->underrun_count++;
+ if (dmap->qhead == 0) /* Wrapped */
+ {
+ dmap->byte_counter += dmap->bytes_in_use;
+ if (dmap->byte_counter >= dmap->max_byte_counter) /* Overflow */
+ {
+ long decr = dmap->byte_counter;
+
+ dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
+ decr -= dmap->byte_counter;
+ dmap->user_counter -= decr;
+ }
+ }
+ if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
+ dmap->flags &= ~DMA_ACTIVE;
- dmap->qlen++;
- if (dmap->flags & DMA_DIRTY && dmap->applic_profile != APF_CPUINTENS)
- {
- dmap->flags &= ~DMA_DIRTY;
- memset (audio_devs[dev]->dmap_out->raw_buf,
- audio_devs[dev]->dmap_out->neutral_byte,
- audio_devs[dev]->dmap_out->buffsize);
- }
- dmap->user_counter += dmap->fragment_size;
- dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
- }
+ while (dmap->qlen < 0)
+ {
+ dmap->underrun_count++;
+
+ dmap->qlen++;
+ if (dmap->flags & DMA_DIRTY && dmap->applic_profile != APF_CPUINTENS)
+ {
+ dmap->flags &= ~DMA_DIRTY;
+ memset(audio_devs[dev]->dmap_out->raw_buf,
+ audio_devs[dev]->dmap_out->neutral_byte,
+ audio_devs[dev]->dmap_out->buffsize);
+ }
+ dmap->user_counter += dmap->fragment_size;
+ dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+ }
- if (dmap->qlen > 0)
- DMAbuf_launch_output (dev, dmap);
+ if (dmap->qlen > 0)
+ DMAbuf_launch_output(dev, dmap);
- restore_flags (flags);
- finish_output_interrupt (dev, dmap);
+ restore_flags(flags);
+ finish_output_interrupt(dev, dmap);
}
void
-DMAbuf_outputintr (int dev, int notify_only)
+DMAbuf_outputintr(int dev, int notify_only)
{
- unsigned long flags;
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
- save_flags (flags);
- cli ();
-
- if (!(dmap->flags & DMA_NODMA))
- {
- int chan = dmap->dma, pos, n;
+ unsigned long flags;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
- clear_dma_ff (chan);
- disable_dma (dmap->dma);
- pos = dmap->bytes_in_use - get_dma_residue (chan);
- enable_dma (dmap->dma);
+ save_flags(flags);
+ cli();
- pos = pos / dmap->fragment_size; /* Actual qhead */
- if (pos < 0 || pos >= dmap->nbufs)
- pos = 0;
-
- n = 0;
- while (dmap->qhead != pos && n++ < dmap->nbufs)
- {
- do_outputintr (dev, notify_only);
- }
- }
- else
- do_outputintr (dev, notify_only);
- restore_flags (flags);
+ if (!(dmap->flags & DMA_NODMA))
+ {
+ int chan = dmap->dma, pos, n;
+
+ clear_dma_ff(chan);
+ disable_dma(dmap->dma);
+ pos = dmap->bytes_in_use - get_dma_residue(chan);
+ enable_dma(dmap->dma);
+
+ pos = pos / dmap->fragment_size; /* Actual qhead */
+ if (pos < 0 || pos >= dmap->nbufs)
+ pos = 0;
+
+ n = 0;
+ while (dmap->qhead != pos && n++ < dmap->nbufs)
+ {
+ do_outputintr(dev, notify_only);
+ }
+ } else
+ do_outputintr(dev, notify_only);
+ restore_flags(flags);
}
static void
-do_inputintr (int dev)
+do_inputintr(int dev)
{
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
- unsigned long flags;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
+ unsigned long flags;
#ifdef OS_DMA_INTR
- if (audio_devs[dev]->dmap_in->dma >= 0)
- sound_dma_intr (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmap_in->dma);
+ if (audio_devs[dev]->dmap_in->dma >= 0)
+ sound_dma_intr(dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmap_in->dma);
#endif
- if (dmap->raw_buf == NULL)
- {
- printk ("Sound: Fatal error. Audio interrupt after freeing buffers.\n");
- return;
- }
-
- if (dmap->mapping_flags & DMA_MAP_MAPPED)
- {
- dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
- if (dmap->qtail == 0) /* Wrapped */
- {
- dmap->byte_counter += dmap->bytes_in_use;
- if (dmap->byte_counter >= dmap->max_byte_counter) /* Overflow */
- {
- long decr = dmap->byte_counter;
-
- dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
- decr -= dmap->byte_counter;
- dmap->user_counter -= decr;
- }
- }
- dmap->qlen++;
-
- if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
- {
- if (dmap->needs_reorg)
- reorganize_buffers (dev, dmap, 0);
- local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
- DMA_MODE_READ);
- audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys +
- dmap->qtail * dmap->fragment_size,
- dmap->fragment_size, 1);
- if (audio_devs[dev]->d->trigger)
- audio_devs[dev]->d->trigger (dev,
- audio_devs[dev]->enable_bits * audio_devs[dev]->go);
- }
-
- dmap->flags |= DMA_ACTIVE;
- }
- else if (dmap->qlen >= (dmap->nbufs - 1))
- {
- /* printk ("Sound: Recording overrun\n"); */
- dmap->underrun_count++;
-
- /* Just throw away the oldest fragment but keep the engine running */
- dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
- dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
- }
- else if (dmap->qlen >= 0 && dmap->qlen < dmap->nbufs)
- {
- dmap->qlen++;
- dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
- if (dmap->qtail == 0) /* Wrapped */
- {
- dmap->byte_counter += dmap->bytes_in_use;
- if (dmap->byte_counter >= dmap->max_byte_counter) /* Overflow */
- {
- long decr = dmap->byte_counter;
-
- dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
- decr -= dmap->byte_counter;
- dmap->user_counter -= decr;
- }
- }
- }
+ if (dmap->raw_buf == NULL)
+ {
+ printk("Sound: Fatal error. Audio interrupt after freeing buffers.\n");
+ return;
+ }
+ if (dmap->mapping_flags & DMA_MAP_MAPPED)
+ {
+ dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+ if (dmap->qtail == 0) /* Wrapped */
+ {
+ dmap->byte_counter += dmap->bytes_in_use;
+ if (dmap->byte_counter >= dmap->max_byte_counter) /* Overflow */
+ {
+ long decr = dmap->byte_counter;
+
+ dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
+ decr -= dmap->byte_counter;
+ dmap->user_counter -= decr;
+ }
+ }
+ dmap->qlen++;
+
+ if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
+ {
+ if (dmap->needs_reorg)
+ reorganize_buffers(dev, dmap, 0);
+ local_start_dma(dev, dmap->raw_buf_phys, dmap->bytes_in_use,
+ DMA_MODE_READ);
+ audio_devs[dev]->d->start_input(dev, dmap->raw_buf_phys +
+ dmap->qtail * dmap->fragment_size,
+ dmap->fragment_size, 1);
+ if (audio_devs[dev]->d->trigger)
+ audio_devs[dev]->d->trigger(dev,
+ audio_devs[dev]->enable_bits * audio_devs[dev]->go);
+ }
+ dmap->flags |= DMA_ACTIVE;
+ } else if (dmap->qlen >= (dmap->nbufs - 1))
+ {
+ printk("Sound: Recording overrun\n");
+ dmap->underrun_count++;
- if (!(audio_devs[dev]->flags & DMA_AUTOMODE) || dmap->flags & DMA_NODMA)
- {
- local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
- DMA_MODE_READ);
- audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys +
+ /* Just throw away the oldest fragment but keep the engine running */
+ dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+ dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+ } else if (dmap->qlen >= 0 && dmap->qlen < dmap->nbufs)
+ {
+ dmap->qlen++;
+ dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+ if (dmap->qtail == 0) /* Wrapped */
+ {
+ dmap->byte_counter += dmap->bytes_in_use;
+ if (dmap->byte_counter >= dmap->max_byte_counter) /* Overflow */
+ {
+ long decr = dmap->byte_counter;
+
+ dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
+ decr -= dmap->byte_counter;
+ dmap->user_counter -= decr;
+ }
+ }
+ }
+ if (!(audio_devs[dev]->flags & DMA_AUTOMODE) || dmap->flags & DMA_NODMA)
+ {
+ local_start_dma(dev, dmap->raw_buf_phys, dmap->bytes_in_use,
+ DMA_MODE_READ);
+ audio_devs[dev]->d->start_input(dev, dmap->raw_buf_phys +
dmap->qtail * dmap->fragment_size,
- dmap->fragment_size, 1);
- if (audio_devs[dev]->d->trigger)
- audio_devs[dev]->d->trigger (dev,
- audio_devs[dev]->enable_bits * audio_devs[dev]->go);
- }
-
- dmap->flags |= DMA_ACTIVE;
-
- save_flags (flags);
- cli ();
- if (dmap->qlen > 0)
- if ((in_sleep_flag[dev].opts & WK_SLEEP))
- {
- {
- in_sleep_flag[dev].opts = WK_WAKEUP;
- wake_up (&in_sleeper[dev]);
- };
- }
- restore_flags (flags);
+ dmap->fragment_size, 1);
+ if (audio_devs[dev]->d->trigger)
+ audio_devs[dev]->d->trigger(dev,
+ audio_devs[dev]->enable_bits * audio_devs[dev]->go);
+ }
+ dmap->flags |= DMA_ACTIVE;
+
+ save_flags(flags);
+ cli();
+ if (dmap->qlen > 0)
+ if ((in_sleep_flag[dev].opts & WK_SLEEP))
+ {
+ {
+ in_sleep_flag[dev].opts = WK_WAKEUP;
+ wake_up(&in_sleeper[dev]);
+ };
+ }
+ restore_flags(flags);
}
void
-DMAbuf_inputintr (int dev)
+DMAbuf_inputintr(int dev)
{
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
- unsigned long flags;
-
- save_flags (flags);
- cli ();
-
- if (!(dmap->flags & DMA_NODMA))
- {
- int chan = dmap->dma, pos, n;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
+ unsigned long flags;
- clear_dma_ff (chan);
- disable_dma (dmap->dma);
- pos = dmap->bytes_in_use - get_dma_residue (chan);
- enable_dma (dmap->dma);
+ save_flags(flags);
+ cli();
- pos = pos / dmap->fragment_size; /* Actual qhead */
- if (pos < 0 || pos >= dmap->nbufs)
- pos = 0;
-
- n = 0;
- while (dmap->qtail != pos && ++n < dmap->nbufs)
- {
- do_inputintr (dev);
- }
- }
- else
- do_inputintr (dev);
- restore_flags (flags);
+ if (!(dmap->flags & DMA_NODMA))
+ {
+ int chan = dmap->dma, pos, n;
+
+ clear_dma_ff(chan);
+ disable_dma(dmap->dma);
+ pos = dmap->bytes_in_use - get_dma_residue(chan);
+ enable_dma(dmap->dma);
+
+ pos = pos / dmap->fragment_size; /* Actual qhead */
+ if (pos < 0 || pos >= dmap->nbufs)
+ pos = 0;
+
+ n = 0;
+ while (dmap->qtail != pos && ++n < dmap->nbufs)
+ {
+ do_inputintr(dev);
+ }
+ } else
+ do_inputintr(dev);
+ restore_flags(flags);
}
int
-DMAbuf_open_dma (int dev)
+DMAbuf_open_dma(int dev)
{
/*
* NOTE! This routine opens only the primary DMA channel (output).
*/
- int chan = audio_devs[dev]->dmap_out->dma;
- int err;
-
- if ((err = open_dmap (dev, OPEN_READWRITE, audio_devs[dev]->dmap_out, chan)) < 0)
- {
- return -EBUSY;
- }
- dma_init_buffers (dev, audio_devs[dev]->dmap_out);
- out_sleep_flag[dev].opts = WK_NONE;
- audio_devs[dev]->dmap_out->flags |= DMA_ALLOC_DONE;
- audio_devs[dev]->dmap_out->fragment_size = audio_devs[dev]->dmap_out->buffsize;
-
- if (chan >= 0)
- {
- unsigned long flags;
-
- save_flags (flags);
- cli ();
- disable_dma (audio_devs[dev]->dmap_out->dma);
- clear_dma_ff (chan);
- restore_flags (flags);
- }
-
- return 0;
+ int chan = audio_devs[dev]->dmap_out->dma;
+ int err;
+
+ if ((err = open_dmap(dev, OPEN_READWRITE, audio_devs[dev]->dmap_out, chan)) < 0)
+ {
+ return -EBUSY;
+ }
+ dma_init_buffers(dev, audio_devs[dev]->dmap_out);
+ out_sleep_flag[dev].opts = WK_NONE;
+ audio_devs[dev]->dmap_out->flags |= DMA_ALLOC_DONE;
+ audio_devs[dev]->dmap_out->fragment_size = audio_devs[dev]->dmap_out->buffsize;
+
+ if (chan >= 0)
+ {
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ disable_dma(audio_devs[dev]->dmap_out->dma);
+ clear_dma_ff(chan);
+ restore_flags(flags);
+ }
+ return 0;
}
void
-DMAbuf_close_dma (int dev)
+DMAbuf_close_dma(int dev)
{
- close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
+ close_dmap(dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
}
void
-DMAbuf_init (int dev, int dma1, int dma2)
+DMAbuf_init(int dev, int dma1, int dma2)
{
- /*
- * NOTE! This routine could be called several times.
- */
+ /*
+ * NOTE! This routine could be called several times.
+ */
- if (audio_devs[dev]->dmap_out == NULL)
- {
- if (audio_devs[dev]->d == NULL)
- panic ("OSS: audio_devs[%d]->d == NULL\n", dev);
-
- if (audio_devs[dev]->parent_dev)
- { /* Use DMA map of the parent dev */
- int parent = audio_devs[dev]->parent_dev - 1;
-
- audio_devs[dev]->dmap_out = audio_devs[parent]->dmap_out;
- audio_devs[dev]->dmap_in = audio_devs[parent]->dmap_in;
- }
- else
- {
- audio_devs[dev]->dmap_out =
- audio_devs[dev]->dmap_in =
- &dmaps[ndmaps++];
- audio_devs[dev]->dmap_out->dma = dma1;
-
- if (audio_devs[dev]->flags & DMA_DUPLEX)
- {
- audio_devs[dev]->dmap_in =
- &dmaps[ndmaps++];
- audio_devs[dev]->dmap_in->dma = dma2;
- }
- }
- }
+ if (audio_devs[dev] && audio_devs[dev]->dmap_out == NULL)
+ {
+ if (audio_devs[dev]->d == NULL)
+ panic("OSS: audio_devs[%d]->d == NULL\n", dev);
+
+ if (audio_devs[dev]->parent_dev)
+ { /* Use DMA map of the parent dev */
+ int parent = audio_devs[dev]->parent_dev - 1;
+
+ audio_devs[dev]->dmap_out = audio_devs[parent]->dmap_out;
+ audio_devs[dev]->dmap_in = audio_devs[parent]->dmap_in;
+ } else
+ {
+ audio_devs[dev]->dmap_out =
+ audio_devs[dev]->dmap_in =
+ &dmaps[ndmaps++];
+ audio_devs[dev]->dmap_out->dma = dma1;
+
+ if (audio_devs[dev]->flags & DMA_DUPLEX)
+ {
+ audio_devs[dev]->dmap_in =
+ &dmaps[ndmaps++];
+ audio_devs[dev]->dmap_in->dma = dma2;
+ }
+ }
+ }
}
int
-DMAbuf_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait)
+DMAbuf_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait)
{
- struct dma_buffparms *dmap;
- unsigned long flags;
-
- switch (sel_type)
- {
- case SEL_IN:
- if (!(audio_devs[dev]->open_mode & OPEN_READ))
- return 0;
-
- dmap = audio_devs[dev]->dmap_in;
-
- if (dmap->mapping_flags & DMA_MAP_MAPPED)
- {
- if (dmap->qlen)
- return 1;
-
- save_flags (flags);
- cli ();
-
- in_sleep_flag[dev].opts = WK_SLEEP;
- poll_wait (&in_sleeper[dev], wait);
- restore_flags (flags);
- return 0;
- }
+ struct dma_buffparms *dmap;
+ unsigned long flags;
- if (dmap->dma_mode != DMODE_INPUT)
- {
- if (dmap->dma_mode == DMODE_NONE &&
- audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT &&
- !dmap->qlen &&
- audio_devs[dev]->go)
- {
- unsigned long flags;
-
- save_flags (flags);
- cli ();
- DMAbuf_activate_recording (dev, dmap);
- restore_flags (flags);
- }
- return 0;
- }
-
- if (!dmap->qlen)
- {
- save_flags (flags);
- cli ();
-
- in_sleep_flag[dev].opts = WK_SLEEP;
- poll_wait (&in_sleeper[dev], wait);
- restore_flags (flags);
- return 0;
- }
- return 1;
- break;
-
- case SEL_OUT:
- dmap = audio_devs[dev]->dmap_out;
+ switch (sel_type)
+ {
+ case SEL_IN:
+ if (!(audio_devs[dev]->open_mode & OPEN_READ))
+ return 0;
+
+ dmap = audio_devs[dev]->dmap_in;
+
+ if (dmap->mapping_flags & DMA_MAP_MAPPED)
+ {
+ if (dmap->qlen)
+ return 1;
+
+ save_flags(flags);
+ cli();
+
+ in_sleep_flag[dev].opts = WK_SLEEP;
+ poll_wait(&in_sleeper[dev], wait);
+ restore_flags(flags);
+ return 0;
+ }
+ if (dmap->dma_mode != DMODE_INPUT)
+ {
+ if (dmap->dma_mode == DMODE_NONE &&
+ audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT &&
+ !dmap->qlen &&
+ audio_devs[dev]->go)
+ {
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ DMAbuf_activate_recording(dev, dmap);
+ restore_flags(flags);
+ }
+ return 0;
+ }
+ if (!dmap->qlen)
+ {
+ save_flags(flags);
+ cli();
+
+ in_sleep_flag[dev].opts = WK_SLEEP;
+ poll_wait(&in_sleeper[dev], wait);
+ restore_flags(flags);
+ return 0;
+ }
+ return 1;
+ break;
+
+ case SEL_OUT:
+ dmap = audio_devs[dev]->dmap_out;
+
+ if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+ return 0;
+
+ if (dmap->mapping_flags & DMA_MAP_MAPPED)
+ {
+ if (dmap->qlen)
+ return 1;
+
+ save_flags(flags);
+ cli();
+
+ out_sleep_flag[dev].opts = WK_SLEEP;
+ poll_wait(&out_sleeper[dev], wait);
+ restore_flags(flags);
+ return 0;
+ }
+ if (dmap->dma_mode == DMODE_INPUT)
+ {
+ return 0;
+ }
+ if (dmap->dma_mode == DMODE_NONE)
+ {
+ return 1;
+ }
+ if (!DMAbuf_space_in_queue(dev))
+ {
+ save_flags(flags);
+ cli();
+
+ out_sleep_flag[dev].opts = WK_SLEEP;
+ poll_wait(&out_sleeper[dev], wait);
+ restore_flags(flags);
+ return 0;
+ }
+ return 1;
+ break;
+
+ case SEL_EX:
+ return 0;
+ }
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
return 0;
-
- if (dmap->mapping_flags & DMA_MAP_MAPPED)
- {
- if (dmap->qlen)
- return 1;
-
- save_flags (flags);
- cli ();
-
- out_sleep_flag[dev].opts = WK_SLEEP;
- poll_wait (&out_sleeper[dev], wait);
- restore_flags (flags);
- return 0;
- }
-
- if (dmap->dma_mode == DMODE_INPUT)
- {
- return 0;
- }
-
- if (dmap->dma_mode == DMODE_NONE)
- {
- return 1;
- }
-
- if (!DMAbuf_space_in_queue (dev))
- {
- save_flags (flags);
- cli ();
-
- out_sleep_flag[dev].opts = WK_SLEEP;
- poll_wait (&out_sleeper[dev], wait);
- restore_flags (flags);
- return 0;
- }
- return 1;
- break;
-
- case SEL_EX:
- return 0;
- }
-
- return 0;
}
+void
+DMAbuf_deinit(int dev)
+{
+/* This routine is called when driver is being unloaded */
+#ifdef RUNTIME_DMA_ALLOC
+ sound_free_dmap (dev, audio_devs[dev]->dmap_out,
+ audio_devs[dev]->dmap_out->dma);
+
+ if (audio_devs[dev]->flags & DMA_DUPLEX)
+ sound_free_dmap (dev, audio_devs[dev]->dmap_in,
+ audio_devs[dev]->dmap_in->dma);
+#endif
+}
#endif
diff --git a/drivers/sound/dmasound.c b/drivers/sound/dmasound.c
index 1c37f9f5a..e7768f07f 100644
--- a/drivers/sound/dmasound.c
+++ b/drivers/sound/dmasound.c
@@ -3,76 +3,76 @@
/*
-OSS/Free compatible Atari TT/Falcon and Amiga DMA sound driver for Linux/m68k
+ OSS/Free compatible Atari TT/Falcon and Amiga DMA sound driver for Linux/m68k
-(c) 1995 by Michael Schlueter & Michael Marte
+ (c) 1995 by Michael Schlueter & Michael Marte
-Michael Schlueter (michael@duck.syd.de) did the basic structure of the VFS
-interface and the u-law to signed byte conversion.
+ Michael Schlueter (michael@duck.syd.de) did the basic structure of the VFS
+ interface and the u-law to signed byte conversion.
-Michael Marte (marte@informatik.uni-muenchen.de) did the sound queue,
-/dev/mixer, /dev/sndstat and complemented the VFS interface. He would like
-to thank:
-Michael Schlueter for initial ideas and documentation on the MFP and
-the DMA sound hardware.
-Therapy? for their CD 'Troublegum' which really made me rock.
+ Michael Marte (marte@informatik.uni-muenchen.de) did the sound queue,
+ /dev/mixer, /dev/sndstat and complemented the VFS interface. He would like
+ to thank:
+ Michael Schlueter for initial ideas and documentation on the MFP and
+ the DMA sound hardware.
+ Therapy? for their CD 'Troublegum' which really made me rock.
-/dev/sndstat is based on code by Hannu Savolainen, the author of the
-VoxWare family of drivers.
+ /dev/sndstat is based on code by Hannu Savolainen, the author of the
+ VoxWare family of drivers.
-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 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.
-History:
-1995/8/25 first release
+ History:
+ 1995/8/25 first release
-1995/9/02 ++roman: fixed atari_stram_alloc() call, the timer programming
- and several race conditions
+ 1995/9/02 ++roman: fixed atari_stram_alloc() call, the timer programming
+ and several race conditions
-1995/9/14 ++roman: After some discussion with Michael Schlueter, revised
- the interrupt disabling
- Slightly speeded up U8->S8 translation by using long
- operations where possible
- Added 4:3 interpolation for /dev/audio
+ 1995/9/14 ++roman: After some discussion with Michael Schlueter, revised
+ the interrupt disabling
+ Slightly speeded up U8->S8 translation by using long
+ operations where possible
+ Added 4:3 interpolation for /dev/audio
-1995/9/20 ++TeSche: Fixed a bug in sq_write and changed /dev/audio
- converting to play at 12517Hz instead of 6258Hz.
+ 1995/9/20 ++TeSche: Fixed a bug in sq_write and changed /dev/audio
+ converting to play at 12517Hz instead of 6258Hz.
-1995/9/23 ++TeSche: Changed sq_interrupt() and sq_play() to pre-program
- the DMA for another frame while there's still one
- running. This allows the IRQ response to be
- arbitrarily delayed and playing will still continue.
+ 1995/9/23 ++TeSche: Changed sq_interrupt() and sq_play() to pre-program
+ the DMA for another frame while there's still one
+ running. This allows the IRQ response to be
+ arbitrarily delayed and playing will still continue.
-1995/10/14 ++Guenther_Kelleter@ac3.maus.de, ++TeSche: better support for
- Falcon audio (the Falcon doesn't raise an IRQ at the
- end of a frame, but at the beginning instead!). uses
- 'if (codec_dma)' in lots of places to simply switch
- between Falcon and TT code.
+ 1995/10/14 ++Guenther_Kelleter@ac3.maus.de, ++TeSche: better support for
+ Falcon audio (the Falcon doesn't raise an IRQ at the
+ end of a frame, but at the beginning instead!). uses
+ 'if (codec_dma)' in lots of places to simply switch
+ between Falcon and TT code.
-1995/11/06 ++TeSche: started introducing a hardware abstraction scheme
- (may perhaps also serve for Amigas?), can now play
- samples at almost all frequencies by means of a more
- generalized expand routine, takes a good deal of care
- to cut data only at sample sizes, buffer size is now
- a kernel runtime option, implemented fsync() & several
- minor improvements
- ++Guenther: useful hints and bug fixes, cross-checked it for
- Falcons
+ 1995/11/06 ++TeSche: started introducing a hardware abstraction scheme
+ (may perhaps also serve for Amigas?), can now play
+ samples at almost all frequencies by means of a more
+ generalized expand routine, takes a good deal of care
+ to cut data only at sample sizes, buffer size is now
+ a kernel runtime option, implemented fsync() & several
+ minor improvements
+ ++Guenther: useful hints and bug fixes, cross-checked it for
+ Falcons
-1996/3/9 ++geert: support added for Amiga, A-law, 16-bit little endian.
- Unification to drivers/sound/dmasound.c.
+ 1996/3/9 ++geert: support added for Amiga, A-law, 16-bit little endian.
+ Unification to drivers/sound/dmasound.c.
-1996/4/6 ++Martin Mitchell: updated to 1.3 kernel.
+ 1996/4/6 ++Martin Mitchell: updated to 1.3 kernel.
-1996/6/13 ++topi: fixed things that were broken (mainly the amiga
- 14-bit routines), /dev/sndstat shows now the real
- hardware frequency, the lowpass filter is disabled
- by default now.
+ 1996/6/13 ++topi: fixed things that were broken (mainly the amiga
+ 14-bit routines), /dev/sndstat shows now the real
+ hardware frequency, the lowpass filter is disabled
+ by default now.
-1996/9/25 ++geert: modularization
+ 1996/9/25 ++geert: modularization
-*/
+ */
#include <linux/module.h>
@@ -94,53 +94,55 @@ History:
#ifdef CONFIG_ATARI
#include <asm/atarihw.h>
#include <asm/atariints.h>
-#endif /* CONFIG_ATARI */
+#endif /* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
#include <asm/amigahw.h>
#include <asm/amigaints.h>
-#endif /* CONFIG_AMIGA */
+#endif /* CONFIG_AMIGA */
#include "dmasound.h"
#include <linux/soundcard.h>
#ifdef MODULE
-static int chrdev_registered = 0;
-static int irq_installed = 0;
-#endif /* MODULE */
-static char **sound_buffers = NULL;
+static int chrdev_registered = 0;
+static int irq_installed = 0;
+
+#endif /* MODULE */
+static char **sound_buffers = NULL;
#ifdef CONFIG_ATARI
-extern void atari_microwire_cmd(int cmd);
-#endif /* CONFIG_ATARI */
+extern void atari_microwire_cmd(int cmd);
+
+#endif /* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
/*
- * The minimum period for audio depends on htotal (for OCS/ECS/AGA)
- * (Imported from arch/m68k/amiga/amisound.c)
+ * The minimum period for audio depends on htotal (for OCS/ECS/AGA)
+ * (Imported from arch/m68k/amiga/amisound.c)
*/
extern volatile u_short amiga_audio_min_period;
/*
- * amiga_mksound() should be able to restore the period after beeping
- * (Imported from arch/m68k/amiga/amisound.c)
+ * amiga_mksound() should be able to restore the period after beeping
+ * (Imported from arch/m68k/amiga/amisound.c)
*/
-extern u_short amiga_audio_period;
+extern u_short amiga_audio_period;
/*
- * Audio DMA masks
+ * Audio DMA masks
*/
#define AMI_AUDIO_OFF (DMAF_AUD0 | DMAF_AUD1 | DMAF_AUD2 | DMAF_AUD3)
#define AMI_AUDIO_8 (DMAF_SETCLR | DMAF_MASTER | DMAF_AUD0 | DMAF_AUD1)
#define AMI_AUDIO_14 (AMI_AUDIO_8 | DMAF_AUD2 | DMAF_AUD3)
-#endif /* CONFIG_AMIGA */
+#endif /* CONFIG_AMIGA */
/*** Some declarations *******************************************************/
@@ -155,7 +157,7 @@ extern u_short amiga_audio_period;
#define MIN_BUFSIZE 4
#define MAX_BUFSIZE 128 /* Limit for Amiga */
-static int catchRadius = 0, numBufs = 4, bufSize = 32;
+static int catchRadius = 0, numBufs = 4, bufSize = 32;
#define arraysize(x) (sizeof(x)/sizeof(*(x)))
@@ -175,76 +177,78 @@ static int catchRadius = 0, numBufs = 4, bufSize = 32;
/* 8 bit mu-law */
-static char ulaw2dma8[] = {
- -126, -122, -118, -114, -110, -106, -102, -98,
- -94, -90, -86, -82, -78, -74, -70, -66,
- -63, -61, -59, -57, -55, -53, -51, -49,
- -47, -45, -43, -41, -39, -37, -35, -33,
- -31, -30, -29, -28, -27, -26, -25, -24,
- -23, -22, -21, -20, -19, -18, -17, -16,
- -16, -15, -15, -14, -14, -13, -13, -12,
- -12, -11, -11, -10, -10, -9, -9, -8,
- -8, -8, -7, -7, -7, -7, -6, -6,
- -6, -6, -5, -5, -5, -5, -4, -4,
- -4, -4, -4, -4, -3, -3, -3, -3,
- -3, -3, -3, -3, -2, -2, -2, -2,
- -2, -2, -2, -2, -2, -2, -2, -2,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 0,
- 125, 121, 117, 113, 109, 105, 101, 97,
- 93, 89, 85, 81, 77, 73, 69, 65,
- 62, 60, 58, 56, 54, 52, 50, 48,
- 46, 44, 42, 40, 38, 36, 34, 32,
- 30, 29, 28, 27, 26, 25, 24, 23,
- 22, 21, 20, 19, 18, 17, 16, 15,
- 15, 14, 14, 13, 13, 12, 12, 11,
- 11, 10, 10, 9, 9, 8, 8, 7,
- 7, 7, 6, 6, 6, 6, 5, 5,
- 5, 5, 4, 4, 4, 4, 3, 3,
- 3, 3, 3, 3, 2, 2, 2, 2,
- 2, 2, 2, 2, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0
+static char ulaw2dma8[] =
+{
+ -126, -122, -118, -114, -110, -106, -102, -98,
+ -94, -90, -86, -82, -78, -74, -70, -66,
+ -63, -61, -59, -57, -55, -53, -51, -49,
+ -47, -45, -43, -41, -39, -37, -35, -33,
+ -31, -30, -29, -28, -27, -26, -25, -24,
+ -23, -22, -21, -20, -19, -18, -17, -16,
+ -16, -15, -15, -14, -14, -13, -13, -12,
+ -12, -11, -11, -10, -10, -9, -9, -8,
+ -8, -8, -7, -7, -7, -7, -6, -6,
+ -6, -6, -5, -5, -5, -5, -4, -4,
+ -4, -4, -4, -4, -3, -3, -3, -3,
+ -3, -3, -3, -3, -2, -2, -2, -2,
+ -2, -2, -2, -2, -2, -2, -2, -2,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 0,
+ 125, 121, 117, 113, 109, 105, 101, 97,
+ 93, 89, 85, 81, 77, 73, 69, 65,
+ 62, 60, 58, 56, 54, 52, 50, 48,
+ 46, 44, 42, 40, 38, 36, 34, 32,
+ 30, 29, 28, 27, 26, 25, 24, 23,
+ 22, 21, 20, 19, 18, 17, 16, 15,
+ 15, 14, 14, 13, 13, 12, 12, 11,
+ 11, 10, 10, 9, 9, 8, 8, 7,
+ 7, 7, 6, 6, 6, 6, 5, 5,
+ 5, 5, 4, 4, 4, 4, 3, 3,
+ 3, 3, 3, 3, 2, 2, 2, 2,
+ 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
};
/* 8 bit A-law */
-static char alaw2dma8[] = {
- -22, -21, -24, -23, -18, -17, -20, -19,
- -30, -29, -32, -31, -26, -25, -28, -27,
- -11, -11, -12, -12, -9, -9, -10, -10,
- -15, -15, -16, -16, -13, -13, -14, -14,
- -86, -82, -94, -90, -70, -66, -78, -74,
- -118, -114, -126, -122, -102, -98, -110, -106,
- -43, -41, -47, -45, -35, -33, -39, -37,
- -59, -57, -63, -61, -51, -49, -55, -53,
- -2, -2, -2, -2, -2, -2, -2, -2,
- -2, -2, -2, -2, -2, -2, -2, -2,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -6, -6, -6, -6, -5, -5, -5, -5,
- -8, -8, -8, -8, -7, -7, -7, -7,
- -3, -3, -3, -3, -3, -3, -3, -3,
- -4, -4, -4, -4, -4, -4, -4, -4,
- 21, 20, 23, 22, 17, 16, 19, 18,
- 29, 28, 31, 30, 25, 24, 27, 26,
- 10, 10, 11, 11, 8, 8, 9, 9,
- 14, 14, 15, 15, 12, 12, 13, 13,
- 86, 82, 94, 90, 70, 66, 78, 74,
- 118, 114, 126, 122, 102, 98, 110, 106,
- 43, 41, 47, 45, 35, 33, 39, 37,
- 59, 57, 63, 61, 51, 49, 55, 53,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 5, 5, 5, 5, 4, 4, 4, 4,
- 7, 7, 7, 7, 6, 6, 6, 6,
- 2, 2, 2, 2, 2, 2, 2, 2,
- 3, 3, 3, 3, 3, 3, 3, 3
+static char alaw2dma8[] =
+{
+ -22, -21, -24, -23, -18, -17, -20, -19,
+ -30, -29, -32, -31, -26, -25, -28, -27,
+ -11, -11, -12, -12, -9, -9, -10, -10,
+ -15, -15, -16, -16, -13, -13, -14, -14,
+ -86, -82, -94, -90, -70, -66, -78, -74,
+ -118, -114, -126, -122, -102, -98, -110, -106,
+ -43, -41, -47, -45, -35, -33, -39, -37,
+ -59, -57, -63, -61, -51, -49, -55, -53,
+ -2, -2, -2, -2, -2, -2, -2, -2,
+ -2, -2, -2, -2, -2, -2, -2, -2,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -6, -6, -6, -6, -5, -5, -5, -5,
+ -8, -8, -8, -8, -7, -7, -7, -7,
+ -3, -3, -3, -3, -3, -3, -3, -3,
+ -4, -4, -4, -4, -4, -4, -4, -4,
+ 21, 20, 23, 22, 17, 16, 19, 18,
+ 29, 28, 31, 30, 25, 24, 27, 26,
+ 10, 10, 11, 11, 8, 8, 9, 9,
+ 14, 14, 15, 15, 12, 12, 13, 13,
+ 86, 82, 94, 90, 70, 66, 78, 74,
+ 118, 114, 126, 122, 102, 98, 110, 106,
+ 43, 41, 47, 45, 35, 33, 39, 37,
+ 59, 57, 63, 61, 51, 49, 55, 53,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 5, 5, 5, 5, 4, 4, 4, 4,
+ 7, 7, 7, 7, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3
};
@@ -252,378 +256,403 @@ static char alaw2dma8[] = {
/* 16 bit mu-law */
-static char ulaw2dma16[] = {
- -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956,
- -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764,
- -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412,
- -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316,
- -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
- -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
- -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
- -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
- -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
- -1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
- -876, -844, -812, -780, -748, -716, -684, -652,
- -620, -588, -556, -524, -492, -460, -428, -396,
- -372, -356, -340, -324, -308, -292, -276, -260,
- -244, -228, -212, -196, -180, -164, -148, -132,
- -120, -112, -104, -96, -88, -80, -72, -64,
- -56, -48, -40, -32, -24, -16, -8, 0,
- 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
- 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
- 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
- 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
- 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
- 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
- 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
- 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
- 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
- 1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
- 876, 844, 812, 780, 748, 716, 684, 652,
- 620, 588, 556, 524, 492, 460, 428, 396,
- 372, 356, 340, 324, 308, 292, 276, 260,
- 244, 228, 212, 196, 180, 164, 148, 132,
- 120, 112, 104, 96, 88, 80, 72, 64,
- 56, 48, 40, 32, 24, 16, 8, 0,
+static char ulaw2dma16[] =
+{
+ -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956,
+ -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764,
+ -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412,
+ -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316,
+ -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
+ -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
+ -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
+ -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
+ -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
+ -1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
+ -876, -844, -812, -780, -748, -716, -684, -652,
+ -620, -588, -556, -524, -492, -460, -428, -396,
+ -372, -356, -340, -324, -308, -292, -276, -260,
+ -244, -228, -212, -196, -180, -164, -148, -132,
+ -120, -112, -104, -96, -88, -80, -72, -64,
+ -56, -48, -40, -32, -24, -16, -8, 0,
+ 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
+ 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
+ 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
+ 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
+ 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
+ 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
+ 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
+ 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
+ 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
+ 1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
+ 876, 844, 812, 780, 748, 716, 684, 652,
+ 620, 588, 556, 524, 492, 460, 428, 396,
+ 372, 356, 340, 324, 308, 292, 276, 260,
+ 244, 228, 212, 196, 180, 164, 148, 132,
+ 120, 112, 104, 96, 88, 80, 72, 64,
+ 56, 48, 40, 32, 24, 16, 8, 0,
};
/* 16 bit A-law */
-static char alaw2dma16[] = {
- -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
- -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
- -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
- -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
- -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944,
- -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136,
- -11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472,
- -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568,
- -344, -328, -376, -360, -280, -264, -312, -296,
- -472, -456, -504, -488, -408, -392, -440, -424,
- -88, -72, -120, -104, -24, -8, -56, -40,
- -216, -200, -248, -232, -152, -136, -184, -168,
- -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
- -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
- -688, -656, -752, -720, -560, -528, -624, -592,
- -944, -912, -1008, -976, -816, -784, -880, -848,
- 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
- 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
- 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
- 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
- 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
- 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
- 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
- 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
- 344, 328, 376, 360, 280, 264, 312, 296,
- 472, 456, 504, 488, 408, 392, 440, 424,
- 88, 72, 120, 104, 24, 8, 56, 40,
- 216, 200, 248, 232, 152, 136, 184, 168,
- 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
- 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
- 688, 656, 752, 720, 560, 528, 624, 592,
- 944, 912, 1008, 976, 816, 784, 880, 848,
+static char alaw2dma16[] =
+{
+ -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
+ -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
+ -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
+ -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
+ -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944,
+ -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136,
+ -11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472,
+ -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568,
+ -344, -328, -376, -360, -280, -264, -312, -296,
+ -472, -456, -504, -488, -408, -392, -440, -424,
+ -88, -72, -120, -104, -24, -8, -56, -40,
+ -216, -200, -248, -232, -152, -136, -184, -168,
+ -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
+ -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
+ -688, -656, -752, -720, -560, -528, -624, -592,
+ -944, -912, -1008, -976, -816, -784, -880, -848,
+ 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
+ 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
+ 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
+ 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
+ 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
+ 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
+ 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
+ 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
+ 344, 328, 376, 360, 280, 264, 312, 296,
+ 472, 456, 504, 488, 408, 392, 440, 424,
+ 88, 72, 120, 104, 24, 8, 56, 40,
+ 216, 200, 248, 232, 152, 136, 184, 168,
+ 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
+ 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
+ 688, 656, 752, 720, 560, 528, 624, 592,
+ 944, 912, 1008, 976, 816, 784, 880, 848,
};
-#endif /* HAS_16BIT_TABLES */
+
+#endif /* HAS_16BIT_TABLES */
#ifdef HAS_14BIT_TABLES
/* 14 bit mu-law (LSB) */
-static char alaw2dma14l[] = {
- 33, 33, 33, 33, 33, 33, 33, 33,
- 33, 33, 33, 33, 33, 33, 33, 33,
- 33, 33, 33, 33, 33, 33, 33, 33,
- 33, 33, 33, 33, 33, 33, 33, 33,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 49, 17, 49, 17, 49, 17, 49, 17,
- 49, 17, 49, 17, 49, 17, 49, 17,
- 41, 57, 9, 25, 41, 57, 9, 25,
- 41, 57, 9, 25, 41, 57, 9, 25,
- 37, 45, 53, 61, 5, 13, 21, 29,
- 37, 45, 53, 61, 5, 13, 21, 29,
- 35, 39, 43, 47, 51, 55, 59, 63,
- 3, 7, 11, 15, 19, 23, 27, 31,
- 34, 36, 38, 40, 42, 44, 46, 48,
- 50, 52, 54, 56, 58, 60, 62, 0,
- 31, 31, 31, 31, 31, 31, 31, 31,
- 31, 31, 31, 31, 31, 31, 31, 31,
- 31, 31, 31, 31, 31, 31, 31, 31,
- 31, 31, 31, 31, 31, 31, 31, 31,
- 63, 63, 63, 63, 63, 63, 63, 63,
- 63, 63, 63, 63, 63, 63, 63, 63,
- 15, 47, 15, 47, 15, 47, 15, 47,
- 15, 47, 15, 47, 15, 47, 15, 47,
- 23, 7, 55, 39, 23, 7, 55, 39,
- 23, 7, 55, 39, 23, 7, 55, 39,
- 27, 19, 11, 3, 59, 51, 43, 35,
- 27, 19, 11, 3, 59, 51, 43, 35,
- 29, 25, 21, 17, 13, 9, 5, 1,
- 61, 57, 53, 49, 45, 41, 37, 33,
- 30, 28, 26, 24, 22, 20, 18, 16,
- 14, 12, 10, 8, 6, 4, 2, 0
+static char alaw2dma14l[] =
+{
+ 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 49, 17, 49, 17, 49, 17, 49, 17,
+ 49, 17, 49, 17, 49, 17, 49, 17,
+ 41, 57, 9, 25, 41, 57, 9, 25,
+ 41, 57, 9, 25, 41, 57, 9, 25,
+ 37, 45, 53, 61, 5, 13, 21, 29,
+ 37, 45, 53, 61, 5, 13, 21, 29,
+ 35, 39, 43, 47, 51, 55, 59, 63,
+ 3, 7, 11, 15, 19, 23, 27, 31,
+ 34, 36, 38, 40, 42, 44, 46, 48,
+ 50, 52, 54, 56, 58, 60, 62, 0,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 15, 47, 15, 47, 15, 47, 15, 47,
+ 15, 47, 15, 47, 15, 47, 15, 47,
+ 23, 7, 55, 39, 23, 7, 55, 39,
+ 23, 7, 55, 39, 23, 7, 55, 39,
+ 27, 19, 11, 3, 59, 51, 43, 35,
+ 27, 19, 11, 3, 59, 51, 43, 35,
+ 29, 25, 21, 17, 13, 9, 5, 1,
+ 61, 57, 53, 49, 45, 41, 37, 33,
+ 30, 28, 26, 24, 22, 20, 18, 16,
+ 14, 12, 10, 8, 6, 4, 2, 0
};
/* 14 bit A-law (LSB) */
-static char alaw2dma14l[] = {
- 32, 32, 32, 32, 32, 32, 32, 32,
- 32, 32, 32, 32, 32, 32, 32, 32,
- 16, 48, 16, 48, 16, 48, 16, 48,
- 16, 48, 16, 48, 16, 48, 16, 48,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 42, 46, 34, 38, 58, 62, 50, 54,
- 10, 14, 2, 6, 26, 30, 18, 22,
- 42, 46, 34, 38, 58, 62, 50, 54,
- 10, 14, 2, 6, 26, 30, 18, 22,
- 40, 56, 8, 24, 40, 56, 8, 24,
- 40, 56, 8, 24, 40, 56, 8, 24,
- 20, 28, 4, 12, 52, 60, 36, 44,
- 20, 28, 4, 12, 52, 60, 36, 44,
- 32, 32, 32, 32, 32, 32, 32, 32,
- 32, 32, 32, 32, 32, 32, 32, 32,
- 48, 16, 48, 16, 48, 16, 48, 16,
- 48, 16, 48, 16, 48, 16, 48, 16,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 22, 18, 30, 26, 6, 2, 14, 10,
- 54, 50, 62, 58, 38, 34, 46, 42,
- 22, 18, 30, 26, 6, 2, 14, 10,
- 54, 50, 62, 58, 38, 34, 46, 42,
- 24, 8, 56, 40, 24, 8, 56, 40,
- 24, 8, 56, 40, 24, 8, 56, 40,
- 44, 36, 60, 52, 12, 4, 28, 20,
- 44, 36, 60, 52, 12, 4, 28, 20
+static char alaw2dma14l[] =
+{
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 16, 48, 16, 48, 16, 48, 16, 48,
+ 16, 48, 16, 48, 16, 48, 16, 48,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 42, 46, 34, 38, 58, 62, 50, 54,
+ 10, 14, 2, 6, 26, 30, 18, 22,
+ 42, 46, 34, 38, 58, 62, 50, 54,
+ 10, 14, 2, 6, 26, 30, 18, 22,
+ 40, 56, 8, 24, 40, 56, 8, 24,
+ 40, 56, 8, 24, 40, 56, 8, 24,
+ 20, 28, 4, 12, 52, 60, 36, 44,
+ 20, 28, 4, 12, 52, 60, 36, 44,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 48, 16, 48, 16, 48, 16, 48, 16,
+ 48, 16, 48, 16, 48, 16, 48, 16,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 22, 18, 30, 26, 6, 2, 14, 10,
+ 54, 50, 62, 58, 38, 34, 46, 42,
+ 22, 18, 30, 26, 6, 2, 14, 10,
+ 54, 50, 62, 58, 38, 34, 46, 42,
+ 24, 8, 56, 40, 24, 8, 56, 40,
+ 24, 8, 56, 40, 24, 8, 56, 40,
+ 44, 36, 60, 52, 12, 4, 28, 20,
+ 44, 36, 60, 52, 12, 4, 28, 20
};
-#endif /* HAS_14BIT_TABLES */
+
+#endif /* HAS_14BIT_TABLES */
/*** Translations ************************************************************/
#ifdef CONFIG_ATARI
-static long ata_ct_law(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-static long ata_ct_s8(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-static long ata_ct_u8(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-static long ata_ct_s16be(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-static long ata_ct_u16be(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-static long ata_ct_s16le(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-static long ata_ct_u16le(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-static long ata_ctx_law(const u_char *userPtr, unsigned long userCount,
+static long ata_ct_law(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ct_s8(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ct_u8(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ct_s16be(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ct_u16be(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ct_s16le(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ct_u16le(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ctx_law(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ctx_s8(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ctx_u8(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ctx_s16be(const u_char * userPtr, unsigned long userCount,
u_char frame[], long *frameUsed, long frameLeft);
-static long ata_ctx_s8(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-static long ata_ctx_u8(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-static long ata_ctx_s16be(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-static long ata_ctx_u16be(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-static long ata_ctx_s16le(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-static long ata_ctx_u16le(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-#endif /* CONFIG_ATARI */
+static long ata_ctx_u16be(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ctx_s16le(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+static long ata_ctx_u16le(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+
+#endif /* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
-static long ami_ct_law(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-static long ami_ct_s8(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-static long ami_ct_u8(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-static long ami_ct_s16be(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-static long ami_ct_u16be(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-static long ami_ct_s16le(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-static long ami_ct_u16le(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft);
-#endif /* CONFIG_AMIGA */
+static long ami_ct_law(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+static long ami_ct_s8(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+static long ami_ct_u8(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+static long ami_ct_s16be(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+static long ami_ct_u16be(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+static long ami_ct_s16le(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+static long ami_ct_u16le(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft);
+
+#endif /* CONFIG_AMIGA */
/*** Machine definitions *****************************************************/
-typedef struct {
- int type;
- void *(*dma_alloc)(unsigned int, int);
- void (*dma_free)(void *, unsigned int);
- int (*irqinit)(void);
+typedef struct
+ {
+ int type;
+ void *(*dma_alloc) (unsigned int, int);
+ void (*dma_free) (void *, unsigned int);
+ int (*irqinit) (void);
#ifdef MODULE
- void (*irqcleanup)(void);
-#endif /* MODULE */
- void (*init)(void);
- void (*silence)(void);
- int (*setFormat)(int);
- int (*setVolume)(int);
- int (*setBass)(int);
- int (*setTreble)(int);
- void (*play)(void);
-} MACHINE;
+ void (*irqcleanup) (void);
+#endif /* MODULE */
+ void (*init) (void);
+ void (*silence) (void);
+ int (*setFormat) (int);
+ int (*setVolume) (int);
+ int (*setBass) (int);
+ int (*setTreble) (int);
+ void (*play) (void);
+ }
+MACHINE;
/*** Low level stuff *********************************************************/
-typedef struct {
- int format; /* AFMT_* */
- int stereo; /* 0 = mono, 1 = stereo */
- int size; /* 8/16 bit*/
- int speed; /* speed */
-} SETTINGS;
-
-typedef struct {
- long (*ct_ulaw)(const u_char *, unsigned long, u_char *, long *, long);
- long (*ct_alaw)(const u_char *, unsigned long, u_char *, long *, long);
- long (*ct_s8)(const u_char *, unsigned long, u_char *, long *, long);
- long (*ct_u8)(const u_char *, unsigned long, u_char *, long *, long);
- long (*ct_s16be)(const u_char *, unsigned long, u_char *, long *, long);
- long (*ct_u16be)(const u_char *, unsigned long, u_char *, long *, long);
- long (*ct_s16le)(const u_char *, unsigned long, u_char *, long *, long);
- long (*ct_u16le)(const u_char *, unsigned long, u_char *, long *, long);
-} TRANS;
-
-struct sound_settings {
- MACHINE mach; /* machine dependent things */
- SETTINGS hard; /* hardware settings */
- SETTINGS soft; /* software settings */
- SETTINGS dsp; /* /dev/dsp default settings */
- TRANS *trans; /* supported translations */
- int volume_left; /* volume (range is machine dependent) */
- int volume_right;
- int bass; /* tone (range is machine dependent) */
- int treble;
- int minDev; /* minor device number currently open */
+typedef struct
+ {
+ int format; /* AFMT_* */
+ int stereo; /* 0 = mono, 1 = stereo */
+ int size; /* 8/16 bit */
+ int speed; /* speed */
+ }
+SETTINGS;
+
+typedef struct
+ {
+ long (*ct_ulaw) (const u_char *, unsigned long, u_char *, long *, long);
+ long (*ct_alaw) (const u_char *, unsigned long, u_char *, long *, long);
+ long (*ct_s8) (const u_char *, unsigned long, u_char *, long *, long);
+ long (*ct_u8) (const u_char *, unsigned long, u_char *, long *, long);
+ long (*ct_s16be) (const u_char *, unsigned long, u_char *, long *, long);
+ long (*ct_u16be) (const u_char *, unsigned long, u_char *, long *, long);
+ long (*ct_s16le) (const u_char *, unsigned long, u_char *, long *, long);
+ long (*ct_u16le) (const u_char *, unsigned long, u_char *, long *, long);
+ }
+TRANS;
+
+struct sound_settings
+ {
+ MACHINE mach; /* machine dependent things */
+ SETTINGS hard; /* hardware settings */
+ SETTINGS soft; /* software settings */
+ SETTINGS dsp; /* /dev/dsp default settings */
+ TRANS *trans; /* supported translations */
+ int volume_left; /* volume (range is machine dependent) */
+ int volume_right;
+ int bass; /* tone (range is machine dependent) */
+ int treble;
+ int minDev; /* minor device number currently open */
#ifdef CONFIG_ATARI
- int bal; /* balance factor for expanding (not volume!) */
- u_long data; /* data for expanding */
-#endif /* CONFIG_ATARI */
-};
+ int bal; /* balance factor for expanding (not volume!) */
+ u_long data; /* data for expanding */
+#endif /* CONFIG_ATARI */
+ };
static struct sound_settings sound;
#ifdef CONFIG_ATARI
-static void *AtaAlloc(unsigned int size, int flags);
-static void AtaFree(void *, unsigned int size);
-static int AtaIrqInit(void);
+static void *AtaAlloc(unsigned int size, int flags);
+static void AtaFree(void *, unsigned int size);
+static int AtaIrqInit(void);
+
#ifdef MODULE
-static void AtaIrqCleanUp(void);
-#endif /* MODULE */
-static int AtaSetBass(int bass);
-static int AtaSetTreble(int treble);
-static void TTSilence(void);
-static void TTInit(void);
-static int TTSetFormat(int format);
-static int TTSetVolume(int volume);
-static void FalconSilence(void);
-static void FalconInit(void);
-static int FalconSetFormat(int format);
-static int FalconSetVolume(int volume);
-static void ata_sq_play_next_frame(int index);
-static void AtaPlay(void);
-static void ata_sq_interrupt(int irq, void *dummy, struct pt_regs *fp);
-#endif /* CONFIG_ATARI */
+static void AtaIrqCleanUp(void);
+
+#endif /* MODULE */
+static int AtaSetBass(int bass);
+static int AtaSetTreble(int treble);
+static void TTSilence(void);
+static void TTInit(void);
+static int TTSetFormat(int format);
+static int TTSetVolume(int volume);
+static void FalconSilence(void);
+static void FalconInit(void);
+static int FalconSetFormat(int format);
+static int FalconSetVolume(int volume);
+static void ata_sq_play_next_frame(int index);
+static void AtaPlay(void);
+static void ata_sq_interrupt(int irq, void *dummy, struct pt_regs *fp);
+
+#endif /* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
-static void *AmiAlloc(unsigned int size, int flags);
-static void AmiFree(void *, unsigned int);
-static int AmiIrqInit(void);
+static void *AmiAlloc(unsigned int size, int flags);
+static void AmiFree(void *, unsigned int);
+static int AmiIrqInit(void);
+
#ifdef MODULE
-static void AmiIrqCleanUp(void);
-#endif /* MODULE */
-static void AmiSilence(void);
-static void AmiInit(void);
-static int AmiSetFormat(int format);
-static int AmiSetVolume(int volume);
-static int AmiSetTreble(int treble);
-static void ami_sq_play_next_frame(int index);
-static void AmiPlay(void);
-static void ami_sq_interrupt(int irq, void *dummy, struct pt_regs *fp);
-#endif /* CONFIG_AMIGA */
+static void AmiIrqCleanUp(void);
+
+#endif /* MODULE */
+static void AmiSilence(void);
+static void AmiInit(void);
+static int AmiSetFormat(int format);
+static int AmiSetVolume(int volume);
+static int AmiSetTreble(int treble);
+static void ami_sq_play_next_frame(int index);
+static void AmiPlay(void);
+static void ami_sq_interrupt(int irq, void *dummy, struct pt_regs *fp);
+
+#endif /* CONFIG_AMIGA */
/*** Mid level stuff *********************************************************/
-static void sound_silence(void);
-static void sound_init(void);
-static int sound_set_format(int format);
-static int sound_set_speed(int speed);
-static int sound_set_stereo(int stereo);
-static int sound_set_volume(int volume);
+static void sound_silence(void);
+static void sound_init(void);
+static int sound_set_format(int format);
+static int sound_set_speed(int speed);
+static int sound_set_stereo(int stereo);
+static int sound_set_volume(int volume);
+
#ifdef CONFIG_ATARI
-static int sound_set_bass(int bass);
-#endif /* CONFIG_ATARI */
-static int sound_set_treble(int treble);
-static long sound_copy_translate(const u_char *userPtr,
- unsigned long userCount,
- u_char frame[], long *frameUsed,
- long frameLeft);
+static int sound_set_bass(int bass);
+
+#endif /* CONFIG_ATARI */
+static int sound_set_treble(int treble);
+static long sound_copy_translate(const u_char * userPtr,
+ unsigned long userCount,
+ u_char frame[], long *frameUsed,
+ long frameLeft);
/*
* /dev/mixer abstraction
*/
-struct sound_mixer {
- int busy;
-};
+struct sound_mixer
+ {
+ int busy;
+ };
static struct sound_mixer mixer;
-static void mixer_init(void);
-static int mixer_open(int open_mode);
-static int mixer_release(void);
-static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd,
- u_long arg);
+static void mixer_init(void);
+static int mixer_open(int open_mode);
+static int mixer_release(void);
+static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd,
+ u_long arg);
/*
* Sound queue stuff, the heart of the driver
*/
-struct sound_queue {
- int max_count, block_size;
- char **buffers;
-
- /* it shouldn't be necessary to declare any of these volatile */
- int front, rear, count;
- int rear_size;
- /*
- * The use of the playing field depends on the hardware
- *
- * Atari: The number of frames that are loaded/playing
- *
- * Amiga: Bit 0 is set: a frame is loaded
- * Bit 1 is set: a frame is playing
- */
- int playing;
- struct wait_queue *write_queue, *open_queue, *sync_queue;
- int open_mode;
- int busy, syncing;
+struct sound_queue
+ {
+ int max_count, block_size;
+ char **buffers;
+
+ /* it shouldn't be necessary to declare any of these volatile */
+ int front, rear, count;
+ int rear_size;
+ /*
+ * The use of the playing field depends on the hardware
+ *
+ * Atari: The number of frames that are loaded/playing
+ *
+ * Amiga: Bit 0 is set: a frame is loaded
+ * Bit 1 is set: a frame is playing
+ */
+ int playing;
+ struct wait_queue *write_queue, *open_queue, *sync_queue;
+ int open_mode;
+ int busy, syncing;
#ifdef CONFIG_ATARI
- int ignore_int; /* ++TeSche: used for Falcon */
-#endif /* CONFIG_ATARI */
+ int ignore_int; /* ++TeSche: used for Falcon */
+#endif /* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
- int block_size_half, block_size_quarter;
-#endif /* CONFIG_AMIGA */
-};
+ int block_size_half, block_size_quarter;
+#endif /* CONFIG_AMIGA */
+ };
static struct sound_queue sq;
@@ -637,69 +666,72 @@ static struct sound_queue sq;
interruptible_sleep_on(&queue);
#define WAKE_UP(queue) (wake_up_interruptible(&queue))
-static void sq_init(int numBufs, int bufSize, char **buffers);
-static void sq_play(void);
-static long sq_write(const char *src, unsigned long uLeft);
-static int sq_open(int open_mode);
-static void sq_reset(void);
-static int sq_sync(void);
-static int sq_release(void);
+static void sq_init(int numBufs, int bufSize, char **buffers);
+static void sq_play(void);
+static long sq_write(const char *src, unsigned long uLeft);
+static int sq_open(int open_mode);
+static void sq_reset(void);
+static int sq_sync(void);
+static int sq_release(void);
/*
* /dev/sndstat
*/
-struct sound_state {
- int busy;
- char buf[512];
- int len, ptr;
-};
+struct sound_state
+ {
+ int busy;
+ char buf[512];
+ int len, ptr;
+ };
static struct sound_state state;
-static void state_init(void);
-static int state_open(int open_mode);
-static int state_release(void);
-static long state_read(char *dest, unsigned long count);
+static void state_init(void);
+static int state_open(int open_mode);
+static int state_release(void);
+static long state_read(char *dest, unsigned long count);
/*** High level stuff ********************************************************/
-static int sound_open(struct inode *inode, struct file *file);
-static int sound_fsync(struct file *filp, struct dentry *dentry);
-static void sound_release(struct inode *inode, struct file *file);
-static long long sound_lseek(struct file *file, long long offset, int orig);
-static long sound_read(struct inode *inode, struct file *file, char *buf,
- unsigned long count);
-static long sound_write(struct inode *inode, struct file *file,
- const char *buf, unsigned long count);
-static inline int ioctl_return(int *addr, int value)
+static int sound_open(struct inode *inode, struct file *file);
+static int sound_fsync(struct inode *inode, struct file *filp);
+static void sound_release(struct inode *inode, struct file *file);
+static long long sound_lseek(struct inode *inode, struct file *file,
+ long long offset, int orig);
+static long sound_read(struct inode *inode, struct file *file, char *buf,
+ unsigned long count);
+static long sound_write(struct inode *inode, struct file *file,
+ const char *buf, unsigned long count);
+static inline int
+ioctl_return(int *addr, int value)
{
- if (value < 0)
- return(value);
+ if (value < 0)
+ return (value);
- return put_user(value, addr);
+ return put_user(value, addr);
}
-static int unknown_minor_dev(char *fname, int dev);
-static int sound_ioctl(struct inode *inode, struct file *file, u_int cmd,
- u_long arg);
+static int unknown_minor_dev(char *fname, int dev);
+static int sound_ioctl(struct inode *inode, struct file *file, u_int cmd,
+ u_long arg);
/*** Config & Setup **********************************************************/
-void soundcard_init(void);
-void dmasound_setup(char *str, int *ints);
-void sound_setup(char *str, int *ints); /* ++Martin: stub for now */
+void soundcard_init(void);
+void dmasound_setup(char *str, int *ints);
+void sound_setup(char *str, int *ints); /* ++Martin: stub for now */
/*** Translations ************************************************************/
/* ++TeSche: radically changed for new expanding purposes...
- *
+
* These two routines now deal with copying/expanding/translating the samples
* from user space into our buffer at the right frequency. They take care about
* how much data there's actually to read, how much buffer space there is and
@@ -723,849 +755,1014 @@ void sound_setup(char *str, int *ints); /* ++Martin: stub for now */
*/
#ifdef CONFIG_ATARI
-static long ata_ct_law(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
+static long
+ata_ct_law(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
{
- char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8;
- long count, used;
- u_char *p = &frame[*frameUsed];
+ char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8;
+ long count, used;
+ u_char *p = &frame[*frameUsed];
- count = min(userCount, frameLeft);
- if (sound.soft.stereo)
- count &= ~1;
- used = count;
- while (count > 0) {
- u_char data;
- get_user(data, userPtr++);
- *p++ = table[data];
- count--;
- }
- *frameUsed += used;
- return(used);
+ count = min(userCount, frameLeft);
+ if (sound.soft.stereo)
+ count &= ~1;
+ used = count;
+ while (count > 0)
+ {
+ u_char data;
+
+ get_user(data, userPtr++);
+ *p++ = table[data];
+ count--;
+ }
+ *frameUsed += used;
+ return (used);
}
-static long ata_ct_s8(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
+static long
+ata_ct_s8(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
{
- long count, used;
- void *p = &frame[*frameUsed];
+ long count, used;
+ void *p = &frame[*frameUsed];
- count = min(userCount, frameLeft);
- if (sound.soft.stereo)
- count &= ~1;
- used = count;
- copy_from_user(p, userPtr, count);
- *frameUsed += used;
- return(used);
+ count = min(userCount, frameLeft);
+ if (sound.soft.stereo)
+ count &= ~1;
+ used = count;
+ copy_from_user(p, userPtr, count);
+ *frameUsed += used;
+ return (used);
}
-static long ata_ct_u8(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
+static long
+ata_ct_u8(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
{
- long count, used;
+ long count, used;
- if (!sound.soft.stereo) {
- u_char *p = &frame[*frameUsed];
- count = min(userCount, frameLeft);
- used = count;
- while (count > 0) {
- u_char data;
- get_user(data, userPtr++);
- *p++ = data ^ 0x80;
- count--;
- }
- } else {
- u_short *p = (u_short *)&frame[*frameUsed];
- count = min(userCount, frameLeft)>>1;
- used = count*2;
- while (count > 0) {
- u_short data;
- get_user(data, ((u_short *)userPtr)++);
- *p++ = data ^ 0x8080;
- count--;
- }
- }
- *frameUsed += used;
- return(used);
+ if (!sound.soft.stereo)
+ {
+ u_char *p = &frame[*frameUsed];
+
+ count = min(userCount, frameLeft);
+ used = count;
+ while (count > 0)
+ {
+ u_char data;
+
+ get_user(data, userPtr++);
+ *p++ = data ^ 0x80;
+ count--;
+ }
+ } else
+ {
+ u_short *p = (u_short *) & frame[*frameUsed];
+
+ count = min(userCount, frameLeft) >> 1;
+ used = count * 2;
+ while (count > 0)
+ {
+ u_short data;
+
+ get_user(data, ((u_short *) userPtr)++);
+ *p++ = data ^ 0x8080;
+ count--;
+ }
+ }
+ *frameUsed += used;
+ return (used);
}
-static long ata_ct_s16be(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
+static long
+ata_ct_s16be(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
{
- long count, used;
- u_long data;
+ long count, used;
+ u_long data;
- if (!sound.soft.stereo) {
- u_short *p = (u_short *)&frame[*frameUsed];
- count = min(userCount, frameLeft)>>1;
- used = count*2;
- while (count > 0) {
- get_user(data, ((u_short *)userPtr)++);
- *p++ = data;
- *p++ = data;
- count--;
- }
- *frameUsed += used*2;
- } else {
- void *p = (u_short *)&frame[*frameUsed];
- count = min(userCount, frameLeft) & ~3;
- used = count;
- copy_from_user(p, userPtr, count);
- *frameUsed += used;
- }
- return(used);
+ if (!sound.soft.stereo)
+ {
+ u_short *p = (u_short *) & frame[*frameUsed];
+
+ count = min(userCount, frameLeft) >> 1;
+ used = count * 2;
+ while (count > 0)
+ {
+ get_user(data, ((u_short *) userPtr)++);
+ *p++ = data;
+ *p++ = data;
+ count--;
+ }
+ *frameUsed += used * 2;
+ } else
+ {
+ void *p = (u_short *) & frame[*frameUsed];
+
+ count = min(userCount, frameLeft) & ~3;
+ used = count;
+ copy_from_user(p, userPtr, count);
+ *frameUsed += used;
+ }
+ return (used);
}
-static long ata_ct_u16be(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
+static long
+ata_ct_u16be(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
{
- long count, used;
- u_long data;
+ long count, used;
+ u_long data;
- if (!sound.soft.stereo) {
- u_short *p = (u_short *)&frame[*frameUsed];
- count = min(userCount, frameLeft)>>1;
- used = count*2;
- while (count > 0) {
- get_user(data, ((u_short *)userPtr)++);
- data ^= 0x8000;
- *p++ = data;
- *p++ = data;
- count--;
- }
- *frameUsed += used*2;
- } else {
- u_long *p = (u_long *)&frame[*frameUsed];
- count = min(userCount, frameLeft)>>2;
- used = count*4;
- while (count > 0) {
- get_user(data, ((u_int *)userPtr)++);
- *p++ = data ^ 0x80008000;
- count--;
- }
- *frameUsed += used;
- }
- return(used);
+ if (!sound.soft.stereo)
+ {
+ u_short *p = (u_short *) & frame[*frameUsed];
+
+ count = min(userCount, frameLeft) >> 1;
+ used = count * 2;
+ while (count > 0)
+ {
+ get_user(data, ((u_short *) userPtr)++);
+ data ^= 0x8000;
+ *p++ = data;
+ *p++ = data;
+ count--;
+ }
+ *frameUsed += used * 2;
+ } else
+ {
+ u_long *p = (u_long *) & frame[*frameUsed];
+
+ count = min(userCount, frameLeft) >> 2;
+ used = count * 4;
+ while (count > 0)
+ {
+ get_user(data, ((u_int *) userPtr)++);
+ *p++ = data ^ 0x80008000;
+ count--;
+ }
+ *frameUsed += used;
+ }
+ return (used);
}
-static long ata_ct_s16le(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
+static long
+ata_ct_s16le(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
{
- long count, used;
- u_long data;
+ long count, used;
+ u_long data;
- count = frameLeft;
- if (!sound.soft.stereo) {
- u_short *p = (u_short *)&frame[*frameUsed];
- count = min(userCount, frameLeft)>>1;
- used = count*2;
- while (count > 0) {
- get_user(data, ((u_short *)userPtr)++);
- data = le2be16(data);
- *p++ = data;
- *p++ = data;
- count--;
- }
- *frameUsed += used*2;
- } else {
- u_long *p = (u_long *)&frame[*frameUsed];
- count = min(userCount, frameLeft)>>2;
- used = count*4;
- while (count > 0) {
- get_user(data, ((u_int *)userPtr)++);
- data = le2be16dbl(data);
- *p++ = data;
- count--;
- }
+ count = frameLeft;
+ if (!sound.soft.stereo)
+ {
+ u_short *p = (u_short *) & frame[*frameUsed];
+
+ count = min(userCount, frameLeft) >> 1;
+ used = count * 2;
+ while (count > 0)
+ {
+ get_user(data, ((u_short *) userPtr)++);
+ data = le2be16(data);
+ *p++ = data;
+ *p++ = data;
+ count--;
+ }
+ *frameUsed += used * 2;
+ } else
+ {
+ u_long *p = (u_long *) & frame[*frameUsed];
+
+ count = min(userCount, frameLeft) >> 2;
+ used = count * 4;
+ while (count > 0)
+ {
+ get_user(data, ((u_int *) userPtr)++);
+ data = le2be16dbl(data);
+ *p++ = data;
+ count--;
+ }
+ *frameUsed += used;
+ }
+ return (used);
+}
+
+
+static long
+ata_ct_u16le(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
+{
+ long count, used;
+ u_long data;
+
+ count = frameLeft;
+ if (!sound.soft.stereo)
+ {
+ u_short *p = (u_short *) & frame[*frameUsed];
+
+ count = min(userCount, frameLeft) >> 1;
+ used = count * 2;
+ while (count > 0)
+ {
+ get_user(data, ((u_short *) userPtr)++);
+ data = le2be16(data) ^ 0x8000;
+ *p++ = data;
+ *p++ = data;
+ }
+ *frameUsed += used * 2;
+ } else
+ {
+ u_long *p = (u_long *) & frame[*frameUsed];
+
+ count = min(userCount, frameLeft) >> 2;
+ used = count;
+ while (count > 0)
+ {
+ get_user(data, ((u_int *) userPtr)++);
+ data = le2be16dbl(data) ^ 0x80008000;
+ *p++ = data;
+ count--;
+ }
+ *frameUsed += used;
+ }
+ return (used);
+}
+
+
+static long
+ata_ctx_law(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
+{
+ char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8;
+
+ /* this should help gcc to stuff everything into registers */
+ u_long data = sound.data;
+ long bal = sound.bal;
+ long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
+ long used, usedf;
+
+ used = userCount;
+ usedf = frameLeft;
+ if (!sound.soft.stereo)
+ {
+ u_char *p = &frame[*frameUsed];
+
+ while (frameLeft)
+ {
+ u_char c;
+
+ if (bal < 0)
+ {
+ if (!userCount)
+ break;
+ get_user(c, userPtr++);
+ data = table[c];
+ userCount--;
+ bal += hSpeed;
+ }
+ *p++ = data;
+ frameLeft--;
+ bal -= sSpeed;
+ }
+ } else
+ {
+ u_short *p = (u_short *) & frame[*frameUsed];
+
+ while (frameLeft >= 2)
+ {
+ u_char c;
+
+ if (bal < 0)
+ {
+ if (userCount < 2)
+ break;
+ get_user(c, userPtr++);
+ data = table[c] << 8;
+ get_user(c, userPtr++);
+ data |= table[c];
+ userCount -= 2;
+ bal += hSpeed;
+ }
+ *p++ = data;
+ frameLeft -= 2;
+ bal -= sSpeed;
+ }
+ }
+ sound.bal = bal;
+ sound.data = data;
+ used -= userCount;
+ *frameUsed += usedf - frameLeft;
+ return (used);
+}
+
+
+static long
+ata_ctx_s8(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
+{
+ /* this should help gcc to stuff everything into registers */
+ u_long data = sound.data;
+ long bal = sound.bal;
+ long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
+ long used, usedf;
+
+ used = userCount;
+ usedf = frameLeft;
+ if (!sound.soft.stereo)
+ {
+ u_char *p = &frame[*frameUsed];
+
+ while (frameLeft)
+ {
+ if (bal < 0)
+ {
+ if (!userCount)
+ break;
+ get_user(data, userPtr++);
+ userCount--;
+ bal += hSpeed;
+ }
+ *p++ = data;
+ frameLeft--;
+ bal -= sSpeed;
+ }
+ } else
+ {
+ u_short *p = (u_short *) & frame[*frameUsed];
+
+ while (frameLeft >= 2)
+ {
+ if (bal < 0)
+ {
+ if (userCount < 2)
+ break;
+ get_user(data, ((u_short *) userPtr)++);
+ userCount -= 2;
+ bal += hSpeed;
+ }
+ *p++ = data;
+ frameLeft -= 2;
+ bal -= sSpeed;
+ }
+ }
+ sound.bal = bal;
+ sound.data = data;
+ used -= userCount;
+ *frameUsed += usedf - frameLeft;
+ return (used);
+}
+
+
+static long
+ata_ctx_u8(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
+{
+ /* this should help gcc to stuff everything into registers */
+ u_long data = sound.data;
+ long bal = sound.bal;
+ long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
+ long used, usedf;
+
+ used = userCount;
+ usedf = frameLeft;
+ if (!sound.soft.stereo)
+ {
+ u_char *p = &frame[*frameUsed];
+
+ while (frameLeft)
+ {
+ if (bal < 0)
+ {
+ if (!userCount)
+ break;
+ get_user(data, userPtr++);
+ data ^= 0x80;
+ userCount--;
+ bal += hSpeed;
+ }
+ *p++ = data;
+ frameLeft--;
+ bal -= sSpeed;
+ }
+ } else
+ {
+ u_short *p = (u_short *) & frame[*frameUsed];
+
+ while (frameLeft >= 2)
+ {
+ if (bal < 0)
+ {
+ if (userCount < 2)
+ break;
+ get_user(data, ((u_short *) userPtr)++);
+ data ^= 0x8080;
+ userCount -= 2;
+ bal += hSpeed;
+ }
+ *p++ = data;
+ frameLeft -= 2;
+ bal -= sSpeed;
+ }
+ }
+ sound.bal = bal;
+ sound.data = data;
+ used -= userCount;
+ *frameUsed += usedf - frameLeft;
+ return (used);
+}
+
+
+static long
+ata_ctx_s16be(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
+{
+ /* this should help gcc to stuff everything into registers */
+ u_long data = sound.data;
+ long bal = sound.bal;
+ long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
+ long used, usedf;
+
+ used = userCount;
+ usedf = frameLeft;
+ if (!sound.soft.stereo)
+ {
+ u_short *p = (u_short *) & frame[*frameUsed];
+
+ while (frameLeft >= 4)
+ {
+ if (bal < 0)
+ {
+ if (userCount < 2)
+ break;
+ get_user(data, ((u_short *) userPtr)++);
+ userCount -= 2;
+ bal += hSpeed;
+ }
+ *p++ = data;
+ *p++ = data;
+ frameLeft -= 4;
+ bal -= sSpeed;
+ }
+ } else
+ {
+ u_long *p = (u_long *) & frame[*frameUsed];
+
+ while (frameLeft >= 4)
+ {
+ if (bal < 0)
+ {
+ if (userCount < 4)
+ break;
+ get_user(data, ((u_int *) userPtr)++);
+ userCount -= 4;
+ bal += hSpeed;
+ }
+ *p++ = data;
+ frameLeft -= 4;
+ bal -= sSpeed;
+ }
+ }
+ sound.bal = bal;
+ sound.data = data;
+ used -= userCount;
+ *frameUsed += usedf - frameLeft;
+ return (used);
+}
+
+
+static long
+ata_ctx_u16be(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
+{
+ /* this should help gcc to stuff everything into registers */
+ u_long data = sound.data;
+ long bal = sound.bal;
+ long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
+ long used, usedf;
+
+ used = userCount;
+ usedf = frameLeft;
+ if (!sound.soft.stereo)
+ {
+ u_short *p = (u_short *) & frame[*frameUsed];
+
+ while (frameLeft >= 4)
+ {
+ if (bal < 0)
+ {
+ if (userCount < 2)
+ break;
+ get_user(data, ((u_short *) userPtr)++);
+ data ^= 0x8000;
+ userCount -= 2;
+ bal += hSpeed;
+ }
+ *p++ = data;
+ *p++ = data;
+ frameLeft -= 4;
+ bal -= sSpeed;
+ }
+ } else
+ {
+ u_long *p = (u_long *) & frame[*frameUsed];
+
+ while (frameLeft >= 4)
+ {
+ if (bal < 0)
+ {
+ if (userCount < 4)
+ break;
+ get_user(data, ((u_int *) userPtr)++);
+ data ^= 0x80008000;
+ userCount -= 4;
+ bal += hSpeed;
+ }
+ *p++ = data;
+ frameLeft -= 4;
+ bal -= sSpeed;
+ }
+ }
+ sound.bal = bal;
+ sound.data = data;
+ used -= userCount;
+ *frameUsed += usedf - frameLeft;
+ return (used);
+}
+
+
+static long
+ata_ctx_s16le(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
+{
+ /* this should help gcc to stuff everything into registers */
+ u_long data = sound.data;
+ long bal = sound.bal;
+ long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
+ long used, usedf;
+
+ used = userCount;
+ usedf = frameLeft;
+ if (!sound.soft.stereo)
+ {
+ u_short *p = (u_short *) & frame[*frameUsed];
+
+ while (frameLeft >= 4)
+ {
+ if (bal < 0)
+ {
+ if (userCount < 2)
+ break;
+ get_user(data, ((u_short *) userPtr)++);
+ data = le2be16(data);
+ userCount -= 2;
+ bal += hSpeed;
+ }
+ *p++ = data;
+ *p++ = data;
+ frameLeft -= 4;
+ bal -= sSpeed;
+ }
+ } else
+ {
+ u_long *p = (u_long *) & frame[*frameUsed];
+
+ while (frameLeft >= 4)
+ {
+ if (bal < 0)
+ {
+ if (userCount < 4)
+ break;
+ get_user(data, ((u_int *) userPtr)++);
+ data = le2be16dbl(data);
+ userCount -= 4;
+ bal += hSpeed;
+ }
+ *p++ = data;
+ frameLeft -= 4;
+ bal -= sSpeed;
+ }
+ }
+ sound.bal = bal;
+ sound.data = data;
+ used -= userCount;
+ *frameUsed += usedf - frameLeft;
+ return (used);
+}
+
+
+static long
+ata_ctx_u16le(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
+{
+ /* this should help gcc to stuff everything into registers */
+ u_long data = sound.data;
+ long bal = sound.bal;
+ long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
+ long used, usedf;
+
+ used = userCount;
+ usedf = frameLeft;
+ if (!sound.soft.stereo)
+ {
+ u_short *p = (u_short *) & frame[*frameUsed];
+
+ while (frameLeft >= 4)
+ {
+ if (bal < 0)
+ {
+ if (userCount < 2)
+ break;
+ get_user(data, ((u_short *) userPtr)++);
+ data = le2be16(data) ^ 0x8000;
+ userCount -= 2;
+ bal += hSpeed;
+ }
+ *p++ = data;
+ *p++ = data;
+ frameLeft -= 4;
+ bal -= sSpeed;
+ }
+ } else
+ {
+ u_long *p = (u_long *) & frame[*frameUsed];
+
+ while (frameLeft >= 4)
+ {
+ if (bal < 0)
+ {
+ if (userCount < 4)
+ break;
+ get_user(data, ((u_int *) userPtr)++);
+ data = le2be16dbl(data) ^ 0x80008000;
+ userCount -= 4;
+ bal += hSpeed;
+ }
+ *p++ = data;
+ frameLeft -= 4;
+ bal -= sSpeed;
+ }
+ }
+ sound.bal = bal;
+ sound.data = data;
+ used -= userCount;
+ *frameUsed += usedf - frameLeft;
+ return (used);
+}
+#endif /* CONFIG_ATARI */
+
+
+#ifdef CONFIG_AMIGA
+static long
+ami_ct_law(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
+{
+ char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8;
+ long count, used;
+
+ if (!sound.soft.stereo)
+ {
+ u_char *p = &frame[*frameUsed];
+
+ count = min(userCount, frameLeft) & ~1;
+ used = count;
+ while (count > 0)
+ {
+ u_char data;
+
+ get_user(data, userPtr++);
+ *p++ = table[data];
+ count--;
+ }
+ } else
+ {
+ u_char *left = &frame[*frameUsed >> 1];
+ u_char *right = left + sq.block_size_half;
+
+ count = min(userCount, frameLeft) >> 1 & ~1;
+ used = count * 2;
+ while (count > 0)
+ {
+ u_char data;
+
+ get_user(data, userPtr++);
+ *left++ = table[data];
+ get_user(data, userPtr++);
+ *right++ = table[data];
+ count--;
+ }
+ }
*frameUsed += used;
- }
- return(used);
+ return (used);
}
-static long ata_ct_u16le(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
+static long
+ami_ct_s8(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
{
- long count, used;
- u_long data;
+ long count, used;
- count = frameLeft;
- if (!sound.soft.stereo) {
- u_short *p = (u_short *)&frame[*frameUsed];
- count = min(userCount, frameLeft)>>1;
- used = count*2;
- while (count > 0) {
- get_user(data, ((u_short *)userPtr)++);
- data = le2be16(data) ^ 0x8000;
- *p++ = data;
- *p++ = data;
- }
- *frameUsed += used*2;
- } else {
- u_long *p = (u_long *)&frame[*frameUsed];
- count = min(userCount, frameLeft)>>2;
- used = count;
- while (count > 0) {
- get_user(data, ((u_int *)userPtr)++);
- data = le2be16dbl(data) ^ 0x80008000;
- *p++ = data;
- count--;
- }
+ if (!sound.soft.stereo)
+ {
+ void *p = &frame[*frameUsed];
+
+ count = min(userCount, frameLeft) & ~1;
+ used = count;
+ copy_from_user(p, userPtr, count);
+ } else
+ {
+ u_char *left = &frame[*frameUsed >> 1];
+ u_char *right = left + sq.block_size_half;
+
+ count = min(userCount, frameLeft) >> 1 & ~1;
+ used = count * 2;
+ while (count > 0)
+ {
+ get_user(*left++, userPtr++);
+ get_user(*right++, userPtr++);
+ count--;
+ }
+ }
*frameUsed += used;
- }
- return(used);
-}
-
-
-static long ata_ctx_law(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
-{
- char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8;
- /* this should help gcc to stuff everything into registers */
- u_long data = sound.data;
- long bal = sound.bal;
- long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
- long used, usedf;
-
- used = userCount;
- usedf = frameLeft;
- if (!sound.soft.stereo) {
- u_char *p = &frame[*frameUsed];
- while (frameLeft) {
- u_char c;
- if (bal < 0) {
- if (!userCount)
- break;
- get_user(c, userPtr++);
- data = table[c];
- userCount--;
- bal += hSpeed;
- }
- *p++ = data;
- frameLeft--;
- bal -= sSpeed;
- }
- } else {
- u_short *p = (u_short *)&frame[*frameUsed];
- while (frameLeft >= 2) {
- u_char c;
- if (bal < 0) {
- if (userCount < 2)
- break;
- get_user(c, userPtr++);
- data = table[c] << 8;
- get_user(c, userPtr++);
- data |= table[c];
- userCount -= 2;
- bal += hSpeed;
- }
- *p++ = data;
- frameLeft -= 2;
- bal -= sSpeed;
- }
- }
- sound.bal = bal;
- sound.data = data;
- used -= userCount;
- *frameUsed += usedf-frameLeft;
- return(used);
-}
-
-
-static long ata_ctx_s8(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
-{
- /* this should help gcc to stuff everything into registers */
- u_long data = sound.data;
- long bal = sound.bal;
- long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
- long used, usedf;
-
- used = userCount;
- usedf = frameLeft;
- if (!sound.soft.stereo) {
- u_char *p = &frame[*frameUsed];
- while (frameLeft) {
- if (bal < 0) {
- if (!userCount)
- break;
- get_user(data, userPtr++);
- userCount--;
- bal += hSpeed;
- }
- *p++ = data;
- frameLeft--;
- bal -= sSpeed;
- }
- } else {
- u_short *p = (u_short *)&frame[*frameUsed];
- while (frameLeft >= 2) {
- if (bal < 0) {
- if (userCount < 2)
- break;
- get_user(data, ((u_short *)userPtr)++);
- userCount -= 2;
- bal += hSpeed;
- }
- *p++ = data;
- frameLeft -= 2;
- bal -= sSpeed;
- }
- }
- sound.bal = bal;
- sound.data = data;
- used -= userCount;
- *frameUsed += usedf-frameLeft;
- return(used);
-}
-
-
-static long ata_ctx_u8(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
-{
- /* this should help gcc to stuff everything into registers */
- u_long data = sound.data;
- long bal = sound.bal;
- long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
- long used, usedf;
-
- used = userCount;
- usedf = frameLeft;
- if (!sound.soft.stereo) {
- u_char *p = &frame[*frameUsed];
- while (frameLeft) {
- if (bal < 0) {
- if (!userCount)
- break;
- get_user(data, userPtr++);
- data ^= 0x80;
- userCount--;
- bal += hSpeed;
- }
- *p++ = data;
- frameLeft--;
- bal -= sSpeed;
- }
- } else {
- u_short *p = (u_short *)&frame[*frameUsed];
- while (frameLeft >= 2) {
- if (bal < 0) {
- if (userCount < 2)
- break;
- get_user(data, ((u_short *)userPtr)++);
- data ^= 0x8080;
- userCount -= 2;
- bal += hSpeed;
- }
- *p++ = data;
- frameLeft -= 2;
- bal -= sSpeed;
- }
- }
- sound.bal = bal;
- sound.data = data;
- used -= userCount;
- *frameUsed += usedf-frameLeft;
- return(used);
-}
-
-
-static long ata_ctx_s16be(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
-{
- /* this should help gcc to stuff everything into registers */
- u_long data = sound.data;
- long bal = sound.bal;
- long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
- long used, usedf;
-
- used = userCount;
- usedf = frameLeft;
- if (!sound.soft.stereo) {
- u_short *p = (u_short *)&frame[*frameUsed];
- while (frameLeft >= 4) {
- if (bal < 0) {
- if (userCount < 2)
- break;
- get_user(data, ((u_short *)userPtr)++);
- userCount -= 2;
- bal += hSpeed;
- }
- *p++ = data;
- *p++ = data;
- frameLeft -= 4;
- bal -= sSpeed;
- }
- } else {
- u_long *p = (u_long *)&frame[*frameUsed];
- while (frameLeft >= 4) {
- if (bal < 0) {
- if (userCount < 4)
- break;
- get_user(data, ((u_int *)userPtr)++);
- userCount -= 4;
- bal += hSpeed;
- }
- *p++ = data;
- frameLeft -= 4;
- bal -= sSpeed;
- }
- }
- sound.bal = bal;
- sound.data = data;
- used -= userCount;
- *frameUsed += usedf-frameLeft;
- return(used);
-}
-
-
-static long ata_ctx_u16be(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
-{
- /* this should help gcc to stuff everything into registers */
- u_long data = sound.data;
- long bal = sound.bal;
- long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
- long used, usedf;
-
- used = userCount;
- usedf = frameLeft;
- if (!sound.soft.stereo) {
- u_short *p = (u_short *)&frame[*frameUsed];
- while (frameLeft >= 4) {
- if (bal < 0) {
- if (userCount < 2)
- break;
- get_user(data, ((u_short *)userPtr)++);
- data ^= 0x8000;
- userCount -= 2;
- bal += hSpeed;
- }
- *p++ = data;
- *p++ = data;
- frameLeft -= 4;
- bal -= sSpeed;
- }
- } else {
- u_long *p = (u_long *)&frame[*frameUsed];
- while (frameLeft >= 4) {
- if (bal < 0) {
- if (userCount < 4)
- break;
- get_user(data, ((u_int *)userPtr)++);
- data ^= 0x80008000;
- userCount -= 4;
- bal += hSpeed;
- }
- *p++ = data;
- frameLeft -= 4;
- bal -= sSpeed;
- }
- }
- sound.bal = bal;
- sound.data = data;
- used -= userCount;
- *frameUsed += usedf-frameLeft;
- return(used);
-}
-
-
-static long ata_ctx_s16le(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
-{
- /* this should help gcc to stuff everything into registers */
- u_long data = sound.data;
- long bal = sound.bal;
- long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
- long used, usedf;
-
- used = userCount;
- usedf = frameLeft;
- if (!sound.soft.stereo) {
- u_short *p = (u_short *)&frame[*frameUsed];
- while (frameLeft >= 4) {
- if (bal < 0) {
- if (userCount < 2)
- break;
- get_user(data, ((u_short *)userPtr)++);
- data = le2be16(data);
- userCount -= 2;
- bal += hSpeed;
- }
- *p++ = data;
- *p++ = data;
- frameLeft -= 4;
- bal -= sSpeed;
- }
- } else {
- u_long *p = (u_long *)&frame[*frameUsed];
- while (frameLeft >= 4) {
- if (bal < 0) {
- if (userCount < 4)
- break;
- get_user(data, ((u_int *)userPtr)++);
- data = le2be16dbl(data);
- userCount -= 4;
- bal += hSpeed;
- }
- *p++ = data;
- frameLeft -= 4;
- bal -= sSpeed;
- }
- }
- sound.bal = bal;
- sound.data = data;
- used -= userCount;
- *frameUsed += usedf-frameLeft;
- return(used);
-}
-
-
-static long ata_ctx_u16le(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
-{
- /* this should help gcc to stuff everything into registers */
- u_long data = sound.data;
- long bal = sound.bal;
- long hSpeed = sound.hard.speed, sSpeed = sound.soft.speed;
- long used, usedf;
-
- used = userCount;
- usedf = frameLeft;
- if (!sound.soft.stereo) {
- u_short *p = (u_short *)&frame[*frameUsed];
- while (frameLeft >= 4) {
- if (bal < 0) {
- if (userCount < 2)
- break;
- get_user(data, ((u_short *)userPtr)++);
- data = le2be16(data) ^ 0x8000;
- userCount -= 2;
- bal += hSpeed;
- }
- *p++ = data;
- *p++ = data;
- frameLeft -= 4;
- bal -= sSpeed;
- }
- } else {
- u_long *p = (u_long *)&frame[*frameUsed];
- while (frameLeft >= 4) {
- if (bal < 0) {
- if (userCount < 4)
- break;
- get_user(data, ((u_int *)userPtr)++);
- data = le2be16dbl(data) ^ 0x80008000;
- userCount -= 4;
- bal += hSpeed;
- }
- *p++ = data;
- frameLeft -= 4;
- bal -= sSpeed;
- }
- }
- sound.bal = bal;
- sound.data = data;
- used -= userCount;
- *frameUsed += usedf-frameLeft;
- return(used);
+ return (used);
}
-#endif /* CONFIG_ATARI */
-#ifdef CONFIG_AMIGA
-static long ami_ct_law(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
+static long
+ami_ct_u8(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
{
- char *table = sound.soft.format == AFMT_MU_LAW ? ulaw2dma8 : alaw2dma8;
- long count, used;
+ long count, used;
- if (!sound.soft.stereo) {
- u_char *p = &frame[*frameUsed];
- count = min(userCount, frameLeft) & ~1;
- used = count;
- while (count > 0) {
- u_char data;
- get_user(data, userPtr++);
- *p++ = table[data];
- count--;
- }
- } else {
- u_char *left = &frame[*frameUsed>>1];
- u_char *right = left+sq.block_size_half;
- count = min(userCount, frameLeft)>>1 & ~1;
- used = count*2;
- while (count > 0) {
- u_char data;
- get_user(data, userPtr++);
- *left++ = table[data];
- get_user(data, userPtr++);
- *right++ = table[data];
- count--;
- }
- }
- *frameUsed += used;
- return(used);
+ if (!sound.soft.stereo)
+ {
+ char *p = &frame[*frameUsed];
+
+ count = min(userCount, frameLeft) & ~1;
+ used = count;
+ while (count > 0)
+ {
+ u_char data;
+
+ get_user(data, userPtr++);
+ *p++ = data ^ 0x80;
+ count--;
+ }
+ } else
+ {
+ u_char *left = &frame[*frameUsed >> 1];
+ u_char *right = left + sq.block_size_half;
+
+ count = min(userCount, frameLeft) >> 1 & ~1;
+ used = count * 2;
+ while (count > 0)
+ {
+ u_char data;
+
+ get_user(data, userPtr++);
+ *left++ = data ^ 0x80;
+ get_user(data, userPtr++);
+ *right++ = data ^ 0x80;
+ count--;
+ }
+ }
+ *frameUsed += used;
+ return (used);
}
-static long ami_ct_s8(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
+static long
+ami_ct_s16be(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
{
- long count, used;
+ long count, used;
+ u_long data;
- if (!sound.soft.stereo) {
- void *p = &frame[*frameUsed];
- count = min(userCount, frameLeft) & ~1;
- used = count;
- copy_from_user(p, userPtr, count);
- } else {
- u_char *left = &frame[*frameUsed>>1];
- u_char *right = left+sq.block_size_half;
- count = min(userCount, frameLeft)>>1 & ~1;
- used = count*2;
- while (count > 0) {
- get_user(*left++, userPtr++);
- get_user(*right++, userPtr++);
- count--;
- }
- }
- *frameUsed += used;
- return(used);
+ if (!sound.soft.stereo)
+ {
+ u_char *high = &frame[*frameUsed >> 1];
+ u_char *low = high + sq.block_size_half;
+
+ count = min(userCount, frameLeft) >> 1 & ~1;
+ used = count * 2;
+ while (count > 0)
+ {
+ get_user(data, ((u_short *) userPtr)++);
+ *high++ = data >> 8;
+ *low++ = (data >> 2) & 0x3f;
+ count--;
+ }
+ } else
+ {
+ u_char *lefth = &frame[*frameUsed >> 2];
+ u_char *leftl = lefth + sq.block_size_quarter;
+ u_char *righth = lefth + sq.block_size_half;
+ u_char *rightl = righth + sq.block_size_quarter;
+
+ count = min(userCount, frameLeft) >> 2 & ~1;
+ used = count * 4;
+ while (count > 0)
+ {
+ get_user(data, ((u_short *) userPtr)++);
+ *lefth++ = data >> 8;
+ *leftl++ = (data >> 2) & 0x3f;
+ get_user(data, ((u_short *) userPtr)++);
+ *righth++ = data >> 8;
+ *rightl++ = (data >> 2) & 0x3f;
+ count--;
+ }
+ }
+ *frameUsed += used;
+ return (used);
}
-static long ami_ct_u8(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
+static long
+ami_ct_u16be(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
{
- long count, used;
+ long count, used;
+ u_long data;
- if (!sound.soft.stereo) {
- char *p = &frame[*frameUsed];
- count = min(userCount, frameLeft) & ~1;
- used = count;
- while (count > 0) {
- u_char data;
- get_user(data, userPtr++);
- *p++ = data ^ 0x80;
- count--;
- }
- } else {
- u_char *left = &frame[*frameUsed>>1];
- u_char *right = left+sq.block_size_half;
- count = min(userCount, frameLeft)>>1 & ~1;
- used = count*2;
- while (count > 0) {
- u_char data;
- get_user(data, userPtr++);
- *left++ = data ^ 0x80;
- get_user(data, userPtr++);
- *right++ = data ^ 0x80;
- count--;
- }
- }
- *frameUsed += used;
- return(used);
-}
-
-
-static long ami_ct_s16be(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
-{
- long count, used;
- u_long data;
-
- if (!sound.soft.stereo) {
- u_char *high = &frame[*frameUsed>>1];
- u_char *low = high+sq.block_size_half;
- count = min(userCount, frameLeft)>>1 & ~1;
- used = count*2;
- while (count > 0) {
- get_user(data, ((u_short *)userPtr)++);
- *high++ = data>>8;
- *low++ = (data>>2) & 0x3f;
- count--;
- }
- } else {
- u_char *lefth = &frame[*frameUsed>>2];
- u_char *leftl = lefth+sq.block_size_quarter;
- u_char *righth = lefth+sq.block_size_half;
- u_char *rightl = righth+sq.block_size_quarter;
- count = min(userCount, frameLeft)>>2 & ~1;
- used = count*4;
- while (count > 0) {
- get_user(data, ((u_short *)userPtr)++);
- *lefth++ = data>>8;
- *leftl++ = (data>>2) & 0x3f;
- get_user(data, ((u_short *)userPtr)++);
- *righth++ = data>>8;
- *rightl++ = (data>>2) & 0x3f;
- count--;
- }
- }
- *frameUsed += used;
- return(used);
-}
-
-
-static long ami_ct_u16be(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
-{
- long count, used;
- u_long data;
-
- if (!sound.soft.stereo) {
- u_char *high = &frame[*frameUsed>>1];
- u_char *low = high+sq.block_size_half;
- count = min(userCount, frameLeft)>>1 & ~1;
- used = count*2;
- while (count > 0) {
- get_user(data, ((u_short *)userPtr)++);
- data ^= 0x8000;
- *high++ = data>>8;
- *low++ = (data>>2) & 0x3f;
- count--;
- }
- } else {
- u_char *lefth = &frame[*frameUsed>>2];
- u_char *leftl = lefth+sq.block_size_quarter;
- u_char *righth = lefth+sq.block_size_half;
- u_char *rightl = righth+sq.block_size_quarter;
- count = min(userCount, frameLeft)>>2 & ~1;
- used = count*4;
- while (count > 0) {
- get_user(data, ((u_short *)userPtr)++);
- data ^= 0x8000;
- *lefth++ = data>>8;
- *leftl++ = (data>>2) & 0x3f;
- get_user(data, ((u_short *)userPtr)++);
- data ^= 0x8000;
- *righth++ = data>>8;
- *rightl++ = (data>>2) & 0x3f;
- count--;
- }
- }
- *frameUsed += used;
- return(used);
+ if (!sound.soft.stereo)
+ {
+ u_char *high = &frame[*frameUsed >> 1];
+ u_char *low = high + sq.block_size_half;
+
+ count = min(userCount, frameLeft) >> 1 & ~1;
+ used = count * 2;
+ while (count > 0)
+ {
+ get_user(data, ((u_short *) userPtr)++);
+ data ^= 0x8000;
+ *high++ = data >> 8;
+ *low++ = (data >> 2) & 0x3f;
+ count--;
+ }
+ } else
+ {
+ u_char *lefth = &frame[*frameUsed >> 2];
+ u_char *leftl = lefth + sq.block_size_quarter;
+ u_char *righth = lefth + sq.block_size_half;
+ u_char *rightl = righth + sq.block_size_quarter;
+
+ count = min(userCount, frameLeft) >> 2 & ~1;
+ used = count * 4;
+ while (count > 0)
+ {
+ get_user(data, ((u_short *) userPtr)++);
+ data ^= 0x8000;
+ *lefth++ = data >> 8;
+ *leftl++ = (data >> 2) & 0x3f;
+ get_user(data, ((u_short *) userPtr)++);
+ data ^= 0x8000;
+ *righth++ = data >> 8;
+ *rightl++ = (data >> 2) & 0x3f;
+ count--;
+ }
+ }
+ *frameUsed += used;
+ return (used);
}
-static long ami_ct_s16le(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
+static long
+ami_ct_s16le(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
{
- long count, used;
- u_long data;
+ long count, used;
+ u_long data;
- if (!sound.soft.stereo) {
- u_char *high = &frame[*frameUsed>>1];
- u_char *low = high+sq.block_size_half;
- count = min(userCount, frameLeft)>>1 & ~1;
- used = count*2;
- while (count > 0) {
- get_user(data, ((u_short *)userPtr)++);
- data = le2be16(data);
- *high++ = data>>8;
- *low++ = (data>>2) & 0x3f;
- count--;
- }
- } else {
- u_char *lefth = &frame[*frameUsed>>2];
- u_char *leftl = lefth+sq.block_size_quarter;
- u_char *righth = lefth+sq.block_size_half;
- u_char *rightl = righth+sq.block_size_quarter;
- count = min(userCount, frameLeft)>>2 & ~1;
- used = count*4;
- while (count > 0) {
- get_user(data, ((u_short *)userPtr)++);
- data = le2be16(data);
- *lefth++ = data>>8;
- *leftl++ = (data>>2) & 0x3f;
- get_user(data, ((u_short *)userPtr)++);
- data = le2be16(data);
- *righth++ = data>>8;
- *rightl++ = (data>>2) & 0x3f;
- count--;
- }
- }
- *frameUsed += used;
- return(used);
+ if (!sound.soft.stereo)
+ {
+ u_char *high = &frame[*frameUsed >> 1];
+ u_char *low = high + sq.block_size_half;
+
+ count = min(userCount, frameLeft) >> 1 & ~1;
+ used = count * 2;
+ while (count > 0)
+ {
+ get_user(data, ((u_short *) userPtr)++);
+ data = le2be16(data);
+ *high++ = data >> 8;
+ *low++ = (data >> 2) & 0x3f;
+ count--;
+ }
+ } else
+ {
+ u_char *lefth = &frame[*frameUsed >> 2];
+ u_char *leftl = lefth + sq.block_size_quarter;
+ u_char *righth = lefth + sq.block_size_half;
+ u_char *rightl = righth + sq.block_size_quarter;
+
+ count = min(userCount, frameLeft) >> 2 & ~1;
+ used = count * 4;
+ while (count > 0)
+ {
+ get_user(data, ((u_short *) userPtr)++);
+ data = le2be16(data);
+ *lefth++ = data >> 8;
+ *leftl++ = (data >> 2) & 0x3f;
+ get_user(data, ((u_short *) userPtr)++);
+ data = le2be16(data);
+ *righth++ = data >> 8;
+ *rightl++ = (data >> 2) & 0x3f;
+ count--;
+ }
+ }
+ *frameUsed += used;
+ return (used);
}
-static long ami_ct_u16le(const u_char *userPtr, unsigned long userCount,
- u_char frame[], long *frameUsed, long frameLeft)
+static long
+ami_ct_u16le(const u_char * userPtr, unsigned long userCount,
+ u_char frame[], long *frameUsed, long frameLeft)
{
- long count, used;
- u_long data;
+ long count, used;
+ u_long data;
- if (!sound.soft.stereo) {
- u_char *high = &frame[*frameUsed>>1];
- u_char *low = high+sq.block_size_half;
- count = min(userCount, frameLeft)>>1 & ~1;
- used = count*2;
- while (count > 0) {
- get_user(data, ((u_short *)userPtr)++);
- data = le2be16(data) ^ 0x8000;
- *high++ = data>>8;
- *low++ = (data>>2) & 0x3f;
- count--;
- }
- } else {
- u_char *lefth = &frame[*frameUsed>>2];
- u_char *leftl = lefth+sq.block_size_quarter;
- u_char *righth = lefth+sq.block_size_half;
- u_char *rightl = righth+sq.block_size_quarter;
- count = min(userCount, frameLeft)>>2 & ~1;
- used = count*4;
- while (count > 0) {
- get_user(data, ((u_short *)userPtr)++);
- data = le2be16(data) ^ 0x8000;
- *lefth++ = data>>8;
- *leftl++ = (data>>2) & 0x3f;
- get_user(data, ((u_short *)userPtr)++);
- data = le2be16(data) ^ 0x8000;
- *righth++ = data>>8;
- *rightl++ = (data>>2) & 0x3f;
- count--;
- }
- }
- *frameUsed += used;
- return(used);
+ if (!sound.soft.stereo)
+ {
+ u_char *high = &frame[*frameUsed >> 1];
+ u_char *low = high + sq.block_size_half;
+
+ count = min(userCount, frameLeft) >> 1 & ~1;
+ used = count * 2;
+ while (count > 0)
+ {
+ get_user(data, ((u_short *) userPtr)++);
+ data = le2be16(data) ^ 0x8000;
+ *high++ = data >> 8;
+ *low++ = (data >> 2) & 0x3f;
+ count--;
+ }
+ } else
+ {
+ u_char *lefth = &frame[*frameUsed >> 2];
+ u_char *leftl = lefth + sq.block_size_quarter;
+ u_char *righth = lefth + sq.block_size_half;
+ u_char *rightl = righth + sq.block_size_quarter;
+
+ count = min(userCount, frameLeft) >> 2 & ~1;
+ used = count * 4;
+ while (count > 0)
+ {
+ get_user(data, ((u_short *) userPtr)++);
+ data = le2be16(data) ^ 0x8000;
+ *lefth++ = data >> 8;
+ *leftl++ = (data >> 2) & 0x3f;
+ get_user(data, ((u_short *) userPtr)++);
+ data = le2be16(data) ^ 0x8000;
+ *righth++ = data >> 8;
+ *rightl++ = (data >> 2) & 0x3f;
+ count--;
+ }
+ }
+ *frameUsed += used;
+ return (used);
}
-#endif /* CONFIG_AMIGA */
+#endif /* CONFIG_AMIGA */
#ifdef CONFIG_ATARI
-static TRANS transTTNormal = {
- ata_ct_law, ata_ct_law, ata_ct_s8, ata_ct_u8, NULL, NULL, NULL, NULL
+static TRANS transTTNormal =
+{
+ ata_ct_law, ata_ct_law, ata_ct_s8, ata_ct_u8, NULL, NULL, NULL, NULL
};
-static TRANS transTTExpanding = {
- ata_ctx_law, ata_ctx_law, ata_ctx_s8, ata_ctx_u8, NULL, NULL, NULL, NULL
+static TRANS transTTExpanding =
+{
+ ata_ctx_law, ata_ctx_law, ata_ctx_s8, ata_ctx_u8, NULL, NULL, NULL, NULL
};
-static TRANS transFalconNormal = {
- ata_ct_law, ata_ct_law, ata_ct_s8, ata_ct_u8, ata_ct_s16be, ata_ct_u16be,
- ata_ct_s16le, ata_ct_u16le
+static TRANS transFalconNormal =
+{
+ata_ct_law, ata_ct_law, ata_ct_s8, ata_ct_u8, ata_ct_s16be, ata_ct_u16be,
+ ata_ct_s16le, ata_ct_u16le
};
-static TRANS transFalconExpanding = {
- ata_ctx_law, ata_ctx_law, ata_ctx_s8, ata_ctx_u8, ata_ctx_s16be,
- ata_ctx_u16be, ata_ctx_s16le, ata_ctx_u16le
+static TRANS transFalconExpanding =
+{
+ ata_ctx_law, ata_ctx_law, ata_ctx_s8, ata_ctx_u8, ata_ctx_s16be,
+ ata_ctx_u16be, ata_ctx_s16le, ata_ctx_u16le
};
-#endif /* CONFIG_ATARI */
+
+#endif /* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
-static TRANS transAmiga = {
- ami_ct_law, ami_ct_law, ami_ct_s8, ami_ct_u8, ami_ct_s16be, ami_ct_u16be,
- ami_ct_s16le, ami_ct_u16le
+static TRANS transAmiga =
+{
+ami_ct_law, ami_ct_law, ami_ct_s8, ami_ct_u8, ami_ct_s16be, ami_ct_u16be,
+ ami_ct_s16le, ami_ct_u16le
};
-#endif /* CONFIG_AMIGA */
+
+#endif /* CONFIG_AMIGA */
/*** Low level stuff *********************************************************/
@@ -1577,60 +1774,68 @@ static TRANS transAmiga = {
* Atari (TT/Falcon)
*/
-static void *AtaAlloc(unsigned int size, int flags)
-{
- int order;
- unsigned int a_size;
- order = 0;
- a_size = PAGE_SIZE;
- while (a_size < size) {
- order++;
- a_size <<= 1;
- }
- return (void *) __get_dma_pages(flags, order);
-}
-
-static void AtaFree(void *obj, unsigned int size)
-{
- int order;
- unsigned int a_size;
- order = 0;
- a_size = PAGE_SIZE;
- while (a_size < size) {
- order++;
- a_size <<= 1;
- }
- free_pages ((unsigned long) obj, order);
-}
-
-static int AtaIrqInit(void)
-{
- /* Set up timer A. Timer A
- will receive a signal upon end of playing from the sound
- hardware. Furthermore Timer A is able to count events
- and will cause an interrupt after a programmed number
- of events. So all we need to keep the music playing is
- to provide the sound hardware with new data upon
- an interrupt from timer A. */
- mfp.tim_ct_a = 0; /* ++roman: Stop timer before programming! */
- mfp.tim_dt_a = 1; /* Cause interrupt after first event. */
- mfp.tim_ct_a = 8; /* Turn on event counting. */
- /* Register interrupt handler. */
- request_irq(IRQ_MFP_TIMA, ata_sq_interrupt, IRQ_TYPE_SLOW,
- "DMA sound", ata_sq_interrupt);
- mfp.int_en_a |= 0x20; /* Turn interrupt on. */
- mfp.int_mk_a |= 0x20;
- return(1);
+static void *
+AtaAlloc(unsigned int size, int flags)
+{
+ int order;
+ unsigned int a_size;
+
+ order = 0;
+ a_size = PAGE_SIZE;
+ while (a_size < size)
+ {
+ order++;
+ a_size <<= 1;
+ }
+ return (void *) __get_dma_pages(flags, order);
+}
+
+static void
+AtaFree(void *obj, unsigned int size)
+{
+ int order;
+ unsigned int a_size;
+
+ order = 0;
+ a_size = PAGE_SIZE;
+ while (a_size < size)
+ {
+ order++;
+ a_size <<= 1;
+ }
+ free_pages((unsigned long) obj, order);
+}
+
+static int
+AtaIrqInit(void)
+{
+ /* Set up timer A. Timer A
+ will receive a signal upon end of playing from the sound
+ hardware. Furthermore Timer A is able to count events
+ and will cause an interrupt after a programmed number
+ of events. So all we need to keep the music playing is
+ to provide the sound hardware with new data upon
+ an interrupt from timer A. */
+ mfp.tim_ct_a = 0; /* ++roman: Stop timer before programming! */
+ mfp.tim_dt_a = 1; /* Cause interrupt after first event. */
+ mfp.tim_ct_a = 8; /* Turn on event counting. */
+ /* Register interrupt handler. */
+ request_irq(IRQ_MFP_TIMA, ata_sq_interrupt, IRQ_TYPE_SLOW,
+ "DMA sound", ata_sq_interrupt);
+ mfp.int_en_a |= 0x20; /* Turn interrupt on. */
+ mfp.int_mk_a |= 0x20;
+ return (1);
}
#ifdef MODULE
-static void AtaIrqCleanUp(void)
+static void
+AtaIrqCleanUp(void)
{
- mfp.tim_ct_a = 0; /* stop timer */
- mfp.int_en_a &= ~0x20; /* turn interrupt off */
- free_irq(IRQ_MFP_TIMA, ata_sq_interrupt);
+ mfp.tim_ct_a = 0; /* stop timer */
+ mfp.int_en_a &= ~0x20; /* turn interrupt off */
+ free_irq(IRQ_MFP_TIMA, ata_sq_interrupt);
}
-#endif /* MODULE */
+#endif /* MODULE */
#define TONE_VOXWARE_TO_DB(v) \
@@ -1638,19 +1843,21 @@ static void AtaIrqCleanUp(void)
#define TONE_DB_TO_VOXWARE(v) (((v) * 25 + ((v) > 0 ? 5 : -5)) / 6 + 50)
-static int AtaSetBass(int bass)
+static int
+AtaSetBass(int bass)
{
- sound.bass = TONE_VOXWARE_TO_DB(bass);
- atari_microwire_cmd(MW_LM1992_BASS(sound.bass));
- return(TONE_DB_TO_VOXWARE(sound.bass));
+ sound.bass = TONE_VOXWARE_TO_DB(bass);
+ atari_microwire_cmd(MW_LM1992_BASS(sound.bass));
+ return (TONE_DB_TO_VOXWARE(sound.bass));
}
-static int AtaSetTreble(int treble)
+static int
+AtaSetTreble(int treble)
{
- sound.treble = TONE_VOXWARE_TO_DB(treble);
- atari_microwire_cmd(MW_LM1992_TREBLE(sound.treble));
- return(TONE_DB_TO_VOXWARE(sound.treble));
+ sound.treble = TONE_VOXWARE_TO_DB(treble);
+ atari_microwire_cmd(MW_LM1992_TREBLE(sound.treble));
+ return (TONE_DB_TO_VOXWARE(sound.treble));
}
@@ -1660,88 +1867,100 @@ static int AtaSetTreble(int treble)
*/
-static void TTSilence(void)
+static void
+TTSilence(void)
{
- tt_dmasnd.ctrl = DMASND_CTRL_OFF;
- atari_microwire_cmd(MW_LM1992_PSG_HIGH); /* mix in PSG signal 1:1 */
+ tt_dmasnd.ctrl = DMASND_CTRL_OFF;
+ atari_microwire_cmd(MW_LM1992_PSG_HIGH); /* mix in PSG signal 1:1 */
}
-static void TTInit(void)
+static void
+TTInit(void)
{
- int mode, i, idx;
- const int freq[4] = {50066, 25033, 12517, 6258};
+ int mode, i, idx;
+ const int freq[4] =
+ {50066, 25033, 12517, 6258};
- /* search a frequency that fits into the allowed error range */
+ /* search a frequency that fits into the allowed error range */
- idx = -1;
- for (i = 0; i < arraysize(freq); i++)
- /* this isn't as much useful for a TT than for a Falcon, but
- * then it doesn't hurt very much to implement it for a TT too.
- */
- if ((100 * abs(sound.soft.speed - freq[i]) / freq[i]) < catchRadius)
- idx = i;
- if (idx > -1) {
- sound.soft.speed = freq[idx];
- sound.trans = &transTTNormal;
- } else
- sound.trans = &transTTExpanding;
-
- TTSilence();
- sound.hard = sound.soft;
-
- if (sound.hard.speed > 50066) {
- /* we would need to squeeze the sound, but we won't do that */
- sound.hard.speed = 50066;
- mode = DMASND_MODE_50KHZ;
- sound.trans = &transTTNormal;
- } else if (sound.hard.speed > 25033) {
- sound.hard.speed = 50066;
- mode = DMASND_MODE_50KHZ;
- } else if (sound.hard.speed > 12517) {
- sound.hard.speed = 25033;
- mode = DMASND_MODE_25KHZ;
- } else if (sound.hard.speed > 6258) {
- sound.hard.speed = 12517;
- mode = DMASND_MODE_12KHZ;
- } else {
- sound.hard.speed = 6258;
- mode = DMASND_MODE_6KHZ;
- }
-
- tt_dmasnd.mode = (sound.hard.stereo ?
- DMASND_MODE_STEREO : DMASND_MODE_MONO) |
- DMASND_MODE_8BIT | mode;
-
- sound.bal = -sound.soft.speed;
-}
-
-
-static int TTSetFormat(int format)
-{
- /* TT sound DMA supports only 8bit modes */
-
- switch (format) {
- case AFMT_QUERY:
- return(sound.soft.format);
- case AFMT_MU_LAW:
- case AFMT_A_LAW:
- case AFMT_S8:
- case AFMT_U8:
- break;
- default:
- format = AFMT_S8;
- }
-
- sound.soft.format = format;
- sound.soft.size = 8;
- if (sound.minDev == SND_DEV_DSP) {
- sound.dsp.format = format;
- sound.dsp.size = 8;
- }
- TTInit();
+ idx = -1;
+ for (i = 0; i < arraysize(freq); i++)
+ /* this isn't as much useful for a TT than for a Falcon, but
+ * then it doesn't hurt very much to implement it for a TT too.
+ */
+ if ((100 * abs(sound.soft.speed - freq[i]) / freq[i]) < catchRadius)
+ idx = i;
+ if (idx > -1)
+ {
+ sound.soft.speed = freq[idx];
+ sound.trans = &transTTNormal;
+ } else
+ sound.trans = &transTTExpanding;
+
+ TTSilence();
+ sound.hard = sound.soft;
+
+ if (sound.hard.speed > 50066)
+ {
+ /* we would need to squeeze the sound, but we won't do that */
+ sound.hard.speed = 50066;
+ mode = DMASND_MODE_50KHZ;
+ sound.trans = &transTTNormal;
+ } else if (sound.hard.speed > 25033)
+ {
+ sound.hard.speed = 50066;
+ mode = DMASND_MODE_50KHZ;
+ } else if (sound.hard.speed > 12517)
+ {
+ sound.hard.speed = 25033;
+ mode = DMASND_MODE_25KHZ;
+ } else if (sound.hard.speed > 6258)
+ {
+ sound.hard.speed = 12517;
+ mode = DMASND_MODE_12KHZ;
+ } else
+ {
+ sound.hard.speed = 6258;
+ mode = DMASND_MODE_6KHZ;
+ }
+
+ tt_dmasnd.mode = (sound.hard.stereo ?
+ DMASND_MODE_STEREO : DMASND_MODE_MONO) |
+ DMASND_MODE_8BIT | mode;
- return(format);
+ sound.bal = -sound.soft.speed;
+}
+
+
+static int
+TTSetFormat(int format)
+{
+ /* TT sound DMA supports only 8bit modes */
+
+ switch (format)
+ {
+ case AFMT_QUERY:
+ return (sound.soft.format);
+ case AFMT_MU_LAW:
+ case AFMT_A_LAW:
+ case AFMT_S8:
+ case AFMT_U8:
+ break;
+ default:
+ format = AFMT_S8;
+ }
+
+ sound.soft.format = format;
+ sound.soft.size = 8;
+ if (sound.minDev == SND_DEV_DSP)
+ {
+ sound.dsp.format = format;
+ sound.dsp.size = 8;
+ }
+ TTInit();
+
+ return (format);
}
@@ -1750,14 +1969,15 @@ static int TTSetFormat(int format)
#define VOLUME_DB_TO_VOXWARE(v) ((((v) + 40) * 5 + 1) / 2)
-static int TTSetVolume(int volume)
+static int
+TTSetVolume(int volume)
{
- sound.volume_left = VOLUME_VOXWARE_TO_DB(volume & 0xff);
- atari_microwire_cmd(MW_LM1992_BALLEFT(sound.volume_left));
- sound.volume_right = VOLUME_VOXWARE_TO_DB((volume & 0xff00) >> 8);
- atari_microwire_cmd(MW_LM1992_BALRIGHT(sound.volume_right));
- return(VOLUME_DB_TO_VOXWARE(sound.volume_left) |
- (VOLUME_DB_TO_VOXWARE(sound.volume_right) << 8));
+ sound.volume_left = VOLUME_VOXWARE_TO_DB(volume & 0xff);
+ atari_microwire_cmd(MW_LM1992_BALLEFT(sound.volume_left));
+ sound.volume_right = VOLUME_VOXWARE_TO_DB((volume & 0xff00) >> 8);
+ atari_microwire_cmd(MW_LM1992_BALRIGHT(sound.volume_right));
+ return (VOLUME_DB_TO_VOXWARE(sound.volume_left) |
+ (VOLUME_DB_TO_VOXWARE(sound.volume_right) << 8));
}
@@ -1767,135 +1987,151 @@ static int TTSetVolume(int volume)
*/
-static void FalconSilence(void)
+static void
+FalconSilence(void)
{
- /* stop playback, set sample rate 50kHz for PSG sound */
- tt_dmasnd.ctrl = DMASND_CTRL_OFF;
- tt_dmasnd.mode = DMASND_MODE_50KHZ | DMASND_MODE_STEREO | DMASND_MODE_8BIT;
- tt_dmasnd.int_div = 0; /* STE compatible divider */
- tt_dmasnd.int_ctrl = 0x0;
- tt_dmasnd.cbar_src = 0x0000; /* no matrix inputs */
- tt_dmasnd.cbar_dst = 0x0000; /* no matrix outputs */
- tt_dmasnd.dac_src = 1; /* connect ADC to DAC, disconnect matrix */
- tt_dmasnd.adc_src = 3; /* ADC Input = PSG */
+ /* stop playback, set sample rate 50kHz for PSG sound */
+ tt_dmasnd.ctrl = DMASND_CTRL_OFF;
+ tt_dmasnd.mode = DMASND_MODE_50KHZ | DMASND_MODE_STEREO | DMASND_MODE_8BIT;
+ tt_dmasnd.int_div = 0; /* STE compatible divider */
+ tt_dmasnd.int_ctrl = 0x0;
+ tt_dmasnd.cbar_src = 0x0000; /* no matrix inputs */
+ tt_dmasnd.cbar_dst = 0x0000; /* no matrix outputs */
+ tt_dmasnd.dac_src = 1; /* connect ADC to DAC, disconnect matrix */
+ tt_dmasnd.adc_src = 3; /* ADC Input = PSG */
}
-static void FalconInit(void)
+static void
+FalconInit(void)
{
- int divider, i, idx;
- const int freq[8] = {49170, 32780, 24585, 19668, 16390, 12292, 9834, 8195};
+ int divider, i, idx;
+ const int freq[8] =
+ {49170, 32780, 24585, 19668, 16390, 12292, 9834, 8195};
- /* search a frequency that fits into the allowed error range */
+ /* search a frequency that fits into the allowed error range */
- idx = -1;
- for (i = 0; i < arraysize(freq); i++)
- /* if we will tolerate 3% error 8000Hz->8195Hz (2.38%) would
- * be playable without expanding, but that now a kernel runtime
- * option
- */
- if ((100 * abs(sound.soft.speed - freq[i]) / freq[i]) < catchRadius)
- idx = i;
- if (idx > -1) {
- sound.soft.speed = freq[idx];
- sound.trans = &transFalconNormal;
- } else
- sound.trans = &transFalconExpanding;
-
- FalconSilence();
- sound.hard = sound.soft;
-
- if (sound.hard.size == 16) {
- /* the Falcon can play 16bit samples only in stereo */
- sound.hard.stereo = 1;
- }
-
- if (sound.hard.speed > 49170) {
- /* we would need to squeeze the sound, but we won't do that */
- sound.hard.speed = 49170;
- divider = 1;
- sound.trans = &transFalconNormal;
- } else if (sound.hard.speed > 32780) {
- sound.hard.speed = 49170;
- divider = 1;
- } else if (sound.hard.speed > 24585) {
- sound.hard.speed = 32780;
- divider = 2;
- } else if (sound.hard.speed > 19668) {
- sound.hard.speed = 24585;
- divider = 3;
- } else if (sound.hard.speed > 16390) {
- sound.hard.speed = 19668;
- divider = 4;
- } else if (sound.hard.speed > 12292) {
- sound.hard.speed = 16390;
- divider = 5;
- } else if (sound.hard.speed > 9834) {
- sound.hard.speed = 12292;
- divider = 7;
- } else if (sound.hard.speed > 8195) {
- sound.hard.speed = 9834;
- divider = 9;
- } else {
- sound.hard.speed = 8195;
- divider = 11;
- }
- tt_dmasnd.int_div = divider;
-
- /* Setup Falcon sound DMA for playback */
- tt_dmasnd.int_ctrl = 0x4; /* Timer A int at play end */
- tt_dmasnd.track_select = 0x0; /* play 1 track, track 1 */
- tt_dmasnd.cbar_src = 0x0001; /* DMA(25MHz) --> DAC */
- tt_dmasnd.cbar_dst = 0x0000;
- tt_dmasnd.rec_track_select = 0;
- tt_dmasnd.dac_src = 2; /* connect matrix to DAC */
- tt_dmasnd.adc_src = 0; /* ADC Input = Mic */
-
- tt_dmasnd.mode = (sound.hard.stereo ?
- DMASND_MODE_STEREO : DMASND_MODE_MONO) |
- ((sound.hard.size == 8) ?
- DMASND_MODE_8BIT : DMASND_MODE_16BIT) |
- DMASND_MODE_6KHZ;
-
- sound.bal = -sound.soft.speed;
-}
-
-
-static int FalconSetFormat(int format)
-{
- int size;
- /* Falcon sound DMA supports 8bit and 16bit modes */
-
- switch (format) {
- case AFMT_QUERY:
- return(sound.soft.format);
- case AFMT_MU_LAW:
- case AFMT_A_LAW:
- case AFMT_U8:
- case AFMT_S8:
- size = 8;
- break;
- case AFMT_S16_BE:
- case AFMT_U16_BE:
- case AFMT_S16_LE:
- case AFMT_U16_LE:
- size = 16;
- break;
- default: /* :-) */
- size = 8;
- format = AFMT_S8;
- }
-
- sound.soft.format = format;
- sound.soft.size = size;
- if (sound.minDev == SND_DEV_DSP) {
- sound.dsp.format = format;
- sound.dsp.size = sound.soft.size;
- }
-
- FalconInit();
-
- return(format);
+ idx = -1;
+ for (i = 0; i < arraysize(freq); i++)
+ /* if we will tolerate 3% error 8000Hz->8195Hz (2.38%) would
+ * be playable without expanding, but that now a kernel runtime
+ * option
+ */
+ if ((100 * abs(sound.soft.speed - freq[i]) / freq[i]) < catchRadius)
+ idx = i;
+ if (idx > -1)
+ {
+ sound.soft.speed = freq[idx];
+ sound.trans = &transFalconNormal;
+ } else
+ sound.trans = &transFalconExpanding;
+
+ FalconSilence();
+ sound.hard = sound.soft;
+
+ if (sound.hard.size == 16)
+ {
+ /* the Falcon can play 16bit samples only in stereo */
+ sound.hard.stereo = 1;
+ }
+ if (sound.hard.speed > 49170)
+ {
+ /* we would need to squeeze the sound, but we won't do that */
+ sound.hard.speed = 49170;
+ divider = 1;
+ sound.trans = &transFalconNormal;
+ } else if (sound.hard.speed > 32780)
+ {
+ sound.hard.speed = 49170;
+ divider = 1;
+ } else if (sound.hard.speed > 24585)
+ {
+ sound.hard.speed = 32780;
+ divider = 2;
+ } else if (sound.hard.speed > 19668)
+ {
+ sound.hard.speed = 24585;
+ divider = 3;
+ } else if (sound.hard.speed > 16390)
+ {
+ sound.hard.speed = 19668;
+ divider = 4;
+ } else if (sound.hard.speed > 12292)
+ {
+ sound.hard.speed = 16390;
+ divider = 5;
+ } else if (sound.hard.speed > 9834)
+ {
+ sound.hard.speed = 12292;
+ divider = 7;
+ } else if (sound.hard.speed > 8195)
+ {
+ sound.hard.speed = 9834;
+ divider = 9;
+ } else
+ {
+ sound.hard.speed = 8195;
+ divider = 11;
+ }
+ tt_dmasnd.int_div = divider;
+
+ /* Setup Falcon sound DMA for playback */
+ tt_dmasnd.int_ctrl = 0x4; /* Timer A int at play end */
+ tt_dmasnd.track_select = 0x0; /* play 1 track, track 1 */
+ tt_dmasnd.cbar_src = 0x0001; /* DMA(25MHz) --> DAC */
+ tt_dmasnd.cbar_dst = 0x0000;
+ tt_dmasnd.rec_track_select = 0;
+ tt_dmasnd.dac_src = 2; /* connect matrix to DAC */
+ tt_dmasnd.adc_src = 0; /* ADC Input = Mic */
+
+ tt_dmasnd.mode = (sound.hard.stereo ?
+ DMASND_MODE_STEREO : DMASND_MODE_MONO) |
+ ((sound.hard.size == 8) ?
+ DMASND_MODE_8BIT : DMASND_MODE_16BIT) |
+ DMASND_MODE_6KHZ;
+
+ sound.bal = -sound.soft.speed;
+}
+
+
+static int
+FalconSetFormat(int format)
+{
+ int size;
+
+ /* Falcon sound DMA supports 8bit and 16bit modes */
+
+ switch (format)
+ {
+ case AFMT_QUERY:
+ return (sound.soft.format);
+ case AFMT_MU_LAW:
+ case AFMT_A_LAW:
+ case AFMT_U8:
+ case AFMT_S8:
+ size = 8;
+ break;
+ case AFMT_S16_BE:
+ case AFMT_U16_BE:
+ case AFMT_S16_LE:
+ case AFMT_U16_LE:
+ size = 16;
+ break;
+ default: /* :-) */
+ size = 8;
+ format = AFMT_S8;
+ }
+
+ sound.soft.format = format;
+ sound.soft.size = size;
+ if (sound.minDev == SND_DEV_DSP)
+ {
+ sound.dsp.format = format;
+ sound.dsp.size = sound.soft.size;
+ }
+ FalconInit();
+
+ return (format);
}
@@ -1907,168 +2143,181 @@ static int FalconSetFormat(int format)
#define VOLUME_ATT_TO_VOXWARE(v) (100 - (v) * 20 / 3)
-static int FalconSetVolume(int volume)
+static int
+FalconSetVolume(int volume)
{
- sound.volume_left = VOLUME_VOXWARE_TO_ATT(volume & 0xff);
- sound.volume_right = VOLUME_VOXWARE_TO_ATT((volume & 0xff00) >> 8);
- tt_dmasnd.output_atten = sound.volume_left << 8 | sound.volume_right << 4;
- return(VOLUME_ATT_TO_VOXWARE(sound.volume_left) |
- VOLUME_ATT_TO_VOXWARE(sound.volume_right) << 8);
+ sound.volume_left = VOLUME_VOXWARE_TO_ATT(volume & 0xff);
+ sound.volume_right = VOLUME_VOXWARE_TO_ATT((volume & 0xff00) >> 8);
+ tt_dmasnd.output_atten = sound.volume_left << 8 | sound.volume_right << 4;
+ return (VOLUME_ATT_TO_VOXWARE(sound.volume_left) |
+ VOLUME_ATT_TO_VOXWARE(sound.volume_right) << 8);
}
-static void ata_sq_play_next_frame(int index)
+static void
+ata_sq_play_next_frame(int index)
{
- char *start, *end;
+ char *start, *end;
- /* used by AtaPlay() if all doubts whether there really is something
- * to be played are already wiped out.
- */
- start = sq_block_address(sq.front);
- end = start+((sq.count == index) ? sq.rear_size : sq.block_size);
- /* end might not be a legal virtual address. */
- DMASNDSetEnd(VTOP(end - 1) + 1);
- DMASNDSetBase(VTOP(start));
- /* Since only an even number of samples per frame can
- be played, we might lose one byte here. (TO DO) */
- sq.front = (sq.front+1) % sq.max_count;
- sq.playing++;
- tt_dmasnd.ctrl = DMASND_CTRL_ON | DMASND_CTRL_REPEAT;
-}
-
-
-static void AtaPlay(void)
-{
- /* ++TeSche: Note that sq.playing is no longer just a flag but holds
- * the number of frames the DMA is currently programmed for instead,
- * may be 0, 1 (currently being played) or 2 (pre-programmed).
- *
- * Changes done to sq.count and sq.playing are a bit more subtle again
- * so now I must admit I also prefer disabling the irq here rather
- * than considering all possible situations. But the point is that
- * disabling the irq doesn't have any bad influence on this version of
- * the driver as we benefit from having pre-programmed the DMA
- * wherever possible: There's no need to reload the DMA at the exact
- * time of an interrupt but only at some time while the pre-programmed
- * frame is playing!
- */
- atari_disable_irq(IRQ_MFP_TIMA);
-
- if (sq.playing == 2 || /* DMA is 'full' */
- sq.count <= 0) { /* nothing to do */
- atari_enable_irq(IRQ_MFP_TIMA);
- return;
- }
-
- if (sq.playing == 0) {
- /* looks like there's nothing 'in' the DMA yet, so try
- * to put two frames into it (at least one is available).
+ /* used by AtaPlay() if all doubts whether there really is something
+ * to be played are already wiped out.
*/
- if (sq.count == 1 && sq.rear_size < sq.block_size && !sq.syncing) {
- /* hmmm, the only existing frame is not
- * yet filled and we're not syncing?
- */
- atari_enable_irq(IRQ_MFP_TIMA);
- return;
- }
- ata_sq_play_next_frame(1);
- if (sq.count == 1) {
- /* no more frames */
- atari_enable_irq(IRQ_MFP_TIMA);
- return;
- }
- if (sq.count == 2 && sq.rear_size < sq.block_size && !sq.syncing) {
- /* hmmm, there were two frames, but the second
- * one is not yet filled and we're not syncing?
- */
- atari_enable_irq(IRQ_MFP_TIMA);
- return;
- }
- ata_sq_play_next_frame(2);
- } else {
- /* there's already a frame being played so we may only stuff
- * one new into the DMA, but even if this may be the last
- * frame existing the previous one is still on sq.count.
+ start = sq_block_address(sq.front);
+ end = start + ((sq.count == index) ? sq.rear_size : sq.block_size);
+ /* end might not be a legal virtual address. */
+ DMASNDSetEnd(VTOP(end - 1) + 1);
+ DMASNDSetBase(VTOP(start));
+ /* Since only an even number of samples per frame can
+ be played, we might lose one byte here. (TO DO) */
+ sq.front = (sq.front + 1) % sq.max_count;
+ sq.playing++;
+ tt_dmasnd.ctrl = DMASND_CTRL_ON | DMASND_CTRL_REPEAT;
+}
+
+
+static void
+AtaPlay(void)
+{
+ /* ++TeSche: Note that sq.playing is no longer just a flag but holds
+ * the number of frames the DMA is currently programmed for instead,
+ * may be 0, 1 (currently being played) or 2 (pre-programmed).
+ *
+ * Changes done to sq.count and sq.playing are a bit more subtle again
+ * so now I must admit I also prefer disabling the irq here rather
+ * than considering all possible situations. But the point is that
+ * disabling the irq doesn't have any bad influence on this version of
+ * the driver as we benefit from having pre-programmed the DMA
+ * wherever possible: There's no need to reload the DMA at the exact
+ * time of an interrupt but only at some time while the pre-programmed
+ * frame is playing!
*/
- if (sq.count == 2 && sq.rear_size < sq.block_size && !sq.syncing) {
- /* hmmm, the only existing frame is not
- * yet filled and we're not syncing?
- */
- atari_enable_irq(IRQ_MFP_TIMA);
- return;
- }
- ata_sq_play_next_frame(2);
- }
- atari_enable_irq(IRQ_MFP_TIMA);
+ atari_disable_irq(IRQ_MFP_TIMA);
+
+ if (sq.playing == 2 || /* DMA is 'full' */
+ sq.count <= 0)
+ { /* nothing to do */
+ atari_enable_irq(IRQ_MFP_TIMA);
+ return;
+ }
+ if (sq.playing == 0)
+ {
+ /* looks like there's nothing 'in' the DMA yet, so try
+ * to put two frames into it (at least one is available).
+ */
+ if (sq.count == 1 && sq.rear_size < sq.block_size && !sq.syncing)
+ {
+ /* hmmm, the only existing frame is not
+ * yet filled and we're not syncing?
+ */
+ atari_enable_irq(IRQ_MFP_TIMA);
+ return;
+ }
+ ata_sq_play_next_frame(1);
+ if (sq.count == 1)
+ {
+ /* no more frames */
+ atari_enable_irq(IRQ_MFP_TIMA);
+ return;
+ }
+ if (sq.count == 2 && sq.rear_size < sq.block_size && !sq.syncing)
+ {
+ /* hmmm, there were two frames, but the second
+ * one is not yet filled and we're not syncing?
+ */
+ atari_enable_irq(IRQ_MFP_TIMA);
+ return;
+ }
+ ata_sq_play_next_frame(2);
+ } else
+ {
+ /* there's already a frame being played so we may only stuff
+ * one new into the DMA, but even if this may be the last
+ * frame existing the previous one is still on sq.count.
+ */
+ if (sq.count == 2 && sq.rear_size < sq.block_size && !sq.syncing)
+ {
+ /* hmmm, the only existing frame is not
+ * yet filled and we're not syncing?
+ */
+ atari_enable_irq(IRQ_MFP_TIMA);
+ return;
+ }
+ ata_sq_play_next_frame(2);
+ }
+ atari_enable_irq(IRQ_MFP_TIMA);
}
-static void ata_sq_interrupt(int irq, void *dummy, struct pt_regs *fp)
+static void
+ata_sq_interrupt(int irq, void *dummy, struct pt_regs *fp)
{
#if 0
- /* ++TeSche: if you should want to test this... */
- static int cnt = 0;
- if (sq.playing == 2)
- if (++cnt == 10) {
- /* simulate losing an interrupt */
- cnt = 0;
- return;
- }
+ /* ++TeSche: if you should want to test this... */
+ static int cnt = 0;
+
+ if (sq.playing == 2)
+ if (++cnt == 10)
+ {
+ /* simulate losing an interrupt */
+ cnt = 0;
+ return;
+ }
#endif
- if (sq.ignore_int && (sound.mach.type == DMASND_FALCON)) {
- /* ++TeSche: Falcon only: ignore first irq because it comes
- * immediately after starting a frame. after that, irqs come
- * (almost) like on the TT.
- */
- sq.ignore_int = 0;
- return;
- }
-
- if (!sq.playing) {
- /* playing was interrupted and sq_reset() has already cleared
- * the sq variables, so better don't do anything here.
+ if (sq.ignore_int && (sound.mach.type == DMASND_FALCON))
+ {
+ /* ++TeSche: Falcon only: ignore first irq because it comes
+ * immediately after starting a frame. after that, irqs come
+ * (almost) like on the TT.
+ */
+ sq.ignore_int = 0;
+ return;
+ }
+ if (!sq.playing)
+ {
+ /* playing was interrupted and sq_reset() has already cleared
+ * the sq variables, so better don't do anything here.
+ */
+ WAKE_UP(sq.sync_queue);
+ return;
+ }
+ /* Probably ;) one frame is finished. Well, in fact it may be that a
+ * pre-programmed one is also finished because there has been a long
+ * delay in interrupt delivery and we've completely lost one, but
+ * there's no way to detect such a situation. In such a case the last
+ * frame will be played more than once and the situation will recover
+ * as soon as the irq gets through.
*/
- WAKE_UP(sq.sync_queue);
- return;
- }
-
- /* Probably ;) one frame is finished. Well, in fact it may be that a
- * pre-programmed one is also finished because there has been a long
- * delay in interrupt delivery and we've completely lost one, but
- * there's no way to detect such a situation. In such a case the last
- * frame will be played more than once and the situation will recover
- * as soon as the irq gets through.
- */
- sq.count--;
- sq.playing--;
-
- if (!sq.playing) {
- tt_dmasnd.ctrl = DMASND_CTRL_OFF;
- sq.ignore_int = 1;
- }
+ sq.count--;
+ sq.playing--;
- WAKE_UP(sq.write_queue);
+ if (!sq.playing)
+ {
+ tt_dmasnd.ctrl = DMASND_CTRL_OFF;
+ sq.ignore_int = 1;
+ }
+ WAKE_UP(sq.write_queue);
/* At least one block of the queue is free now
- so wake up a writing process blocked because
- of a full queue. */
-
- if ((sq.playing != 1) || (sq.count != 1))
- /* We must be a bit carefully here: sq.count indicates the
- * number of buffers used and not the number of frames to
- * be played. If sq.count==1 and sq.playing==1 that means
- * the only remaining frame was already programmed earlier
- * (and is currently running) so we mustn't call AtaPlay()
- * here, otherwise we'll play one frame too much.
- */
- AtaPlay();
+ so wake up a writing process blocked because
+ of a full queue. */
+
+ if ((sq.playing != 1) || (sq.count != 1))
+ /* We must be a bit carefully here: sq.count indicates the
+ * number of buffers used and not the number of frames to
+ * be played. If sq.count==1 and sq.playing==1 that means
+ * the only remaining frame was already programmed earlier
+ * (and is currently running) so we mustn't call AtaPlay()
+ * here, otherwise we'll play one frame too much.
+ */
+ AtaPlay();
- if (!sq.playing) WAKE_UP(sq.sync_queue);
+ if (!sq.playing)
+ WAKE_UP(sq.sync_queue);
/* We are not playing after AtaPlay(), so there
- is nothing to play any more. Wake up a process
- waiting for audio output to drain. */
+ is nothing to play any more. Wake up a process
+ waiting for audio output to drain. */
}
-#endif /* CONFIG_ATARI */
+#endif /* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
@@ -2078,109 +2327,120 @@ static void ata_sq_interrupt(int irq, void *dummy, struct pt_regs *fp)
*/
-static void *AmiAlloc(unsigned int size, int flags)
+static void *
+AmiAlloc(unsigned int size, int flags)
{
- return(amiga_chip_alloc((long)size));
+ return (amiga_chip_alloc((long) size));
}
-static void AmiFree(void *obj, unsigned int size)
+static void
+AmiFree(void *obj, unsigned int size)
{
- amiga_chip_free (obj);
+ amiga_chip_free(obj);
}
-static int AmiIrqInit(void)
+static int
+AmiIrqInit(void)
{
- /* turn off DMA for audio channels */
- custom.dmacon = AMI_AUDIO_OFF;
+ /* turn off DMA for audio channels */
+ custom.dmacon = AMI_AUDIO_OFF;
- /* Register interrupt handler. */
- if (request_irq(IRQ_AMIGA_AUD0, ami_sq_interrupt, 0,
- "DMA sound", ami_sq_interrupt))
- return(0);
- return(1);
+ /* Register interrupt handler. */
+ if (request_irq(IRQ_AMIGA_AUD0, ami_sq_interrupt, 0,
+ "DMA sound", ami_sq_interrupt))
+ return (0);
+ return (1);
}
#ifdef MODULE
-static void AmiIrqCleanUp(void)
+static void
+AmiIrqCleanUp(void)
{
- /* turn off DMA for audio channels */
- custom.dmacon = AMI_AUDIO_OFF;
- /* release the interrupt */
- free_irq(IRQ_AMIGA_AUD0, ami_sq_interrupt);
+ /* turn off DMA for audio channels */
+ custom.dmacon = AMI_AUDIO_OFF;
+ /* release the interrupt */
+ free_irq(IRQ_AMIGA_AUD0, ami_sq_interrupt);
}
-#endif /* MODULE */
+#endif /* MODULE */
-static void AmiSilence(void)
+static void
+AmiSilence(void)
{
- /* turn off DMA for audio channels */
- custom.dmacon = AMI_AUDIO_OFF;
+ /* turn off DMA for audio channels */
+ custom.dmacon = AMI_AUDIO_OFF;
}
-static void AmiInit(void)
+static void
+AmiInit(void)
{
- int period, i;
+ int period, i;
- AmiSilence();
+ AmiSilence();
- if (sound.soft.speed)
- period = amiga_colorclock/sound.soft.speed-1;
- else
- period = amiga_audio_min_period;
- sound.hard = sound.soft;
- sound.trans = &transAmiga;
+ if (sound.soft.speed)
+ period = amiga_colorclock / sound.soft.speed - 1;
+ else
+ period = amiga_audio_min_period;
+ sound.hard = sound.soft;
+ sound.trans = &transAmiga;
- if (period < amiga_audio_min_period) {
- /* we would need to squeeze the sound, but we won't do that */
- period = amiga_audio_min_period;
- } else if (period > 65535) {
- period = 65535;
- }
- sound.hard.speed = amiga_colorclock/(period+1);
+ if (period < amiga_audio_min_period)
+ {
+ /* we would need to squeeze the sound, but we won't do that */
+ period = amiga_audio_min_period;
+ } else if (period > 65535)
+ {
+ period = 65535;
+ }
+ sound.hard.speed = amiga_colorclock / (period + 1);
- for (i = 0; i < 4; i++)
- custom.aud[i].audper = period;
- amiga_audio_period = period;
+ for (i = 0; i < 4; i++)
+ custom.aud[i].audper = period;
+ amiga_audio_period = period;
- AmiSetTreble(50); /* recommended for newer amiga models */
+ AmiSetTreble(50); /* recommended for newer amiga models */
}
-static int AmiSetFormat(int format)
+static int
+AmiSetFormat(int format)
{
- int size;
+ int size;
- /* Amiga sound DMA supports 8bit and 16bit (pseudo 14 bit) modes */
+ /* Amiga sound DMA supports 8bit and 16bit (pseudo 14 bit) modes */
- switch (format) {
- case AFMT_QUERY:
- return(sound.soft.format);
- case AFMT_MU_LAW:
- case AFMT_A_LAW:
- case AFMT_U8:
- case AFMT_S8:
- size = 8;
- break;
- case AFMT_S16_BE:
- case AFMT_U16_BE:
- case AFMT_S16_LE:
- case AFMT_U16_LE:
- size = 16;
- break;
- default: /* :-) */
- size = 8;
- format = AFMT_S8;
- }
+ switch (format)
+ {
+ case AFMT_QUERY:
+ return (sound.soft.format);
+ case AFMT_MU_LAW:
+ case AFMT_A_LAW:
+ case AFMT_U8:
+ case AFMT_S8:
+ size = 8;
+ break;
+ case AFMT_S16_BE:
+ case AFMT_U16_BE:
+ case AFMT_S16_LE:
+ case AFMT_U16_LE:
+ size = 16;
+ break;
+ default: /* :-) */
+ size = 8;
+ format = AFMT_S8;
+ }
- sound.soft.format = format;
- sound.soft.size = size;
- if (sound.minDev == SND_DEV_DSP) {
- sound.dsp.format = format;
- sound.dsp.size = sound.soft.size;
- }
- AmiInit();
+ sound.soft.format = format;
+ sound.soft.size = size;
+ if (sound.minDev == SND_DEV_DSP)
+ {
+ sound.dsp.format = format;
+ sound.dsp.size = sound.soft.size;
+ }
+ AmiInit();
- return(format);
+ return (format);
}
@@ -2188,24 +2448,26 @@ static int AmiSetFormat(int format)
(((v) < 0) ? 0 : ((v) > 100) ? 64 : ((v) * 64)/100)
#define VOLUME_AMI_TO_VOXWARE(v) ((v)*100/64)
-static int AmiSetVolume(int volume)
+static int
+AmiSetVolume(int volume)
{
- sound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff);
- custom.aud[0].audvol = sound.volume_left;
- sound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8);
- custom.aud[1].audvol = sound.volume_right;
- return(VOLUME_AMI_TO_VOXWARE(sound.volume_left) |
- (VOLUME_AMI_TO_VOXWARE(sound.volume_right) << 8));
+ sound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff);
+ custom.aud[0].audvol = sound.volume_left;
+ sound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8);
+ custom.aud[1].audvol = sound.volume_right;
+ return (VOLUME_AMI_TO_VOXWARE(sound.volume_left) |
+ (VOLUME_AMI_TO_VOXWARE(sound.volume_right) << 8));
}
-static int AmiSetTreble(int treble)
+static int
+AmiSetTreble(int treble)
{
- sound.treble = treble;
- if (treble < 50)
- ciaa.pra &= ~0x02;
- else
- ciaa.pra |= 0x02;
- return(treble);
+ sound.treble = treble;
+ if (treble < 50)
+ ciaa.pra &= ~0x02;
+ else
+ ciaa.pra |= 0x02;
+ return (treble);
}
@@ -2214,279 +2476,302 @@ static int AmiSetTreble(int treble)
#define AMI_PLAY_MASK 3
-static void ami_sq_play_next_frame(int index)
-{
- u_char *start, *ch0, *ch1, *ch2, *ch3;
- u_long size;
-
- /* used by AmiPlay() if all doubts whether there really is something
- * to be played are already wiped out.
- */
- start = sq_block_address(sq.front);
- size = (sq.count == index ? sq.rear_size : sq.block_size)>>1;
-
- if (sound.hard.stereo) {
- ch0 = start;
- ch1 = start+sq.block_size_half;
- size >>= 1;
- } else {
- ch0 = start;
- ch1 = start;
- }
- if (sound.hard.size == 8) {
- custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
- custom.aud[0].audlen = size;
- custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
- custom.aud[1].audlen = size;
- custom.dmacon = AMI_AUDIO_8;
- } else {
- size >>= 1;
- custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
- custom.aud[0].audlen = size;
- custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
- custom.aud[1].audlen = size;
- if (sound.volume_left == 64 && sound.volume_right == 64) {
- /* We can play pseudo 14-bit only with the maximum volume */
- ch3 = ch0+sq.block_size_quarter;
- ch2 = ch1+sq.block_size_quarter;
- custom.aud[2].audvol = 1; /* we are being affected by the beeps */
- custom.aud[3].audvol = 1; /* restoring volume here helps a bit */
- custom.aud[2].audlc = (u_short *)ZTWO_PADDR(ch2);
- custom.aud[2].audlen = size;
- custom.aud[3].audlc = (u_short *)ZTWO_PADDR(ch3);
- custom.aud[3].audlen = size;
- custom.dmacon = AMI_AUDIO_14;
- } else
- custom.dmacon = AMI_AUDIO_8;
- }
- sq.front = (sq.front+1) % sq.max_count;
- sq.playing |= AMI_PLAY_LOADED;
-}
-
-
-static void AmiPlay(void)
+static void
+ami_sq_play_next_frame(int index)
{
- int minframes = 1;
-
- custom.intena = IF_AUD0;
-
- if (sq.playing & AMI_PLAY_LOADED) {
- /* There's already a frame loaded */
- custom.intena = IF_SETCLR | IF_AUD0;
- return;
- }
-
- if (sq.playing & AMI_PLAY_PLAYING)
- /* Increase threshold: frame 1 is already being played */
- minframes = 2;
+ u_char *start, *ch0, *ch1, *ch2, *ch3;
+ u_long size;
- if (sq.count < minframes) {
- /* Nothing to do */
- custom.intena = IF_SETCLR | IF_AUD0;
- return;
- }
-
- if (sq.count <= minframes && sq.rear_size < sq.block_size && !sq.syncing) {
- /* hmmm, the only existing frame is not
- * yet filled and we're not syncing?
+ /* used by AmiPlay() if all doubts whether there really is something
+ * to be played are already wiped out.
*/
- custom.intena = IF_SETCLR | IF_AUD0;
- return;
- }
-
- ami_sq_play_next_frame(minframes);
+ start = sq_block_address(sq.front);
+ size = (sq.count == index ? sq.rear_size : sq.block_size) >> 1;
+
+ if (sound.hard.stereo)
+ {
+ ch0 = start;
+ ch1 = start + sq.block_size_half;
+ size >>= 1;
+ } else
+ {
+ ch0 = start;
+ ch1 = start;
+ }
+ if (sound.hard.size == 8)
+ {
+ custom.aud[0].audlc = (u_short *) ZTWO_PADDR(ch0);
+ custom.aud[0].audlen = size;
+ custom.aud[1].audlc = (u_short *) ZTWO_PADDR(ch1);
+ custom.aud[1].audlen = size;
+ custom.dmacon = AMI_AUDIO_8;
+ } else
+ {
+ size >>= 1;
+ custom.aud[0].audlc = (u_short *) ZTWO_PADDR(ch0);
+ custom.aud[0].audlen = size;
+ custom.aud[1].audlc = (u_short *) ZTWO_PADDR(ch1);
+ custom.aud[1].audlen = size;
+ if (sound.volume_left == 64 && sound.volume_right == 64)
+ {
+ /* We can play pseudo 14-bit only with the maximum volume */
+ ch3 = ch0 + sq.block_size_quarter;
+ ch2 = ch1 + sq.block_size_quarter;
+ custom.aud[2].audvol = 1; /* we are being affected by the beeps */
+ custom.aud[3].audvol = 1; /* restoring volume here helps a bit */
+ custom.aud[2].audlc = (u_short *) ZTWO_PADDR(ch2);
+ custom.aud[2].audlen = size;
+ custom.aud[3].audlc = (u_short *) ZTWO_PADDR(ch3);
+ custom.aud[3].audlen = size;
+ custom.dmacon = AMI_AUDIO_14;
+ } else
+ custom.dmacon = AMI_AUDIO_8;
+ }
+ sq.front = (sq.front + 1) % sq.max_count;
+ sq.playing |= AMI_PLAY_LOADED;
+}
+
+
+static void
+AmiPlay(void)
+{
+ int minframes = 1;
+
+ custom.intena = IF_AUD0;
+
+ if (sq.playing & AMI_PLAY_LOADED)
+ {
+ /* There's already a frame loaded */
+ custom.intena = IF_SETCLR | IF_AUD0;
+ return;
+ }
+ if (sq.playing & AMI_PLAY_PLAYING)
+ /* Increase threshold: frame 1 is already being played */
+ minframes = 2;
+
+ if (sq.count < minframes)
+ {
+ /* Nothing to do */
+ custom.intena = IF_SETCLR | IF_AUD0;
+ return;
+ }
+ if (sq.count <= minframes && sq.rear_size < sq.block_size && !sq.syncing)
+ {
+ /* hmmm, the only existing frame is not
+ * yet filled and we're not syncing?
+ */
+ custom.intena = IF_SETCLR | IF_AUD0;
+ return;
+ }
+ ami_sq_play_next_frame(minframes);
- custom.intena = IF_SETCLR | IF_AUD0;
+ custom.intena = IF_SETCLR | IF_AUD0;
}
-static void ami_sq_interrupt(int irq, void *dummy, struct pt_regs *fp)
+static void
+ami_sq_interrupt(int irq, void *dummy, struct pt_regs *fp)
{
- int minframes = 1;
+ int minframes = 1;
- if (!sq.playing) {
- /* Playing was interrupted and sq_reset() has already cleared
- * the sq variables, so better don't do anything here.
- */
- WAKE_UP(sq.sync_queue);
- return;
- }
+ if (!sq.playing)
+ {
+ /* Playing was interrupted and sq_reset() has already cleared
+ * the sq variables, so better don't do anything here.
+ */
+ WAKE_UP(sq.sync_queue);
+ return;
+ }
+ if (sq.playing & AMI_PLAY_PLAYING)
+ {
+ /* We've just finished a frame */
+ sq.count--;
+ WAKE_UP(sq.write_queue);
+ }
+ if (sq.playing & AMI_PLAY_LOADED)
+ /* Increase threshold: frame 1 is already being played */
+ minframes = 2;
- if (sq.playing & AMI_PLAY_PLAYING) {
- /* We've just finished a frame */
- sq.count--;
- WAKE_UP(sq.write_queue);
- }
+ /* Shift the flags */
+ sq.playing = (sq.playing << 1) & AMI_PLAY_MASK;
- if (sq.playing & AMI_PLAY_LOADED)
- /* Increase threshold: frame 1 is already being played */
- minframes = 2;
+ if (!sq.playing)
+ /* No frame is playing, disable audio DMA */
+ custom.dmacon = AMI_AUDIO_OFF;
- /* Shift the flags */
- sq.playing = (sq.playing<<1) & AMI_PLAY_MASK;
+ if (sq.count >= minframes)
+ /* Try to play the next frame */
+ AmiPlay();
- if (!sq.playing)
- /* No frame is playing, disable audio DMA */
- custom.dmacon = AMI_AUDIO_OFF;
-
- if (sq.count >= minframes)
- /* Try to play the next frame */
- AmiPlay();
-
- if (!sq.playing)
- /* Nothing to play anymore.
- Wake up a process waiting for audio output to drain. */
- WAKE_UP(sq.sync_queue);
+ if (!sq.playing)
+ /* Nothing to play anymore.
+ Wake up a process waiting for audio output to drain. */
+ WAKE_UP(sq.sync_queue);
}
-#endif /* CONFIG_AMIGA */
+#endif /* CONFIG_AMIGA */
/*** Machine definitions *****************************************************/
#ifdef CONFIG_ATARI
-static MACHINE machTT = {
- DMASND_TT, AtaAlloc, AtaFree, AtaIrqInit,
+static MACHINE machTT =
+{
+ DMASND_TT, AtaAlloc, AtaFree, AtaIrqInit,
#ifdef MODULE
- AtaIrqCleanUp,
-#endif /* MODULE */
- TTInit, TTSilence, TTSetFormat, TTSetVolume, AtaSetBass, AtaSetTreble,
- AtaPlay
+ AtaIrqCleanUp,
+#endif /* MODULE */
+ TTInit, TTSilence, TTSetFormat, TTSetVolume, AtaSetBass, AtaSetTreble,
+ AtaPlay
};
-static MACHINE machFalcon = {
- DMASND_FALCON, AtaAlloc, AtaFree, AtaIrqInit,
+static MACHINE machFalcon =
+{
+ DMASND_FALCON, AtaAlloc, AtaFree, AtaIrqInit,
#ifdef MODULE
- AtaIrqCleanUp,
-#endif /* MODULE */
- FalconInit, FalconSilence, FalconSetFormat, FalconSetVolume, AtaSetBass,
- AtaSetTreble, AtaPlay
+ AtaIrqCleanUp,
+#endif /* MODULE */
+ FalconInit, FalconSilence, FalconSetFormat, FalconSetVolume, AtaSetBass,
+ AtaSetTreble, AtaPlay
};
-#endif /* CONFIG_ATARI */
+
+#endif /* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
-static MACHINE machAmiga = {
- DMASND_AMIGA, AmiAlloc, AmiFree, AmiIrqInit,
+static MACHINE machAmiga =
+{
+ DMASND_AMIGA, AmiAlloc, AmiFree, AmiIrqInit,
#ifdef MODULE
- AmiIrqCleanUp,
-#endif /* MODULE */
- AmiInit, AmiSilence, AmiSetFormat, AmiSetVolume, NULL, AmiSetTreble,
- AmiPlay
+ AmiIrqCleanUp,
+#endif /* MODULE */
+ AmiInit, AmiSilence, AmiSetFormat, AmiSetVolume, NULL, AmiSetTreble,
+ AmiPlay
};
-#endif /* CONFIG_AMIGA */
+
+#endif /* CONFIG_AMIGA */
/*** Mid level stuff *********************************************************/
-static void sound_silence(void)
+static void
+sound_silence(void)
{
- /* update hardware settings one more */
- (*sound.mach.init)();
+ /* update hardware settings one more */
+ (*sound.mach.init) ();
- (*sound.mach.silence)();
+ (*sound.mach.silence) ();
}
-static void sound_init(void)
+static void
+sound_init(void)
{
- (*sound.mach.init)();
+ (*sound.mach.init) ();
}
-static int sound_set_format(int format)
+static int
+sound_set_format(int format)
{
- return(*sound.mach.setFormat)(format);
+ return (*sound.mach.setFormat) (format);
}
-static int sound_set_speed(int speed)
+static int
+sound_set_speed(int speed)
{
- if (speed < 0)
- return(sound.soft.speed);
+ if (speed < 0)
+ return (sound.soft.speed);
- sound.soft.speed = speed;
- (*sound.mach.init)();
- if (sound.minDev == SND_DEV_DSP)
- sound.dsp.speed = sound.soft.speed;
+ sound.soft.speed = speed;
+ (*sound.mach.init) ();
+ if (sound.minDev == SND_DEV_DSP)
+ sound.dsp.speed = sound.soft.speed;
- return(sound.soft.speed);
+ return (sound.soft.speed);
}
-static int sound_set_stereo(int stereo)
+static int
+sound_set_stereo(int stereo)
{
- if (stereo < 0)
- return(sound.soft.stereo);
+ if (stereo < 0)
+ return (sound.soft.stereo);
- stereo = !!stereo; /* should be 0 or 1 now */
+ stereo = !!stereo; /* should be 0 or 1 now */
- sound.soft.stereo = stereo;
- if (sound.minDev == SND_DEV_DSP)
- sound.dsp.stereo = stereo;
- (*sound.mach.init)();
+ sound.soft.stereo = stereo;
+ if (sound.minDev == SND_DEV_DSP)
+ sound.dsp.stereo = stereo;
+ (*sound.mach.init) ();
- return(stereo);
+ return (stereo);
}
-static int sound_set_volume(int volume)
+static int
+sound_set_volume(int volume)
{
- return(*sound.mach.setVolume)(volume);
+ return (*sound.mach.setVolume) (volume);
}
#ifdef CONFIG_ATARI
-static int sound_set_bass(int bass)
-{
- return(sound.mach.setBass ? (*sound.mach.setBass)(bass) : 50);
-}
-#endif /* CONFIG_ATARI */
-
-
-static int sound_set_treble(int treble)
-{
- return(sound.mach.setTreble ? (*sound.mach.setTreble)(treble) : 50);
-}
-
-
-static long sound_copy_translate(const u_char *userPtr,
- unsigned long userCount,
- u_char frame[], long *frameUsed,
- long frameLeft)
-{
- long (*ct_func)(const u_char *, unsigned long, u_char *, long *, long) = NULL;
-
- switch (sound.soft.format) {
- case AFMT_MU_LAW:
- ct_func = sound.trans->ct_ulaw;
- break;
- case AFMT_A_LAW:
- ct_func = sound.trans->ct_alaw;
- break;
- case AFMT_S8:
- ct_func = sound.trans->ct_s8;
- break;
- case AFMT_U8:
- ct_func = sound.trans->ct_u8;
- break;
- case AFMT_S16_BE:
- ct_func = sound.trans->ct_s16be;
- break;
- case AFMT_U16_BE:
- ct_func = sound.trans->ct_u16be;
- break;
- case AFMT_S16_LE:
- ct_func = sound.trans->ct_s16le;
- break;
- case AFMT_U16_LE:
- ct_func = sound.trans->ct_u16le;
- break;
- }
- if (ct_func)
- return(ct_func(userPtr, userCount, frame, frameUsed, frameLeft));
- else
- return(0);
+static int
+sound_set_bass(int bass)
+{
+ return (sound.mach.setBass ? (*sound.mach.setBass) (bass) : 50);
+}
+#endif /* CONFIG_ATARI */
+
+
+static int
+sound_set_treble(int treble)
+{
+ return (sound.mach.setTreble ? (*sound.mach.setTreble) (treble) : 50);
+}
+
+
+static long
+sound_copy_translate(const u_char * userPtr,
+ unsigned long userCount,
+ u_char frame[], long *frameUsed,
+ long frameLeft)
+{
+ long (*ct_func) (const u_char *, unsigned long, u_char *, long *, long) = NULL;
+
+ switch (sound.soft.format)
+ {
+ case AFMT_MU_LAW:
+ ct_func = sound.trans->ct_ulaw;
+ break;
+ case AFMT_A_LAW:
+ ct_func = sound.trans->ct_alaw;
+ break;
+ case AFMT_S8:
+ ct_func = sound.trans->ct_s8;
+ break;
+ case AFMT_U8:
+ ct_func = sound.trans->ct_u8;
+ break;
+ case AFMT_S16_BE:
+ ct_func = sound.trans->ct_s16be;
+ break;
+ case AFMT_U16_BE:
+ ct_func = sound.trans->ct_u16be;
+ break;
+ case AFMT_S16_LE:
+ ct_func = sound.trans->ct_s16le;
+ break;
+ case AFMT_U16_LE:
+ ct_func = sound.trans->ct_u16le;
+ break;
+ }
+ if (ct_func)
+ return (ct_func(userPtr, userCount, frame, frameUsed, frameLeft));
+ else
+ return (0);
}
@@ -2500,199 +2785,215 @@ static long sound_copy_translate(const u_char *userPtr,
#define RECLEVEL_GAIN_TO_VOXWARE(v) (((v) * 20 + 2) / 3)
-static void mixer_init(void)
+static void
+mixer_init(void)
{
- mixer.busy = 0;
- sound.treble = 0;
- sound.bass = 0;
- switch (sound.mach.type) {
+ mixer.busy = 0;
+ sound.treble = 0;
+ sound.bass = 0;
+ switch (sound.mach.type)
+ {
#ifdef CONFIG_ATARI
- case DMASND_TT:
- atari_microwire_cmd(MW_LM1992_VOLUME(0));
- sound.volume_left = 0;
- atari_microwire_cmd(MW_LM1992_BALLEFT(0));
- sound.volume_right = 0;
- atari_microwire_cmd(MW_LM1992_BALRIGHT(0));
- atari_microwire_cmd(MW_LM1992_TREBLE(0));
- atari_microwire_cmd(MW_LM1992_BASS(0));
- break;
- case DMASND_FALCON:
- sound.volume_left = (tt_dmasnd.output_atten & 0xf00) >> 8;
- sound.volume_right = (tt_dmasnd.output_atten & 0xf0) >> 4;
- break;
-#endif /* CONFIG_ATARI */
+ case DMASND_TT:
+ atari_microwire_cmd(MW_LM1992_VOLUME(0));
+ sound.volume_left = 0;
+ atari_microwire_cmd(MW_LM1992_BALLEFT(0));
+ sound.volume_right = 0;
+ atari_microwire_cmd(MW_LM1992_BALRIGHT(0));
+ atari_microwire_cmd(MW_LM1992_TREBLE(0));
+ atari_microwire_cmd(MW_LM1992_BASS(0));
+ break;
+ case DMASND_FALCON:
+ sound.volume_left = (tt_dmasnd.output_atten & 0xf00) >> 8;
+ sound.volume_right = (tt_dmasnd.output_atten & 0xf0) >> 4;
+ break;
+#endif /* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
- case DMASND_AMIGA:
- sound.volume_left = 64;
- sound.volume_right = 64;
- custom.aud[0].audvol = sound.volume_left;
- custom.aud[3].audvol = 1; /* For pseudo 14bit */
- custom.aud[1].audvol = sound.volume_right;
- custom.aud[2].audvol = 1; /* For pseudo 14bit */
- sound.treble = 50;
- break;
-#endif /* CONFIG_AMIGA */
- }
+ case DMASND_AMIGA:
+ sound.volume_left = 64;
+ sound.volume_right = 64;
+ custom.aud[0].audvol = sound.volume_left;
+ custom.aud[3].audvol = 1; /* For pseudo 14bit */
+ custom.aud[1].audvol = sound.volume_right;
+ custom.aud[2].audvol = 1; /* For pseudo 14bit */
+ sound.treble = 50;
+ break;
+#endif /* CONFIG_AMIGA */
+ }
}
-static int mixer_open(int open_mode)
+static int
+mixer_open(int open_mode)
{
- if (mixer.busy)
- return(-EBUSY);
- mixer.busy = 1;
- return(0);
+ if (mixer.busy)
+ return (-EBUSY);
+ mixer.busy = 1;
+ return (0);
}
-static int mixer_release(void)
+static int
+mixer_release(void)
{
- mixer.busy = 0;
- return(0);
+ mixer.busy = 0;
+ return (0);
}
-static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd,
- u_long arg)
+static int
+mixer_ioctl(struct inode *inode, struct file *file, u_int cmd,
+ u_long arg)
{
- int data;
- switch (sound.mach.type) {
+ int data;
+
+ switch (sound.mach.type)
+ {
#ifdef CONFIG_ATARI
- case DMASND_FALCON:
- switch (cmd) {
- case SOUND_MIXER_READ_DEVMASK:
- return(IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_SPEAKER));
- case SOUND_MIXER_READ_RECMASK:
- return(IOCTL_OUT(arg, SOUND_MASK_MIC));
- case SOUND_MIXER_READ_STEREODEVS:
- return(IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC));
- case SOUND_MIXER_READ_CAPS:
- return(IOCTL_OUT(arg, SOUND_CAP_EXCL_INPUT));
- case SOUND_MIXER_READ_VOLUME:
- return(IOCTL_OUT(arg,
- VOLUME_ATT_TO_VOXWARE(sound.volume_left) |
- VOLUME_ATT_TO_VOXWARE(sound.volume_right) << 8));
- case SOUND_MIXER_WRITE_MIC:
- IOCTL_IN(arg, data);
- tt_dmasnd.input_gain =
- RECLEVEL_VOXWARE_TO_GAIN(data & 0xff) << 4 |
- RECLEVEL_VOXWARE_TO_GAIN(data >> 8 & 0xff);
- /* fall thru, return set value */
- case SOUND_MIXER_READ_MIC:
- return(IOCTL_OUT(arg,
- RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain >> 4 & 0xf) |
- RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain & 0xf) << 8));
- case SOUND_MIXER_READ_SPEAKER:
+ case DMASND_FALCON:
+ switch (cmd)
{
- int porta;
- cli();
- sound_ym.rd_data_reg_sel = 14;
- porta = sound_ym.rd_data_reg_sel;
- sti();
- return(IOCTL_OUT(arg, porta & 0x40 ? 0 : 100));
+ case SOUND_MIXER_READ_DEVMASK:
+ return (IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_SPEAKER));
+ case SOUND_MIXER_READ_RECMASK:
+ return (IOCTL_OUT(arg, SOUND_MASK_MIC));
+ case SOUND_MIXER_READ_STEREODEVS:
+ return (IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC));
+ case SOUND_MIXER_READ_CAPS:
+ return (IOCTL_OUT(arg, SOUND_CAP_EXCL_INPUT));
+ case SOUND_MIXER_READ_VOLUME:
+ return (IOCTL_OUT(arg,
+ VOLUME_ATT_TO_VOXWARE(sound.volume_left) |
+ VOLUME_ATT_TO_VOXWARE(sound.volume_right) << 8));
+ case SOUND_MIXER_WRITE_MIC:
+ IOCTL_IN(arg, data);
+ tt_dmasnd.input_gain =
+ RECLEVEL_VOXWARE_TO_GAIN(data & 0xff) << 4 |
+ RECLEVEL_VOXWARE_TO_GAIN(data >> 8 & 0xff);
+ /* fall thru, return set value */
+ case SOUND_MIXER_READ_MIC:
+ return (IOCTL_OUT(arg,
+ RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain >> 4 & 0xf) |
+ RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain & 0xf) << 8));
+ case SOUND_MIXER_READ_SPEAKER:
+ {
+ int porta;
+
+ cli();
+ sound_ym.rd_data_reg_sel = 14;
+ porta = sound_ym.rd_data_reg_sel;
+ sti();
+ return (IOCTL_OUT(arg, porta & 0x40 ? 0 : 100));
+ }
+ case SOUND_MIXER_WRITE_VOLUME:
+ IOCTL_IN(arg, data);
+ return (IOCTL_OUT(arg, sound_set_volume(data)));
+ case SOUND_MIXER_WRITE_SPEAKER:
+ {
+ int porta;
+
+ IOCTL_IN(arg, data);
+ cli();
+ sound_ym.rd_data_reg_sel = 14;
+ porta = (sound_ym.rd_data_reg_sel & ~0x40) |
+ (data < 50 ? 0x40 : 0);
+ sound_ym.wd_data = porta;
+ sti();
+ return (IOCTL_OUT(arg, porta & 0x40 ? 0 : 100));
+ }
}
- case SOUND_MIXER_WRITE_VOLUME:
- IOCTL_IN(arg, data);
- return(IOCTL_OUT(arg, sound_set_volume(data)));
- case SOUND_MIXER_WRITE_SPEAKER:
+ break;
+
+ case DMASND_TT:
+ switch (cmd)
{
- int porta;
- IOCTL_IN(arg, data);
- cli();
- sound_ym.rd_data_reg_sel = 14;
- porta = (sound_ym.rd_data_reg_sel & ~0x40) |
- (data < 50 ? 0x40 : 0);
- sound_ym.wd_data = porta;
- sti();
- return(IOCTL_OUT(arg, porta & 0x40 ? 0 : 100));
+ case SOUND_MIXER_READ_DEVMASK:
+ return (IOCTL_OUT(arg,
+ SOUND_MASK_VOLUME | SOUND_MASK_TREBLE | SOUND_MASK_BASS |
+ ((atari_mch_cookie >> 16) == ATARI_MCH_TT ?
+ SOUND_MASK_SPEAKER : 0)));
+ case SOUND_MIXER_READ_RECMASK:
+ return (IOCTL_OUT(arg, 0));
+ case SOUND_MIXER_READ_STEREODEVS:
+ return (IOCTL_OUT(arg, SOUND_MASK_VOLUME));
+ case SOUND_MIXER_READ_VOLUME:
+ return (IOCTL_OUT(arg,
+ VOLUME_DB_TO_VOXWARE(sound.volume_left) |
+ (VOLUME_DB_TO_VOXWARE(sound.volume_right) << 8)));
+ case SOUND_MIXER_READ_BASS:
+ return (IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(sound.bass)));
+ case SOUND_MIXER_READ_TREBLE:
+ return (IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(sound.treble)));
+ case SOUND_MIXER_READ_SPEAKER:
+ {
+ int porta;
+
+ if ((atari_mch_cookie >> 16) == ATARI_MCH_TT)
+ {
+ cli();
+ sound_ym.rd_data_reg_sel = 14;
+ porta = sound_ym.rd_data_reg_sel;
+ sti();
+ return (IOCTL_OUT(arg, porta & 0x40 ? 0 : 100));
+ } else
+ return (-EINVAL);
+ }
+ case SOUND_MIXER_WRITE_VOLUME:
+ IOCTL_IN(arg, data);
+ return (IOCTL_OUT(arg, sound_set_volume(data)));
+ case SOUND_MIXER_WRITE_BASS:
+ IOCTL_IN(arg, data);
+ return (IOCTL_OUT(arg, sound_set_bass(data)));
+ case SOUND_MIXER_WRITE_TREBLE:
+ IOCTL_IN(arg, data);
+ return (IOCTL_OUT(arg, sound_set_treble(data)));
+ case SOUND_MIXER_WRITE_SPEAKER:
+ if ((atari_mch_cookie >> 16) == ATARI_MCH_TT)
+ {
+ int porta;
+
+ IOCTL_IN(arg, data);
+ cli();
+ sound_ym.rd_data_reg_sel = 14;
+ porta = (sound_ym.rd_data_reg_sel & ~0x40) |
+ (data < 50 ? 0x40 : 0);
+ sound_ym.wd_data = porta;
+ sti();
+ return (IOCTL_OUT(arg, porta & 0x40 ? 0 : 100));
+ } else
+ return (-EINVAL);
}
- }
- break;
-
- case DMASND_TT:
- switch (cmd) {
- case SOUND_MIXER_READ_DEVMASK:
- return(IOCTL_OUT(arg,
- SOUND_MASK_VOLUME | SOUND_MASK_TREBLE | SOUND_MASK_BASS |
- ((atari_mch_cookie >> 16) == ATARI_MCH_TT ?
- SOUND_MASK_SPEAKER : 0)));
- case SOUND_MIXER_READ_RECMASK:
- return(IOCTL_OUT(arg, 0));
- case SOUND_MIXER_READ_STEREODEVS:
- return(IOCTL_OUT(arg, SOUND_MASK_VOLUME));
- case SOUND_MIXER_READ_VOLUME:
- return(IOCTL_OUT(arg,
- VOLUME_DB_TO_VOXWARE(sound.volume_left) |
- (VOLUME_DB_TO_VOXWARE(sound.volume_right) << 8)));
- case SOUND_MIXER_READ_BASS:
- return(IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(sound.bass)));
- case SOUND_MIXER_READ_TREBLE:
- return(IOCTL_OUT(arg, TONE_DB_TO_VOXWARE(sound.treble)));
- case SOUND_MIXER_READ_SPEAKER:
+ break;
+#endif /* CONFIG_ATARI */
+
+#ifdef CONFIG_AMIGA
+ case DMASND_AMIGA:
+ switch (cmd)
{
- int porta;
- if ((atari_mch_cookie >> 16) == ATARI_MCH_TT) {
- cli();
- sound_ym.rd_data_reg_sel = 14;
- porta = sound_ym.rd_data_reg_sel;
- sti();
- return(IOCTL_OUT(arg, porta & 0x40 ? 0 : 100));
- } else
- return(-EINVAL);
+ case SOUND_MIXER_READ_DEVMASK:
+ return (IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE));
+ case SOUND_MIXER_READ_RECMASK:
+ return (IOCTL_OUT(arg, 0));
+ case SOUND_MIXER_READ_STEREODEVS:
+ return (IOCTL_OUT(arg, SOUND_MASK_VOLUME));
+ case SOUND_MIXER_READ_VOLUME:
+ return (IOCTL_OUT(arg,
+ VOLUME_AMI_TO_VOXWARE(sound.volume_left) |
+ VOLUME_AMI_TO_VOXWARE(sound.volume_right) << 8));
+ case SOUND_MIXER_WRITE_VOLUME:
+ IOCTL_IN(arg, data);
+ return (IOCTL_OUT(arg, sound_set_volume(data)));
+ case SOUND_MIXER_READ_TREBLE:
+ return (IOCTL_OUT(arg, sound.treble));
+ case SOUND_MIXER_WRITE_TREBLE:
+ IOCTL_IN(arg, data);
+ return (IOCTL_OUT(arg, sound_set_treble(data)));
}
- case SOUND_MIXER_WRITE_VOLUME:
- IOCTL_IN(arg, data);
- return(IOCTL_OUT(arg, sound_set_volume(data)));
- case SOUND_MIXER_WRITE_BASS:
- IOCTL_IN(arg, data);
- return(IOCTL_OUT(arg, sound_set_bass(data)));
- case SOUND_MIXER_WRITE_TREBLE:
- IOCTL_IN(arg, data);
- return(IOCTL_OUT(arg, sound_set_treble(data)));
- case SOUND_MIXER_WRITE_SPEAKER:
- if ((atari_mch_cookie >> 16) == ATARI_MCH_TT) {
- int porta;
- IOCTL_IN(arg, data);
- cli();
- sound_ym.rd_data_reg_sel = 14;
- porta = (sound_ym.rd_data_reg_sel & ~0x40) |
- (data < 50 ? 0x40 : 0);
- sound_ym.wd_data = porta;
- sti();
- return(IOCTL_OUT(arg, porta & 0x40 ? 0 : 100));
- } else
- return(-EINVAL);
- }
- break;
-#endif /* CONFIG_ATARI */
+ break;
+#endif /* CONFIG_AMIGA */
+ }
-#ifdef CONFIG_AMIGA
- case DMASND_AMIGA:
- switch (cmd) {
- case SOUND_MIXER_READ_DEVMASK:
- return(IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE));
- case SOUND_MIXER_READ_RECMASK:
- return(IOCTL_OUT(arg, 0));
- case SOUND_MIXER_READ_STEREODEVS:
- return(IOCTL_OUT(arg, SOUND_MASK_VOLUME));
- case SOUND_MIXER_READ_VOLUME:
- return(IOCTL_OUT(arg,
- VOLUME_AMI_TO_VOXWARE(sound.volume_left) |
- VOLUME_AMI_TO_VOXWARE(sound.volume_right) << 8));
- case SOUND_MIXER_WRITE_VOLUME:
- IOCTL_IN(arg, data);
- return(IOCTL_OUT(arg, sound_set_volume(data)));
- case SOUND_MIXER_READ_TREBLE:
- return(IOCTL_OUT(arg, sound.treble));
- case SOUND_MIXER_WRITE_TREBLE:
- IOCTL_IN(arg, data);
- return(IOCTL_OUT(arg, sound_set_treble(data)));
- }
- break;
-#endif /* CONFIG_AMIGA */
- }
-
- return(-EINVAL);
+ return (-EINVAL);
}
@@ -2702,198 +3003,216 @@ static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd,
*/
-static void sq_init(int numBufs, int bufSize, char **buffers)
+static void
+sq_init(int numBufs, int bufSize, char **buffers)
{
- sq.max_count = numBufs;
- sq.block_size = bufSize;
- sq.buffers = buffers;
+ sq.max_count = numBufs;
+ sq.block_size = bufSize;
+ sq.buffers = buffers;
- sq.front = sq.count = 0;
- sq.rear = -1;
- sq.write_queue = sq.open_queue = sq.sync_queue = 0;
- sq.busy = 0;
- sq.syncing = 0;
+ sq.front = sq.count = 0;
+ sq.rear = -1;
+ sq.write_queue = sq.open_queue = sq.sync_queue = 0;
+ sq.busy = 0;
+ sq.syncing = 0;
- sq.playing = 0;
+ sq.playing = 0;
#ifdef CONFIG_ATARI
- sq.ignore_int = 0;
-#endif /* CONFIG_ATARI */
+ sq.ignore_int = 0;
+#endif /* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
- sq.block_size_half = sq.block_size>>1;
- sq.block_size_quarter = sq.block_size_half>>1;
-#endif /* CONFIG_AMIGA */
-
- sound_silence();
-
- /* whatever you like as startup mode for /dev/dsp,
- * (/dev/audio hasn't got a startup mode). note that
- * once changed a new open() will *not* restore these!
- */
- sound.dsp.format = AFMT_S8;
- sound.dsp.stereo = 0;
- sound.dsp.size = 8;
-
- /* set minimum rate possible without expanding */
- switch (sound.mach.type) {
+ sq.block_size_half = sq.block_size >> 1;
+ sq.block_size_quarter = sq.block_size_half >> 1;
+#endif /* CONFIG_AMIGA */
+
+ sound_silence();
+
+ /* whatever you like as startup mode for /dev/dsp,
+ * (/dev/audio hasn't got a startup mode). note that
+ * once changed a new open() will *not* restore these!
+ */
+ sound.dsp.format = AFMT_S8;
+ sound.dsp.stereo = 0;
+ sound.dsp.size = 8;
+
+ /* set minimum rate possible without expanding */
+ switch (sound.mach.type)
+ {
#ifdef CONFIG_ATARI
- case DMASND_TT:
- sound.dsp.speed = 6258;
- break;
- case DMASND_FALCON:
- sound.dsp.speed = 8195;
- break;
-#endif /* CONFIG_ATARI */
+ case DMASND_TT:
+ sound.dsp.speed = 6258;
+ break;
+ case DMASND_FALCON:
+ sound.dsp.speed = 8195;
+ break;
+#endif /* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
- case DMASND_AMIGA:
- sound.dsp.speed = 8000;
- break;
-#endif /* CONFIG_AMIGA */
- }
+ case DMASND_AMIGA:
+ sound.dsp.speed = 8000;
+ break;
+#endif /* CONFIG_AMIGA */
+ }
- /* before the first open to /dev/dsp this wouldn't be set */
- sound.soft = sound.dsp;
- sound.hard = sound.dsp;
+ /* before the first open to /dev/dsp this wouldn't be set */
+ sound.soft = sound.dsp;
+ sound.hard = sound.dsp;
}
-static void sq_play(void)
+static void
+sq_play(void)
{
- (*sound.mach.play)();
+ (*sound.mach.play) ();
}
/* ++TeSche: radically changed this one too */
-static long sq_write(const char *src, unsigned long uLeft)
-{
- long uWritten = 0;
- u_char *dest;
- long uUsed, bUsed, bLeft;
-
- /* ++TeSche: Is something like this necessary?
- * Hey, that's an honest question! Or does any other part of the
- * filesystem already checks this situation? I really don't know.
- */
- if (uLeft == 0)
- return(0);
-
- /* The interrupt doesn't start to play the last, incomplete frame.
- * Thus we can append to it without disabling the interrupts! (Note
- * also that sq.rear isn't affected by the interrupt.)
- */
-
- if (sq.count > 0 && (bLeft = sq.block_size-sq.rear_size) > 0) {
- dest = sq_block_address(sq.rear);
- bUsed = sq.rear_size;
- uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft);
- src += uUsed;
- uWritten += uUsed;
- uLeft -= uUsed;
- sq.rear_size = bUsed;
- }
-
- do {
- while (sq.count == sq.max_count) {
- sq_play();
- if (NON_BLOCKING(sq.open_mode))
- return(uWritten > 0 ? uWritten : -EAGAIN);
- SLEEP(sq.write_queue, ONE_SECOND);
- if (SIGNAL_RECEIVED)
- return(uWritten > 0 ? uWritten : -EINTR);
- }
+static long
+sq_write(const char *src, unsigned long uLeft)
+{
+ long uWritten = 0;
+ u_char *dest;
+ long uUsed, bUsed, bLeft;
- /* Here, we can avoid disabling the interrupt by first
- * copying and translating the data, and then updating
- * the sq variables. Until this is done, the interrupt
- * won't see the new frame and we can work on it
- * undisturbed.
+ /* ++TeSche: Is something like this necessary?
+ * Hey, that's an honest question! Or does any other part of the
+ * filesystem already checks this situation? I really don't know.
*/
+ if (uLeft == 0)
+ return (0);
- dest = sq_block_address((sq.rear+1) % sq.max_count);
- bUsed = 0;
- bLeft = sq.block_size;
- uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft);
- src += uUsed;
- uWritten += uUsed;
- uLeft -= uUsed;
- if (bUsed) {
- sq.rear = (sq.rear+1) % sq.max_count;
- sq.rear_size = bUsed;
- sq.count++;
- }
- } while (bUsed); /* uUsed may have been 0 */
+ /* The interrupt doesn't start to play the last, incomplete frame.
+ * Thus we can append to it without disabling the interrupts! (Note
+ * also that sq.rear isn't affected by the interrupt.)
+ */
+
+ if (sq.count > 0 && (bLeft = sq.block_size - sq.rear_size) > 0)
+ {
+ dest = sq_block_address(sq.rear);
+ bUsed = sq.rear_size;
+ uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft);
+ src += uUsed;
+ uWritten += uUsed;
+ uLeft -= uUsed;
+ sq.rear_size = bUsed;
+ }
+ do
+ {
+ while (sq.count == sq.max_count)
+ {
+ sq_play();
+ if (NON_BLOCKING(sq.open_mode))
+ return (uWritten > 0 ? uWritten : -EAGAIN);
+ SLEEP(sq.write_queue, ONE_SECOND);
+ if (SIGNAL_RECEIVED)
+ return (uWritten > 0 ? uWritten : -EINTR);
+ }
- sq_play();
+ /* Here, we can avoid disabling the interrupt by first
+ * copying and translating the data, and then updating
+ * the sq variables. Until this is done, the interrupt
+ * won't see the new frame and we can work on it
+ * undisturbed.
+ */
+
+ dest = sq_block_address((sq.rear + 1) % sq.max_count);
+ bUsed = 0;
+ bLeft = sq.block_size;
+ uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft);
+ src += uUsed;
+ uWritten += uUsed;
+ uLeft -= uUsed;
+ if (bUsed)
+ {
+ sq.rear = (sq.rear + 1) % sq.max_count;
+ sq.rear_size = bUsed;
+ sq.count++;
+ }
+ }
+ while (bUsed); /* uUsed may have been 0 */
- return(uWritten);
+ sq_play();
+
+ return (uWritten);
}
-static int sq_open(int open_mode)
+static int
+sq_open(int open_mode)
{
- if (sq.busy) {
- if (NON_BLOCKING(open_mode))
- return(-EBUSY);
- while (sq.busy) {
- SLEEP(sq.open_queue, ONE_SECOND);
- if (SIGNAL_RECEIVED)
- return(-EINTR);
- }
- }
- sq.open_mode = open_mode;
- sq.busy = 1;
+ if (sq.busy)
+ {
+ if (NON_BLOCKING(open_mode))
+ return (-EBUSY);
+ while (sq.busy)
+ {
+ SLEEP(sq.open_queue, ONE_SECOND);
+ if (SIGNAL_RECEIVED)
+ return (-EINTR);
+ }
+ }
+ sq.open_mode = open_mode;
+ sq.busy = 1;
#ifdef CONFIG_ATARI
- sq.ignore_int = 1;
-#endif /* CONFIG_ATARI */
- return(0);
+ sq.ignore_int = 1;
+#endif /* CONFIG_ATARI */
+ return (0);
}
-static void sq_reset(void)
+static void
+sq_reset(void)
{
- sound_silence();
- sq.playing = 0;
- sq.count = 0;
- sq.front = (sq.rear+1) % sq.max_count;
+ sound_silence();
+ sq.playing = 0;
+ sq.count = 0;
+ sq.front = (sq.rear + 1) % sq.max_count;
}
-static int sq_sync(void)
+static int
+sq_sync(void)
{
- int rc = 0;
+ int rc = 0;
- sq.syncing = 1;
- sq_play(); /* there may be an incomplete frame waiting */
+ sq.syncing = 1;
+ sq_play(); /* there may be an incomplete frame waiting */
- while (sq.playing) {
- SLEEP(sq.sync_queue, ONE_SECOND);
- if (SIGNAL_RECEIVED) {
- /* While waiting for audio output to drain, an interrupt occurred.
- Stop audio output immediately and clear the queue. */
- sq_reset();
- rc = -EINTR;
- break;
- }
- }
+ while (sq.playing)
+ {
+ SLEEP(sq.sync_queue, ONE_SECOND);
+ if (SIGNAL_RECEIVED)
+ {
+ /* While waiting for audio output to drain, an interrupt occurred.
+ Stop audio output immediately and clear the queue. */
+ sq_reset();
+ rc = -EINTR;
+ break;
+ }
+ }
- sq.syncing = 0;
- return(rc);
+ sq.syncing = 0;
+ return (rc);
}
-static int sq_release(void)
+static int
+sq_release(void)
{
- int rc = 0;
- if (sq.busy) {
- rc = sq_sync();
- sq.busy = 0;
- WAKE_UP(sq.open_queue);
- /* Wake up a process waiting for the queue being released.
- Note: There may be several processes waiting for a call to open()
- returning. */
- }
- return(rc);
+ int rc = 0;
+
+ if (sq.busy)
+ {
+ rc = sq_sync();
+ sq.busy = 0;
+ WAKE_UP(sq.open_queue);
+ /* Wake up a process waiting for the queue being released.
+ Note: There may be several processes waiting for a call to open()
+ returning. */
+ }
+ return (rc);
}
@@ -2903,128 +3222,136 @@ static int sq_release(void)
*/
-static void state_init(void)
+static void
+state_init(void)
{
- state.busy = 0;
+ state.busy = 0;
}
/* state.buf should not overflow! */
-static int state_open(int open_mode)
+static int
+state_open(int open_mode)
{
- char *buffer = state.buf, *mach = "";
- int len = 0;
+ char *buffer = state.buf, *mach = "";
+ int len = 0;
- if (state.busy)
- return(-EBUSY);
+ if (state.busy)
+ return (-EBUSY);
- state.ptr = 0;
- state.busy = 1;
+ state.ptr = 0;
+ state.busy = 1;
- switch (sound.mach.type) {
+ switch (sound.mach.type)
+ {
#ifdef CONFIG_ATARI
- case DMASND_TT:
- case DMASND_FALCON:
- mach = "Atari ";
- break;
-#endif /* CONFIG_ATARI */
+ case DMASND_TT:
+ case DMASND_FALCON:
+ mach = "Atari ";
+ break;
+#endif /* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
- case DMASND_AMIGA:
- mach = "Amiga ";
- break;
-#endif /* CONFIG_AMIGA */
- }
- len += sprintf(buffer+len, "%sDMA sound driver:\n", mach);
-
- len += sprintf(buffer+len, "\tsound.format = 0x%x", sound.soft.format);
- switch (sound.soft.format) {
- case AFMT_MU_LAW:
- len += sprintf(buffer+len, " (mu-law)");
- break;
- case AFMT_A_LAW:
- len += sprintf(buffer+len, " (A-law)");
- break;
- case AFMT_U8:
- len += sprintf(buffer+len, " (unsigned 8 bit)");
- break;
- case AFMT_S8:
- len += sprintf(buffer+len, " (signed 8 bit)");
- break;
- case AFMT_S16_BE:
- len += sprintf(buffer+len, " (signed 16 bit big)");
- break;
- case AFMT_U16_BE:
- len += sprintf(buffer+len, " (unsigned 16 bit big)");
- break;
- case AFMT_S16_LE:
- len += sprintf(buffer+len, " (signed 16 bit little)");
- break;
- case AFMT_U16_LE:
- len += sprintf(buffer+len, " (unsigned 16 bit little)");
- break;
- }
- len += sprintf(buffer+len, "\n");
- len += sprintf(buffer+len, "\tsound.speed = %dHz (phys. %dHz)\n",
- sound.soft.speed, sound.hard.speed);
- len += sprintf(buffer+len, "\tsound.stereo = 0x%x (%s)\n",
- sound.soft.stereo, sound.soft.stereo ? "stereo" : "mono");
- switch (sound.mach.type) {
+ case DMASND_AMIGA:
+ mach = "Amiga ";
+ break;
+#endif /* CONFIG_AMIGA */
+ }
+ len += sprintf(buffer + len, "%sDMA sound driver:\n", mach);
+
+ len += sprintf(buffer + len, "\tsound.format = 0x%x", sound.soft.format);
+ switch (sound.soft.format)
+ {
+ case AFMT_MU_LAW:
+ len += sprintf(buffer + len, " (mu-law)");
+ break;
+ case AFMT_A_LAW:
+ len += sprintf(buffer + len, " (A-law)");
+ break;
+ case AFMT_U8:
+ len += sprintf(buffer + len, " (unsigned 8 bit)");
+ break;
+ case AFMT_S8:
+ len += sprintf(buffer + len, " (signed 8 bit)");
+ break;
+ case AFMT_S16_BE:
+ len += sprintf(buffer + len, " (signed 16 bit big)");
+ break;
+ case AFMT_U16_BE:
+ len += sprintf(buffer + len, " (unsigned 16 bit big)");
+ break;
+ case AFMT_S16_LE:
+ len += sprintf(buffer + len, " (signed 16 bit little)");
+ break;
+ case AFMT_U16_LE:
+ len += sprintf(buffer + len, " (unsigned 16 bit little)");
+ break;
+ }
+ len += sprintf(buffer + len, "\n");
+ len += sprintf(buffer + len, "\tsound.speed = %dHz (phys. %dHz)\n",
+ sound.soft.speed, sound.hard.speed);
+ len += sprintf(buffer + len, "\tsound.stereo = 0x%x (%s)\n",
+ sound.soft.stereo, sound.soft.stereo ? "stereo" : "mono");
+ switch (sound.mach.type)
+ {
#ifdef CONFIG_ATARI
- case DMASND_TT:
- len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-40...0]\n",
- sound.volume_left);
- len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-40...0]\n",
- sound.volume_right);
- len += sprintf(buffer+len, "\tsound.bass = %ddB [-12...+12]\n",
- sound.bass);
- len += sprintf(buffer+len, "\tsound.treble = %ddB [-12...+12]\n",
- sound.treble);
- break;
- case DMASND_FALCON:
- len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-22.5...0]\n",
- sound.volume_left);
- len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-22.5...0]\n",
- sound.volume_right);
- break;
-#endif /* CONFIG_ATARI */
+ case DMASND_TT:
+ len += sprintf(buffer + len, "\tsound.volume_left = %ddB [-40...0]\n",
+ sound.volume_left);
+ len += sprintf(buffer + len, "\tsound.volume_right = %ddB [-40...0]\n",
+ sound.volume_right);
+ len += sprintf(buffer + len, "\tsound.bass = %ddB [-12...+12]\n",
+ sound.bass);
+ len += sprintf(buffer + len, "\tsound.treble = %ddB [-12...+12]\n",
+ sound.treble);
+ break;
+ case DMASND_FALCON:
+ len += sprintf(buffer + len, "\tsound.volume_left = %ddB [-22.5...0]\n",
+ sound.volume_left);
+ len += sprintf(buffer + len, "\tsound.volume_right = %ddB [-22.5...0]\n",
+ sound.volume_right);
+ break;
+#endif /* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
- case DMASND_AMIGA:
- len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n",
- sound.volume_left);
- len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n",
- sound.volume_right);
- break;
-#endif /* CONFIG_AMIGA */
- }
- len += sprintf(buffer+len, "\tsq.block_size = %d sq.max_count = %d\n",
- sq.block_size, sq.max_count);
- len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n", sq.count,
- sq.rear_size);
- len += sprintf(buffer+len, "\tsq.playing = %d sq.syncing = %d\n",
- sq.playing, sq.syncing);
- state.len = len;
- return(0);
+ case DMASND_AMIGA:
+ len += sprintf(buffer + len, "\tsound.volume_left = %d [0...64]\n",
+ sound.volume_left);
+ len += sprintf(buffer + len, "\tsound.volume_right = %d [0...64]\n",
+ sound.volume_right);
+ break;
+#endif /* CONFIG_AMIGA */
+ }
+ len += sprintf(buffer + len, "\tsq.block_size = %d sq.max_count = %d\n",
+ sq.block_size, sq.max_count);
+ len += sprintf(buffer + len, "\tsq.count = %d sq.rear_size = %d\n", sq.count,
+ sq.rear_size);
+ len += sprintf(buffer + len, "\tsq.playing = %d sq.syncing = %d\n",
+ sq.playing, sq.syncing);
+ state.len = len;
+ return (0);
}
-static int state_release(void)
+static int
+state_release(void)
{
- state.busy = 0;
- return(0);
+ state.busy = 0;
+ return (0);
}
-static long state_read(char *dest, unsigned long count)
+static long
+state_read(char *dest, unsigned long count)
{
- int n = state.len-state.ptr;
- if (n > count)
- n = count;
- if (n <= 0)
- return(0);
- copy_to_user(dest, &state.buf[state.ptr], n);
- state.ptr += n;
- return(n);
+ int n = state.len - state.ptr;
+
+ if (n > count)
+ n = count;
+ if (n <= 0)
+ return (0);
+ copy_to_user(dest, &state.buf[state.ptr], n);
+ state.ptr += n;
+ return (n);
}
@@ -3032,230 +3359,241 @@ static long state_read(char *dest, unsigned long count)
/*** High level stuff ********************************************************/
-static int sound_open(struct inode *inode, struct file *file)
-{
- int dev = MINOR(inode->i_rdev) & 0x0f;
- int rc = 0;
-
- switch (dev) {
- case SND_DEV_STATUS:
- rc = state_open(file->f_flags);
- break;
- case SND_DEV_CTL:
- rc = mixer_open(file->f_flags);
- break;
- case SND_DEV_DSP:
- case SND_DEV_AUDIO:
- rc = sq_open(file->f_flags);
- if (rc == 0) {
- sound.minDev = dev;
- sound.soft = sound.dsp;
- sound.hard = sound.dsp;
- sound_init();
- if (dev == SND_DEV_AUDIO) {
- sound_set_speed(8000);
- sound_set_stereo(0);
- sound_set_format(AFMT_MU_LAW);
- }
- }
- break;
- default:
- rc = -ENXIO;
- }
+static int
+sound_open(struct inode *inode, struct file *file)
+{
+ int dev = MINOR(inode->i_rdev) & 0x0f;
+ int rc = 0;
+
+ switch (dev)
+ {
+ case SND_DEV_STATUS:
+ rc = state_open(file->f_flags);
+ break;
+ case SND_DEV_CTL:
+ rc = mixer_open(file->f_flags);
+ break;
+ case SND_DEV_DSP:
+ case SND_DEV_AUDIO:
+ rc = sq_open(file->f_flags);
+ if (rc == 0)
+ {
+ sound.minDev = dev;
+ sound.soft = sound.dsp;
+ sound.hard = sound.dsp;
+ sound_init();
+ if (dev == SND_DEV_AUDIO)
+ {
+ sound_set_speed(8000);
+ sound_set_stereo(0);
+ sound_set_format(AFMT_MU_LAW);
+ }
+ }
+ break;
+ default:
+ rc = -ENXIO;
+ }
#ifdef MODULE
- if (rc >= 0)
- MOD_INC_USE_COUNT;
+ if (rc >= 0)
+ MOD_INC_USE_COUNT;
#endif
- return(rc);
-}
-
-
-static int sound_fsync(struct file *filp, struct dentry *dentry)
-{
- int dev = MINOR(dentry->d_inode->i_rdev) & 0x0f;
-
- switch (dev) {
- case SND_DEV_STATUS:
- case SND_DEV_CTL:
- return(0);
- case SND_DEV_DSP:
- case SND_DEV_AUDIO:
- return(sq_sync());
- default:
- return(unknown_minor_dev("sound_fsync", dev));
- }
-}
-
-
-static void sound_release(struct inode *inode, struct file *file)
-{
- int dev = MINOR(inode->i_rdev);
-
- switch (dev & 0x0f) {
- case SND_DEV_STATUS:
- state_release();
- break;
- case SND_DEV_CTL:
- mixer_release();
- break;
- case SND_DEV_DSP:
- case SND_DEV_AUDIO:
- sq_release();
- sound.soft = sound.dsp;
- sound.hard = sound.dsp;
- sound_silence();
- break;
- default:
- unknown_minor_dev("sound_release", dev);
- return;
- }
+ return (rc);
+}
+
+
+static int
+sound_fsync(struct inode *inode, struct file *filp)
+{
+ int dev = MINOR(inode->i_rdev) & 0x0f;
+
+ switch (dev)
+ {
+ case SND_DEV_STATUS:
+ case SND_DEV_CTL:
+ return (0);
+ case SND_DEV_DSP:
+ case SND_DEV_AUDIO:
+ return (sq_sync());
+ default:
+ return (unknown_minor_dev("sound_fsync", dev));
+ }
+}
+
+
+static void
+sound_release(struct inode *inode, struct file *file)
+{
+ int dev = MINOR(inode->i_rdev);
+
+ switch (dev & 0x0f)
+ {
+ case SND_DEV_STATUS:
+ state_release();
+ break;
+ case SND_DEV_CTL:
+ mixer_release();
+ break;
+ case SND_DEV_DSP:
+ case SND_DEV_AUDIO:
+ sq_release();
+ sound.soft = sound.dsp;
+ sound.hard = sound.dsp;
+ sound_silence();
+ break;
+ default:
+ unknown_minor_dev("sound_release", dev);
+ return;
+ }
#ifdef MODULE
- MOD_DEC_USE_COUNT;
+ MOD_DEC_USE_COUNT;
#endif
}
-static long long sound_lseek(struct file *file, long long offset, int orig)
+static long long
+sound_lseek(struct inode *inode, struct file *file,
+ long long offset, int orig)
{
- return -ESPIPE;
+ return -ESPIPE;
}
-static long sound_read(struct inode *inode, struct file *file, char *buf,
- unsigned long count)
+static ssize_t sound_read(struct file *file, char *buf, szie_t count, loff_t *ppos)
{
- int dev = MINOR(inode->i_rdev);
+ int dev = MINOR(file->f_dentry->d_inode->i_rdev);
- switch (dev & 0x0f) {
- case SND_DEV_STATUS:
- return(state_read(buf, count));
- case SND_DEV_CTL:
- case SND_DEV_DSP:
- case SND_DEV_AUDIO:
- return(-EPERM);
- default:
- return(unknown_minor_dev("sound_read", dev));
- }
+ switch (dev & 0x0f)
+ {
+ case SND_DEV_STATUS:
+ return (state_read(buf, count));
+ case SND_DEV_CTL:
+ case SND_DEV_DSP:
+ case SND_DEV_AUDIO:
+ return (-EPERM);
+ default:
+ return (unknown_minor_dev("sound_read", dev));
+ }
}
-static long sound_write(struct inode *inode, struct file *file,
- const char *buf, unsigned long count)
+static ssize_t sound_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
- int dev = MINOR(inode->i_rdev);
+ int dev = MINOR(file->f_dentry->d_inode->i_rdev);
- switch (dev & 0x0f) {
- case SND_DEV_STATUS:
- case SND_DEV_CTL:
- return(-EPERM);
- case SND_DEV_DSP:
- case SND_DEV_AUDIO:
- return(sq_write(buf, count));
- default:
- return(unknown_minor_dev("sound_write", dev));
- }
+ switch (dev & 0x0f)
+ {
+ case SND_DEV_STATUS:
+ case SND_DEV_CTL:
+ return (-EPERM);
+ case SND_DEV_DSP:
+ case SND_DEV_AUDIO:
+ return (sq_write(buf, count));
+ default:
+ return (unknown_minor_dev("sound_write", dev));
+ }
}
static int unknown_minor_dev(char *fname, int dev)
{
- /* printk("%s: Unknown minor device %d\n", fname, dev); */
- return(-ENXIO);
-}
-
-
-static int sound_ioctl(struct inode *inode, struct file *file, u_int cmd,
- u_long arg)
-{
- int dev = MINOR(inode->i_rdev);
- u_long fmt;
- int data;
-
- switch (dev & 0x0f) {
- case SND_DEV_STATUS:
- return(-EPERM);
- case SND_DEV_CTL:
- return(mixer_ioctl(inode, file, cmd, arg));
- case SND_DEV_AUDIO:
- case SND_DEV_DSP:
- switch (cmd) {
- case SNDCTL_DSP_RESET:
- sq_reset();
- return(0);
- case SNDCTL_DSP_POST:
- case SNDCTL_DSP_SYNC:
- return(sound_fsync(file, file->f_dentry));
-
- /* ++TeSche: before changing any of these it's probably wise to
- * wait until sound playing has settled down
- */
- case SNDCTL_DSP_SPEED:
- sound_fsync(file, file->f_dentry);
- IOCTL_IN(arg, data);
- return(IOCTL_OUT(arg, sound_set_speed(data)));
- case SNDCTL_DSP_STEREO:
- sound_fsync(file, file->f_dentry);
- IOCTL_IN(arg, data);
- return(IOCTL_OUT(arg, sound_set_stereo(data)));
- case SOUND_PCM_WRITE_CHANNELS:
- sound_fsync(file, file->f_dentry);
- IOCTL_IN(arg, data);
- return(IOCTL_OUT(arg, sound_set_stereo(data-1)+1));
- case SNDCTL_DSP_SETFMT:
- sound_fsync(file, file->f_dentry);
- IOCTL_IN(arg, data);
- return(IOCTL_OUT(arg, sound_set_format(data)));
- case SNDCTL_DSP_GETFMTS:
- fmt = 0;
- if (sound.trans) {
- if (sound.trans->ct_ulaw)
- fmt |= AFMT_MU_LAW;
- if (sound.trans->ct_alaw)
- fmt |= AFMT_A_LAW;
- if (sound.trans->ct_s8)
- fmt |= AFMT_S8;
- if (sound.trans->ct_u8)
- fmt |= AFMT_U8;
- if (sound.trans->ct_s16be)
- fmt |= AFMT_S16_BE;
- if (sound.trans->ct_u16be)
- fmt |= AFMT_U16_BE;
- if (sound.trans->ct_s16le)
- fmt |= AFMT_S16_LE;
- if (sound.trans->ct_u16le)
- fmt |= AFMT_U16_LE;
- }
- return(IOCTL_OUT(arg, fmt));
- case SNDCTL_DSP_GETBLKSIZE:
- return(IOCTL_OUT(arg, 10240));
- case SNDCTL_DSP_SUBDIVIDE:
- case SNDCTL_DSP_SETFRAGMENT:
- break;
+ /* printk("%s: Unknown minor device %d\n", fname, dev); */
+ return (-ENXIO);
+}
+
+
+static int sound_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg)
+{
+ int dev = MINOR(inode->i_rdev);
+ u_long fmt;
+ int data;
+
+ switch (dev & 0x0f)
+ {
+ case SND_DEV_STATUS:
+ return (-EPERM);
+ case SND_DEV_CTL:
+ return (mixer_ioctl(inode, file, cmd, arg));
+ case SND_DEV_AUDIO:
+ case SND_DEV_DSP:
+ switch (cmd)
+ {
+ case SNDCTL_DSP_RESET:
+ sq_reset();
+ return (0);
+ case SNDCTL_DSP_POST:
+ case SNDCTL_DSP_SYNC:
+ return (sound_fsync(inode, file));
+
+ /* ++TeSche: before changing any of these it's probably wise to
+ * wait until sound playing has settled down
+ */
+ case SNDCTL_DSP_SPEED:
+ sound_fsync(inode, file);
+ IOCTL_IN(arg, data);
+ return (IOCTL_OUT(arg, sound_set_speed(data)));
+ case SNDCTL_DSP_STEREO:
+ sound_fsync(inode, file);
+ IOCTL_IN(arg, data);
+ return (IOCTL_OUT(arg, sound_set_stereo(data)));
+ case SOUND_PCM_WRITE_CHANNELS:
+ sound_fsync(inode, file);
+ IOCTL_IN(arg, data);
+ return (IOCTL_OUT(arg, sound_set_stereo(data - 1) + 1));
+ case SNDCTL_DSP_SETFMT:
+ sound_fsync(inode, file);
+ IOCTL_IN(arg, data);
+ return (IOCTL_OUT(arg, sound_set_format(data)));
+ case SNDCTL_DSP_GETFMTS:
+ fmt = 0;
+ if (sound.trans)
+ {
+ if (sound.trans->ct_ulaw)
+ fmt |= AFMT_MU_LAW;
+ if (sound.trans->ct_alaw)
+ fmt |= AFMT_A_LAW;
+ if (sound.trans->ct_s8)
+ fmt |= AFMT_S8;
+ if (sound.trans->ct_u8)
+ fmt |= AFMT_U8;
+ if (sound.trans->ct_s16be)
+ fmt |= AFMT_S16_BE;
+ if (sound.trans->ct_u16be)
+ fmt |= AFMT_U16_BE;
+ if (sound.trans->ct_s16le)
+ fmt |= AFMT_S16_LE;
+ if (sound.trans->ct_u16le)
+ fmt |= AFMT_U16_LE;
+ }
+ return (IOCTL_OUT(arg, fmt));
+ case SNDCTL_DSP_GETBLKSIZE:
+ return (IOCTL_OUT(arg, 10240));
+ case SNDCTL_DSP_SUBDIVIDE:
+ case SNDCTL_DSP_SETFRAGMENT:
+ break;
+ default:
+ return (mixer_ioctl(inode, file, cmd, arg));
+ }
+ break;
default:
- return(mixer_ioctl(inode, file, cmd, arg));
- }
- break;
-
- default:
- return(unknown_minor_dev("sound_ioctl", dev));
- }
- return(-EINVAL);
+ return (unknown_minor_dev("sound_ioctl", dev));
+ }
+ return (-EINVAL);
}
static struct file_operations sound_fops =
{
- sound_lseek,
- sound_read,
- sound_write,
- NULL,
- NULL, /* select */
- sound_ioctl,
- NULL,
- sound_open,
- sound_release,
- sound_fsync
+ sound_lseek,
+ sound_read,
+ sound_write,
+ NULL,
+ NULL, /* select */
+ sound_ioctl,
+ NULL,
+ sound_open,
+ sound_release,
+ sound_fsync
};
@@ -3263,171 +3601,186 @@ static struct file_operations sound_fops =
/*** Config & Setup **********************************************************/
-void soundcard_init(void)
+void
+soundcard_init(void)
{
- int has_sound = 0;
- int i;
+ int has_sound = 0;
+ int i;
- switch (m68k_machtype) {
+ switch (m68k_machtype)
+ {
#ifdef CONFIG_ATARI
- case MACH_ATARI:
- if (ATARIHW_PRESENT(PCM_8BIT)) {
- if (ATARIHW_PRESENT(CODEC))
- sound.mach = machFalcon;
- else if (ATARIHW_PRESENT(MICROWIRE))
- sound.mach = machTT;
- else
- break;
- if ((mfp.int_en_a & mfp.int_mk_a & 0x20) == 0)
- has_sound = 1;
- else
- printk("DMA sound driver: Timer A interrupt already in use\n");
- }
- break;
-
-#endif /* CONFIG_ATARI */
+ case MACH_ATARI:
+ if (ATARIHW_PRESENT(PCM_8BIT))
+ {
+ if (ATARIHW_PRESENT(CODEC))
+ sound.mach = machFalcon;
+ else if (ATARIHW_PRESENT(MICROWIRE))
+ sound.mach = machTT;
+ else
+ break;
+ if ((mfp.int_en_a & mfp.int_mk_a & 0x20) == 0)
+ has_sound = 1;
+ else
+ printk(KERN_ERR "DMA sound driver: Timer A interrupt already in use\n");
+ }
+ break;
+
+#endif /* CONFIG_ATARI */
#ifdef CONFIG_AMIGA
- case MACH_AMIGA:
- if (AMIGAHW_PRESENT(AMI_AUDIO)) {
- sound.mach = machAmiga;
- has_sound = 1;
- }
- break;
-#endif /* CONFIG_AMIGA */
- }
- if (!has_sound)
- return;
+ case MACH_AMIGA:
+ if (AMIGAHW_PRESENT(AMI_AUDIO))
+ {
+ sound.mach = machAmiga;
+ has_sound = 1;
+ }
+ break;
+#endif /* CONFIG_AMIGA */
+ }
+ if (!has_sound)
+ return;
- /* Set up sound queue, /dev/audio and /dev/dsp. */
- sound_buffers = kmalloc (numBufs * sizeof(char *), GFP_KERNEL);
- if (!sound_buffers) {
+ /* Set up sound queue, /dev/audio and /dev/dsp. */
+ sound_buffers = kmalloc(numBufs * sizeof(char *), GFP_KERNEL);
+
+ if (!sound_buffers)
+ {
out_of_memory:
- printk("DMA sound driver: Not enough buffer memory, driver disabled!\n");
- return;
- }
- for (i = 0; i < numBufs; i++) {
- sound_buffers[i] = sound.mach.dma_alloc (bufSize << 10, GFP_KERNEL);
- if (!sound_buffers[i]) {
- while (i--)
- sound.mach.dma_free (sound_buffers[i], bufSize << 10);
- kfree (sound_buffers);
- sound_buffers = 0;
- goto out_of_memory;
- }
- }
+ printk(KERN_ERR "DMA sound driver: Not enough buffer memory, driver disabled!\n");
+ return;
+ }
+ for (i = 0; i < numBufs; i++)
+ {
+ sound_buffers[i] = sound.mach.dma_alloc(bufSize << 10, GFP_KERNEL);
+ if (!sound_buffers[i])
+ {
+ while (i--)
+ sound.mach.dma_free(sound_buffers[i], bufSize << 10);
+ kfree(sound_buffers);
+ sound_buffers = 0;
+ goto out_of_memory;
+ }
+ }
#ifndef MODULE
- /* Register driver with the VFS. */
- register_chrdev(SOUND_MAJOR, "sound", &sound_fops);
+ /* Register driver with the VFS. */
+ register_chrdev(SOUND_MAJOR, "sound", &sound_fops);
#endif
- sq_init(numBufs, bufSize << 10, sound_buffers);
+ sq_init(numBufs, bufSize << 10, sound_buffers);
- /* Set up /dev/sndstat. */
- state_init();
+ /* Set up /dev/sndstat. */
+ state_init();
- /* Set up /dev/mixer. */
- mixer_init();
+ /* Set up /dev/mixer. */
+ mixer_init();
- if (!sound.mach.irqinit()) {
- printk("DMA sound driver: Interrupt initialization failed\n");
- return;
- }
+ if (!sound.mach.irqinit())
+ {
+ printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n");
+ return;
+ }
#ifdef MODULE
- irq_installed = 1;
+ irq_installed = 1;
#endif
- printk("DMA sound driver installed, using %d buffers of %dk.\n", numBufs,
- bufSize);
+ printk(KERN_INFO "DMA sound driver installed, using %d buffers of %dk.\n", numBufs,
+ bufSize);
- return;
+ return;
}
void sound_setup(char *str, int *ints)
{
- /* ++Martin: stub, could possibly be merged with soundcard.c et al later */
+ /* ++Martin: stub, could possibly be merged with soundcard.c et al later */
}
#define MAXARGS 8 /* Should be sufficient for now */
-void dmasound_setup(char *str, int *ints)
-{
- /* check the bootstrap parameter for "dmasound=" */
-
- switch (ints[0]) {
- case 3:
- if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS))
- printk("dmasound_setup: illegal catch radius, using default = %d\n", catchRadius);
- else
- catchRadius = ints[3];
- /* fall through */
- case 2:
- if (ints[1] < MIN_BUFFERS)
- printk("dmasound_setup: illegal number of buffers, using default = %d\n", numBufs);
- else
- numBufs = ints[1];
- if (ints[2] < MIN_BUFSIZE || ints[2] > MAX_BUFSIZE)
- printk("dmasound_setup: illegal buffer size, using default = %d\n", bufSize);
- else
- bufSize = ints[2];
- break;
- case 0:
- break;
- default:
- printk("dmasound_setup: illegal number of arguments\n");
- }
+void
+dmasound_setup(char *str, int *ints)
+{
+ /* check the bootstrap parameter for "dmasound=" */
+
+ switch (ints[0])
+ {
+ case 3:
+ if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS))
+ printk(KERN_WARNING "dmasound_setup: illegal catch radius, using default = %d\n", catchRadius);
+ else
+ catchRadius = ints[3];
+ /* fall through */
+ case 2:
+ if (ints[1] < MIN_BUFFERS)
+ printk(KERN_WARNING "dmasound_setup: illegal number of buffers, using default = %d\n", numBufs);
+ else
+ numBufs = ints[1];
+ if (ints[2] < MIN_BUFSIZE || ints[2] > MAX_BUFSIZE)
+ printk(KERN_WARNING "dmasound_setup: illegal buffer size, using default = %d\n", bufSize);
+ else
+ bufSize = ints[2];
+ break;
+ case 0:
+ break;
+ default:
+ printk(KERN_WARNING "dmasound_setup: illegal number of arguments\n");
+ }
}
#ifdef MODULE
-static int dmasound[MAXARGS] = { 0 };
+static int dmasound[MAXARGS] = {
+ 0
+};
int init_module(void)
{
- int err, i = 0;
- int ints[MAXARGS+1];
+ int err, i = 0;
+ int ints[MAXARGS + 1];
- while (i < MAXARGS && dmasound[i])
- ints[i + 1] = dmasound[i++];
- ints[0] = i;
+ while (i < MAXARGS && dmasound[i])
+ ints[i + 1] = dmasound[i++];
+ ints[0] = i;
- if (i)
- dmasound_setup("dmasound=", ints);
+ if (i)
+ dmasound_setup("dmasound=", ints);
- err = register_chrdev(SOUND_MAJOR, "sound", &sound_fops);
- if (err) {
- printk("dmasound: driver already loaded/included in kernel\n");
- return err;
- }
- chrdev_registered = 1;
- soundcard_init();
+ err = register_chrdev(SOUND_MAJOR, "sound", &sound_fops);
+ if (err)
+ {
+ printk(KERN_ERR "dmasound: driver already loaded/included in kernel\n");
+ return err;
+ }
+ chrdev_registered = 1;
+ soundcard_init();
- return 0;
+ return 0;
}
void cleanup_module(void)
{
- int i;
-
- if (MOD_IN_USE)
- return;
+ int i;
- if (chrdev_registered)
- unregister_chrdev(SOUND_MAJOR, "sound");
+ if (MOD_IN_USE)
+ return;
- if (irq_installed) {
- sound_silence();
- sound.mach.irqcleanup();
- }
+ if (chrdev_registered)
+ unregister_chrdev(SOUND_MAJOR, "sound");
- if (sound_buffers) {
- for (i = 0; i < numBufs; i++)
- sound.mach.dma_free(sound_buffers[i], bufSize << 10);
- kfree(sound_buffers);
- }
+ if (irq_installed)
+ {
+ sound_silence();
+ sound.mach.irqcleanup();
+ }
+ if (sound_buffers)
+ {
+ for (i = 0; i < numBufs; i++)
+ sound.mach.dma_free(sound_buffers[i], bufSize << 10);
+ kfree(sound_buffers);
+ }
}
-#endif /* MODULE */
+#endif /* MODULE */
diff --git a/drivers/sound/gus_card.c b/drivers/sound/gus_card.c
index 1c8881684..eba6072d6 100644
--- a/drivers/sound/gus_card.c
+++ b/drivers/sound/gus_card.c
@@ -11,159 +11,154 @@
* for more info.
*/
#include <linux/config.h>
-
+#include <linux/module.h>
#include "sound_config.h"
+#include "soundmodule.h"
-#if defined(CONFIG_GUSHW)
+#if defined(CONFIG_GUSHW) || defined(MODULE)
#include "gus_hw.h"
-void gusintr (int irq, void *dev_id, struct pt_regs *dummy);
+void gusintr(int irq, void *dev_id, struct pt_regs *dummy);
-int gus_base, gus_irq, gus_dma;
+int gus_base = 0, gus_irq = 0, gus_dma = 0;
extern int gus_wave_volume;
extern int gus_pcm_volume;
extern int have_gus_max;
int gus_pnp_flag = 0;
void
-attach_gus_card (struct address_info *hw_config)
+attach_gus_card(struct address_info *hw_config)
{
- snd_set_irq_handler (hw_config->irq, gusintr, "Gravis Ultrasound", hw_config->osp);
+ snd_set_irq_handler(hw_config->irq, gusintr, "Gravis Ultrasound", hw_config->osp);
- gus_wave_init (hw_config);
+ gus_wave_init(hw_config);
- request_region (hw_config->io_base, 16, "GUS");
- request_region (hw_config->io_base + 0x100, 12, "GUS"); /* 0x10c-> is MAX */
+ request_region(hw_config->io_base, 16, "GUS");
+ request_region(hw_config->io_base + 0x100, 12, "GUS"); /* 0x10c-> is MAX */
- if (sound_alloc_dma (hw_config->dma, "GUS"))
- printk ("gus_card.c: Can't allocate DMA channel\n");
- if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma)
- if (sound_alloc_dma (hw_config->dma2, "GUS(2)"))
- printk ("gus_card.c: Can't allocate DMA channel2\n");
-#ifdef CONFIG_MIDI
- gus_midi_init ();
+ if (sound_alloc_dma(hw_config->dma, "GUS"))
+ printk("gus_card.c: Can't allocate DMA channel\n");
+ if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma)
+ if (sound_alloc_dma(hw_config->dma2, "GUS(2)"))
+ printk("gus_card.c: Can't allocate DMA channel 2\n");
+#if defined(CONFIG_MIDI)
+ gus_midi_init(hw_config);
#endif
}
int
-probe_gus (struct address_info *hw_config)
+probe_gus(struct address_info *hw_config)
{
- int irq;
- int io_addr;
-
- if (hw_config->card_subtype == 1)
- gus_pnp_flag = 1;
-
- irq = hw_config->irq;
-
- if (hw_config->card_subtype == 0) /* GUS/MAX/ACE */
- if (irq != 3 && irq != 5 && irq != 7 && irq != 9 &&
- irq != 11 && irq != 12 && irq != 15)
- {
- printk ("GUS: Unsupported IRQ %d\n", irq);
- return 0;
- }
-
- if (check_region (hw_config->io_base, 16))
- printk ("GUS: I/O range conflict (1)\n");
- else if (check_region (hw_config->io_base + 0x100, 16))
- printk ("GUS: I/O range conflict (2)\n");
- else if (gus_wave_detect (hw_config->io_base))
- return 1;
+ int irq;
+ int io_addr;
+
+ if (hw_config->card_subtype == 1)
+ gus_pnp_flag = 1;
+
+ irq = hw_config->irq;
+
+ if (hw_config->card_subtype == 0) /* GUS/MAX/ACE */
+ if (irq != 3 && irq != 5 && irq != 7 && irq != 9 &&
+ irq != 11 && irq != 12 && irq != 15)
+ {
+ printk("GUS: Unsupported IRQ %d\n", irq);
+ return 0;
+ }
+ if (check_region(hw_config->io_base, 16))
+ printk("GUS: I/O range conflict (1)\n");
+ else if (check_region(hw_config->io_base + 0x100, 16))
+ printk("GUS: I/O range conflict (2)\n");
+ else if (gus_wave_detect(hw_config->io_base))
+ return 1;
#ifndef EXCLUDE_GUS_IODETECT
- /*
- * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6)
- */
-
- for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10)
- if (io_addr != hw_config->io_base) /*
- * Already tested
- */
- if (!check_region (io_addr, 16))
- if (!check_region (io_addr + 0x100, 16))
- if (gus_wave_detect (io_addr))
- {
- hw_config->io_base = io_addr;
- return 1;
- }
-
+ /*
+ * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6)
+ */
+
+ for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10)
+ if (io_addr != hw_config->io_base) /*
+ * Already tested
+ */
+ if (!check_region(io_addr, 16))
+ if (!check_region(io_addr + 0x100, 16))
+ if (gus_wave_detect(io_addr))
+ {
+ hw_config->io_base = io_addr;
+ return 1;
+ }
#endif
- return 0;
+ return 0;
}
void
-unload_gus (struct address_info *hw_config)
+unload_gus(struct address_info *hw_config)
{
- DDB (printk ("unload_gus(%x)\n", hw_config->io_base));
+ DDB(printk("unload_gus(%x)\n", hw_config->io_base));
- gus_wave_unload ();
+ gus_wave_unload(hw_config);
- release_region (hw_config->io_base, 16);
- release_region (hw_config->io_base + 0x100, 12); /* 0x10c-> is MAX */
- snd_release_irq (hw_config->irq);
+ release_region(hw_config->io_base, 16);
+ release_region(hw_config->io_base + 0x100, 12); /* 0x10c-> is MAX */
+ snd_release_irq(hw_config->irq);
- sound_free_dma (hw_config->dma);
+ sound_free_dma(hw_config->dma);
- if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma)
- sound_free_dma (hw_config->dma2);
+ if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma)
+ sound_free_dma(hw_config->dma2);
}
void
-gusintr (int irq, void *dev_id, struct pt_regs *dummy)
+gusintr(int irq, void *dev_id, struct pt_regs *dummy)
{
- unsigned char src;
- extern int gus_timer_enabled;
+ unsigned char src;
+ extern int gus_timer_enabled;
- sti ();
+ sti();
#ifdef CONFIG_GUSMAX
- if (have_gus_max)
- adintr (irq, NULL, NULL);
+ if (have_gus_max)
+ adintr(irq, NULL, NULL);
#endif
- while (1)
- {
- if (!(src = inb (u_IrqStatus)))
- return;
-
- if (src & DMA_TC_IRQ)
- {
- guswave_dma_irq ();
- }
-
- if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ))
- {
-#ifdef CONFIG_MIDI
- gus_midi_interrupt (0);
+ while (1)
+ {
+ if (!(src = inb(u_IrqStatus)))
+ return;
+
+ if (src & DMA_TC_IRQ)
+ {
+ guswave_dma_irq();
+ }
+ if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ))
+ {
+#if defined(CONFIG_MIDI)
+ gus_midi_interrupt(0);
#endif
- }
-
- if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ))
- {
-#ifdef CONFIG_SEQUENCER
- if (gus_timer_enabled)
- {
- sound_timer_interrupt ();
- }
-
- gus_write8 (0x45, 0); /* Ack IRQ */
- gus_timer_command (4, 0x80); /* Reset IRQ flags */
+ }
+ if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ))
+ {
+#if defined(CONFIG_SEQUENCER) || defined(CONFIG_SEQUENCER_MODULE)
+ if (gus_timer_enabled)
+ {
+ sound_timer_interrupt();
+ }
+ gus_write8(0x45, 0); /* Ack IRQ */
+ gus_timer_command(4, 0x80); /* Reset IRQ flags */
#else
- gus_write8 (0x45, 0); /* Stop timers */
+ gus_write8(0x45, 0); /* Stop timers */
#endif
- }
-
- if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ))
- {
- gus_voice_irq ();
- }
- }
+ }
+ if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ))
+ {
+ gus_voice_irq();
+ }
+ }
}
#endif
@@ -171,36 +166,103 @@ gusintr (int irq, void *dev_id, struct pt_regs *dummy)
/*
* Some extra code for the 16 bit sampling option
*/
-#if defined(CONFIG_GUS16)
+#ifdef CONFIG_GUS16
int
-probe_gus_db16 (struct address_info *hw_config)
+probe_gus_db16(struct address_info *hw_config)
{
- return ad1848_detect (hw_config->io_base, NULL, hw_config->osp);
+ return ad1848_detect(hw_config->io_base, NULL, hw_config->osp);
}
void
-attach_gus_db16 (struct address_info *hw_config)
+attach_gus_db16(struct address_info *hw_config)
{
-#ifdef CONFIG_GUSHW
- gus_pcm_volume = 100;
- gus_wave_volume = 90;
+#if defined(CONFIG_GUSHW) || defined(MODULE)
+ gus_pcm_volume = 100;
+ gus_wave_volume = 90;
#endif
- ad1848_init ("GUS 16 bit sampling", hw_config->io_base,
- hw_config->irq,
- hw_config->dma,
- hw_config->dma, 0,
- hw_config->osp);
+ hw_config->slots[3] = ad1848_init("GUS 16 bit sampling", hw_config->io_base,
+ hw_config->irq,
+ hw_config->dma,
+ hw_config->dma, 0,
+ hw_config->osp);
}
void
-unload_gus_db16 (struct address_info *hw_config)
+unload_gus_db16(struct address_info *hw_config)
+{
+
+ ad1848_unload(hw_config->io_base,
+ hw_config->irq,
+ hw_config->dma,
+ hw_config->dma, 0);
+ sound_unload_audiodev(hw_config->slots[3]);
+}
+#endif
+
+#ifdef MODULE
+
+static struct address_info config;
+
+/*
+ * Note DMA2 of -1 has the right meaning in the GUS driver as well
+ * as here.
+ */
+
+int io = -1;
+int irq = -1;
+int dma = -1;
+int dma16 = -1; /* Set this for modules that need it */
+int type = 0; /* 1 for PnP */
+int gus16 = 0;
+static int db16 = 0; /* Has a Gus16 AD1848 on it */
+
+MODULE_PARM(io, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(dma, "i");
+MODULE_PARM(dma16, "i");
+MODULE_PARM(type, "i");
+MODULE_PARM(gus16, "i");
+MODULE_PARM(db16, "i");
+
+int
+init_module(void)
{
+ printk("Gravis Ultrasound audio driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+
+ if (io == -1 || dma == -1 || irq == -1)
+ {
+ printk("I/O, IRQ, and DMA are mandatory\n");
+ return -EINVAL;
+ }
+ config.io_base = io;
+ config.irq = irq;
+ config.dma = dma;
+ config.dma2 = dma16;
+ config.card_subtype = type;
- ad1848_unload (hw_config->io_base,
- hw_config->irq,
- hw_config->dma,
- hw_config->dma, 0);
+#if defined(CONFIG_GUS16)
+ if (probe_gus_db16(&config) && gus16)
+ {
+ attach_gus_db16(&config);
+ db16 = 1;
+ }
+#endif
+ if (probe_gus(&config) == 0)
+ return -ENODEV;
+ attach_gus_card(&config);
+ SOUND_LOCK;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ if (db16)
+ unload_gus_db16(&config);
+ unload_gus(&config);
+ SOUND_LOCK_END;
}
+
#endif
diff --git a/drivers/sound/gus_midi.c b/drivers/sound/gus_midi.c
index 9fc38fc88..fdbde32fd 100644
--- a/drivers/sound/gus_midi.c
+++ b/drivers/sound/gus_midi.c
@@ -17,7 +17,7 @@
#include "gus_hw.h"
-#if defined(CONFIG_GUSHW) && defined(CONFIG_MIDI)
+#if ( defined(CONFIG_GUSHW) && defined(CONFIG_MIDI) ) || defined (MODULE)
static int midi_busy = 0, input_opened = 0;
static int my_dev;
@@ -33,183 +33,178 @@ static volatile unsigned char qhead, qtail;
extern int gus_base, gus_irq, gus_dma;
extern int *gus_osp;
-static int
-GUS_MIDI_STATUS (void)
+static int
+GUS_MIDI_STATUS(void)
{
- return inb (u_MidiStatus);
+ return inb(u_MidiStatus);
}
static int
-gus_midi_open (int dev, int mode,
- void (*input) (int dev, unsigned char data),
- void (*output) (int dev)
+gus_midi_open(int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
)
{
- if (midi_busy)
- {
- printk ("GUS: Midi busy\n");
- return -EBUSY;
- }
-
- outb ((MIDI_RESET), u_MidiControl);
- gus_delay ();
-
- gus_midi_control = 0;
- input_opened = 0;
-
- if (mode == OPEN_READ || mode == OPEN_READWRITE)
- if (!gus_pnp_flag)
- {
- gus_midi_control |= MIDI_ENABLE_RCV;
- input_opened = 1;
- }
-
-
- outb ((gus_midi_control), u_MidiControl); /* Enable */
-
- midi_busy = 1;
- qlen = qhead = qtail = output_used = 0;
- midi_input_intr = input;
-
- return 0;
+ if (midi_busy)
+ {
+ printk("GUS: Midi busy\n");
+ return -EBUSY;
+ }
+ outb((MIDI_RESET), u_MidiControl);
+ gus_delay();
+
+ gus_midi_control = 0;
+ input_opened = 0;
+
+ if (mode == OPEN_READ || mode == OPEN_READWRITE)
+ if (!gus_pnp_flag)
+ {
+ gus_midi_control |= MIDI_ENABLE_RCV;
+ input_opened = 1;
+ }
+ outb((gus_midi_control), u_MidiControl); /* Enable */
+
+ midi_busy = 1;
+ qlen = qhead = qtail = output_used = 0;
+ midi_input_intr = input;
+
+ return 0;
}
static int
-dump_to_midi (unsigned char midi_byte)
+dump_to_midi(unsigned char midi_byte)
{
- unsigned long flags;
- int ok = 0;
-
- output_used = 1;
-
- save_flags (flags);
- cli ();
-
- if (GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY)
- {
- ok = 1;
- outb ((midi_byte), u_MidiData);
- }
- else
- {
- /*
- * Enable Midi xmit interrupts (again)
- */
- gus_midi_control |= MIDI_ENABLE_XMIT;
- outb ((gus_midi_control), u_MidiControl);
- }
-
- restore_flags (flags);
- return ok;
+ unsigned long flags;
+ int ok = 0;
+
+ output_used = 1;
+
+ save_flags(flags);
+ cli();
+
+ if (GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY)
+ {
+ ok = 1;
+ outb((midi_byte), u_MidiData);
+ } else
+ {
+ /*
+ * Enable Midi xmit interrupts (again)
+ */
+ gus_midi_control |= MIDI_ENABLE_XMIT;
+ outb((gus_midi_control), u_MidiControl);
+ }
+
+ restore_flags(flags);
+ return ok;
}
static void
-gus_midi_close (int dev)
+gus_midi_close(int dev)
{
- /*
- * Reset FIFO pointers, disable intrs
- */
+ /*
+ * Reset FIFO pointers, disable intrs
+ */
- outb ((MIDI_RESET), u_MidiControl);
- midi_busy = 0;
+ outb((MIDI_RESET), u_MidiControl);
+ midi_busy = 0;
}
static int
-gus_midi_out (int dev, unsigned char midi_byte)
+gus_midi_out(int dev, unsigned char midi_byte)
{
- unsigned long flags;
+ unsigned long flags;
- /*
- * Drain the local queue first
- */
+ /*
+ * Drain the local queue first
+ */
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- while (qlen && dump_to_midi (tmp_queue[qhead]))
- {
- qlen--;
- qhead++;
- }
+ while (qlen && dump_to_midi(tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
- restore_flags (flags);
+ restore_flags(flags);
- /*
- * Output the byte if the local queue is empty.
- */
+ /*
+ * Output the byte if the local queue is empty.
+ */
- if (!qlen)
- if (dump_to_midi (midi_byte))
- return 1; /*
- * OK
- */
+ if (!qlen)
+ if (dump_to_midi(midi_byte))
+ return 1; /*
+ * OK
+ */
- /*
- * Put to the local queue
- */
+ /*
+ * Put to the local queue
+ */
- if (qlen >= 256)
- return 0; /*
+ if (qlen >= 256)
+ return 0; /*
* Local queue full
*/
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- tmp_queue[qtail] = midi_byte;
- qlen++;
- qtail++;
+ tmp_queue[qtail] = midi_byte;
+ qlen++;
+ qtail++;
- restore_flags (flags);
+ restore_flags(flags);
- return 1;
+ return 1;
}
static int
-gus_midi_start_read (int dev)
+gus_midi_start_read(int dev)
{
- return 0;
+ return 0;
}
static int
-gus_midi_end_read (int dev)
+gus_midi_end_read(int dev)
{
- return 0;
+ return 0;
}
static int
-gus_midi_ioctl (int dev, unsigned cmd, caddr_t arg)
+gus_midi_ioctl(int dev, unsigned cmd, caddr_t arg)
{
- return -EINVAL;
+ return -EINVAL;
}
static void
-gus_midi_kick (int dev)
+gus_midi_kick(int dev)
{
}
static int
-gus_midi_buffer_status (int dev)
+gus_midi_buffer_status(int dev)
{
- unsigned long flags;
-
- if (!output_used)
- return 0;
+ unsigned long flags;
- save_flags (flags);
- cli ();
+ if (!output_used)
+ return 0;
- if (qlen && dump_to_midi (tmp_queue[qhead]))
- {
- qlen--;
- qhead++;
- }
+ save_flags(flags);
+ cli();
- restore_flags (flags);
+ if (qlen && dump_to_midi(tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
+ restore_flags(flags);
- return (qlen > 0) | !(GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY);
+ return (qlen > 0) | !(GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY);
}
#define MIDI_SYNTH_NAME "Gravis Ultrasound Midi"
@@ -218,81 +213,81 @@ gus_midi_buffer_status (int dev)
static struct midi_operations gus_midi_operations =
{
- {"Gravis UltraSound Midi", 0, 0, SNDCARD_GUS},
- &std_midi_synth,
- {0},
- gus_midi_open,
- gus_midi_close,
- gus_midi_ioctl,
- gus_midi_out,
- gus_midi_start_read,
- gus_midi_end_read,
- gus_midi_kick,
- NULL, /*
+ {"Gravis UltraSound Midi", 0, 0, SNDCARD_GUS},
+ &std_midi_synth,
+ {0},
+ gus_midi_open,
+ gus_midi_close,
+ gus_midi_ioctl,
+ gus_midi_out,
+ gus_midi_start_read,
+ gus_midi_end_read,
+ gus_midi_kick,
+ NULL, /*
* command
*/
- gus_midi_buffer_status,
- NULL
+ gus_midi_buffer_status,
+ NULL
};
void
-gus_midi_init (void)
+gus_midi_init(struct address_info *hw_config)
{
- if (num_midis >= MAX_MIDI_DEV)
- {
- printk ("Sound: Too many midi devices detected\n");
- return;
- }
-
- outb ((MIDI_RESET), u_MidiControl);
-
- std_midi_synth.midi_dev = my_dev = num_midis;
- midi_devs[num_midis++] = &gus_midi_operations;
- sequencer_init ();
- return;
+ int dev = sound_alloc_mididev();
+
+ if (dev == -1)
+ {
+ printk(KERN_INFO "gus_midi: Too many midi devices detected\n");
+ return;
+ }
+ outb((MIDI_RESET), u_MidiControl);
+
+ std_midi_synth.midi_dev = my_dev = dev;
+ hw_config->slots[2] = dev;
+ midi_devs[dev] = &gus_midi_operations;
+ sequencer_init();
+ return;
}
void
-gus_midi_interrupt (int dummy)
+gus_midi_interrupt(int dummy)
{
- volatile unsigned char stat, data;
- unsigned long flags;
- int timeout = 10;
-
- save_flags (flags);
- cli ();
-
- while (timeout-- > 0 && (stat = GUS_MIDI_STATUS ()) & (MIDI_RCV_FULL | MIDI_XMIT_EMPTY))
- {
- if (stat & MIDI_RCV_FULL)
- {
- data = inb (u_MidiData);
- if (input_opened)
- midi_input_intr (my_dev, data);
- }
-
- if (stat & MIDI_XMIT_EMPTY)
- {
- while (qlen && dump_to_midi (tmp_queue[qhead]))
- {
- qlen--;
- qhead++;
- }
-
- if (!qlen)
- {
- /*
- * Disable Midi output interrupts, since no data in the buffer
- */
- gus_midi_control &= ~MIDI_ENABLE_XMIT;
- outb ((gus_midi_control), u_MidiControl);
- outb ((gus_midi_control), u_MidiControl);
- }
- }
-
- }
-
- restore_flags (flags);
+ volatile unsigned char stat, data;
+ unsigned long flags;
+ int timeout = 10;
+
+ save_flags(flags);
+ cli();
+
+ while (timeout-- > 0 && (stat = GUS_MIDI_STATUS()) & (MIDI_RCV_FULL | MIDI_XMIT_EMPTY))
+ {
+ if (stat & MIDI_RCV_FULL)
+ {
+ data = inb(u_MidiData);
+ if (input_opened)
+ midi_input_intr(my_dev, data);
+ }
+ if (stat & MIDI_XMIT_EMPTY)
+ {
+ while (qlen && dump_to_midi(tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
+
+ if (!qlen)
+ {
+ /*
+ * Disable Midi output interrupts, since no data in the buffer
+ */
+ gus_midi_control &= ~MIDI_ENABLE_XMIT;
+ outb((gus_midi_control), u_MidiControl);
+ outb((gus_midi_control), u_MidiControl);
+ }
+ }
+ }
+
+ restore_flags(flags);
}
#endif
diff --git a/drivers/sound/gus_vol.c b/drivers/sound/gus_vol.c
index 5eaecc4e3..a42c5c6e1 100644
--- a/drivers/sound/gus_vol.c
+++ b/drivers/sound/gus_vol.c
@@ -1,3 +1,4 @@
+
/*
* gus_vol.c - Compute volume for GUS.
*/
@@ -11,7 +12,7 @@
#include <linux/config.h>
#include "sound_config.h"
-#ifdef CONFIG_GUSHW
+#if defined(CONFIG_GUSHW) || defined(MODULE)
#include "gus_linearvol.h"
#define GUS_VOLUME gus_wave_volume
@@ -34,87 +35,85 @@ extern int gus_wave_volume;
* basses. The normal value is 64. Strings are assigned lower values.
*/
unsigned short
-gus_adagio_vol (int vel, int mainv, int xpn, int voicev)
+gus_adagio_vol(int vel, int mainv, int xpn, int voicev)
{
- int i, m, n, x;
+ int i, m, n, x;
- /*
- * A voice volume of 64 is considered neutral, so adjust the main volume if
- * something other than this neutral value was assigned in the patch
- * library.
- */
- x = 256 + 6 * (voicev - 64);
+ /*
+ * A voice volume of 64 is considered neutral, so adjust the main volume if
+ * something other than this neutral value was assigned in the patch
+ * library.
+ */
+ x = 256 + 6 * (voicev - 64);
- /*
- * Boost expression by voice volume above neutral.
- */
- if (voicev > 65)
- xpn += voicev - 64;
- xpn += (voicev - 64) / 2;
+ /*
+ * Boost expression by voice volume above neutral.
+ */
+ if (voicev > 65)
+ xpn += voicev - 64;
+ xpn += (voicev - 64) / 2;
- /*
- * Combine multiplicative and level components.
- */
- x = vel * xpn * 6 + (voicev / 4) * x;
+ /*
+ * Combine multiplicative and level components.
+ */
+ x = vel * xpn * 6 + (voicev / 4) * x;
#ifdef GUS_VOLUME
- /*
- * Further adjustment by installation-specific master volume control
- * (default 60).
- */
- x = (x * GUS_VOLUME * GUS_VOLUME) / 10000;
+ /*
+ * Further adjustment by installation-specific master volume control
+ * (default 60).
+ */
+ x = (x * GUS_VOLUME * GUS_VOLUME) / 10000;
#endif
#ifdef GUS_USE_CHN_MAIN_VOLUME
- /*
- * Experimental support for the channel main volume
- */
+ /*
+ * Experimental support for the channel main volume
+ */
- mainv = (mainv / 2) + 64; /* Scale to 64 to 127 */
- x = (x * mainv * mainv) / 16384;
+ mainv = (mainv / 2) + 64; /* Scale to 64 to 127 */
+ x = (x * mainv * mainv) / 16384;
#endif
- if (x < 2)
- return (0);
- else if (x >= 65535)
- return ((15 << 8) | 255);
-
- /*
- * Convert to GUS's logarithmic form with 4 bit exponent i and 8 bit
- * mantissa m.
- */
- n = x;
- i = 7;
- if (n < 128)
- {
- while (i > 0 && n < (1 << i))
- i--;
- }
- else
- while (n > 255)
- {
- n >>= 1;
- i++;
- }
- /*
- * Mantissa is part of linear volume not expressed in exponent. (This is
- * not quite like real logs -- I wonder if it's right.)
- */
- m = x - (1 << i);
-
- /*
- * Adjust mantissa to 8 bits.
- */
- if (m > 0)
- {
- if (i > 8)
- m >>= i - 8;
- else if (i < 8)
- m <<= 8 - i;
- }
-
- return ((i << 8) + m);
+ if (x < 2)
+ return (0);
+ else if (x >= 65535)
+ return ((15 << 8) | 255);
+
+ /*
+ * Convert to GUS's logarithmic form with 4 bit exponent i and 8 bit
+ * mantissa m.
+ */
+ n = x;
+ i = 7;
+ if (n < 128)
+ {
+ while (i > 0 && n < (1 << i))
+ i--;
+ } else
+ while (n > 255)
+ {
+ n >>= 1;
+ i++;
+ }
+ /*
+ * Mantissa is part of linear volume not expressed in exponent. (This is
+ * not quite like real logs -- I wonder if it's right.)
+ */
+ m = x - (1 << i);
+
+ /*
+ * Adjust mantissa to 8 bits.
+ */
+ if (m > 0)
+ {
+ if (i > 8)
+ m >>= i - 8;
+ else if (i < 8)
+ m <<= 8 - i;
+ }
+ return ((i << 8) + m);
}
/*
@@ -124,31 +123,31 @@ gus_adagio_vol (int vel, int mainv, int xpn, int voicev)
*/
unsigned short
-gus_linear_vol (int vol, int mainvol)
+gus_linear_vol(int vol, int mainvol)
{
- int mixer_mainvol;
+ int mixer_mainvol;
- if (vol <= 0)
- vol = 0;
- else if (vol >= 127)
- vol = 127;
+ if (vol <= 0)
+ vol = 0;
+ else if (vol >= 127)
+ vol = 127;
#ifdef GUS_VOLUME
- mixer_mainvol = GUS_VOLUME;
+ mixer_mainvol = GUS_VOLUME;
#else
- mixer_mainvol = 100;
+ mixer_mainvol = 100;
#endif
#ifdef GUS_USE_CHN_MAIN_VOLUME
- if (mainvol <= 0)
- mainvol = 0;
- else if (mainvol >= 127)
- mainvol = 127;
+ if (mainvol <= 0)
+ mainvol = 0;
+ else if (mainvol >= 127)
+ mainvol = 127;
#else
- mainvol = 127;
+ mainvol = 127;
#endif
- return gus_linearvol[(((vol * mainvol) / 127) * mixer_mainvol) / 100];
+ return gus_linearvol[(((vol * mainvol) / 127) * mixer_mainvol) / 100];
}
#endif
diff --git a/drivers/sound/gus_wave.c b/drivers/sound/gus_wave.c
index 8c1523750..2814ffe12 100644
--- a/drivers/sound/gus_wave.c
+++ b/drivers/sound/gus_wave.c
@@ -19,7 +19,7 @@
#include <linux/ultrasound.h>
#include "gus_hw.h"
-#if defined(CONFIG_GUSHW)
+#if defined(CONFIG_GUSHW) || defined(MODULE)
#define GUS_BANK_SIZE (((iw_mode) ? 256*1024*1024 : 256*1024))
@@ -30,38 +30,39 @@
struct voice_info
{
- unsigned long orig_freq;
- unsigned long current_freq;
- unsigned long mode;
- int fixed_pitch;
- int bender;
- int bender_range;
- int panning;
- int midi_volume;
- unsigned int initial_volume;
- unsigned int current_volume;
- int loop_irq_mode, loop_irq_parm;
+ unsigned long orig_freq;
+ unsigned long current_freq;
+ unsigned long mode;
+ int fixed_pitch;
+ int bender;
+ int bender_range;
+ int panning;
+ int midi_volume;
+ unsigned int initial_volume;
+ unsigned int current_volume;
+ int loop_irq_mode, loop_irq_parm;
#define LMODE_FINISH 1
#define LMODE_PCM 2
#define LMODE_PCM_STOP 3
- int volume_irq_mode, volume_irq_parm;
+ int volume_irq_mode, volume_irq_parm;
#define VMODE_HALT 1
#define VMODE_ENVELOPE 2
#define VMODE_START_NOTE 3
- int env_phase;
- unsigned char env_rate[6];
- unsigned char env_offset[6];
+ int env_phase;
+ unsigned char env_rate[6];
+ unsigned char env_offset[6];
- /*
- * Volume computation parameters for gus_adagio_vol()
- */
- int main_vol, expression_vol, patch_vol;
+ /*
+ * Volume computation parameters for gus_adagio_vol()
+ */
+ int main_vol, expression_vol, patch_vol;
- /* Variables for "Ultraclick" removal */
- int dev_pending, note_pending, volume_pending, sample_pending;
- char kill_pending;
- long offset_pending;
+ /* Variables for "Ultraclick" removal */
+ int dev_pending, note_pending, volume_pending,
+ sample_pending;
+ char kill_pending;
+ long offset_pending;
};
@@ -73,7 +74,6 @@ extern int gus_pnp_flag;
static int gus_dma2 = -1;
static int dual_dma_mode = 0;
static long gus_mem_size = 0;
-static long gus_rom_size = 0;
static long free_mem_ptr = 0;
static int gus_busy = 0;
static int gus_no_dma = 0;
@@ -137,26 +137,26 @@ static struct voice_info voices[32];
static int freq_div_table[] =
{
- 44100,
- 44100, /* 14 */
- 41160, /* 15 */
- 38587, /* 16 */
- 36317, /* 17 */
- 34300, /* 18 */
- 32494, /* 19 */
- 30870, /* 20 */
- 29400, /* 21 */
- 28063, /* 22 */
- 26843, /* 23 */
- 25725, /* 24 */
- 24696, /* 25 */
- 23746, /* 26 */
- 22866, /* 27 */
- 22050, /* 28 */
- 21289, /* 29 */
- 20580, /* 30 */
- 19916, /* 31 */
- 19293 /* 32 */
+ 44100,
+ 44100, /* 14 */
+ 41160, /* 15 */
+ 38587, /* 16 */
+ 36317, /* 17 */
+ 34300, /* 18 */
+ 32494, /* 19 */
+ 30870, /* 20 */
+ 29400, /* 21 */
+ 28063, /* 22 */
+ 26843, /* 23 */
+ 25725, /* 24 */
+ 24696, /* 25 */
+ 23746, /* 26 */
+ 22866, /* 27 */
+ 22050, /* 28 */
+ 21289, /* 29 */
+ 20580, /* 30 */
+ 19916, /* 31 */
+ 19293 /* 32 */
};
static struct patch_info *samples;
@@ -172,720 +172,711 @@ static int patch_map[32];
static struct synth_info gus_info =
{"Gravis UltraSound", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, 0, 16, 0, MAX_PATCH};
-static void gus_poke (long addr, unsigned char data);
-static void compute_and_set_volume (int voice, int volume, int ramp_time);
-extern unsigned short gus_adagio_vol (int vel, int mainv, int xpn, int voicev);
-extern unsigned short gus_linear_vol (int vol, int mainvol);
-static void compute_volume (int voice, int volume);
-static void do_volume_irq (int voice);
-static void set_input_volumes (void);
-static void gus_tmr_install (int io_base);
+static void gus_poke(long addr, unsigned char data);
+static void compute_and_set_volume(int voice, int volume, int ramp_time);
+extern unsigned short gus_adagio_vol(int vel, int mainv, int xpn, int voicev);
+extern unsigned short gus_linear_vol(int vol, int mainvol);
+static void compute_volume(int voice, int volume);
+static void do_volume_irq(int voice);
+static void set_input_volumes(void);
+static void gus_tmr_install(int io_base);
#define INSTANT_RAMP -1 /* Instant change. No ramping */
#define FAST_RAMP 0 /* Fastest possible ramp */
static void
-reset_sample_memory (void)
+reset_sample_memory(void)
{
- int i;
+ int i;
- for (i = 0; i <= MAX_SAMPLE; i++)
- sample_ptrs[i] = -1;
- for (i = 0; i < 32; i++)
- sample_map[i] = -1;
- for (i = 0; i < 32; i++)
- patch_map[i] = -1;
+ for (i = 0; i <= MAX_SAMPLE; i++)
+ sample_ptrs[i] = -1;
+ for (i = 0; i < 32; i++)
+ sample_map[i] = -1;
+ for (i = 0; i < 32; i++)
+ patch_map[i] = -1;
- gus_poke (0, 0); /* Put a silent sample to the beginning */
- gus_poke (1, 0);
- free_mem_ptr = 2;
+ gus_poke(0, 0); /* Put a silent sample to the beginning */
+ gus_poke(1, 0);
+ free_mem_ptr = 2;
- free_sample = 0;
+ free_sample = 0;
- for (i = 0; i < MAX_PATCH; i++)
- patch_table[i] = NOT_SAMPLE;
+ for (i = 0; i < MAX_PATCH; i++)
+ patch_table[i] = NOT_SAMPLE;
}
void
-gus_delay (void)
+gus_delay(void)
{
- int i;
+ int i;
- for (i = 0; i < 7; i++)
- inb (u_DRAMIO);
+ for (i = 0; i < 7; i++)
+ inb(u_DRAMIO);
}
static void
-gus_poke (long addr, unsigned char data)
+gus_poke(long addr, unsigned char data)
{ /* Writes a byte to the DRAM */
- unsigned long flags;
+ unsigned long flags;
- save_flags (flags);
- cli ();
- outb ((0x43), u_Command);
- outb ((addr & 0xff), u_DataLo);
- outb (((addr >> 8) & 0xff), u_DataHi);
+ save_flags(flags);
+ cli();
+ outb((0x43), u_Command);
+ outb((addr & 0xff), u_DataLo);
+ outb(((addr >> 8) & 0xff), u_DataHi);
- outb ((0x44), u_Command);
- outb (((addr >> 16) & 0xff), u_DataHi);
- outb ((data), u_DRAMIO);
- restore_flags (flags);
+ outb((0x44), u_Command);
+ outb(((addr >> 16) & 0xff), u_DataHi);
+ outb((data), u_DRAMIO);
+ restore_flags(flags);
}
static unsigned char
-gus_peek (long addr)
+gus_peek(long addr)
{ /* Reads a byte from the DRAM */
- unsigned long flags;
- unsigned char tmp;
+ unsigned long flags;
+ unsigned char tmp;
- save_flags (flags);
- cli ();
- outb ((0x43), u_Command);
- outb ((addr & 0xff), u_DataLo);
- outb (((addr >> 8) & 0xff), u_DataHi);
+ save_flags(flags);
+ cli();
+ outb((0x43), u_Command);
+ outb((addr & 0xff), u_DataLo);
+ outb(((addr >> 8) & 0xff), u_DataHi);
- outb ((0x44), u_Command);
- outb (((addr >> 16) & 0xff), u_DataHi);
- tmp = inb (u_DRAMIO);
- restore_flags (flags);
+ outb((0x44), u_Command);
+ outb(((addr >> 16) & 0xff), u_DataHi);
+ tmp = inb(u_DRAMIO);
+ restore_flags(flags);
- return tmp;
+ return tmp;
}
void
-gus_write8 (int reg, unsigned int data)
+gus_write8(int reg, unsigned int data)
{ /* Writes to an indirect register (8 bit) */
- unsigned long flags;
+ unsigned long flags;
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- outb ((reg), u_Command);
- outb (((unsigned char) (data & 0xff)), u_DataHi);
+ outb((reg), u_Command);
+ outb(((unsigned char) (data & 0xff)), u_DataHi);
- restore_flags (flags);
+ restore_flags(flags);
}
static unsigned char
-gus_read8 (int reg)
+gus_read8(int reg)
{ /* Reads from an indirect register (8 bit). Offset 0x80. */
- unsigned long flags;
- unsigned char val;
+ unsigned long flags;
+ unsigned char val;
- save_flags (flags);
- cli ();
- outb ((reg | 0x80), u_Command);
- val = inb (u_DataHi);
- restore_flags (flags);
+ save_flags(flags);
+ cli();
+ outb((reg | 0x80), u_Command);
+ val = inb(u_DataHi);
+ restore_flags(flags);
- return val;
+ return val;
}
static unsigned char
-gus_look8 (int reg)
+gus_look8(int reg)
{ /* Reads from an indirect register (8 bit). No additional offset. */
- unsigned long flags;
- unsigned char val;
+ unsigned long flags;
+ unsigned char val;
- save_flags (flags);
- cli ();
- outb ((reg), u_Command);
- val = inb (u_DataHi);
- restore_flags (flags);
+ save_flags(flags);
+ cli();
+ outb((reg), u_Command);
+ val = inb(u_DataHi);
+ restore_flags(flags);
- return val;
+ return val;
}
static void
-gus_write16 (int reg, unsigned int data)
+gus_write16(int reg, unsigned int data)
{ /* Writes to an indirect register (16 bit) */
- unsigned long flags;
+ unsigned long flags;
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- outb ((reg), u_Command);
+ outb((reg), u_Command);
- outb (((unsigned char) (data & 0xff)), u_DataLo);
- outb (((unsigned char) ((data >> 8) & 0xff)), u_DataHi);
+ outb(((unsigned char) (data & 0xff)), u_DataLo);
+ outb(((unsigned char) ((data >> 8) & 0xff)), u_DataHi);
- restore_flags (flags);
+ restore_flags(flags);
}
static unsigned short
-gus_read16 (int reg)
+gus_read16(int reg)
{ /* Reads from an indirect register (16 bit). Offset 0x80. */
- unsigned long flags;
- unsigned char hi, lo;
+ unsigned long flags;
+ unsigned char hi, lo;
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- outb ((reg | 0x80), u_Command);
+ outb((reg | 0x80), u_Command);
- lo = inb (u_DataLo);
- hi = inb (u_DataHi);
+ lo = inb(u_DataLo);
+ hi = inb(u_DataHi);
- restore_flags (flags);
+ restore_flags(flags);
- return ((hi << 8) & 0xff00) | lo;
+ return ((hi << 8) & 0xff00) | lo;
}
static unsigned short
-gus_look16 (int reg)
+gus_look16(int reg)
{ /* Reads from an indirect register (16 bit). No additional offset. */
- unsigned long flags;
- unsigned char hi, lo;
+ unsigned long flags;
+ unsigned char hi, lo;
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- outb ((reg), u_Command);
+ outb((reg), u_Command);
- lo = inb (u_DataLo);
- hi = inb (u_DataHi);
+ lo = inb(u_DataLo);
+ hi = inb(u_DataHi);
- restore_flags (flags);
+ restore_flags(flags);
- return ((hi << 8) & 0xff00) | lo;
+ return ((hi << 8) & 0xff00) | lo;
}
static void
-gus_write_addr (int reg, unsigned long address, int frac, int is16bit)
+gus_write_addr(int reg, unsigned long address, int frac, int is16bit)
{ /* Writes an 24 bit memory address */
- unsigned long hold_address;
- unsigned long flags;
-
- save_flags (flags);
- cli ();
- if (is16bit)
- {
- if (iw_mode)
- {
- /* Interwave spesific address translations */
- address >>= 1;
- }
- else
- {
- /*
- * Special processing required for 16 bit patches
- */
+ unsigned long hold_address;
+ unsigned long flags;
- hold_address = address;
- address = address >> 1;
- address &= 0x0001ffffL;
- address |= (hold_address & 0x000c0000L);
- }
- }
-
- gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff));
- gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff)
- + (frac << 5));
- /* Could writing twice fix problems with GUS_VOICE_POS() ? Lets try... */
- gus_delay ();
- gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff));
- gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff)
- + (frac << 5));
- restore_flags (flags);
+ save_flags(flags);
+ cli();
+ if (is16bit)
+ {
+ if (iw_mode)
+ {
+ /* Interwave spesific address translations */
+ address >>= 1;
+ } else
+ {
+ /*
+ * Special processing required for 16 bit patches
+ */
+
+ hold_address = address;
+ address = address >> 1;
+ address &= 0x0001ffffL;
+ address |= (hold_address & 0x000c0000L);
+ }
+ }
+ gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff));
+ gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff)
+ + (frac << 5));
+ /* Could writing twice fix problems with GUS_VOICE_POS() ? Lets try... */
+ gus_delay();
+ gus_write16(reg, (unsigned short) ((address >> 7) & 0xffff));
+ gus_write16(reg + 1, (unsigned short) ((address << 9) & 0xffff)
+ + (frac << 5));
+ restore_flags(flags);
}
static void
-gus_select_voice (int voice)
+gus_select_voice(int voice)
{
- if (voice < 0 || voice > 31)
- return;
+ if (voice < 0 || voice > 31)
+ return;
- outb ((voice), u_Voice);
+ outb((voice), u_Voice);
}
static void
-gus_select_max_voices (int nvoices)
+gus_select_max_voices(int nvoices)
{
- if (iw_mode)
- nvoices = 32;
- if (nvoices < 14)
- nvoices = 14;
- if (nvoices > 32)
- nvoices = 32;
+ if (iw_mode)
+ nvoices = 32;
+ if (nvoices < 14)
+ nvoices = 14;
+ if (nvoices > 32)
+ nvoices = 32;
- voice_alloc->max_voice = nr_voices = nvoices;
+ voice_alloc->max_voice = nr_voices = nvoices;
- gus_write8 (0x0e, (nvoices - 1) | 0xc0);
+ gus_write8(0x0e, (nvoices - 1) | 0xc0);
}
static void
-gus_voice_on (unsigned int mode)
+gus_voice_on(unsigned int mode)
{
- gus_write8 (0x00, (unsigned char) (mode & 0xfc));
- gus_delay ();
- gus_write8 (0x00, (unsigned char) (mode & 0xfc));
+ gus_write8(0x00, (unsigned char) (mode & 0xfc));
+ gus_delay();
+ gus_write8(0x00, (unsigned char) (mode & 0xfc));
}
static void
-gus_voice_off (void)
+gus_voice_off(void)
{
- gus_write8 (0x00, gus_read8 (0x00) | 0x03);
+ gus_write8(0x00, gus_read8(0x00) | 0x03);
}
static void
-gus_voice_mode (unsigned int m)
+gus_voice_mode(unsigned int m)
{
- unsigned char mode = (unsigned char) (m & 0xff);
+ unsigned char mode = (unsigned char) (m & 0xff);
- gus_write8 (0x00, (gus_read8 (0x00) & 0x03) |
- (mode & 0xfc)); /* Don't touch last two bits */
- gus_delay ();
- gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc));
+ gus_write8(0x00, (gus_read8(0x00) & 0x03) |
+ (mode & 0xfc)); /* Don't touch last two bits */
+ gus_delay();
+ gus_write8(0x00, (gus_read8(0x00) & 0x03) | (mode & 0xfc));
}
static void
-gus_voice_freq (unsigned long freq)
+gus_voice_freq(unsigned long freq)
{
- unsigned long divisor = freq_div_table[nr_voices - 14];
- unsigned short fc;
+ unsigned long divisor = freq_div_table[nr_voices - 14];
+ unsigned short fc;
- /* Interwave plays at 44100 Hz with any number of voices */
- if (iw_mode)
- fc = (unsigned short) (((freq << 9) + (44100 >> 1)) / 44100);
- else
- fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor);
- fc = fc << 1;
+ /* Interwave plays at 44100 Hz with any number of voices */
+ if (iw_mode)
+ fc = (unsigned short) (((freq << 9) + (44100 >> 1)) / 44100);
+ else
+ fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor);
+ fc = fc << 1;
- gus_write16 (0x01, fc);
+ gus_write16(0x01, fc);
}
static void
-gus_voice_volume (unsigned int vol)
+gus_voice_volume(unsigned int vol)
{
- gus_write8 (0x0d, 0x03); /* Stop ramp before setting volume */
- gus_write16 (0x09, (unsigned short) (vol << 4));
+ gus_write8(0x0d, 0x03); /* Stop ramp before setting volume */
+ gus_write16(0x09, (unsigned short) (vol << 4));
}
static void
-gus_voice_balance (unsigned int balance)
+gus_voice_balance(unsigned int balance)
{
- gus_write8 (0x0c, (unsigned char) (balance & 0xff));
+ gus_write8(0x0c, (unsigned char) (balance & 0xff));
}
static void
-gus_ramp_range (unsigned int low, unsigned int high)
+gus_ramp_range(unsigned int low, unsigned int high)
{
- gus_write8 (0x07, (unsigned char) ((low >> 4) & 0xff));
- gus_write8 (0x08, (unsigned char) ((high >> 4) & 0xff));
+ gus_write8(0x07, (unsigned char) ((low >> 4) & 0xff));
+ gus_write8(0x08, (unsigned char) ((high >> 4) & 0xff));
}
static void
-gus_ramp_rate (unsigned int scale, unsigned int rate)
+gus_ramp_rate(unsigned int scale, unsigned int rate)
{
- gus_write8 (0x06, (unsigned char) (((scale & 0x03) << 6) | (rate & 0x3f)));
+ gus_write8(0x06, (unsigned char) (((scale & 0x03) << 6) | (rate & 0x3f)));
}
static void
-gus_rampon (unsigned int m)
+gus_rampon(unsigned int m)
{
- unsigned char mode = (unsigned char) (m & 0xff);
+ unsigned char mode = (unsigned char) (m & 0xff);
- gus_write8 (0x0d, mode & 0xfc);
- gus_delay ();
- gus_write8 (0x0d, mode & 0xfc);
+ gus_write8(0x0d, mode & 0xfc);
+ gus_delay();
+ gus_write8(0x0d, mode & 0xfc);
}
static void
-gus_ramp_mode (unsigned int m)
+gus_ramp_mode(unsigned int m)
{
- unsigned char mode = (unsigned char) (m & 0xff);
+ unsigned char mode = (unsigned char) (m & 0xff);
- gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) |
- (mode & 0xfc)); /* Leave the last 2 bits alone */
- gus_delay ();
- gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc));
+ gus_write8(0x0d, (gus_read8(0x0d) & 0x03) |
+ (mode & 0xfc)); /* Leave the last 2 bits alone */
+ gus_delay();
+ gus_write8(0x0d, (gus_read8(0x0d) & 0x03) | (mode & 0xfc));
}
static void
-gus_rampoff (void)
+gus_rampoff(void)
{
- gus_write8 (0x0d, 0x03);
+ gus_write8(0x0d, 0x03);
}
static void
-gus_set_voice_pos (int voice, long position)
+gus_set_voice_pos(int voice, long position)
{
- int sample_no;
+ int sample_no;
- if ((sample_no = sample_map[voice]) != -1)
- if (position < samples[sample_no].len)
- if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
- voices[voice].offset_pending = position;
- else
- gus_write_addr (0x0a, sample_ptrs[sample_no] + position, 0,
- samples[sample_no].mode & WAVE_16_BITS);
+ if ((sample_no = sample_map[voice]) != -1)
+ if (position < samples[sample_no].len)
+ if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+ voices[voice].offset_pending = position;
+ else
+ gus_write_addr(0x0a, sample_ptrs[sample_no] + position, 0,
+ samples[sample_no].mode & WAVE_16_BITS);
}
static void
-gus_voice_init (int voice)
+gus_voice_init(int voice)
{
- unsigned long flags;
+ unsigned long flags;
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- gus_voice_volume (0);
- gus_voice_off ();
- gus_write_addr (0x0a, 0, 0, 0); /* Set current position to 0 */
- gus_write8 (0x00, 0x03); /* Voice off */
- gus_write8 (0x0d, 0x03); /* Ramping off */
- voice_alloc->map[voice] = 0;
- voice_alloc->alloc_times[voice] = 0;
- restore_flags (flags);
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ gus_voice_volume(0);
+ gus_voice_off();
+ gus_write_addr(0x0a, 0, 0, 0); /* Set current position to 0 */
+ gus_write8(0x00, 0x03); /* Voice off */
+ gus_write8(0x0d, 0x03); /* Ramping off */
+ voice_alloc->map[voice] = 0;
+ voice_alloc->alloc_times[voice] = 0;
+ restore_flags(flags);
}
static void
-gus_voice_init2 (int voice)
-{
- voices[voice].panning = 0;
- voices[voice].mode = 0;
- voices[voice].orig_freq = 20000;
- voices[voice].current_freq = 20000;
- voices[voice].bender = 0;
- voices[voice].bender_range = 200;
- voices[voice].initial_volume = 0;
- voices[voice].current_volume = 0;
- voices[voice].loop_irq_mode = 0;
- voices[voice].loop_irq_parm = 0;
- voices[voice].volume_irq_mode = 0;
- voices[voice].volume_irq_parm = 0;
- voices[voice].env_phase = 0;
- voices[voice].main_vol = 127;
- voices[voice].patch_vol = 127;
- voices[voice].expression_vol = 127;
- voices[voice].sample_pending = -1;
- voices[voice].fixed_pitch = 0;
+gus_voice_init2(int voice)
+{
+ voices[voice].panning = 0;
+ voices[voice].mode = 0;
+ voices[voice].orig_freq = 20000;
+ voices[voice].current_freq = 20000;
+ voices[voice].bender = 0;
+ voices[voice].bender_range = 200;
+ voices[voice].initial_volume = 0;
+ voices[voice].current_volume = 0;
+ voices[voice].loop_irq_mode = 0;
+ voices[voice].loop_irq_parm = 0;
+ voices[voice].volume_irq_mode = 0;
+ voices[voice].volume_irq_parm = 0;
+ voices[voice].env_phase = 0;
+ voices[voice].main_vol = 127;
+ voices[voice].patch_vol = 127;
+ voices[voice].expression_vol = 127;
+ voices[voice].sample_pending = -1;
+ voices[voice].fixed_pitch = 0;
}
static void
-step_envelope (int voice)
-{
- unsigned vol, prev_vol, phase;
- unsigned char rate;
- long int flags;
-
- if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2)
- {
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- gus_rampoff ();
- restore_flags (flags);
- return;
- /*
- * Sustain phase begins. Continue envelope after receiving note off.
- */
- }
-
- if (voices[voice].env_phase >= 5)
- { /* Envelope finished. Shoot the voice down */
- gus_voice_init (voice);
- return;
- }
-
- prev_vol = voices[voice].current_volume;
- phase = ++voices[voice].env_phase;
- compute_volume (voice, voices[voice].midi_volume);
- vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255;
- rate = voices[voice].env_rate[phase];
-
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
-
- gus_voice_volume (prev_vol);
-
-
- gus_write8 (0x06, rate); /* Ramping rate */
-
- voices[voice].volume_irq_mode = VMODE_ENVELOPE;
-
- if (((vol - prev_vol) / 64) == 0) /* No significant volume change */
- {
- restore_flags (flags);
- step_envelope (voice); /* Continue the envelope on the next step */
- return;
- }
-
- if (vol > prev_vol)
- {
- if (vol >= (4096 - 64))
- vol = 4096 - 65;
- gus_ramp_range (0, vol);
- gus_rampon (0x20); /* Increasing volume, with IRQ */
- }
- else
- {
- if (vol <= 64)
- vol = 65;
- gus_ramp_range (vol, 4030);
- gus_rampon (0x60); /* Decreasing volume, with IRQ */
- }
- voices[voice].current_volume = vol;
- restore_flags (flags);
+step_envelope(int voice)
+{
+ unsigned vol, prev_vol, phase;
+ unsigned char rate;
+ long int flags;
+
+ if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2)
+ {
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ gus_rampoff();
+ restore_flags(flags);
+ return;
+ /*
+ * Sustain phase begins. Continue envelope after receiving note off.
+ */
+ }
+ if (voices[voice].env_phase >= 5)
+ { /* Envelope finished. Shoot the voice down */
+ gus_voice_init(voice);
+ return;
+ }
+ prev_vol = voices[voice].current_volume;
+ phase = ++voices[voice].env_phase;
+ compute_volume(voice, voices[voice].midi_volume);
+ vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255;
+ rate = voices[voice].env_rate[phase];
+
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+
+ gus_voice_volume(prev_vol);
+
+
+ gus_write8(0x06, rate); /* Ramping rate */
+
+ voices[voice].volume_irq_mode = VMODE_ENVELOPE;
+
+ if (((vol - prev_vol) / 64) == 0) /* No significant volume change */
+ {
+ restore_flags(flags);
+ step_envelope(voice); /* Continue the envelope on the next step */
+ return;
+ }
+ if (vol > prev_vol)
+ {
+ if (vol >= (4096 - 64))
+ vol = 4096 - 65;
+ gus_ramp_range(0, vol);
+ gus_rampon(0x20); /* Increasing volume, with IRQ */
+ } else
+ {
+ if (vol <= 64)
+ vol = 65;
+ gus_ramp_range(vol, 4030);
+ gus_rampon(0x60); /* Decreasing volume, with IRQ */
+ }
+ voices[voice].current_volume = vol;
+ restore_flags(flags);
}
static void
-init_envelope (int voice)
+init_envelope(int voice)
{
- voices[voice].env_phase = -1;
- voices[voice].current_volume = 64;
+ voices[voice].env_phase = -1;
+ voices[voice].current_volume = 64;
- step_envelope (voice);
+ step_envelope(voice);
}
static void
-start_release (int voice, long int flags)
+start_release(int voice, long int flags)
{
- if (gus_read8 (0x00) & 0x03)
- return; /* Voice already stopped */
+ if (gus_read8(0x00) & 0x03)
+ return; /* Voice already stopped */
- voices[voice].env_phase = 2; /* Will be incremented by step_envelope */
+ voices[voice].env_phase = 2; /* Will be incremented by step_envelope */
- voices[voice].current_volume =
- voices[voice].initial_volume =
- gus_read16 (0x09) >> 4; /* Get current volume */
+ voices[voice].current_volume =
+ voices[voice].initial_volume =
+ gus_read16(0x09) >> 4; /* Get current volume */
- voices[voice].mode &= ~WAVE_SUSTAIN_ON;
- gus_rampoff ();
- restore_flags (flags);
- step_envelope (voice);
+ voices[voice].mode &= ~WAVE_SUSTAIN_ON;
+ gus_rampoff();
+ restore_flags(flags);
+ step_envelope(voice);
}
static void
-gus_voice_fade (int voice)
-{
- int instr_no = sample_map[voice], is16bits;
- long int flags;
-
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
-
- if (instr_no < 0 || instr_no > MAX_SAMPLE)
- {
- gus_write8 (0x00, 0x03); /* Hard stop */
- voice_alloc->map[voice] = 0;
- restore_flags (flags);
- return;
- }
-
- is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bits */
-
- if (voices[voice].mode & WAVE_ENVELOPES)
- {
- start_release (voice, flags);
- return;
- }
-
- /*
- * Ramp the volume down but not too quickly.
- */
- if ((int) (gus_read16 (0x09) >> 4) < 100) /* Get current volume */
- {
- gus_voice_off ();
- gus_rampoff ();
- gus_voice_init (voice);
- return;
- }
-
- gus_ramp_range (65, 4030);
- gus_ramp_rate (2, 4);
- gus_rampon (0x40 | 0x20); /* Down, once, with IRQ */
- voices[voice].volume_irq_mode = VMODE_HALT;
- restore_flags (flags);
+gus_voice_fade(int voice)
+{
+ int instr_no = sample_map[voice], is16bits;
+ long int flags;
+
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+
+ if (instr_no < 0 || instr_no > MAX_SAMPLE)
+ {
+ gus_write8(0x00, 0x03); /* Hard stop */
+ voice_alloc->map[voice] = 0;
+ restore_flags(flags);
+ return;
+ }
+ is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bits */
+
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ {
+ start_release(voice, flags);
+ restore_flags(flags);
+ return;
+ }
+ /*
+ * Ramp the volume down but not too quickly.
+ */
+ if ((int) (gus_read16(0x09) >> 4) < 100) /* Get current volume */
+ {
+ gus_voice_off();
+ gus_rampoff();
+ gus_voice_init(voice);
+ restore_flags(flags);
+ return;
+ }
+ gus_ramp_range(65, 4030);
+ gus_ramp_rate(2, 4);
+ gus_rampon(0x40 | 0x20); /* Down, once, with IRQ */
+ voices[voice].volume_irq_mode = VMODE_HALT;
+ restore_flags(flags);
}
static void
-gus_reset (void)
+gus_reset(void)
{
- int i;
+ int i;
- gus_select_max_voices (24);
- volume_base = 3071;
- volume_scale = 4;
- volume_method = VOL_METHOD_ADAGIO;
+ gus_select_max_voices(24);
+ volume_base = 3071;
+ volume_scale = 4;
+ volume_method = VOL_METHOD_ADAGIO;
- for (i = 0; i < 32; i++)
- {
- gus_voice_init (i); /* Turn voice off */
- gus_voice_init2 (i);
- }
+ for (i = 0; i < 32; i++)
+ {
+ gus_voice_init(i); /* Turn voice off */
+ gus_voice_init2(i);
+ }
}
static void
-gus_initialize (void)
+gus_initialize(void)
{
- unsigned long flags;
- unsigned char dma_image, irq_image, tmp;
+ unsigned long flags;
+ unsigned char dma_image, irq_image, tmp;
+
+ static unsigned char gus_irq_map[16] =
+ {0, 0, 0, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7};
- static unsigned char gus_irq_map[16] =
- {0, 0, 0, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7};
-
- static unsigned char gus_dma_map[8] =
- {0, 1, 0, 2, 0, 3, 4, 5};
+ static unsigned char gus_dma_map[8] =
+ {0, 1, 0, 2, 0, 3, 4, 5};
- save_flags (flags);
- cli ();
- gus_write8 (0x4c, 0); /* Reset GF1 */
- gus_delay ();
- gus_delay ();
+ save_flags(flags);
+ cli();
+ gus_write8(0x4c, 0); /* Reset GF1 */
+ gus_delay();
+ gus_delay();
- gus_write8 (0x4c, 1); /* Release Reset */
- gus_delay ();
- gus_delay ();
+ gus_write8(0x4c, 1); /* Release Reset */
+ gus_delay();
+ gus_delay();
- /*
- * Clear all interrupts
- */
+ /*
+ * Clear all interrupts
+ */
- gus_write8 (0x41, 0); /* DMA control */
- gus_write8 (0x45, 0); /* Timer control */
- gus_write8 (0x49, 0); /* Sample control */
+ gus_write8(0x41, 0); /* DMA control */
+ gus_write8(0x45, 0); /* Timer control */
+ gus_write8(0x49, 0); /* Sample control */
- gus_select_max_voices (24);
+ gus_select_max_voices(24);
- inb (u_Status); /* Touch the status register */
+ inb(u_Status); /* Touch the status register */
- gus_look8 (0x41); /* Clear any pending DMA IRQs */
- gus_look8 (0x49); /* Clear any pending sample IRQs */
- gus_read8 (0x0f); /* Clear pending IRQs */
+ gus_look8(0x41); /* Clear any pending DMA IRQs */
+ gus_look8(0x49); /* Clear any pending sample IRQs */
+ gus_read8(0x0f); /* Clear pending IRQs */
- gus_reset (); /* Resets all voices */
+ gus_reset(); /* Resets all voices */
- gus_look8 (0x41); /* Clear any pending DMA IRQs */
- gus_look8 (0x49); /* Clear any pending sample IRQs */
- gus_read8 (0x0f); /* Clear pending IRQs */
+ gus_look8(0x41); /* Clear any pending DMA IRQs */
+ gus_look8(0x49); /* Clear any pending sample IRQs */
+ gus_read8(0x0f); /* Clear pending IRQs */
- gus_write8 (0x4c, 7); /* Master reset | DAC enable | IRQ enable */
+ gus_write8(0x4c, 7); /* Master reset | DAC enable | IRQ enable */
- /*
- * Set up for Digital ASIC
- */
+ /*
+ * Set up for Digital ASIC
+ */
- outb ((0x05), gus_base + 0x0f);
+ outb((0x05), gus_base + 0x0f);
- mix_image |= 0x02; /* Disable line out (for a moment) */
- outb ((mix_image), u_Mixer);
+ mix_image |= 0x02; /* Disable line out (for a moment) */
+ outb((mix_image), u_Mixer);
- outb ((0x00), u_IRQDMAControl);
+ outb((0x00), u_IRQDMAControl);
- outb ((0x00), gus_base + 0x0f);
+ outb((0x00), gus_base + 0x0f);
- /*
- * Now set up the DMA and IRQ interface
- *
- * The GUS supports two IRQs and two DMAs.
- *
- * Just one DMA channel is used. This prevents simultaneous ADC and DAC.
- * Adding this support requires significant changes to the dmabuf.c, dsp.c
- * and audio.c also.
- */
+ /*
+ * Now set up the DMA and IRQ interface
+ *
+ * The GUS supports two IRQs and two DMAs.
+ *
+ * Just one DMA channel is used. This prevents simultaneous ADC and DAC.
+ * Adding this support requires significant changes to the dmabuf.c, dsp.c
+ * and audio.c also.
+ */
- irq_image = 0;
- tmp = gus_irq_map[gus_irq];
- if (!gus_pnp_flag && !tmp)
- printk ("Warning! GUS IRQ not selected\n");
- irq_image |= tmp;
- irq_image |= 0x40; /* Combine IRQ1 (GF1) and IRQ2 (Midi) */
+ irq_image = 0;
+ tmp = gus_irq_map[gus_irq];
+ if (!gus_pnp_flag && !tmp)
+ printk("Warning! GUS IRQ not selected\n");
+ irq_image |= tmp;
+ irq_image |= 0x40; /* Combine IRQ1 (GF1) and IRQ2 (Midi) */
- dual_dma_mode = 1;
- if (gus_dma2 == gus_dma || gus_dma2 == -1)
- {
- dual_dma_mode = 0;
- dma_image = 0x40; /* Combine DMA1 (DRAM) and IRQ2 (ADC) */
+ dual_dma_mode = 1;
+ if (gus_dma2 == gus_dma || gus_dma2 == -1)
+ {
+ dual_dma_mode = 0;
+ dma_image = 0x40; /* Combine DMA1 (DRAM) and IRQ2 (ADC) */
- tmp = gus_dma_map[gus_dma];
- if (!tmp)
- printk ("Warning! GUS DMA not selected\n");
+ tmp = gus_dma_map[gus_dma];
+ if (!tmp)
+ printk("Warning! GUS DMA not selected\n");
- dma_image |= tmp;
- }
- else
- /* Setup dual DMA channel mode for GUS MAX */
- {
- dma_image = gus_dma_map[gus_dma];
- if (!dma_image)
- printk ("Warning! GUS DMA not selected\n");
+ dma_image |= tmp;
+ } else
+ /* Setup dual DMA channel mode for GUS MAX */
+ {
+ dma_image = gus_dma_map[gus_dma];
+ if (!dma_image)
+ printk("Warning! GUS DMA not selected\n");
+
+ tmp = gus_dma_map[gus_dma2] << 3;
+ if (!tmp)
+ {
+ printk("Warning! Invalid GUS MAX DMA\n");
+ tmp = 0x40; /* Combine DMA channels */
+ dual_dma_mode = 0;
+ }
+ dma_image |= tmp;
+ }
- tmp = gus_dma_map[gus_dma2] << 3;
- if (!tmp)
- {
- printk ("Warning! Invalid GUS MAX DMA\n");
- tmp = 0x40; /* Combine DMA channels */
- dual_dma_mode = 0;
- }
+ /*
+ * For some reason the IRQ and DMA addresses must be written twice
+ */
- dma_image |= tmp;
- }
+ /*
+ * Doing it first time
+ */
- /*
- * For some reason the IRQ and DMA addresses must be written twice
- */
-
- /*
- * Doing it first time
- */
-
- outb ((mix_image), u_Mixer); /* Select DMA control */
- outb ((dma_image | 0x80), u_IRQDMAControl); /* Set DMA address */
+ outb((mix_image), u_Mixer); /* Select DMA control */
+ outb((dma_image | 0x80), u_IRQDMAControl); /* Set DMA address */
- outb ((mix_image | 0x40), u_Mixer); /* Select IRQ control */
- outb ((irq_image), u_IRQDMAControl); /* Set IRQ address */
+ outb((mix_image | 0x40), u_Mixer); /* Select IRQ control */
+ outb((irq_image), u_IRQDMAControl); /* Set IRQ address */
- /*
- * Doing it second time
- */
+ /*
+ * Doing it second time
+ */
- outb ((mix_image), u_Mixer); /* Select DMA control */
- outb ((dma_image), u_IRQDMAControl); /* Set DMA address */
+ outb((mix_image), u_Mixer); /* Select DMA control */
+ outb((dma_image), u_IRQDMAControl); /* Set DMA address */
- outb ((mix_image | 0x40), u_Mixer); /* Select IRQ control */
- outb ((irq_image), u_IRQDMAControl); /* Set IRQ address */
+ outb((mix_image | 0x40), u_Mixer); /* Select IRQ control */
+ outb((irq_image), u_IRQDMAControl); /* Set IRQ address */
- gus_select_voice (0); /* This disables writes to IRQ/DMA reg */
+ gus_select_voice(0); /* This disables writes to IRQ/DMA reg */
- mix_image &= ~0x02; /* Enable line out */
- mix_image |= 0x08; /* Enable IRQ */
- outb ((mix_image), u_Mixer); /*
- * Turn mixer channels on
- * Note! Mic in is left off.
- */
+ mix_image &= ~0x02; /* Enable line out */
+ mix_image |= 0x08; /* Enable IRQ */
+ outb((mix_image), u_Mixer); /*
+ * Turn mixer channels on
+ * Note! Mic in is left off.
+ */
- gus_select_voice (0); /* This disables writes to IRQ/DMA reg */
+ gus_select_voice(0); /* This disables writes to IRQ/DMA reg */
- gusintr (gus_irq, NULL, NULL); /* Serve pending interrupts */
+ gusintr(gus_irq, NULL, NULL); /* Serve pending interrupts */
- inb (u_Status); /* Touch the status register */
+ inb(u_Status); /* Touch the status register */
- gus_look8 (0x41); /* Clear any pending DMA IRQs */
- gus_look8 (0x49); /* Clear any pending sample IRQs */
+ gus_look8(0x41); /* Clear any pending DMA IRQs */
+ gus_look8(0x49); /* Clear any pending sample IRQs */
- gus_read8 (0x0f); /* Clear pending IRQs */
+ gus_read8(0x0f); /* Clear pending IRQs */
- if (iw_mode)
- gus_write8 (0x19, gus_read8 (0x19) | 0x01);
- restore_flags (flags);
+ if (iw_mode)
+ gus_write8(0x19, gus_read8(0x19) | 0x01);
+ restore_flags(flags);
}
static void
-pnp_mem_init (void)
+pnp_mem_init(void)
{
#include "iwmem.h"
#define CHUNK_SIZE (256*1024)
#define BANK_SIZE (4*1024*1024)
#define CHUNKS_PER_BANK (BANK_SIZE/CHUNK_SIZE)
- int bank, chunk, addr, total = 0;
- int bank_sizes[4];
- int i, j, bits = -1, nbanks = 0;
+ int bank, chunk, addr, total = 0;
+ int bank_sizes[4];
+ int i, j, bits = -1, nbanks = 0;
/*
* This routine determines what kind of RAM is installed in each of the four
@@ -895,68 +886,68 @@ pnp_mem_init (void)
/*
* Place the chip into enhanced mode
*/
- gus_write8 (0x19, gus_read8 (0x19) | 0x01);
- gus_write8 (0x53, gus_look8 (0x53) & ~0x02); /* Select DRAM I/O access */
+ gus_write8(0x19, gus_read8(0x19) | 0x01);
+ gus_write8(0x53, gus_look8(0x53) & ~0x02); /* Select DRAM I/O access */
/*
* Set memory configuration to 4 DRAM banks of 4M in each (16M total).
*/
- gus_write16 (0x52, (gus_look16 (0x52) & 0xfff0) | 0x000c);
+ gus_write16(0x52, (gus_look16(0x52) & 0xfff0) | 0x000c);
/*
* Perform the DRAM size detection for each bank individually.
*/
- for (bank = 0; bank < 4; bank++)
- {
- int size = 0;
-
- addr = bank * BANK_SIZE;
-
- /* Clean check points of each chunk */
- for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++)
- {
- gus_poke (addr + chunk * CHUNK_SIZE + 0L, 0x00);
- gus_poke (addr + chunk * CHUNK_SIZE + 1L, 0x00);
- }
-
- /* Write a value to each chunk point and verify the result */
- for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++)
- {
- gus_poke (addr + chunk * CHUNK_SIZE + 0L, 0x55);
- gus_poke (addr + chunk * CHUNK_SIZE + 1L, 0xAA);
-
- if (gus_peek (addr + chunk * CHUNK_SIZE + 0L) == 0x55 &&
- gus_peek (addr + chunk * CHUNK_SIZE + 1L) == 0xAA)
- { /* OK. There is RAM. Now check for possible shadows */
- int ok = 1, chunk2;
-
- for (chunk2 = 0; ok && chunk2 < chunk; chunk2++)
- if (gus_peek (addr + chunk2 * CHUNK_SIZE + 0L) ||
- gus_peek (addr + chunk2 * CHUNK_SIZE + 1L))
- ok = 0; /* Addressing wraps */
-
- if (ok)
- size = (chunk + 1) * CHUNK_SIZE;
- }
- gus_poke (addr + chunk * CHUNK_SIZE + 0L, 0x00);
- gus_poke (addr + chunk * CHUNK_SIZE + 1L, 0x00);
- }
-
- bank_sizes[bank] = size;
- if (size)
- nbanks = bank + 1;
- DDB (printk ("Interwave: Bank %d, size=%dk\n", bank, size / 1024));
- }
-
- if (nbanks == 0) /* No RAM - Give up */
- {
- printk ("Sound: An Interwave audio chip detected but no DRAM\n");
- printk ("Sound: Unable to work with this card.\n");
- gus_write8 (0x19, gus_read8 (0x19) & ~0x01);
- gus_mem_size = 0;
- return;
- }
+ for (bank = 0; bank < 4; bank++)
+ {
+ int size = 0;
+
+ addr = bank * BANK_SIZE;
+
+ /* Clean check points of each chunk */
+ for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++)
+ {
+ gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x00);
+ gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0x00);
+ }
+
+ /* Write a value to each chunk point and verify the result */
+ for (chunk = 0; chunk < CHUNKS_PER_BANK; chunk++)
+ {
+ gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x55);
+ gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0xAA);
+
+ if (gus_peek(addr + chunk * CHUNK_SIZE + 0L) == 0x55 &&
+ gus_peek(addr + chunk * CHUNK_SIZE + 1L) == 0xAA)
+ { /* OK. There is RAM. Now check for possible shadows */
+ int ok = 1, chunk2;
+
+ for (chunk2 = 0; ok && chunk2 < chunk; chunk2++)
+ if (gus_peek(addr + chunk2 * CHUNK_SIZE + 0L) ||
+ gus_peek(addr + chunk2 * CHUNK_SIZE + 1L))
+ ok = 0; /* Addressing wraps */
+
+ if (ok)
+ size = (chunk + 1) * CHUNK_SIZE;
+ }
+ gus_poke(addr + chunk * CHUNK_SIZE + 0L, 0x00);
+ gus_poke(addr + chunk * CHUNK_SIZE + 1L, 0x00);
+ }
+
+ bank_sizes[bank] = size;
+ if (size)
+ nbanks = bank + 1;
+ DDB(printk("Interwave: Bank %d, size=%dk\n", bank, size / 1024));
+ }
+
+ if (nbanks == 0) /* No RAM - Give up */
+ {
+ printk("Sound: An Interwave audio chip detected but no DRAM\n");
+ printk("Sound: Unable to work with this card.\n");
+ gus_write8(0x19, gus_read8(0x19) & ~0x01);
+ gus_mem_size = 0;
+ return;
+ }
/*
* Now we know how much DRAM there is in each bank. The next step is
* to find a DRAM size encoding (0 to 12) which is best for the combination
@@ -966,668 +957,640 @@ pnp_mem_init (void)
* of memory we have.
*/
- for (i = 0; bits == -1 && i < 13; i++)
- {
- bits = i;
+ for (i = 0; bits == -1 && i < 13; i++)
+ {
+ bits = i;
- for (j = 0; bits != -1 && j < 4; j++)
- if (mem_decode[i][j] != bank_sizes[j])
- bits = -1; /* No hit */
- }
+ for (j = 0; bits != -1 && j < 4; j++)
+ if (mem_decode[i][j] != bank_sizes[j])
+ bits = -1; /* No hit */
+ }
/*
* If necessary, try to find a combination where other than the last
* bank matches our configuration and the last bank is left oversized.
* In this way we don't leave holes in the middle of memory.
*/
- if (bits == -1) /* No luck yet */
- for (i = 0; bits == -1 && i < 13; i++)
- {
- bits = i;
-
- for (j = 0; bits != -1 && j < nbanks - 1; j++)
- if (mem_decode[i][j] != bank_sizes[j])
- bits = -1; /* No hit */
- if (mem_decode[i][nbanks - 1] < bank_sizes[nbanks - 1])
- bits = -1; /* The last bank is too small */
- }
-
+ if (bits == -1) /* No luck yet */
+ for (i = 0; bits == -1 && i < 13; i++)
+ {
+ bits = i;
+
+ for (j = 0; bits != -1 && j < nbanks - 1; j++)
+ if (mem_decode[i][j] != bank_sizes[j])
+ bits = -1; /* No hit */
+ if (mem_decode[i][nbanks - 1] < bank_sizes[nbanks - 1])
+ bits = -1; /* The last bank is too small */
+ }
/*
* The last resort is to search for a combination where the last bank is
* smaller than the actual SIMM. This leaves some memory in the last bank
* unused but doesn't leave holes in the DRAM address space.
*/
- if (bits == -1) /* No luck yet */
- {
- for (i = 0; bits == -1 && i < 13; i++)
- {
- bits = i;
-
- for (j = 0; bits != -1 && j < nbanks - 1; j++)
- if (mem_decode[i][j] != bank_sizes[j])
- bits = -1; /* No hit */
- }
-
- if (bits != -1)
- {
- printk ("Interwave: Can't use all installed RAM.\n");
- printk ("Interwave: Try reordering SIMMS.\n");
- }
- }
-
- if (bits == -1)
- {
- printk ("Interwave: Can't find working DRAM encoding.\n");
- printk ("Interwave: Defaulting to 256k. Try reordering SIMMS.\n");
- bits = 0;
- }
-
- DDB (printk ("Interwave: Selecting DRAM addressing mode %d\n", bits));
-
- for (bank = 0; bank < 4; bank++)
- {
- DDB (printk (" Bank %d, mem=%dk (limit %dk)\n",
- bank, bank_sizes[bank] / 1024,
- mem_decode[bits][bank] / 1024));
-
- if (bank_sizes[bank] > mem_decode[bits][bank])
- total += mem_decode[bits][bank];
- else
- total += bank_sizes[bank];
- }
-
- DDB (printk ("Total %dk of DRAM (enhanced mode)\n", total / 1024));
+ if (bits == -1) /* No luck yet */
+ {
+ for (i = 0; bits == -1 && i < 13; i++)
+ {
+ bits = i;
+
+ for (j = 0; bits != -1 && j < nbanks - 1; j++)
+ if (mem_decode[i][j] != bank_sizes[j])
+ bits = -1; /* No hit */
+ }
+
+ if (bits != -1)
+ {
+ printk("Interwave: Can't use all installed RAM.\n");
+ printk("Interwave: Try reordering SIMMS.\n");
+ }
+ }
+ if (bits == -1)
+ {
+ printk("Interwave: Can't find working DRAM encoding.\n");
+ printk("Interwave: Defaulting to 256k. Try reordering SIMMS.\n");
+ bits = 0;
+ }
+ DDB(printk("Interwave: Selecting DRAM addressing mode %d\n", bits));
+
+ for (bank = 0; bank < 4; bank++)
+ {
+ DDB(printk(" Bank %d, mem=%dk (limit %dk)\n", bank, bank_sizes[bank] / 1024, mem_decode[bits][bank] / 1024));
+
+ if (bank_sizes[bank] > mem_decode[bits][bank])
+ total += mem_decode[bits][bank];
+ else
+ total += bank_sizes[bank];
+ }
+
+ DDB(printk("Total %dk of DRAM (enhanced mode)\n", total / 1024));
/*
* Set the memory addressing mode.
*/
- gus_write16 (0x52, (gus_look16 (0x52) & 0xfff0) | bits);
+ gus_write16(0x52, (gus_look16(0x52) & 0xfff0) | bits);
/* Leave the chip into enhanced mode. Disable LFO */
- gus_mem_size = total;
- iw_mode = 1;
- gus_write8 (0x19, (gus_read8 (0x19) | 0x01) & ~0x02);
+ gus_mem_size = total;
+ iw_mode = 1;
+ gus_write8(0x19, (gus_read8(0x19) | 0x01) & ~0x02);
}
int
-gus_wave_detect (int baseaddr)
+gus_wave_detect(int baseaddr)
{
- unsigned long i, max_mem = 1024L;
- unsigned long loc;
- unsigned char val;
+ unsigned long i, max_mem = 1024L;
+ unsigned long loc;
+ unsigned char val;
- gus_base = baseaddr;
+ gus_base = baseaddr;
- gus_write8 (0x4c, 0); /* Reset GF1 */
- gus_delay ();
- gus_delay ();
+ gus_write8(0x4c, 0); /* Reset GF1 */
+ gus_delay();
+ gus_delay();
- gus_write8 (0x4c, 1); /* Release Reset */
- gus_delay ();
- gus_delay ();
+ gus_write8(0x4c, 1); /* Release Reset */
+ gus_delay();
+ gus_delay();
#ifdef GUSPNP_AUTODETECT
- val = gus_look8 (0x5b); /* Version number register */
- gus_write8 (0x5b, ~val); /* Invert all bits */
-
- if ((gus_look8 (0x5b) & 0xf0) == (val & 0xf0)) /* No change */
- if ((gus_look8 (0x5b) & 0x0f) == ((~val) & 0x0f)) /* Change */
- {
- DDB (printk ("Interwave chip version %d detected\n", (val & 0xf0) >> 4));
- gus_pnp_flag = 1;
- }
- else
- {
- DDB (printk ("Not an Interwave chip (%x)\n", gus_look8 (0x5b)));
- gus_pnp_flag = 0;
- }
- gus_write8 (0x5b, val); /* Restore all bits */
+ val = gus_look8(0x5b); /* Version number register */
+ gus_write8(0x5b, ~val); /* Invert all bits */
+
+ if ((gus_look8(0x5b) & 0xf0) == (val & 0xf0)) /* No change */
+ if ((gus_look8(0x5b) & 0x0f) == ((~val) & 0x0f)) /* Change */
+ {
+ DDB(printk("Interwave chip version %d detected\n", (val & 0xf0) >> 4));
+ gus_pnp_flag = 1;
+ } else
+ {
+ DDB(printk("Not an Interwave chip (%x)\n", gus_look8(0x5b)));
+ gus_pnp_flag = 0;
+ }
+ gus_write8(0x5b, val); /* Restore all bits */
#endif
- if (gus_pnp_flag)
- pnp_mem_init ();
- if (iw_mode)
- return 1;
-
- /* See if there is first block there.... */
- gus_poke (0L, 0xaa);
- if (gus_peek (0L) != 0xaa)
- return (0);
-
- /* Now zero it out so that I can check for mirroring .. */
- gus_poke (0L, 0x00);
- for (i = 1L; i < max_mem; i++)
- {
- int n, failed;
-
- /* check for mirroring ... */
- if (gus_peek (0L) != 0)
- break;
- loc = i << 10;
-
- for (n = loc - 1, failed = 0; n <= loc; n++)
- {
- gus_poke (loc, 0xaa);
- if (gus_peek (loc) != 0xaa)
- failed = 1;
-
- gus_poke (loc, 0x55);
- if (gus_peek (loc) != 0x55)
- failed = 1;
- }
-
- if (failed)
- break;
- }
- gus_mem_size = i << 10;
- return 1;
+ if (gus_pnp_flag)
+ pnp_mem_init();
+ if (iw_mode)
+ return 1;
+
+ /* See if there is first block there.... */
+ gus_poke(0L, 0xaa);
+ if (gus_peek(0L) != 0xaa)
+ return (0);
+
+ /* Now zero it out so that I can check for mirroring .. */
+ gus_poke(0L, 0x00);
+ for (i = 1L; i < max_mem; i++)
+ {
+ int n, failed;
+
+ /* check for mirroring ... */
+ if (gus_peek(0L) != 0)
+ break;
+ loc = i << 10;
+
+ for (n = loc - 1, failed = 0; n <= loc; n++)
+ {
+ gus_poke(loc, 0xaa);
+ if (gus_peek(loc) != 0xaa)
+ failed = 1;
+
+ gus_poke(loc, 0x55);
+ if (gus_peek(loc) != 0x55)
+ failed = 1;
+ }
+
+ if (failed)
+ break;
+ }
+ gus_mem_size = i << 10;
+ return 1;
}
static int
-guswave_ioctl (int dev,
- unsigned int cmd, caddr_t arg)
+guswave_ioctl(int dev,
+ unsigned int cmd, caddr_t arg)
{
- switch (cmd)
- {
- case SNDCTL_SYNTH_INFO:
- gus_info.nr_voices = nr_voices;
- memcpy ((&((char *) arg)[0]), (char *) &gus_info, sizeof (gus_info));
- return 0;
- break;
+ switch (cmd)
+ {
+ case SNDCTL_SYNTH_INFO:
+ gus_info.nr_voices = nr_voices;
+ memcpy((&((char *) arg)[0]), (char *) &gus_info, sizeof(gus_info));
+ return 0;
+ break;
- case SNDCTL_SEQ_RESETSAMPLES:
- reset_sample_memory ();
- return 0;
- break;
+ case SNDCTL_SEQ_RESETSAMPLES:
+ reset_sample_memory();
+ return 0;
+ break;
- case SNDCTL_SEQ_PERCMODE:
- return 0;
- break;
+ case SNDCTL_SEQ_PERCMODE:
+ return 0;
+ break;
- case SNDCTL_SYNTH_MEMAVL:
- return (gus_mem_size == 0) ? 0 : gus_mem_size - free_mem_ptr - 32;
+ case SNDCTL_SYNTH_MEMAVL:
+ return (gus_mem_size == 0) ? 0 : gus_mem_size - free_mem_ptr - 32;
- default:
- return -EINVAL;
- }
+ default:
+ return -EINVAL;
+ }
}
static int
-guswave_set_instr (int dev, int voice, int instr_no)
+guswave_set_instr(int dev, int voice, int instr_no)
{
- int sample_no;
+ int sample_no;
- if (instr_no < 0 || instr_no > MAX_PATCH)
- instr_no = 0; /* Default to acoustic piano */
+ if (instr_no < 0 || instr_no > MAX_PATCH)
+ instr_no = 0; /* Default to acoustic piano */
- if (voice < 0 || voice > 31)
- return -EINVAL;
+ if (voice < 0 || voice > 31)
+ return -EINVAL;
- if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
- {
- voices[voice].sample_pending = instr_no;
- return 0;
- }
-
- sample_no = patch_table[instr_no];
- patch_map[voice] = -1;
-
- if (sample_no == NOT_SAMPLE)
- {
- printk ("GUS: Undefined patch %d for voice %d\n", instr_no, voice);
- return -EINVAL; /* Patch not defined */
- }
-
- if (sample_ptrs[sample_no] == -1) /* Sample not loaded */
- {
- printk ("GUS: Sample #%d not loaded for patch %d (voice %d)\n",
- sample_no, instr_no, voice);
- return -EINVAL;
- }
+ if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+ {
+ voices[voice].sample_pending = instr_no;
+ return 0;
+ }
+ sample_no = patch_table[instr_no];
+ patch_map[voice] = -1;
- sample_map[voice] = sample_no;
- patch_map[voice] = instr_no;
- return 0;
+ if (sample_no == NOT_SAMPLE)
+ {
+ printk("GUS: Undefined patch %d for voice %d\n", instr_no, voice);
+ return -EINVAL; /* Patch not defined */
+ }
+ if (sample_ptrs[sample_no] == -1) /* Sample not loaded */
+ {
+ printk("GUS: Sample #%d not loaded for patch %d (voice %d)\n", sample_no, instr_no, voice);
+ return -EINVAL;
+ }
+ sample_map[voice] = sample_no;
+ patch_map[voice] = instr_no;
+ return 0;
}
static int
-guswave_kill_note (int dev, int voice, int note, int velocity)
+guswave_kill_note(int dev, int voice, int note, int velocity)
{
- unsigned long flags;
+ unsigned long flags;
- save_flags (flags);
- cli ();
- /* voice_alloc->map[voice] = 0xffff; */
- if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
- {
- voices[voice].kill_pending = 1;
- restore_flags (flags);
- }
- else
- {
- restore_flags (flags);
- gus_voice_fade (voice);
- }
+ save_flags(flags);
+ cli();
+ /* voice_alloc->map[voice] = 0xffff; */
+ if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+ {
+ voices[voice].kill_pending = 1;
+ restore_flags(flags);
+ } else
+ {
+ restore_flags(flags);
+ gus_voice_fade(voice);
+ }
- restore_flags (flags);
- return 0;
+ restore_flags(flags);
+ return 0;
}
static void
-guswave_aftertouch (int dev, int voice, int pressure)
+guswave_aftertouch(int dev, int voice, int pressure)
{
}
static void
-guswave_panning (int dev, int voice, int value)
+guswave_panning(int dev, int voice, int value)
{
- if (voice >= 0 || voice < 32)
- voices[voice].panning = value;
+ if (voice >= 0 || voice < 32)
+ voices[voice].panning = value;
}
static void
-guswave_volume_method (int dev, int mode)
+guswave_volume_method(int dev, int mode)
{
- if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO)
- volume_method = mode;
+ if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO)
+ volume_method = mode;
}
static void
-compute_volume (int voice, int volume)
+compute_volume(int voice, int volume)
{
- if (volume < 128)
- voices[voice].midi_volume = volume;
-
- switch (volume_method)
- {
- case VOL_METHOD_ADAGIO:
- voices[voice].initial_volume =
- gus_adagio_vol (voices[voice].midi_volume, voices[voice].main_vol,
- voices[voice].expression_vol,
- voices[voice].patch_vol);
- break;
+ if (volume < 128)
+ voices[voice].midi_volume = volume;
- case VOL_METHOD_LINEAR: /* Totally ignores patch-volume and expression */
- voices[voice].initial_volume =
- gus_linear_vol (volume, voices[voice].main_vol);
- break;
+ switch (volume_method)
+ {
+ case VOL_METHOD_ADAGIO:
+ voices[voice].initial_volume =
+ gus_adagio_vol(voices[voice].midi_volume, voices[voice].main_vol,
+ voices[voice].expression_vol,
+ voices[voice].patch_vol);
+ break;
+
+ case VOL_METHOD_LINEAR: /* Totally ignores patch-volume and expression */
+ voices[voice].initial_volume =
+ gus_linear_vol(volume, voices[voice].main_vol);
+ break;
- default:
- voices[voice].initial_volume = volume_base +
- (voices[voice].midi_volume * volume_scale);
- }
+ default:
+ voices[voice].initial_volume = volume_base +
+ (voices[voice].midi_volume * volume_scale);
+ }
- if (voices[voice].initial_volume > 4030)
- voices[voice].initial_volume = 4030;
+ if (voices[voice].initial_volume > 4030)
+ voices[voice].initial_volume = 4030;
}
static void
-compute_and_set_volume (int voice, int volume, int ramp_time)
-{
- int curr, target, rate;
- unsigned long flags;
-
- compute_volume (voice, volume);
- voices[voice].current_volume = voices[voice].initial_volume;
-
- save_flags (flags);
- cli ();
- /*
- * CAUTION! Interrupts disabled. Enable them before returning
- */
-
- gus_select_voice (voice);
-
- curr = gus_read16 (0x09) >> 4;
- target = voices[voice].initial_volume;
-
- if (ramp_time == INSTANT_RAMP)
- {
- gus_rampoff ();
- gus_voice_volume (target);
- restore_flags (flags);
- return;
- }
-
- if (ramp_time == FAST_RAMP)
- rate = 63;
- else
- rate = 16;
- gus_ramp_rate (0, rate);
-
- if ((target - curr) / 64 == 0) /* Close enough to target. */
- {
- gus_rampoff ();
- gus_voice_volume (target);
- restore_flags (flags);
- return;
- }
-
- if (target > curr)
- {
- if (target > (4095 - 65))
- target = 4095 - 65;
- gus_ramp_range (curr, target);
- gus_rampon (0x00); /* Ramp up, once, no IRQ */
- }
- else
- {
- if (target < 65)
- target = 65;
-
- gus_ramp_range (target, curr);
- gus_rampon (0x40); /* Ramp down, once, no irq */
- }
- restore_flags (flags);
+compute_and_set_volume(int voice, int volume, int ramp_time)
+{
+ int curr, target, rate;
+ unsigned long flags;
+
+ compute_volume(voice, volume);
+ voices[voice].current_volume = voices[voice].initial_volume;
+
+ save_flags(flags);
+ cli();
+ /*
+ * CAUTION! Interrupts disabled. Enable them before returning
+ */
+
+ gus_select_voice(voice);
+
+ curr = gus_read16(0x09) >> 4;
+ target = voices[voice].initial_volume;
+
+ if (ramp_time == INSTANT_RAMP)
+ {
+ gus_rampoff();
+ gus_voice_volume(target);
+ restore_flags(flags);
+ return;
+ }
+ if (ramp_time == FAST_RAMP)
+ rate = 63;
+ else
+ rate = 16;
+ gus_ramp_rate(0, rate);
+
+ if ((target - curr) / 64 == 0) /* Close enough to target. */
+ {
+ gus_rampoff();
+ gus_voice_volume(target);
+ restore_flags(flags);
+ return;
+ }
+ if (target > curr)
+ {
+ if (target > (4095 - 65))
+ target = 4095 - 65;
+ gus_ramp_range(curr, target);
+ gus_rampon(0x00); /* Ramp up, once, no IRQ */
+ } else
+ {
+ if (target < 65)
+ target = 65;
+
+ gus_ramp_range(target, curr);
+ gus_rampon(0x40); /* Ramp down, once, no irq */
+ }
+ restore_flags(flags);
}
static void
-dynamic_volume_change (int voice)
+dynamic_volume_change(int voice)
{
- unsigned char status;
- unsigned long flags;
-
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- status = gus_read8 (0x00); /* Get voice status */
- restore_flags (flags);
+ unsigned char status;
+ unsigned long flags;
- if (status & 0x03)
- return; /* Voice was not running */
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ status = gus_read8(0x00); /* Get voice status */
+ restore_flags(flags);
- if (!(voices[voice].mode & WAVE_ENVELOPES))
- {
- compute_and_set_volume (voice, voices[voice].midi_volume, 1);
- return;
- }
+ if (status & 0x03)
+ return; /* Voice was not running */
- /*
- * Voice is running and has envelopes.
- */
-
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- status = gus_read8 (0x0d); /* Ramping status */
- restore_flags (flags);
+ if (!(voices[voice].mode & WAVE_ENVELOPES))
+ {
+ compute_and_set_volume(voice, voices[voice].midi_volume, 1);
+ return;
+ }
+ /*
+ * Voice is running and has envelopes.
+ */
- if (status & 0x03) /* Sustain phase? */
- {
- compute_and_set_volume (voice, voices[voice].midi_volume, 1);
- return;
- }
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ status = gus_read8(0x0d); /* Ramping status */
+ restore_flags(flags);
- if (voices[voice].env_phase < 0)
- return;
+ if (status & 0x03) /* Sustain phase? */
+ {
+ compute_and_set_volume(voice, voices[voice].midi_volume, 1);
+ return;
+ }
+ if (voices[voice].env_phase < 0)
+ return;
- compute_volume (voice, voices[voice].midi_volume);
+ compute_volume(voice, voices[voice].midi_volume);
}
static void
-guswave_controller (int dev, int voice, int ctrl_num, int value)
-{
- unsigned long flags;
- unsigned long freq;
-
- if (voice < 0 || voice > 31)
- return;
-
- switch (ctrl_num)
- {
- case CTRL_PITCH_BENDER:
- voices[voice].bender = value;
-
- if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
- {
- freq = compute_finetune (voices[voice].orig_freq, value,
- voices[voice].bender_range, 0);
- voices[voice].current_freq = freq;
-
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- gus_voice_freq (freq);
- restore_flags (flags);
- }
- break;
-
- case CTRL_PITCH_BENDER_RANGE:
- voices[voice].bender_range = value;
- break;
- case CTL_EXPRESSION:
- value /= 128;
- case CTRL_EXPRESSION:
- if (volume_method == VOL_METHOD_ADAGIO)
- {
- voices[voice].expression_vol = value;
- if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
- dynamic_volume_change (voice);
- }
- break;
-
- case CTL_PAN:
- voices[voice].panning = (value * 2) - 128;
- break;
-
- case CTL_MAIN_VOLUME:
- value = (value * 100) / 16383;
-
- case CTRL_MAIN_VOLUME:
- voices[voice].main_vol = value;
- if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
- dynamic_volume_change (voice);
- break;
-
- default:
- break;
- }
+guswave_controller(int dev, int voice, int ctrl_num, int value)
+{
+ unsigned long flags;
+ unsigned long freq;
+
+ if (voice < 0 || voice > 31)
+ return;
+
+ switch (ctrl_num)
+ {
+ case CTRL_PITCH_BENDER:
+ voices[voice].bender = value;
+
+ if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
+ {
+ freq = compute_finetune(voices[voice].orig_freq, value,
+ voices[voice].bender_range, 0);
+ voices[voice].current_freq = freq;
+
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ gus_voice_freq(freq);
+ restore_flags(flags);
+ }
+ break;
+
+ case CTRL_PITCH_BENDER_RANGE:
+ voices[voice].bender_range = value;
+ break;
+ case CTL_EXPRESSION:
+ value /= 128;
+ case CTRL_EXPRESSION:
+ if (volume_method == VOL_METHOD_ADAGIO)
+ {
+ voices[voice].expression_vol = value;
+ if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
+ dynamic_volume_change(voice);
+ }
+ break;
+
+ case CTL_PAN:
+ voices[voice].panning = (value * 2) - 128;
+ break;
+
+ case CTL_MAIN_VOLUME:
+ value = (value * 100) / 16383;
+
+ case CTRL_MAIN_VOLUME:
+ voices[voice].main_vol = value;
+ if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
+ dynamic_volume_change(voice);
+ break;
+
+ default:
+ break;
+ }
}
static int
-guswave_start_note2 (int dev, int voice, int note_num, int volume)
-{
- int sample, best_sample, best_delta, delta_freq;
- int is16bits, samplep, patch, pan;
- unsigned long note_freq, base_note, freq, flags;
- unsigned char mode = 0;
-
- if (voice < 0 || voice > 31)
- {
- printk ("GUS: Invalid voice\n");
- return -EINVAL;
- }
-
- if (note_num == 255)
- {
- if (voices[voice].mode & WAVE_ENVELOPES)
- {
- voices[voice].midi_volume = volume;
- dynamic_volume_change (voice);
- return 0;
- }
-
- compute_and_set_volume (voice, volume, 1);
- return 0;
- }
-
- if ((patch = patch_map[voice]) == -1)
- {
- return -EINVAL;
- }
-
- if ((samplep = patch_table[patch]) == NOT_SAMPLE)
- {
- return -EINVAL;
- }
-
- note_freq = note_to_freq (note_num);
-
- /*
- * Find a sample within a patch so that the note_freq is between low_note
- * and high_note.
- */
- sample = -1;
-
- best_sample = samplep;
- best_delta = 1000000;
- while (samplep != 0 && samplep != NOT_SAMPLE && sample == -1)
- {
- delta_freq = note_freq - samples[samplep].base_note;
- if (delta_freq < 0)
- delta_freq = -delta_freq;
- if (delta_freq < best_delta)
- {
- best_sample = samplep;
- best_delta = delta_freq;
- }
- if (samples[samplep].low_note <= note_freq &&
- note_freq <= samples[samplep].high_note)
- sample = samplep;
- else
- samplep = samples[samplep].key; /* Link to next sample */
- }
- if (sample == -1)
- sample = best_sample;
-
- if (sample == -1)
- {
- printk ("GUS: Patch %d not defined for note %d\n", patch, note_num);
- return 0; /* Should play default patch ??? */
- }
-
- is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0;
- voices[voice].mode = samples[sample].mode;
- voices[voice].patch_vol = samples[sample].volume;
-
- if (iw_mode)
- gus_write8 (0x15, 0x00); /* RAM, Reset voice deactivate bit of SMSI */
-
- if (voices[voice].mode & WAVE_ENVELOPES)
- {
- int i;
-
- for (i = 0; i < 6; i++)
- {
- voices[voice].env_rate[i] = samples[sample].env_rate[i];
- voices[voice].env_offset[i] = samples[sample].env_offset[i];
- }
- }
-
- sample_map[voice] = sample;
-
- if (voices[voice].fixed_pitch) /* Fixed pitch */
- {
- freq = samples[sample].base_freq;
- }
- else
- {
- base_note = samples[sample].base_note / 100;
- note_freq /= 100;
-
- freq = samples[sample].base_freq * note_freq / base_note;
- }
-
- voices[voice].orig_freq = freq;
-
- /*
- * Since the pitch bender may have been set before playing the note, we
- * have to calculate the bending now.
- */
-
- freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender,
- voices[voice].bender_range, 0);
- voices[voice].current_freq = freq;
-
- pan = (samples[sample].panning + voices[voice].panning) / 32;
- pan += 7;
- if (pan < 0)
- pan = 0;
- if (pan > 15)
- pan = 15;
-
- if (samples[sample].mode & WAVE_16_BITS)
- {
- mode |= 0x04; /* 16 bits */
- if ((sample_ptrs[sample] / GUS_BANK_SIZE) !=
- ((sample_ptrs[sample] + samples[sample].len) / GUS_BANK_SIZE))
- printk ("GUS: Sample address error\n");
- }
+guswave_start_note2(int dev, int voice, int note_num, int volume)
+{
+ int sample, best_sample, best_delta, delta_freq;
+ int is16bits, samplep, patch, pan;
+ unsigned long note_freq, base_note, freq, flags;
+ unsigned char mode = 0;
+
+ if (voice < 0 || voice > 31)
+ {
+ printk("GUS: Invalid voice\n");
+ return -EINVAL;
+ }
+ if (note_num == 255)
+ {
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ {
+ voices[voice].midi_volume = volume;
+ dynamic_volume_change(voice);
+ return 0;
+ }
+ compute_and_set_volume(voice, volume, 1);
+ return 0;
+ }
+ if ((patch = patch_map[voice]) == -1)
+ {
+ return -EINVAL;
+ }
+ if ((samplep = patch_table[patch]) == NOT_SAMPLE)
+ {
+ return -EINVAL;
+ }
+ note_freq = note_to_freq(note_num);
+
+ /*
+ * Find a sample within a patch so that the note_freq is between low_note
+ * and high_note.
+ */
+ sample = -1;
+
+ best_sample = samplep;
+ best_delta = 1000000;
+ while (samplep != 0 && samplep != NOT_SAMPLE && sample == -1)
+ {
+ delta_freq = note_freq - samples[samplep].base_note;
+ if (delta_freq < 0)
+ delta_freq = -delta_freq;
+ if (delta_freq < best_delta)
+ {
+ best_sample = samplep;
+ best_delta = delta_freq;
+ }
+ if (samples[samplep].low_note <= note_freq &&
+ note_freq <= samples[samplep].high_note)
+ sample = samplep;
+ else
+ samplep = samples[samplep].key; /* Link to next sample */
+ }
+ if (sample == -1)
+ sample = best_sample;
+
+ if (sample == -1)
+ {
+ printk("GUS: Patch %d not defined for note %d\n", patch, note_num);
+ return 0; /* Should play default patch ??? */
+ }
+ is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0;
+ voices[voice].mode = samples[sample].mode;
+ voices[voice].patch_vol = samples[sample].volume;
+
+ if (iw_mode)
+ gus_write8(0x15, 0x00); /* RAM, Reset voice deactivate bit of SMSI */
+
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ {
+ int i;
+
+ for (i = 0; i < 6; i++)
+ {
+ voices[voice].env_rate[i] = samples[sample].env_rate[i];
+ voices[voice].env_offset[i] = samples[sample].env_offset[i];
+ }
+ }
+ sample_map[voice] = sample;
+
+ if (voices[voice].fixed_pitch) /* Fixed pitch */
+ {
+ freq = samples[sample].base_freq;
+ } else
+ {
+ base_note = samples[sample].base_note / 100;
+ note_freq /= 100;
+
+ freq = samples[sample].base_freq * note_freq / base_note;
+ }
+
+ voices[voice].orig_freq = freq;
+ /*
+ * Since the pitch bender may have been set before playing the note, we
+ * have to calculate the bending now.
+ */
+
+ freq = compute_finetune(voices[voice].orig_freq, voices[voice].bender,
+ voices[voice].bender_range, 0);
+ voices[voice].current_freq = freq;
+
+ pan = (samples[sample].panning + voices[voice].panning) / 32;
+ pan += 7;
+ if (pan < 0)
+ pan = 0;
+ if (pan > 15)
+ pan = 15;
+
+ if (samples[sample].mode & WAVE_16_BITS)
+ {
+ mode |= 0x04; /* 16 bits */
+ if ((sample_ptrs[sample] / GUS_BANK_SIZE) !=
+ ((sample_ptrs[sample] + samples[sample].len) / GUS_BANK_SIZE))
+ printk("GUS: Sample address error\n");
+ }
/*************************************************************************
* CAUTION! Interrupts disabled. Don't return before enabling
*************************************************************************/
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- gus_voice_off ();
- gus_rampoff ();
-
- restore_flags (flags);
-
- if (voices[voice].mode & WAVE_ENVELOPES)
- {
- compute_volume (voice, volume);
- init_envelope (voice);
- }
- else
- {
- compute_and_set_volume (voice, volume, 0);
- }
-
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
-
- if (samples[sample].mode & WAVE_LOOP_BACK)
- gus_write_addr (0x0a, sample_ptrs[sample] + samples[sample].len -
- voices[voice].offset_pending, 0, is16bits); /* start=end */
- else
- gus_write_addr (0x0a, sample_ptrs[sample] + voices[voice].offset_pending,
- 0, is16bits); /* Sample start=begin */
-
- if (samples[sample].mode & WAVE_LOOPING)
- {
- mode |= 0x08;
-
- if (samples[sample].mode & WAVE_BIDIR_LOOP)
- mode |= 0x10;
-
- if (samples[sample].mode & WAVE_LOOP_BACK)
- {
- gus_write_addr (0x0a,
- sample_ptrs[sample] + samples[sample].loop_end -
- voices[voice].offset_pending,
- (samples[sample].fractions >> 4) & 0x0f, is16bits);
- mode |= 0x40;
- }
-
- gus_write_addr (0x02, sample_ptrs[sample] + samples[sample].loop_start,
- samples[sample].fractions & 0x0f,
- is16bits); /* Loop start location */
- gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].loop_end,
- (samples[sample].fractions >> 4) & 0x0f,
- is16bits); /* Loop end location */
- }
- else
- {
- mode |= 0x20; /* Loop IRQ at the end */
- voices[voice].loop_irq_mode = LMODE_FINISH; /* Ramp down at the end */
- voices[voice].loop_irq_parm = 1;
- gus_write_addr (0x02, sample_ptrs[sample],
- 0, is16bits); /* Loop start location */
- gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].len - 1,
- (samples[sample].fractions >> 4) & 0x0f,
- is16bits); /* Loop end location */
- }
- gus_voice_freq (freq);
- gus_voice_balance (pan);
- gus_voice_on (mode);
- restore_flags (flags);
-
- return 0;
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ gus_voice_off();
+ gus_rampoff();
+
+ restore_flags(flags);
+
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ {
+ compute_volume(voice, volume);
+ init_envelope(voice);
+ } else
+ {
+ compute_and_set_volume(voice, volume, 0);
+ }
+
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+
+ if (samples[sample].mode & WAVE_LOOP_BACK)
+ gus_write_addr(0x0a, sample_ptrs[sample] + samples[sample].len -
+ voices[voice].offset_pending, 0, is16bits); /* start=end */
+ else
+ gus_write_addr(0x0a, sample_ptrs[sample] + voices[voice].offset_pending,
+ 0, is16bits); /* Sample start=begin */
+
+ if (samples[sample].mode & WAVE_LOOPING)
+ {
+ mode |= 0x08;
+
+ if (samples[sample].mode & WAVE_BIDIR_LOOP)
+ mode |= 0x10;
+
+ if (samples[sample].mode & WAVE_LOOP_BACK)
+ {
+ gus_write_addr(0x0a,
+ sample_ptrs[sample] + samples[sample].loop_end -
+ voices[voice].offset_pending,
+ (samples[sample].fractions >> 4) & 0x0f, is16bits);
+ mode |= 0x40;
+ }
+ gus_write_addr(0x02, sample_ptrs[sample] + samples[sample].loop_start,
+ samples[sample].fractions & 0x0f,
+ is16bits); /* Loop start location */
+ gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].loop_end,
+ (samples[sample].fractions >> 4) & 0x0f,
+ is16bits); /* Loop end location */
+ } else
+ {
+ mode |= 0x20; /* Loop IRQ at the end */
+ voices[voice].loop_irq_mode = LMODE_FINISH; /* Ramp down at the end */
+ voices[voice].loop_irq_parm = 1;
+ gus_write_addr(0x02, sample_ptrs[sample],
+ 0, is16bits); /* Loop start location */
+ gus_write_addr(0x04, sample_ptrs[sample] + samples[sample].len - 1,
+ (samples[sample].fractions >> 4) & 0x0f,
+ is16bits); /* Loop end location */
+ }
+ gus_voice_freq(freq);
+ gus_voice_balance(pan);
+ gus_voice_on(mode);
+ restore_flags(flags);
+
+ return 0;
}
/*
@@ -1637,1946 +1600,1925 @@ guswave_start_note2 (int dev, int voice, int note_num, int volume)
*/
static int
-guswave_start_note (int dev, int voice, int note_num, int volume)
-{
- long int flags;
- int mode;
- int ret_val = 0;
-
- save_flags (flags);
- cli ();
- if (note_num == 255)
- {
- if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
- {
- voices[voice].volume_pending = volume;
- }
- else
- {
- ret_val = guswave_start_note2 (dev, voice, note_num, volume);
- }
- }
- else
- {
- gus_select_voice (voice);
- mode = gus_read8 (0x00);
- if (mode & 0x20)
- gus_write8 (0x00, mode & 0xdf); /* No interrupt! */
-
- voices[voice].offset_pending = 0;
- voices[voice].kill_pending = 0;
- voices[voice].volume_irq_mode = 0;
- voices[voice].loop_irq_mode = 0;
-
- if (voices[voice].sample_pending >= 0)
- {
- restore_flags (flags); /* Run temporarily with interrupts enabled */
- guswave_set_instr (voices[voice].dev_pending, voice,
- voices[voice].sample_pending);
- voices[voice].sample_pending = -1;
- save_flags (flags);
- cli ();
- gus_select_voice (voice); /* Reselect the voice (just to be sure) */
- }
-
- if ((mode & 0x01) || (int) ((gus_read16 (0x09) >> 4) < (unsigned) 2065))
- {
- ret_val = guswave_start_note2 (dev, voice, note_num, volume);
- }
- else
- {
- voices[voice].dev_pending = dev;
- voices[voice].note_pending = note_num;
- voices[voice].volume_pending = volume;
- voices[voice].volume_irq_mode = VMODE_START_NOTE;
-
- gus_rampoff ();
- gus_ramp_range (2000, 4065);
- gus_ramp_rate (0, 63); /* Fastest possible rate */
- gus_rampon (0x20 | 0x40); /* Ramp down, once, irq */
- }
- }
- restore_flags (flags);
- return ret_val;
+guswave_start_note(int dev, int voice, int note_num, int volume)
+{
+ long int flags;
+ int mode;
+ int ret_val = 0;
+
+ save_flags(flags);
+ cli();
+ if (note_num == 255)
+ {
+ if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+ {
+ voices[voice].volume_pending = volume;
+ } else
+ {
+ ret_val = guswave_start_note2(dev, voice, note_num, volume);
+ }
+ } else
+ {
+ gus_select_voice(voice);
+ mode = gus_read8(0x00);
+ if (mode & 0x20)
+ gus_write8(0x00, mode & 0xdf); /* No interrupt! */
+
+ voices[voice].offset_pending = 0;
+ voices[voice].kill_pending = 0;
+ voices[voice].volume_irq_mode = 0;
+ voices[voice].loop_irq_mode = 0;
+
+ if (voices[voice].sample_pending >= 0)
+ {
+ restore_flags(flags); /* Run temporarily with interrupts enabled */
+ guswave_set_instr(voices[voice].dev_pending, voice,
+ voices[voice].sample_pending);
+ voices[voice].sample_pending = -1;
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice); /* Reselect the voice (just to be sure) */
+ }
+ if ((mode & 0x01) || (int) ((gus_read16(0x09) >> 4) < (unsigned) 2065))
+ {
+ ret_val = guswave_start_note2(dev, voice, note_num, volume);
+ } else
+ {
+ voices[voice].dev_pending = dev;
+ voices[voice].note_pending = note_num;
+ voices[voice].volume_pending = volume;
+ voices[voice].volume_irq_mode = VMODE_START_NOTE;
+
+ gus_rampoff();
+ gus_ramp_range(2000, 4065);
+ gus_ramp_rate(0, 63); /* Fastest possible rate */
+ gus_rampon(0x20 | 0x40); /* Ramp down, once, irq */
+ }
+ }
+ restore_flags(flags);
+ return ret_val;
}
static void
-guswave_reset (int dev)
+guswave_reset(int dev)
{
- int i;
+ int i;
- for (i = 0; i < 32; i++)
- {
- gus_voice_init (i);
- gus_voice_init2 (i);
- }
+ for (i = 0; i < 32; i++)
+ {
+ gus_voice_init(i);
+ gus_voice_init2(i);
+ }
}
static int
-guswave_open (int dev, int mode)
+guswave_open(int dev, int mode)
{
- int err;
+ int err;
- if (gus_busy)
- return -EBUSY;
+ if (gus_busy)
+ return -EBUSY;
- voice_alloc->timestamp = 0;
+ voice_alloc->timestamp = 0;
- if ((err = DMAbuf_open_dma (gus_devnum)) < 0)
- {
- /* printk ("GUS: Loading samples without DMA\n"); */
- gus_no_dma = 1; /* Upload samples using PIO */
- }
- else
- gus_no_dma = 0;
+ if ((err = DMAbuf_open_dma(gus_devnum)) < 0)
+ {
+ /* printk( "GUS: Loading samples without DMA\n"); */
+ gus_no_dma = 1; /* Upload samples using PIO */
+ } else
+ gus_no_dma = 0;
- dram_sleep_flag.opts = WK_NONE;
- gus_busy = 1;
- active_device = GUS_DEV_WAVE;
+ dram_sleep_flag.opts = WK_NONE;
+ gus_busy = 1;
+ active_device = GUS_DEV_WAVE;
- gusintr (gus_irq, NULL, NULL); /* Serve pending interrupts */
- gus_initialize ();
- gus_reset ();
- gusintr (gus_irq, NULL, NULL); /* Serve pending interrupts */
+ gusintr(gus_irq, NULL, NULL); /* Serve pending interrupts */
+ gus_initialize();
+ gus_reset();
+ gusintr(gus_irq, NULL, NULL); /* Serve pending interrupts */
- return 0;
+ return 0;
}
static void
-guswave_close (int dev)
+guswave_close(int dev)
{
- gus_busy = 0;
- active_device = 0;
- gus_reset ();
+ gus_busy = 0;
+ active_device = 0;
+ gus_reset();
- if (!gus_no_dma)
- DMAbuf_close_dma (gus_devnum);
+ if (!gus_no_dma)
+ DMAbuf_close_dma(gus_devnum);
}
static int
-guswave_load_patch (int dev, int format, const char *addr,
- int offs, int count, int pmgr_flag)
-{
- struct patch_info patch;
- int instr;
- long sizeof_patch;
-
- unsigned long blk_sz, blk_end, left, src_offs, target;
-
- sizeof_patch = (long) &patch.data[0] - (long) &patch; /* Header size */
-
- if (format != GUS_PATCH)
- {
- printk ("GUS Error: Invalid patch format (key) 0x%x\n", format);
- return -EINVAL;
- }
-
- if (count < sizeof_patch)
- {
- printk ("GUS Error: Patch header too short\n");
- return -EINVAL;
- }
-
- count -= sizeof_patch;
-
- if (free_sample >= MAX_SAMPLE)
- {
- printk ("GUS: Sample table full\n");
- return -ENOSPC;
- }
-
- /*
- * Copy the header from user space but ignore the first bytes which have
- * been transferred already.
- */
-
- copy_from_user (&((char *) &patch)[offs], &(addr)[offs], sizeof_patch - offs);
-
- if (patch.mode & WAVE_ROM)
- return -EINVAL;
- if (gus_mem_size == 0)
-
- return -ENOSPC;
-
- instr = patch.instr_no;
-
- if (instr < 0 || instr > MAX_PATCH)
- {
- printk ("GUS: Invalid patch number %d\n", instr);
- return -EINVAL;
- }
-
- if (count < patch.len)
- {
- printk ("GUS Warning: Patch record too short (%d<%d)\n",
- count, (int) patch.len);
- patch.len = count;
- }
-
- if (patch.len <= 0 || patch.len > gus_mem_size)
- {
- printk ("GUS: Invalid sample length %d\n", (int) patch.len);
- return -EINVAL;
- }
-
- if (patch.mode & WAVE_LOOPING)
- {
- if (patch.loop_start < 0 || patch.loop_start >= patch.len)
- {
- printk ("GUS: Invalid loop start\n");
- return -EINVAL;
- }
-
- if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len)
- {
- printk ("GUS: Invalid loop end\n");
- return -EINVAL;
- }
- }
-
- free_mem_ptr = (free_mem_ptr + 31) & ~31; /* 32 byte alignment */
-
- if (patch.mode & WAVE_16_BITS)
- {
- /*
- * 16 bit samples must fit one 256k bank.
- */
- if (patch.len >= GUS_BANK_SIZE)
- {
- printk ("GUS: Sample (16 bit) too long %d\n", (int) patch.len);
- return -ENOSPC;
- }
-
- if ((free_mem_ptr / GUS_BANK_SIZE) !=
- ((free_mem_ptr + patch.len) / GUS_BANK_SIZE))
- {
- unsigned long tmp_mem = /* Align to 256K */
- ((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE;
-
- if ((tmp_mem + patch.len) > gus_mem_size)
- return -ENOSPC;
-
- free_mem_ptr = tmp_mem; /* This leaves unusable memory */
- }
- }
-
- if ((free_mem_ptr + patch.len) > gus_mem_size)
- return -ENOSPC;
-
- sample_ptrs[free_sample] = free_mem_ptr;
-
- /*
- * Tremolo is not possible with envelopes
- */
-
- if (patch.mode & WAVE_ENVELOPES)
- patch.mode &= ~WAVE_TREMOLO;
-
- if (!(patch.mode & WAVE_FRACTIONS))
- {
- patch.fractions = 0;
- }
-
- memcpy ((char *) &samples[free_sample], &patch, sizeof_patch);
-
- /*
- * Link this_one sample to the list of samples for patch 'instr'.
- */
-
- samples[free_sample].key = patch_table[instr];
- patch_table[instr] = free_sample;
-
- /*
- * Use DMA to transfer the wave data to the DRAM
- */
-
- left = patch.len;
- src_offs = 0;
- target = free_mem_ptr;
-
- while (left) /* Not completely transferred yet */
- {
- blk_sz = audio_devs[gus_devnum]->dmap_out->bytes_in_use;
- if (blk_sz > left)
- blk_sz = left;
-
- /*
- * DMA cannot cross bank (256k) boundaries. Check for that.
- */
- blk_end = target + blk_sz;
-
- if ((target / GUS_BANK_SIZE) != (blk_end / GUS_BANK_SIZE))
- { /* Split the block */
-
- blk_end &= ~(GUS_BANK_SIZE - 1);
- blk_sz = blk_end - target;
- }
-
- if (gus_no_dma)
- {
- /*
- * For some reason the DMA is not possible. We have to use PIO.
- */
- long i;
- unsigned char data;
-
- for (i = 0; i < blk_sz; i++)
- {
- get_user (*(unsigned char *) &data, (unsigned char *) &((addr)[sizeof_patch + i]));
- if (patch.mode & WAVE_UNSIGNED)
- if (!(patch.mode & WAVE_16_BITS) || (i & 0x01))
- data ^= 0x80; /* Convert to signed */
- gus_poke (target + i, data);
- }
- }
- else
- {
- unsigned long address, hold_address;
- unsigned char dma_command;
- unsigned long flags;
-
- if (audio_devs[gus_devnum]->dmap_out->raw_buf == NULL)
- {
- printk ("GUS: DMA buffer == NULL\n");
- return -ENOSPC;
- }
+guswave_load_patch(int dev, int format, const char *addr,
+ int offs, int count, int pmgr_flag)
+{
+ struct patch_info patch;
+ int instr;
+ long sizeof_patch;
- /*
- * OK, move now. First in and then out.
- */
+ unsigned long blk_sz, blk_end, left, src_offs, target;
- copy_from_user (audio_devs[gus_devnum]->dmap_out->raw_buf, &(addr)[sizeof_patch + src_offs], blk_sz);
+ sizeof_patch = (long) &patch.data[0] - (long) &patch; /* Header size */
- save_flags (flags);
- cli ();
-/******** INTERRUPTS DISABLED NOW ********/
- gus_write8 (0x41, 0); /* Disable GF1 DMA */
- DMAbuf_start_dma (gus_devnum,
- audio_devs[gus_devnum]->dmap_out->raw_buf_phys,
- blk_sz, DMA_MODE_WRITE);
+ if (format != GUS_PATCH)
+ {
+ printk("GUS Error: Invalid patch format (key) 0x%x\n", format);
+ return -EINVAL;
+ }
+ if (count < sizeof_patch)
+ {
+ printk("GUS Error: Patch header too short\n");
+ return -EINVAL;
+ }
+ count -= sizeof_patch;
- /*
- * Set the DRAM address for the wave data
- */
+ if (free_sample >= MAX_SAMPLE)
+ {
+ printk("GUS: Sample table full\n");
+ return -ENOSPC;
+ }
+ /*
+ * Copy the header from user space but ignore the first bytes which have
+ * been transferred already.
+ */
- if (iw_mode)
- {
- /* Different address translation in enhanced mode */
+ copy_from_user(&((char *) &patch)[offs], &(addr)[offs], sizeof_patch - offs);
- unsigned char hi;
+ if (patch.mode & WAVE_ROM)
+ return -EINVAL;
+ if (gus_mem_size == 0)
- if (gus_dma > 4)
- address = target >> 1; /* Convert to 16 bit word address */
- else
- address = target;
+ return -ENOSPC;
- hi = (unsigned char) ((address >> 16) & 0xf0);
- hi += (unsigned char) (address & 0x0f);
+ instr = patch.instr_no;
- gus_write16 (0x42, (address >> 4) & 0xffff); /* DMA address (low) */
- gus_write8 (0x50, hi);
- }
- else
- {
- address = target;
+ if (instr < 0 || instr > MAX_PATCH)
+ {
+ printk("GUS: Invalid patch number %d\n", instr);
+ return -EINVAL;
+ }
+ if (count < patch.len)
+ {
+ printk("GUS Warning: Patch record too short (%d<%d)\n", count, (int) patch.len);
+ patch.len = count;
+ }
+ if (patch.len <= 0 || patch.len > gus_mem_size)
+ {
+ printk("GUS: Invalid sample length %d\n", (int) patch.len);
+ return -EINVAL;
+ }
+ if (patch.mode & WAVE_LOOPING)
+ {
+ if (patch.loop_start < 0 || patch.loop_start >= patch.len)
+ {
+ printk("GUS: Invalid loop start\n");
+ return -EINVAL;
+ }
+ if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len)
+ {
+ printk("GUS: Invalid loop end\n");
+ return -EINVAL;
+ }
+ }
+ free_mem_ptr = (free_mem_ptr + 31) & ~31; /* 32 byte alignment */
- if (audio_devs[gus_devnum]->dmap_out->dma > 3)
- {
- hold_address = address;
- address = address >> 1;
- address &= 0x0001ffffL;
- address |= (hold_address & 0x000c0000L);
- }
+ if (patch.mode & WAVE_16_BITS)
+ {
+ /*
+ * 16 bit samples must fit one 256k bank.
+ */
+ if (patch.len >= GUS_BANK_SIZE)
+ {
+ printk("GUS: Sample (16 bit) too long %d\n", (int) patch.len);
+ return -ENOSPC;
+ }
+ if ((free_mem_ptr / GUS_BANK_SIZE) !=
+ ((free_mem_ptr + patch.len) / GUS_BANK_SIZE))
+ {
+ unsigned long tmp_mem = /* Align to 256K */
+ ((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE;
+
+ if ((tmp_mem + patch.len) > gus_mem_size)
+ return -ENOSPC;
+
+ free_mem_ptr = tmp_mem; /* This leaves unusable memory */
+ }
+ }
+ if ((free_mem_ptr + patch.len) > gus_mem_size)
+ return -ENOSPC;
- gus_write16 (0x42, (address >> 4) & 0xffff); /* DRAM DMA address */
- }
+ sample_ptrs[free_sample] = free_mem_ptr;
- /*
- * Start the DMA transfer
- */
+ /*
+ * Tremolo is not possible with envelopes
+ */
- dma_command = 0x21; /* IRQ enable, DMA start */
- if (patch.mode & WAVE_UNSIGNED)
- dma_command |= 0x80; /* Invert MSB */
- if (patch.mode & WAVE_16_BITS)
- dma_command |= 0x40; /* 16 bit _DATA_ */
- if (audio_devs[gus_devnum]->dmap_out->dma > 3)
- dma_command |= 0x04; /* 16 bit DMA _channel_ */
+ if (patch.mode & WAVE_ENVELOPES)
+ patch.mode &= ~WAVE_TREMOLO;
- gus_write8 (0x41, dma_command); /* Lets go luteet (=bugs) */
+ if (!(patch.mode & WAVE_FRACTIONS))
+ {
+ patch.fractions = 0;
+ }
+ memcpy((char *) &samples[free_sample], &patch, sizeof_patch);
- /*
- * Sleep here until the DRAM DMA done interrupt is served
- */
- active_device = GUS_DEV_WAVE;
+ /*
+ * Link this_one sample to the list of samples for patch 'instr'.
+ */
+
+ samples[free_sample].key = patch_table[instr];
+ patch_table[instr] = free_sample;
+ /*
+ * Use DMA to transfer the wave data to the DRAM
+ */
+ left = patch.len;
+ src_offs = 0;
+ target = free_mem_ptr;
+
+ while (left) /* Not completely transferred yet */
{
- unsigned long tlimit;
+ blk_sz = audio_devs[gus_devnum]->dmap_out->bytes_in_use;
+ if (blk_sz > left)
+ blk_sz = left;
+
+ /*
+ * DMA cannot cross bank (256k) boundaries. Check for that.
+ */
+ blk_end = target + blk_sz;
+
+ if ((target / GUS_BANK_SIZE) != (blk_end / GUS_BANK_SIZE))
+ { /* Split the block */
+
+ blk_end &= ~(GUS_BANK_SIZE - 1);
+ blk_sz = blk_end - target;
+ }
+ if (gus_no_dma)
+ {
+ /*
+ * For some reason the DMA is not possible. We have to use PIO.
+ */
+ long i;
+ unsigned char data;
+
+ for (i = 0; i < blk_sz; i++)
+ {
+ get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[sizeof_patch + i]));
+ if (patch.mode & WAVE_UNSIGNED)
+ if (!(patch.mode & WAVE_16_BITS) || (i & 0x01))
+ data ^= 0x80; /* Convert to signed */
+ gus_poke(target + i, data);
+ }
+ } else
+ {
+ unsigned long address, hold_address;
+ unsigned char dma_command;
+ unsigned long flags;
+
+ if (audio_devs[gus_devnum]->dmap_out->raw_buf == NULL)
+ {
+ printk("GUS: DMA buffer == NULL\n");
+ return -ENOSPC;
+ }
+ /*
+ * OK, move now. First in and then out.
+ */
+
+ copy_from_user(audio_devs[gus_devnum]->dmap_out->raw_buf, &(addr)[sizeof_patch + src_offs], blk_sz);
+
+ save_flags(flags);
+ cli();
+/******** INTERRUPTS DISABLED NOW ********/
+ gus_write8(0x41, 0); /* Disable GF1 DMA */
+ DMAbuf_start_dma(gus_devnum,
+ audio_devs[gus_devnum]->dmap_out->raw_buf_phys,
+ blk_sz, DMA_MODE_WRITE);
+
+ /*
+ * Set the DRAM address for the wave data
+ */
+
+ if (iw_mode)
+ {
+ /* Different address translation in enhanced mode */
+
+ unsigned char hi;
+
+ if (gus_dma > 4)
+ address = target >> 1; /* Convert to 16 bit word address */
+ else
+ address = target;
+
+ hi = (unsigned char) ((address >> 16) & 0xf0);
+ hi += (unsigned char) (address & 0x0f);
+
+ gus_write16(0x42, (address >> 4) & 0xffff); /* DMA address (low) */
+ gus_write8(0x50, hi);
+ } else
+ {
+ address = target;
+
+ if (audio_devs[gus_devnum]->dmap_out->dma > 3)
+ {
+ hold_address = address;
+ address = address >> 1;
+ address &= 0x0001ffffL;
+ address |= (hold_address & 0x000c0000L);
+ }
+ gus_write16(0x42, (address >> 4) & 0xffff); /* DRAM DMA address */
+ }
+
+ /*
+ * Start the DMA transfer
+ */
+
+ dma_command = 0x21; /* IRQ enable, DMA start */
+ if (patch.mode & WAVE_UNSIGNED)
+ dma_command |= 0x80; /* Invert MSB */
+ if (patch.mode & WAVE_16_BITS)
+ dma_command |= 0x40; /* 16 bit _DATA_ */
+ if (audio_devs[gus_devnum]->dmap_out->dma > 3)
+ dma_command |= 0x04; /* 16 bit DMA _channel_ */
+
+ gus_write8(0x41, dma_command); /* Lets go luteet (=bugs) */
+
+ /*
+ * Sleep here until the DRAM DMA done interrupt is served
+ */
+ active_device = GUS_DEV_WAVE;
+
+
+ {
+ unsigned long tlimit;
+
+ if (HZ)
+ current->timeout = tlimit = jiffies + (HZ);
+ else
+ tlimit = (unsigned long) -1;
+ dram_sleep_flag.opts = WK_SLEEP;
+ interruptible_sleep_on(&dram_sleeper);
+ if (!(dram_sleep_flag.opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ dram_sleep_flag.opts |= WK_TIMEOUT;
+ }
+ dram_sleep_flag.opts &= ~WK_SLEEP;
+ };
+ if ((dram_sleep_flag.opts & WK_TIMEOUT))
+ printk("GUS: DMA Transfer timed out\n");
+ restore_flags(flags);
+ }
+
+ /*
+ * Now the next part
+ */
+
+ left -= blk_sz;
+ src_offs += blk_sz;
+ target += blk_sz;
+
+ gus_write8(0x41, 0); /* Stop DMA */
+ }
- if (HZ)
- current->timeout = tlimit = jiffies + (HZ);
- else
- tlimit = (unsigned long) -1;
- dram_sleep_flag.opts = WK_SLEEP;
- interruptible_sleep_on (&dram_sleeper);
- if (!(dram_sleep_flag.opts & WK_WAKEUP))
- {
- if (jiffies >= tlimit)
- dram_sleep_flag.opts |= WK_TIMEOUT;
- }
- dram_sleep_flag.opts &= ~WK_SLEEP;
- };
- if ((dram_sleep_flag.opts & WK_TIMEOUT))
- printk ("GUS: DMA Transfer timed out\n");
- restore_flags (flags);
- }
+ free_mem_ptr += patch.len;
- /*
- * Now the next part
- */
+ free_sample++;
+ return 0;
+}
- left -= blk_sz;
- src_offs += blk_sz;
- target += blk_sz;
+static void
+guswave_hw_control(int dev, unsigned char *event_rec)
+{
+ int voice, cmd;
+ unsigned short p1, p2;
+ unsigned int plong;
+ unsigned flags;
- gus_write8 (0x41, 0); /* Stop DMA */
- }
+ cmd = event_rec[2];
+ voice = event_rec[3];
+ p1 = *(unsigned short *) &event_rec[4];
+ p2 = *(unsigned short *) &event_rec[6];
+ plong = *(unsigned int *) &event_rec[4];
- free_mem_ptr += patch.len;
+ if ((voices[voice].volume_irq_mode == VMODE_START_NOTE) &&
+ (cmd != _GUS_VOICESAMPLE) && (cmd != _GUS_VOICE_POS))
+ do_volume_irq(voice);
- free_sample++;
- return 0;
-}
+ switch (cmd)
+ {
-static void
-guswave_hw_control (int dev, unsigned char *event_rec)
-{
- int voice, cmd;
- unsigned short p1, p2;
- unsigned int plong;
- unsigned flags;
-
- cmd = event_rec[2];
- voice = event_rec[3];
- p1 = *(unsigned short *) &event_rec[4];
- p2 = *(unsigned short *) &event_rec[6];
- plong = *(unsigned int *) &event_rec[4];
-
- if ((voices[voice].volume_irq_mode == VMODE_START_NOTE) &&
- (cmd != _GUS_VOICESAMPLE) && (cmd != _GUS_VOICE_POS))
- do_volume_irq (voice);
-
- switch (cmd)
- {
-
- case _GUS_NUMVOICES:
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- gus_select_max_voices (p1);
- restore_flags (flags);
- break;
-
- case _GUS_VOICESAMPLE:
- guswave_set_instr (dev, voice, p1);
- break;
-
- case _GUS_VOICEON:
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- p1 &= ~0x20; /* Don't allow interrupts */
- gus_voice_on (p1);
- restore_flags (flags);
- break;
-
- case _GUS_VOICEOFF:
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- gus_voice_off ();
- restore_flags (flags);
- break;
-
- case _GUS_VOICEFADE:
- gus_voice_fade (voice);
- break;
-
- case _GUS_VOICEMODE:
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- p1 &= ~0x20; /* Don't allow interrupts */
- gus_voice_mode (p1);
- restore_flags (flags);
- break;
-
- case _GUS_VOICEBALA:
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- gus_voice_balance (p1);
- restore_flags (flags);
- break;
-
- case _GUS_VOICEFREQ:
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- gus_voice_freq (plong);
- restore_flags (flags);
- break;
-
- case _GUS_VOICEVOL:
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- gus_voice_volume (p1);
- restore_flags (flags);
- break;
-
- case _GUS_VOICEVOL2: /* Just update the software voice level */
- voices[voice].initial_volume =
- voices[voice].current_volume = p1;
- break;
-
- case _GUS_RAMPRANGE:
- if (voices[voice].mode & WAVE_ENVELOPES)
- break; /* NO-NO */
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- gus_ramp_range (p1, p2);
- restore_flags (flags);
- break;
-
- case _GUS_RAMPRATE:
- if (voices[voice].mode & WAVE_ENVELOPES)
- break; /* NJET-NJET */
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- gus_ramp_rate (p1, p2);
- restore_flags (flags);
- break;
-
- case _GUS_RAMPMODE:
- if (voices[voice].mode & WAVE_ENVELOPES)
- break; /* NO-NO */
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- p1 &= ~0x20; /* Don't allow interrupts */
- gus_ramp_mode (p1);
- restore_flags (flags);
- break;
-
- case _GUS_RAMPON:
- if (voices[voice].mode & WAVE_ENVELOPES)
- break; /* EI-EI */
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- p1 &= ~0x20; /* Don't allow interrupts */
- gus_rampon (p1);
- restore_flags (flags);
- break;
-
- case _GUS_RAMPOFF:
- if (voices[voice].mode & WAVE_ENVELOPES)
- break; /* NEJ-NEJ */
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- gus_rampoff ();
- restore_flags (flags);
- break;
-
- case _GUS_VOLUME_SCALE:
- volume_base = p1;
- volume_scale = p2;
- break;
-
- case _GUS_VOICE_POS:
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- gus_set_voice_pos (voice, plong);
- restore_flags (flags);
- break;
-
- default:;
- }
+ case _GUS_NUMVOICES:
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ gus_select_max_voices(p1);
+ restore_flags(flags);
+ break;
+
+ case _GUS_VOICESAMPLE:
+ guswave_set_instr(dev, voice, p1);
+ break;
+
+ case _GUS_VOICEON:
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ p1 &= ~0x20; /* Don't allow interrupts */
+ gus_voice_on(p1);
+ restore_flags(flags);
+ break;
+
+ case _GUS_VOICEOFF:
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ gus_voice_off();
+ restore_flags(flags);
+ break;
+
+ case _GUS_VOICEFADE:
+ gus_voice_fade(voice);
+ break;
+
+ case _GUS_VOICEMODE:
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ p1 &= ~0x20; /* Don't allow interrupts */
+ gus_voice_mode(p1);
+ restore_flags(flags);
+ break;
+
+ case _GUS_VOICEBALA:
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ gus_voice_balance(p1);
+ restore_flags(flags);
+ break;
+
+ case _GUS_VOICEFREQ:
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ gus_voice_freq(plong);
+ restore_flags(flags);
+ break;
+
+ case _GUS_VOICEVOL:
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ gus_voice_volume(p1);
+ restore_flags(flags);
+ break;
+
+ case _GUS_VOICEVOL2: /* Just update the software voice level */
+ voices[voice].initial_volume =
+ voices[voice].current_volume = p1;
+ break;
+
+ case _GUS_RAMPRANGE:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /* NO-NO */
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ gus_ramp_range(p1, p2);
+ restore_flags(flags);
+ break;
+
+ case _GUS_RAMPRATE:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /* NJET-NJET */
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ gus_ramp_rate(p1, p2);
+ restore_flags(flags);
+ break;
+
+ case _GUS_RAMPMODE:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /* NO-NO */
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ p1 &= ~0x20; /* Don't allow interrupts */
+ gus_ramp_mode(p1);
+ restore_flags(flags);
+ break;
+
+ case _GUS_RAMPON:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /* EI-EI */
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ p1 &= ~0x20; /* Don't allow interrupts */
+ gus_rampon(p1);
+ restore_flags(flags);
+ break;
+
+ case _GUS_RAMPOFF:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /* NEJ-NEJ */
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ gus_rampoff();
+ restore_flags(flags);
+ break;
+
+ case _GUS_VOLUME_SCALE:
+ volume_base = p1;
+ volume_scale = p2;
+ break;
+
+ case _GUS_VOICE_POS:
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ gus_set_voice_pos(voice, plong);
+ restore_flags(flags);
+ break;
+
+ default:;
+ }
}
static int
-gus_audio_set_speed (int speed)
+gus_audio_set_speed(int speed)
{
- if (speed <= 0)
- speed = gus_audio_speed;
+ if (speed <= 0)
+ speed = gus_audio_speed;
- if (speed < 4000)
- speed = 4000;
+ if (speed < 4000)
+ speed = 4000;
- if (speed > 44100)
- speed = 44100;
+ if (speed > 44100)
+ speed = 44100;
- gus_audio_speed = speed;
+ gus_audio_speed = speed;
- if (only_read_access)
- {
- /* Compute nearest valid recording speed and return it */
+ if (only_read_access)
+ {
+ /* Compute nearest valid recording speed and return it */
- /* speed = (9878400 / (gus_audio_speed + 2)) / 16; */
- speed = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16;
- speed = (9878400 / (speed * 16)) - 2;
- }
- return speed;
+ /* speed = (9878400 / (gus_audio_speed + 2)) / 16; */
+ speed = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16;
+ speed = (9878400 / (speed * 16)) - 2;
+ }
+ return speed;
}
static int
-gus_audio_set_channels (int channels)
+gus_audio_set_channels(int channels)
{
- if (!channels)
- return gus_audio_channels;
- if (channels > 2)
- channels = 2;
- if (channels < 1)
- channels = 1;
- gus_audio_channels = channels;
- return channels;
+ if (!channels)
+ return gus_audio_channels;
+ if (channels > 2)
+ channels = 2;
+ if (channels < 1)
+ channels = 1;
+ gus_audio_channels = channels;
+ return channels;
}
static int
-gus_audio_set_bits (int bits)
+gus_audio_set_bits(int bits)
{
- if (!bits)
- return gus_audio_bits;
+ if (!bits)
+ return gus_audio_bits;
- if (bits != 8 && bits != 16)
- bits = 8;
+ if (bits != 8 && bits != 16)
+ bits = 8;
- if (only_8_bits)
- bits = 8;
+ if (only_8_bits)
+ bits = 8;
- gus_audio_bits = bits;
- return bits;
+ gus_audio_bits = bits;
+ return bits;
}
static int
-gus_audio_ioctl (int dev, unsigned int cmd, caddr_t arg)
+gus_audio_ioctl(int dev, unsigned int cmd, caddr_t arg)
{
- int val;
+ int val;
- switch (cmd)
- {
- case SOUND_PCM_WRITE_RATE:
- val = *(int *) arg;
- return (*(int *) arg = gus_audio_set_speed (val));
- break;
+ switch (cmd)
+ {
+ case SOUND_PCM_WRITE_RATE:
+ val = *(int *) arg;
+ return (*(int *) arg = gus_audio_set_speed(val));
+ break;
- case SOUND_PCM_READ_RATE:
- return (*(int *) arg = gus_audio_speed);
- break;
+ case SOUND_PCM_READ_RATE:
+ return (*(int *) arg = gus_audio_speed);
+ break;
- case SNDCTL_DSP_STEREO:
- val = *(int *) arg;
- return (*(int *) arg = gus_audio_set_channels (val + 1) - 1);
- break;
+ case SNDCTL_DSP_STEREO:
+ val = *(int *) arg;
+ return (*(int *) arg = gus_audio_set_channels(val + 1) - 1);
+ break;
- case SOUND_PCM_WRITE_CHANNELS:
- val = *(int *) arg;
- return (*(int *) arg = gus_audio_set_channels (val));
- break;
+ case SOUND_PCM_WRITE_CHANNELS:
+ val = *(int *) arg;
+ return (*(int *) arg = gus_audio_set_channels(val));
+ break;
- case SOUND_PCM_READ_CHANNELS:
- return (*(int *) arg = gus_audio_channels);
- break;
+ case SOUND_PCM_READ_CHANNELS:
+ return (*(int *) arg = gus_audio_channels);
+ break;
- case SNDCTL_DSP_SETFMT:
- val = *(int *) arg;
- return (*(int *) arg = gus_audio_set_bits (val));
- break;
+ case SNDCTL_DSP_SETFMT:
+ val = *(int *) arg;
+ return (*(int *) arg = gus_audio_set_bits(val));
+ break;
- case SOUND_PCM_READ_BITS:
- return (*(int *) arg = gus_audio_bits);
+ case SOUND_PCM_READ_BITS:
+ return (*(int *) arg = gus_audio_bits);
- case SOUND_PCM_WRITE_FILTER: /* NOT POSSIBLE */
- return (*(int *) arg = -EINVAL);
- break;
+ case SOUND_PCM_WRITE_FILTER: /* NOT POSSIBLE */
+ return (*(int *) arg = -EINVAL);
+ break;
- case SOUND_PCM_READ_FILTER:
- return (*(int *) arg = -EINVAL);
- break;
+ case SOUND_PCM_READ_FILTER:
+ return (*(int *) arg = -EINVAL);
+ break;
- }
- return -EINVAL;
+ }
+ return -EINVAL;
}
static void
-gus_audio_reset (int dev)
+gus_audio_reset(int dev)
{
- if (recording_active)
- {
- gus_write8 (0x49, 0x00); /* Halt recording */
- set_input_volumes ();
- }
+ if (recording_active)
+ {
+ gus_write8(0x49, 0x00); /* Halt recording */
+ set_input_volumes();
+ }
}
static int saved_iw_mode; /* A hack hack hack */
static int
-gus_audio_open (int dev, int mode)
-{
- if (gus_busy)
- return -EBUSY;
-
- if (gus_pnp_flag && mode & OPEN_READ)
- {
- printk ("Sound: This audio device doesn't have recording capability\n");
- return -EIO;
- }
- gus_initialize ();
-
- gus_busy = 1;
- active_device = 0;
-
- gus_reset ();
- reset_sample_memory ();
- gus_select_max_voices (14);
- saved_iw_mode = iw_mode;
- if (iw_mode)
- {
- /* There are some problems with audio in enhanced mode so disable it */
- gus_write8 (0x19, gus_read8 (0x19) & ~0x01); /* Disable enhanced mode */
- iw_mode = 0;
- }
-
- pcm_active = 0;
- dma_active = 0;
- pcm_opened = 1;
- if (mode & OPEN_READ)
- {
- recording_active = 1;
- set_input_volumes ();
- }
- only_read_access = !(mode & OPEN_WRITE);
- only_8_bits = mode & OPEN_READ;
- if (only_8_bits)
- audio_devs[dev]->format_mask = AFMT_U8;
- else
- audio_devs[dev]->format_mask = AFMT_U8 | AFMT_S16_LE;
-
- return 0;
+gus_audio_open(int dev, int mode)
+{
+ if (gus_busy)
+ return -EBUSY;
+
+ if (gus_pnp_flag && mode & OPEN_READ)
+ {
+ printk("GUS: Audio device #%d is playback only.\n", dev);
+ return -EIO;
+ }
+ gus_initialize();
+
+ gus_busy = 1;
+ active_device = 0;
+
+ gus_reset();
+ reset_sample_memory();
+ gus_select_max_voices(14);
+ saved_iw_mode = iw_mode;
+ if (iw_mode)
+ {
+ /* There are some problems with audio in enhanced mode so disable it */
+ gus_write8(0x19, gus_read8(0x19) & ~0x01); /* Disable enhanced mode */
+ iw_mode = 0;
+ }
+ pcm_active = 0;
+ dma_active = 0;
+ pcm_opened = 1;
+ if (mode & OPEN_READ)
+ {
+ recording_active = 1;
+ set_input_volumes();
+ }
+ only_read_access = !(mode & OPEN_WRITE);
+ only_8_bits = mode & OPEN_READ;
+ if (only_8_bits)
+ audio_devs[dev]->format_mask = AFMT_U8;
+ else
+ audio_devs[dev]->format_mask = AFMT_U8 | AFMT_S16_LE;
+
+ return 0;
}
static void
-gus_audio_close (int dev)
+gus_audio_close(int dev)
{
- iw_mode = saved_iw_mode;
- gus_reset ();
- gus_busy = 0;
- pcm_opened = 0;
- active_device = 0;
+ iw_mode = saved_iw_mode;
+ gus_reset();
+ gus_busy = 0;
+ pcm_opened = 0;
+ active_device = 0;
- if (recording_active)
- {
- gus_write8 (0x49, 0x00); /* Halt recording */
- set_input_volumes ();
- }
+ if (recording_active)
+ {
+ gus_write8(0x49, 0x00); /* Halt recording */
+ set_input_volumes();
+ }
+ recording_active = 0;
+}
+
+static void
+gus_audio_update_volume(void)
+{
+ unsigned long flags;
+ int voice;
- recording_active = 0;
+ if (pcm_active && pcm_opened)
+ for (voice = 0; voice < gus_audio_channels; voice++)
+ {
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ gus_rampoff();
+ gus_voice_volume(1530 + (25 * gus_pcm_volume));
+ gus_ramp_range(65, 1530 + (25 * gus_pcm_volume));
+ restore_flags(flags);
+ }
}
static void
-gus_audio_update_volume (void)
+play_next_pcm_block(void)
{
- unsigned long flags;
- int voice;
+ unsigned long flags;
+ int speed = gus_audio_speed;
+ int this_one, is16bits, chn;
+ unsigned long dram_loc;
+ unsigned char mode[2], ramp_mode[2];
+
+ if (!pcm_qlen)
+ return;
- if (pcm_active && pcm_opened)
- for (voice = 0; voice < gus_audio_channels; voice++)
- {
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- gus_rampoff ();
- gus_voice_volume (1530 + (25 * gus_pcm_volume));
- gus_ramp_range (65, 1530 + (25 * gus_pcm_volume));
- restore_flags (flags);
- }
+ this_one = pcm_head;
+
+ for (chn = 0; chn < gus_audio_channels; chn++)
+ {
+ mode[chn] = 0x00;
+ ramp_mode[chn] = 0x03; /* Ramping and rollover off */
+
+ if (chn == 0)
+ {
+ mode[chn] |= 0x20; /* Loop IRQ */
+ voices[chn].loop_irq_mode = LMODE_PCM;
+ }
+ if (gus_audio_bits != 8)
+ {
+ is16bits = 1;
+ mode[chn] |= 0x04; /* 16 bit data */
+ } else
+ is16bits = 0;
+
+ dram_loc = this_one * pcm_bsize;
+ dram_loc += chn * pcm_banksize;
+
+ if (this_one == (pcm_nblk - 1)) /* Last fragment of the DRAM buffer */
+ {
+ mode[chn] |= 0x08; /* Enable loop */
+ ramp_mode[chn] = 0x03; /* Disable rollover bit */
+ } else
+ {
+ if (chn == 0)
+ ramp_mode[chn] = 0x04; /* Enable rollover bit */
+ }
+
+ save_flags(flags);
+ cli();
+ gus_select_voice(chn);
+ gus_voice_freq(speed);
+
+ if (gus_audio_channels == 1)
+ gus_voice_balance(7); /* mono */
+ else if (chn == 0)
+ gus_voice_balance(0); /* left */
+ else
+ gus_voice_balance(15); /* right */
+
+ if (!pcm_active) /* Playback not already active */
+ {
+ /*
+ * The playback was not started yet (or there has been a pause).
+ * Start the voice (again) and ask for a rollover irq at the end of
+ * this_one block. If this_one one is last of the buffers, use just
+ * the normal loop with irq.
+ */
+
+ gus_voice_off();
+ gus_rampoff();
+ gus_voice_volume(1530 + (25 * gus_pcm_volume));
+ gus_ramp_range(65, 1530 + (25 * gus_pcm_volume));
+
+ gus_write_addr(0x0a, chn * pcm_banksize, 0, is16bits); /* Starting position */
+ gus_write_addr(0x02, chn * pcm_banksize, 0, is16bits); /* Loop start */
+
+ if (chn != 0)
+ gus_write_addr(0x04, pcm_banksize + (pcm_bsize * pcm_nblk) - 1,
+ 0, is16bits); /* Loop end location */
+ }
+ if (chn == 0)
+ gus_write_addr(0x04, dram_loc + pcm_bsize - 1,
+ 0, is16bits); /* Loop end location */
+ else
+ mode[chn] |= 0x08; /* Enable looping */
+
+
+ restore_flags(flags);
+ }
+
+ for (chn = 0; chn < gus_audio_channels; chn++)
+ {
+ save_flags(flags);
+ cli();
+ gus_select_voice(chn);
+ gus_write8(0x0d, ramp_mode[chn]);
+ if (iw_mode)
+ gus_write8(0x15, 0x00); /* Reset voice deactivate bit of SMSI */
+ gus_voice_on(mode[chn]);
+ restore_flags(flags);
+ }
+
+ pcm_active = 1;
}
static void
-play_next_pcm_block (void)
-{
- unsigned long flags;
- int speed = gus_audio_speed;
- int this_one, is16bits, chn;
- unsigned long dram_loc;
- unsigned char mode[2], ramp_mode[2];
-
- if (!pcm_qlen)
- return;
-
- this_one = pcm_head;
-
- for (chn = 0; chn < gus_audio_channels; chn++)
- {
- mode[chn] = 0x00;
- ramp_mode[chn] = 0x03; /* Ramping and rollover off */
-
- if (chn == 0)
- {
- mode[chn] |= 0x20; /* Loop IRQ */
- voices[chn].loop_irq_mode = LMODE_PCM;
- }
-
- if (gus_audio_bits != 8)
- {
- is16bits = 1;
- mode[chn] |= 0x04; /* 16 bit data */
- }
- else
- is16bits = 0;
-
- dram_loc = this_one * pcm_bsize;
- dram_loc += chn * pcm_banksize;
-
- if (this_one == (pcm_nblk - 1)) /* Last fragment of the DRAM buffer */
- {
- mode[chn] |= 0x08; /* Enable loop */
- ramp_mode[chn] = 0x03; /* Disable rollover bit */
- }
- else
- {
- if (chn == 0)
- ramp_mode[chn] = 0x04; /* Enable rollover bit */
- }
-
- save_flags (flags);
- cli ();
- gus_select_voice (chn);
- gus_voice_freq (speed);
-
- if (gus_audio_channels == 1)
- gus_voice_balance (7); /* mono */
- else if (chn == 0)
- gus_voice_balance (0); /* left */
- else
- gus_voice_balance (15); /* right */
-
- if (!pcm_active) /* Playback not already active */
- {
- /*
- * The playback was not started yet (or there has been a pause).
- * Start the voice (again) and ask for a rollover irq at the end of
- * this_one block. If this_one one is last of the buffers, use just
- * the normal loop with irq.
- */
+gus_transfer_output_block(int dev, unsigned long buf,
+ int total_count, int intrflag, int chn)
+{
+ /*
+ * This routine transfers one block of audio data to the DRAM. In mono mode
+ * it's called just once. When in stereo mode, this_one routine is called
+ * once for both channels.
+ *
+ * The left/mono channel data is transferred to the beginning of dram and the
+ * right data to the area pointed by gus_page_size.
+ */
- gus_voice_off ();
- gus_rampoff ();
- gus_voice_volume (1530 + (25 * gus_pcm_volume));
- gus_ramp_range (65, 1530 + (25 * gus_pcm_volume));
+ int this_one, count;
+ unsigned long flags;
+ unsigned char dma_command;
+ unsigned long address, hold_address;
- gus_write_addr (0x0a, chn * pcm_banksize, 0, is16bits); /* Starting position */
- gus_write_addr (0x02, chn * pcm_banksize, 0, is16bits); /* Loop start */
+ save_flags(flags);
+ cli();
- if (chn != 0)
- gus_write_addr (0x04, pcm_banksize + (pcm_bsize * pcm_nblk) - 1,
- 0, is16bits); /* Loop end location */
- }
+ count = total_count / gus_audio_channels;
- if (chn == 0)
- gus_write_addr (0x04, dram_loc + pcm_bsize - 1,
- 0, is16bits); /* Loop end location */
- else
- mode[chn] |= 0x08; /* Enable looping */
+ if (chn == 0)
+ {
+ if (pcm_qlen >= pcm_nblk)
+ printk("GUS Warning: PCM buffers out of sync\n");
+ this_one = pcm_current_block = pcm_tail;
+ pcm_qlen++;
+ pcm_tail = (pcm_tail + 1) % pcm_nblk;
+ pcm_datasize[this_one] = count;
+ } else
+ this_one = pcm_current_block;
- restore_flags (flags);
- }
+ gus_write8(0x41, 0); /* Disable GF1 DMA */
+ DMAbuf_start_dma(dev, buf + (chn * count), count, DMA_MODE_WRITE);
- for (chn = 0; chn < gus_audio_channels; chn++)
- {
- save_flags (flags);
- cli ();
- gus_select_voice (chn);
- gus_write8 (0x0d, ramp_mode[chn]);
- if (iw_mode)
- gus_write8 (0x15, 0x00); /* Reset voice deactivate bit of SMSI */
- gus_voice_on (mode[chn]);
- restore_flags (flags);
- }
+ address = this_one * pcm_bsize;
+ address += chn * pcm_banksize;
- pcm_active = 1;
-}
+ if (audio_devs[dev]->dmap_out->dma > 3)
+ {
+ hold_address = address;
+ address = address >> 1;
+ address &= 0x0001ffffL;
+ address |= (hold_address & 0x000c0000L);
+ }
+ gus_write16(0x42, (address >> 4) & 0xffff); /* DRAM DMA address */
-static void
-gus_transfer_output_block (int dev, unsigned long buf,
- int total_count, int intrflag, int chn)
-{
- /*
- * This routine transfers one block of audio data to the DRAM. In mono mode
- * it's called just once. When in stereo mode, this_one routine is called
- * once for both channels.
- *
- * The left/mono channel data is transferred to the beginning of dram and the
- * right data to the area pointed by gus_page_size.
- */
-
- int this_one, count;
- unsigned long flags;
- unsigned char dma_command;
- unsigned long address, hold_address;
-
- save_flags (flags);
- cli ();
-
- count = total_count / gus_audio_channels;
-
- if (chn == 0)
- {
- if (pcm_qlen >= pcm_nblk)
- printk ("GUS Warning: PCM buffers out of sync\n");
-
- this_one = pcm_current_block = pcm_tail;
- pcm_qlen++;
- pcm_tail = (pcm_tail + 1) % pcm_nblk;
- pcm_datasize[this_one] = count;
- }
- else
- this_one = pcm_current_block;
-
- gus_write8 (0x41, 0); /* Disable GF1 DMA */
- DMAbuf_start_dma (dev, buf + (chn * count), count, DMA_MODE_WRITE);
-
- address = this_one * pcm_bsize;
- address += chn * pcm_banksize;
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- {
- hold_address = address;
- address = address >> 1;
- address &= 0x0001ffffL;
- address |= (hold_address & 0x000c0000L);
- }
-
- gus_write16 (0x42, (address >> 4) & 0xffff); /* DRAM DMA address */
-
- dma_command = 0x21; /* IRQ enable, DMA start */
-
- if (gus_audio_bits != 8)
- dma_command |= 0x40; /* 16 bit _DATA_ */
- else
- dma_command |= 0x80; /* Invert MSB */
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- dma_command |= 0x04; /* 16 bit DMA channel */
-
- gus_write8 (0x41, dma_command); /* Kick start */
-
- if (chn == (gus_audio_channels - 1)) /* Last channel */
- {
- /*
- * Last (right or mono) channel data
- */
- dma_active = 1; /* DMA started. There is a unacknowledged buffer */
- active_device = GUS_DEV_PCM_DONE;
- if (!pcm_active && (pcm_qlen > 1 || count < pcm_bsize))
- {
- play_next_pcm_block ();
- }
- }
- else
- {
- /*
- * Left channel data. The right channel
- * is transferred after DMA interrupt
- */
- active_device = GUS_DEV_PCM_CONTINUE;
- }
-
- restore_flags (flags);
+ dma_command = 0x21; /* IRQ enable, DMA start */
+
+ if (gus_audio_bits != 8)
+ dma_command |= 0x40; /* 16 bit _DATA_ */
+ else
+ dma_command |= 0x80; /* Invert MSB */
+
+ if (audio_devs[dev]->dmap_out->dma > 3)
+ dma_command |= 0x04; /* 16 bit DMA channel */
+
+ gus_write8(0x41, dma_command); /* Kick start */
+
+ if (chn == (gus_audio_channels - 1)) /* Last channel */
+ {
+ /*
+ * Last (right or mono) channel data
+ */
+ dma_active = 1; /* DMA started. There is a unacknowledged buffer */
+ active_device = GUS_DEV_PCM_DONE;
+ if (!pcm_active && (pcm_qlen > 1 || count < pcm_bsize))
+ {
+ play_next_pcm_block();
+ }
+ } else
+ {
+ /*
+ * Left channel data. The right channel
+ * is transferred after DMA interrupt
+ */
+ active_device = GUS_DEV_PCM_CONTINUE;
+ }
+
+ restore_flags(flags);
}
static void
-gus_uninterleave8 (char *buf, int l)
+gus_uninterleave8(char *buf, int l)
{
/* This routine uninterleaves 8 bit stereo output (LRLRLR->LLLRRR) */
- int i, p = 0, halfsize = l / 2;
- char *buf2 = buf + halfsize, *src = bounce_buf;
+ int i, p = 0, halfsize = l / 2;
+ char *buf2 = buf + halfsize, *src = bounce_buf;
- memcpy (bounce_buf, buf, l);
+ memcpy(bounce_buf, buf, l);
- for (i = 0; i < halfsize; i++)
- {
- buf[i] = src[p++]; /* Left channel */
- buf2[i] = src[p++]; /* Right channel */
- }
+ for (i = 0; i < halfsize; i++)
+ {
+ buf[i] = src[p++]; /* Left channel */
+ buf2[i] = src[p++]; /* Right channel */
+ }
}
static void
-gus_uninterleave16 (short *buf, int l)
+gus_uninterleave16(short *buf, int l)
{
/* This routine uninterleaves 16 bit stereo output (LRLRLR->LLLRRR) */
- int i, p = 0, halfsize = l / 2;
- short *buf2 = buf + halfsize, *src = (short *) bounce_buf;
+ int i, p = 0, halfsize = l / 2;
+ short *buf2 = buf + halfsize, *src = (short *) bounce_buf;
- memcpy (bounce_buf, (char *) buf, l * 2);
+ memcpy(bounce_buf, (char *) buf, l * 2);
- for (i = 0; i < halfsize; i++)
- {
- buf[i] = src[p++]; /* Left channel */
- buf2[i] = src[p++]; /* Right channel */
- }
+ for (i = 0; i < halfsize; i++)
+ {
+ buf[i] = src[p++]; /* Left channel */
+ buf2[i] = src[p++]; /* Right channel */
+ }
}
static void
-gus_audio_output_block (int dev, unsigned long buf, int total_count,
- int intrflag)
+gus_audio_output_block(int dev, unsigned long buf, int total_count,
+ int intrflag)
{
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
- dmap->flags |= DMA_NODMA | DMA_NOTIMEOUT;
+ dmap->flags |= DMA_NODMA | DMA_NOTIMEOUT;
- pcm_current_buf = buf;
- pcm_current_count = total_count;
- pcm_current_intrflag = intrflag;
- pcm_current_dev = dev;
- if (gus_audio_channels == 2)
- {
- char *b = dmap->raw_buf + (buf - dmap->raw_buf_phys);
+ pcm_current_buf = buf;
+ pcm_current_count = total_count;
+ pcm_current_intrflag = intrflag;
+ pcm_current_dev = dev;
+ if (gus_audio_channels == 2)
+ {
+ char *b = dmap->raw_buf + (buf - dmap->raw_buf_phys);
- if (gus_audio_bits == 8)
- gus_uninterleave8 (b, total_count);
- else
- gus_uninterleave16 ((short *) b, total_count / 2);
- }
- gus_transfer_output_block (dev, buf, total_count, intrflag, 0);
+ if (gus_audio_bits == 8)
+ gus_uninterleave8(b, total_count);
+ else
+ gus_uninterleave16((short *) b, total_count / 2);
+ }
+ gus_transfer_output_block(dev, buf, total_count, intrflag, 0);
}
static void
-gus_audio_start_input (int dev, unsigned long buf, int count,
- int intrflag)
+gus_audio_start_input(int dev, unsigned long buf, int count,
+ int intrflag)
{
- unsigned long flags;
- unsigned char mode;
+ unsigned long flags;
+ unsigned char mode;
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+ DMAbuf_start_dma(dev, buf, count, DMA_MODE_READ);
- mode = 0xa0; /* DMA IRQ enabled, invert MSB */
+ mode = 0xa0; /* DMA IRQ enabled, invert MSB */
- if (audio_devs[dev]->dmap_in->dma > 3)
- mode |= 0x04; /* 16 bit DMA channel */
- if (gus_audio_channels > 1)
- mode |= 0x02; /* Stereo */
- mode |= 0x01; /* DMA enable */
+ if (audio_devs[dev]->dmap_in->dma > 3)
+ mode |= 0x04; /* 16 bit DMA channel */
+ if (gus_audio_channels > 1)
+ mode |= 0x02; /* Stereo */
+ mode |= 0x01; /* DMA enable */
- gus_write8 (0x49, mode);
+ gus_write8(0x49, mode);
- restore_flags (flags);
+ restore_flags(flags);
}
static int
-gus_audio_prepare_for_input (int dev, int bsize, int bcount)
+gus_audio_prepare_for_input(int dev, int bsize, int bcount)
{
- unsigned int rate;
-
- gus_audio_bsize = bsize;
- audio_devs[dev]->dmap_in->flags |= DMA_NODMA;
- rate = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16;
+ unsigned int rate;
- gus_write8 (0x48, rate & 0xff); /* Set sampling rate */
+ gus_audio_bsize = bsize;
+ audio_devs[dev]->dmap_in->flags |= DMA_NODMA;
+ rate = (((9878400 + gus_audio_speed / 2) / (gus_audio_speed + 2)) + 8) / 16;
- if (gus_audio_bits != 8)
- {
- printk ("GUS Error: 16 bit recording not supported\n");
- return -EINVAL;
- }
+ gus_write8(0x48, rate & 0xff); /* Set sampling rate */
- return 0;
+ if (gus_audio_bits != 8)
+ {
+ printk("GUS Error: 16 bit recording not supported\n");
+ return -EINVAL;
+ }
+ return 0;
}
static int
-gus_audio_prepare_for_output (int dev, int bsize, int bcount)
+gus_audio_prepare_for_output(int dev, int bsize, int bcount)
{
- int i;
+ int i;
- long mem_ptr, mem_size;
+ long mem_ptr, mem_size;
- audio_devs[dev]->dmap_out->flags |= DMA_NODMA | DMA_NOTIMEOUT;
- mem_ptr = 0;
- mem_size = gus_mem_size / gus_audio_channels;
+ audio_devs[dev]->dmap_out->flags |= DMA_NODMA | DMA_NOTIMEOUT;
+ mem_ptr = 0;
+ mem_size = gus_mem_size / gus_audio_channels;
- if (mem_size > (256 * 1024))
- mem_size = 256 * 1024;
+ if (mem_size > (256 * 1024))
+ mem_size = 256 * 1024;
- pcm_bsize = bsize / gus_audio_channels;
- pcm_head = pcm_tail = pcm_qlen = 0;
+ pcm_bsize = bsize / gus_audio_channels;
+ pcm_head = pcm_tail = pcm_qlen = 0;
- pcm_nblk = 2; /* MAX_PCM_BUFFERS; */
- if ((pcm_bsize * pcm_nblk) > mem_size)
- pcm_nblk = mem_size / pcm_bsize;
+ pcm_nblk = 2; /* MAX_PCM_BUFFERS; */
+ if ((pcm_bsize * pcm_nblk) > mem_size)
+ pcm_nblk = mem_size / pcm_bsize;
- for (i = 0; i < pcm_nblk; i++)
- pcm_datasize[i] = 0;
+ for (i = 0; i < pcm_nblk; i++)
+ pcm_datasize[i] = 0;
- pcm_banksize = pcm_nblk * pcm_bsize;
+ pcm_banksize = pcm_nblk * pcm_bsize;
- if (gus_audio_bits != 8 && pcm_banksize == (256 * 1024))
- pcm_nblk--;
- gus_write8 (0x41, 0); /* Disable GF1 DMA */
+ if (gus_audio_bits != 8 && pcm_banksize == (256 * 1024))
+ pcm_nblk--;
+ gus_write8(0x41, 0); /* Disable GF1 DMA */
- return 0;
+ return 0;
}
static int
-gus_local_qlen (int dev)
+gus_local_qlen(int dev)
{
- return pcm_qlen;
+ return pcm_qlen;
}
static struct audio_driver gus_audio_driver =
{
- gus_audio_open,
- gus_audio_close,
- gus_audio_output_block,
- gus_audio_start_input,
- gus_audio_ioctl,
- gus_audio_prepare_for_input,
- gus_audio_prepare_for_output,
- gus_audio_reset,
- gus_local_qlen,
- NULL
+ gus_audio_open,
+ gus_audio_close,
+ gus_audio_output_block,
+ gus_audio_start_input,
+ gus_audio_ioctl,
+ gus_audio_prepare_for_input,
+ gus_audio_prepare_for_output,
+ gus_audio_reset,
+ gus_local_qlen,
+ NULL
};
static void
-guswave_setup_voice (int dev, int voice, int chn)
+guswave_setup_voice(int dev, int voice, int chn)
{
- struct channel_info *info =
- &synth_devs[dev]->chn_info[chn];
+ struct channel_info *info =
+ &synth_devs[dev]->chn_info[chn];
- guswave_set_instr (dev, voice, info->pgm_num);
+ guswave_set_instr(dev, voice, info->pgm_num);
- voices[voice].expression_vol =
- info->controllers[CTL_EXPRESSION]; /* Just MSB */
- voices[voice].main_vol =
- (info->controllers[CTL_MAIN_VOLUME] * 100) / (unsigned) 128;
- voices[voice].panning =
- (info->controllers[CTL_PAN] * 2) - 128;
- voices[voice].bender = 0;
- voices[voice].bender_range = info->bender_range;
+ voices[voice].expression_vol =
+ info->controllers[CTL_EXPRESSION]; /* Just MSB */
+ voices[voice].main_vol =
+ (info->controllers[CTL_MAIN_VOLUME] * 100) / (unsigned) 128;
+ voices[voice].panning =
+ (info->controllers[CTL_PAN] * 2) - 128;
+ voices[voice].bender = 0;
+ voices[voice].bender_range = info->bender_range;
- if (chn == 9)
- voices[voice].fixed_pitch = 1;
+ if (chn == 9)
+ voices[voice].fixed_pitch = 1;
}
static void
-guswave_bender (int dev, int voice, int value)
+guswave_bender(int dev, int voice, int value)
{
- int freq;
- unsigned long flags;
+ int freq;
+ unsigned long flags;
- voices[voice].bender = value - 8192;
- freq = compute_finetune (voices[voice].orig_freq, value - 8192,
- voices[voice].bender_range, 0);
- voices[voice].current_freq = freq;
+ voices[voice].bender = value - 8192;
+ freq = compute_finetune(voices[voice].orig_freq, value - 8192,
+ voices[voice].bender_range, 0);
+ voices[voice].current_freq = freq;
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
- gus_voice_freq (freq);
- restore_flags (flags);
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
+ gus_voice_freq(freq);
+ restore_flags(flags);
}
static int
-guswave_alloc (int dev, int chn, int note, struct voice_alloc_info *alloc)
-{
- int i, p, best = -1, best_time = 0x7fffffff;
-
- p = alloc->ptr;
- /*
- * First look for a completely stopped voice
- */
-
- for (i = 0; i < alloc->max_voice; i++)
- {
- if (alloc->map[p] == 0)
- {
- alloc->ptr = p;
- return p;
- }
- if (alloc->alloc_times[p] < best_time)
- {
- best = p;
- best_time = alloc->alloc_times[p];
- }
- p = (p + 1) % alloc->max_voice;
- }
-
- /*
- * Then look for a releasing voice
- */
-
- for (i = 0; i < alloc->max_voice; i++)
- {
- if (alloc->map[p] == 0xffff)
- {
- alloc->ptr = p;
- return p;
- }
- p = (p + 1) % alloc->max_voice;
- }
-
- if (best >= 0)
- p = best;
-
- alloc->ptr = p;
- return p;
+guswave_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc)
+{
+ int i, p, best = -1, best_time = 0x7fffffff;
+
+ p = alloc->ptr;
+ /*
+ * First look for a completely stopped voice
+ */
+
+ for (i = 0; i < alloc->max_voice; i++)
+ {
+ if (alloc->map[p] == 0)
+ {
+ alloc->ptr = p;
+ return p;
+ }
+ if (alloc->alloc_times[p] < best_time)
+ {
+ best = p;
+ best_time = alloc->alloc_times[p];
+ }
+ p = (p + 1) % alloc->max_voice;
+ }
+
+ /*
+ * Then look for a releasing voice
+ */
+
+ for (i = 0; i < alloc->max_voice; i++)
+ {
+ if (alloc->map[p] == 0xffff)
+ {
+ alloc->ptr = p;
+ return p;
+ }
+ p = (p + 1) % alloc->max_voice;
+ }
+
+ if (best >= 0)
+ p = best;
+
+ alloc->ptr = p;
+ return p;
}
static struct synth_operations guswave_operations =
{
- "GUS",
- &gus_info,
- 0,
- SYNTH_TYPE_SAMPLE,
- SAMPLE_TYPE_GUS,
- guswave_open,
- guswave_close,
- guswave_ioctl,
- guswave_kill_note,
- guswave_start_note,
- guswave_set_instr,
- guswave_reset,
- guswave_hw_control,
- guswave_load_patch,
- guswave_aftertouch,
- guswave_controller,
- guswave_panning,
- guswave_volume_method,
- guswave_bender,
- guswave_alloc,
- guswave_setup_voice
+ "GUS",
+ &gus_info,
+ 0,
+ SYNTH_TYPE_SAMPLE,
+ SAMPLE_TYPE_GUS,
+ guswave_open,
+ guswave_close,
+ guswave_ioctl,
+ guswave_kill_note,
+ guswave_start_note,
+ guswave_set_instr,
+ guswave_reset,
+ guswave_hw_control,
+ guswave_load_patch,
+ guswave_aftertouch,
+ guswave_controller,
+ guswave_panning,
+ guswave_volume_method,
+ guswave_bender,
+ guswave_alloc,
+ guswave_setup_voice
};
static void
-set_input_volumes (void)
+set_input_volumes(void)
{
- unsigned long flags;
- unsigned char mask = 0xff & ~0x06; /* Just line out enabled */
-
- if (have_gus_max) /* Don't disturb GUS MAX */
- return;
+ unsigned long flags;
+ unsigned char mask = 0xff & ~0x06; /* Just line out enabled */
- save_flags (flags);
- cli ();
+ if (have_gus_max) /* Don't disturb GUS MAX */
+ return;
- /*
- * Enable channels having vol > 10%
- * Note! bit 0x01 means the line in DISABLED while 0x04 means
- * the mic in ENABLED.
- */
- if (gus_line_vol > 10)
- mask &= ~0x01;
- if (gus_mic_vol > 10)
- mask |= 0x04;
+ save_flags(flags);
+ cli();
- if (recording_active)
- {
- /*
- * Disable channel, if not selected for recording
- */
- if (!(gus_recmask & SOUND_MASK_LINE))
- mask |= 0x01;
- if (!(gus_recmask & SOUND_MASK_MIC))
- mask &= ~0x04;
- }
+ /*
+ * Enable channels having vol > 10%
+ * Note! bit 0x01 means the line in DISABLED while 0x04 means
+ * the mic in ENABLED.
+ */
+ if (gus_line_vol > 10)
+ mask &= ~0x01;
+ if (gus_mic_vol > 10)
+ mask |= 0x04;
- mix_image &= ~0x07;
- mix_image |= mask & 0x07;
- outb ((mix_image), u_Mixer);
+ if (recording_active)
+ {
+ /*
+ * Disable channel, if not selected for recording
+ */
+ if (!(gus_recmask & SOUND_MASK_LINE))
+ mask |= 0x01;
+ if (!(gus_recmask & SOUND_MASK_MIC))
+ mask &= ~0x04;
+ }
+ mix_image &= ~0x07;
+ mix_image |= mask & 0x07;
+ outb((mix_image), u_Mixer);
- restore_flags (flags);
+ restore_flags(flags);
}
int
-gus_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
+gus_default_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
{
#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \
SOUND_MASK_SYNTH|SOUND_MASK_PCM)
- if (((cmd >> 8) & 0xff) == 'M')
- {
- if (_SIOC_DIR (cmd) & _SIOC_WRITE)
- switch (cmd & 0xff)
- {
- case SOUND_MIXER_RECSRC:
- gus_recmask = *(int *) arg;
- gus_recmask &= MIX_DEVS;
- if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE)))
- gus_recmask = SOUND_MASK_MIC;
- /* Note! Input volumes are updated during next open for recording */
- return (*(int *) arg = gus_recmask);
- break;
-
- case SOUND_MIXER_MIC:
- {
- int vol;
-
- vol = *(int *) arg;
- vol &= 0xff;
-
- if (vol < 0)
- vol = 0;
- if (vol > 100)
- vol = 100;
- gus_mic_vol = vol;
- set_input_volumes ();
- return (*(int *) arg = vol | (vol << 8));
- }
- break;
-
- case SOUND_MIXER_LINE:
- {
- int vol;
-
- vol = *(int *) arg;
- vol &= 0xff;
-
- if (vol < 0)
- vol = 0;
- if (vol > 100)
- vol = 100;
- gus_line_vol = vol;
- set_input_volumes ();
- return (*(int *) arg = vol | (vol << 8));
- }
- break;
-
- case SOUND_MIXER_PCM:
- gus_pcm_volume = *(int *) arg;
- gus_pcm_volume &= 0xff;
- if (gus_pcm_volume < 0)
- gus_pcm_volume = 0;
- if (gus_pcm_volume > 100)
- gus_pcm_volume = 100;
- gus_audio_update_volume ();
- return (*(int *) arg = gus_pcm_volume | (gus_pcm_volume << 8));
- break;
-
- case SOUND_MIXER_SYNTH:
- {
- int voice;
-
- gus_wave_volume = *(int *) arg;
- gus_wave_volume &= 0xff;
-
- if (gus_wave_volume < 0)
- gus_wave_volume = 0;
- if (gus_wave_volume > 100)
- gus_wave_volume = 100;
-
- if (active_device == GUS_DEV_WAVE)
- for (voice = 0; voice < nr_voices; voice++)
- dynamic_volume_change (voice); /* Apply the new vol */
-
- return (*(int *) arg = gus_wave_volume | (gus_wave_volume << 8));
- }
- break;
-
- default:
- return -EINVAL;
- }
- else
- switch (cmd & 0xff) /*
- * Return parameters
- */
+ if (((cmd >> 8) & 0xff) == 'M')
{
-
- case SOUND_MIXER_RECSRC:
- return (*(int *) arg = gus_recmask);
- break;
-
- case SOUND_MIXER_DEVMASK:
- return (*(int *) arg = MIX_DEVS);
- break;
-
- case SOUND_MIXER_STEREODEVS:
- return (*(int *) arg = 0);
- break;
-
- case SOUND_MIXER_RECMASK:
- return (*(int *) arg = SOUND_MASK_MIC | SOUND_MASK_LINE);
- break;
-
- case SOUND_MIXER_CAPS:
- return (*(int *) arg = 0);
- break;
-
- case SOUND_MIXER_MIC:
- return (*(int *) arg = gus_mic_vol | (gus_mic_vol << 8));
- break;
-
- case SOUND_MIXER_LINE:
- return (*(int *) arg = gus_line_vol | (gus_line_vol << 8));
- break;
-
- case SOUND_MIXER_PCM:
- return (*(int *) arg = gus_pcm_volume | (gus_pcm_volume << 8));
- break;
-
- case SOUND_MIXER_SYNTH:
- return (*(int *) arg = gus_wave_volume | (gus_wave_volume << 8));
- break;
-
- default:
- return -EINVAL;
- }
- }
- else
- return -EINVAL;
+ if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+ switch (cmd & 0xff)
+ {
+ case SOUND_MIXER_RECSRC:
+ gus_recmask = *(int *) arg;
+ gus_recmask &= MIX_DEVS;
+ if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE)))
+ gus_recmask = SOUND_MASK_MIC;
+ /* Note! Input volumes are updated during next open for recording */
+ return (*(int *) arg = gus_recmask);
+ break;
+
+ case SOUND_MIXER_MIC:
+ {
+ int vol;
+
+ vol = *(int *) arg;
+ vol &= 0xff;
+
+ if (vol < 0)
+ vol = 0;
+ if (vol > 100)
+ vol = 100;
+ gus_mic_vol = vol;
+ set_input_volumes();
+ return (*(int *) arg = vol | (vol << 8));
+ }
+ break;
+
+ case SOUND_MIXER_LINE:
+ {
+ int vol;
+
+ vol = *(int *) arg;
+ vol &= 0xff;
+
+ if (vol < 0)
+ vol = 0;
+ if (vol > 100)
+ vol = 100;
+ gus_line_vol = vol;
+ set_input_volumes();
+ return (*(int *) arg = vol | (vol << 8));
+ }
+ break;
+
+ case SOUND_MIXER_PCM:
+ gus_pcm_volume = *(int *) arg;
+ gus_pcm_volume &= 0xff;
+ if (gus_pcm_volume < 0)
+ gus_pcm_volume = 0;
+ if (gus_pcm_volume > 100)
+ gus_pcm_volume = 100;
+ gus_audio_update_volume();
+ return (*(int *) arg = gus_pcm_volume | (gus_pcm_volume << 8));
+ break;
+
+ case SOUND_MIXER_SYNTH:
+ {
+ int voice;
+
+ gus_wave_volume = *(int *) arg;
+ gus_wave_volume &= 0xff;
+
+ if (gus_wave_volume < 0)
+ gus_wave_volume = 0;
+ if (gus_wave_volume > 100)
+ gus_wave_volume = 100;
+
+ if (active_device == GUS_DEV_WAVE)
+ for (voice = 0; voice < nr_voices; voice++)
+ dynamic_volume_change(voice); /* Apply the new vol */
+
+ return (*(int *) arg = gus_wave_volume | (gus_wave_volume << 8));
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ } else
+ switch (cmd & 0xff) /*
+ * Return parameters
+ */
+ {
+
+ case SOUND_MIXER_RECSRC:
+ return (*(int *) arg = gus_recmask);
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ return (*(int *) arg = MIX_DEVS);
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ return (*(int *) arg = 0);
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ return (*(int *) arg = SOUND_MASK_MIC | SOUND_MASK_LINE);
+ break;
+
+ case SOUND_MIXER_CAPS:
+ return (*(int *) arg = 0);
+ break;
+
+ case SOUND_MIXER_MIC:
+ return (*(int *) arg = gus_mic_vol | (gus_mic_vol << 8));
+ break;
+
+ case SOUND_MIXER_LINE:
+ return (*(int *) arg = gus_line_vol | (gus_line_vol << 8));
+ break;
+
+ case SOUND_MIXER_PCM:
+ return (*(int *) arg = gus_pcm_volume | (gus_pcm_volume << 8));
+ break;
+
+ case SOUND_MIXER_SYNTH:
+ return (*(int *) arg = gus_wave_volume | (gus_wave_volume << 8));
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ } else
+ return -EINVAL;
}
static struct mixer_operations gus_mixer_operations =
{
- "GUS",
- "Gravis Ultrasound",
- gus_default_mixer_ioctl
+ "GUS",
+ "Gravis Ultrasound",
+ gus_default_mixer_ioctl
};
-static void
-gus_default_mixer_init (void)
+static int
+gus_default_mixer_init(void)
{
- if (num_mixers < MAX_MIXER_DEV) /*
- * Don't install if there is another
- * mixer
- */
- mixer_devs[num_mixers++] = &gus_mixer_operations;
+ int n;
- if (have_gus_max)
- {
+ if ((n = sound_alloc_mixerdev()) != -1)
+ { /*
+ * Don't install if there is another
+ * mixer
+ */
+ mixer_devs[n] = &gus_mixer_operations;
+ }
+ if (have_gus_max)
+ {
/*
* Enable all mixer channels on the GF1 side. Otherwise recording will
* not be possible using GUS MAX.
*/
- mix_image &= ~0x07;
- mix_image |= 0x04; /* All channels enabled */
- outb ((mix_image), u_Mixer);
- }
+ mix_image &= ~0x07;
+ mix_image |= 0x04; /* All channels enabled */
+ outb((mix_image), u_Mixer);
+ }
+ return n;
}
void
-gus_wave_init (struct address_info *hw_config)
-{
- unsigned long flags;
- unsigned char val;
- char *model_num = "2.4";
- char tmp[64], tmp2[64];
- int gus_type = 0x24; /* 2.4 */
-
- int irq = hw_config->irq, dma = hw_config->dma, dma2 = hw_config->dma2;
-
- if (!gus_pnp_flag)
- if (irq < 0 || irq > 15)
- {
- printk ("ERROR! Invalid IRQ#%d. GUS Disabled", irq);
- return;
- }
-
- if (dma < 0 || dma > 7 || dma == 4)
- {
- printk ("ERROR! Invalid DMA#%d. GUS Disabled", dma);
- return;
- }
-
- gus_irq = irq;
- gus_dma = dma;
- gus_dma2 = dma2;
-
- if (gus_dma2 == -1)
- gus_dma2 = dma;
-
- /*
- * Try to identify the GUS model.
- *
- * Versions < 3.6 don't have the digital ASIC. Try to probe it first.
- */
-
- save_flags (flags);
- cli ();
- outb ((0x20), gus_base + 0x0f);
- val = inb (gus_base + 0x0f);
- restore_flags (flags);
-
- if (gus_pnp_flag || (val != 0xff && (val & 0x06))) /* Should be 0x02?? */
- {
- int ad_flags = 0;
-
- if (gus_pnp_flag)
- ad_flags = 0x12345678; /* Interwave "magic" */
- /*
- * It has the digital ASIC so the card is at least v3.4.
- * Next try to detect the true model.
- */
-
- if (gus_pnp_flag) /* Hack hack hack */
- val = 10;
- else
- val = inb (u_MixSelect);
-
- /*
- * Value 255 means pre-3.7 which don't have mixer.
- * Values 5 thru 9 mean v3.7 which has a ICS2101 mixer.
- * 10 and above is GUS MAX which has the CS4231 codec/mixer.
- *
- */
-
- if (val == 255 || val < 5)
- {
- model_num = "3.4";
- gus_type = 0x34;
- }
- else if (val < 10)
- {
- model_num = "3.7";
- gus_type = 0x37;
- mixer_type = ICS2101;
- request_region (u_MixSelect, 1, "GUS mixer");
- }
- else
- {
- model_num = "MAX";
- gus_type = 0x40;
- mixer_type = CS4231;
-#ifdef CONFIG_GUSMAX
+gus_wave_init(struct address_info *hw_config)
+{
+ unsigned long flags;
+ unsigned char val;
+ char *model_num = "2.4";
+ char tmp[64], tmp2[64];
+ int gus_type = 0x24; /* 2.4 */
+
+ int irq = hw_config->irq, dma = hw_config->dma,
+ dma2 = hw_config->dma2;
+ int dev;
+ int sdev;
+
+ hw_config->slots[0] = -1; /* No wave */
+ hw_config->slots[1] = -1; /* No ad1848 */
+ hw_config->slots[4] = -1; /* No audio */
+ hw_config->slots[5] = -1; /* No mixer */
+
+ if (!gus_pnp_flag)
+ if (irq < 0 || irq > 15)
+ {
+ printk("ERROR! Invalid IRQ#%d. GUS Disabled", irq);
+ return;
+ }
+ if (dma < 0 || dma > 7 || dma == 4)
{
- unsigned char max_config = 0x40; /* Codec enable */
-
- if (gus_dma2 == -1)
- gus_dma2 = gus_dma;
+ printk("ERROR! Invalid DMA#%d. GUS Disabled", dma);
+ return;
+ }
+ gus_irq = irq;
+ gus_dma = dma;
+ gus_dma2 = dma2;
- if (gus_dma > 3)
- max_config |= 0x10; /* 16 bit capture DMA */
+ if (gus_dma2 == -1)
+ gus_dma2 = dma;
- if (gus_dma2 > 3)
- max_config |= 0x20; /* 16 bit playback DMA */
+ /*
+ * Try to identify the GUS model.
+ *
+ * Versions < 3.6 don't have the digital ASIC. Try to probe it first.
+ */
- max_config |= (gus_base >> 4) & 0x0f; /* Extract the X from 2X0 */
+ save_flags(flags);
+ cli();
+ outb((0x20), gus_base + 0x0f);
+ val = inb(gus_base + 0x0f);
+ restore_flags(flags);
- outb ((max_config), gus_base + 0x106); /* UltraMax control */
+ if (gus_pnp_flag || (val != 0xff && (val & 0x06))) /* Should be 0x02?? */
+ {
+ int ad_flags = 0;
+
+ if (gus_pnp_flag)
+ ad_flags = 0x12345678; /* Interwave "magic" */
+ /*
+ * It has the digital ASIC so the card is at least v3.4.
+ * Next try to detect the true model.
+ */
+
+ if (gus_pnp_flag) /* Hack hack hack */
+ val = 10;
+ else
+ val = inb(u_MixSelect);
+
+ /*
+ * Value 255 means pre-3.7 which don't have mixer.
+ * Values 5 thru 9 mean v3.7 which has a ICS2101 mixer.
+ * 10 and above is GUS MAX which has the CS4231 codec/mixer.
+ *
+ */
+
+ if (val == 255 || val < 5)
+ {
+ model_num = "3.4";
+ gus_type = 0x34;
+ } else if (val < 10)
+ {
+ model_num = "3.7";
+ gus_type = 0x37;
+ mixer_type = ICS2101;
+ request_region(u_MixSelect, 1, "GUS mixer");
+ } else
+ {
+ model_num = "MAX";
+ gus_type = 0x40;
+ mixer_type = CS4231;
+#ifdef CONFIG_GUSMAX
+ {
+ unsigned char max_config = 0x40; /* Codec enable */
+
+ if (gus_dma2 == -1)
+ gus_dma2 = gus_dma;
+
+ if (gus_dma > 3)
+ max_config |= 0x10; /* 16 bit capture DMA */
+
+ if (gus_dma2 > 3)
+ max_config |= 0x20; /* 16 bit playback DMA */
+
+ max_config |= (gus_base >> 4) & 0x0f; /* Extract the X from 2X0 */
+
+ outb((max_config), gus_base + 0x106); /* UltraMax control */
+ }
+
+ if (ad1848_detect(gus_base + 0x10c, &ad_flags, hw_config->osp))
+ {
+ char *name = "GUS MAX";
+ int old_num_mixers = num_mixers;
+
+ if (gus_pnp_flag)
+ name = "GUS PnP";
+
+ gus_mic_vol = gus_line_vol = gus_pcm_volume = 100;
+ gus_wave_volume = 90;
+ have_gus_max = 1;
+ if (hw_config->name)
+ name = hw_config->name;
+
+ hw_config->slots[1] = ad1848_init(name, gus_base + 0x10c,
+ -irq,
+ gus_dma2, /* Playback DMA */
+ gus_dma, /* Capture DMA */
+ 1, /* Share DMA channels with GF1 */
+ hw_config->osp);
+
+ if (num_mixers > old_num_mixers)
+ { /* GUS has it's own mixer map */
+ AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_SYNTH);
+ AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD);
+ AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE);
+ }
+ } else
+ printk("[Where's the CS4231?]");
+#else
+ printk("\n\n\nGUS MAX support was not compiled in!!!\n\n\n\n");
+#endif
+ }
+ } else
+ {
+ /*
+ * ASIC not detected so the card must be 2.2 or 2.4.
+ * There could still be the 16-bit/mixer daughter card.
+ */
}
- if (ad1848_detect (gus_base + 0x10c, &ad_flags, hw_config->osp))
- {
- char *name = "GUS MAX";
- int old_num_mixers = num_mixers;
+ if (hw_config->name)
+ {
- if (gus_pnp_flag)
- name = "GUS PnP";
+ strncpy(tmp, hw_config->name, 45);
+ tmp[45] = 0;
+ sprintf(tmp2, "%s (%dk)", tmp, (int) gus_mem_size / 1024);
+ tmp2[sizeof(tmp2) - 1] = 0;
+ } else if (gus_pnp_flag)
+ {
+ sprintf(tmp2, "Gravis UltraSound PnP (%dk)",
+ (int) gus_mem_size / 1024);
+ } else
+ sprintf(tmp2, "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024);
- gus_mic_vol = gus_line_vol = gus_pcm_volume = 100;
- gus_wave_volume = 90;
- have_gus_max = 1;
- if (hw_config->name)
- name = hw_config->name;
- ad1848_init (name, gus_base + 0x10c,
- -irq,
- gus_dma2, /* Playback DMA */
- gus_dma, /* Capture DMA */
- 1, /* Share DMA channels with GF1 */
- hw_config->osp);
+ samples = (struct patch_info *) (sound_mem_blocks[sound_nblocks] = vmalloc((MAX_SAMPLE + 1) * sizeof(*samples)));
+ sound_mem_sizes[sound_nblocks] = (MAX_SAMPLE + 1) * sizeof(*samples);
+ if (sound_nblocks < 1024)
+ sound_nblocks++;;
+ if (samples == NULL)
+ {
+ printk(KERN_WARNING "gus_init: Cant allocate memory for instrument tables\n");
+ return;
+ }
+ conf_printf(tmp2, hw_config);
+ tmp2[sizeof(gus_info.name) - 1] = 0;
+ strcpy(gus_info.name, tmp2);
- if (num_mixers > old_num_mixers)
- { /* GUS has it's own mixer map */
- AD1848_REROUTE (SOUND_MIXER_LINE1, SOUND_MIXER_SYNTH);
- AD1848_REROUTE (SOUND_MIXER_LINE2, SOUND_MIXER_CD);
- AD1848_REROUTE (SOUND_MIXER_LINE3, SOUND_MIXER_LINE);
- }
- }
- else
- printk ("[Where's the CS4231?]");
-#else
- printk ("\n\n\nGUS MAX support was not compiled in!!!\n\n\n\n");
-#endif
- }
- }
- else
- {
- /*
- * ASIC not detected so the card must be 2.2 or 2.4.
- * There could still be the 16-bit/mixer daughter card.
- */
- }
-
- if (hw_config->name)
- {
-
- strncpy (tmp, hw_config->name, 45);
- tmp[45] = 0;
- sprintf (tmp2, "%s (%dk)", tmp, (int) gus_mem_size / 1024);
- tmp2[sizeof (tmp2) - 1] = 0;
- }
- else if (gus_pnp_flag)
- {
- sprintf (tmp2, "Gravis UltraSound PnP (%dk)",
- (int) gus_mem_size / 1024);
- }
- else
- sprintf (tmp2, "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024);
-
-
- samples = (struct patch_info *) (sound_mem_blocks[sound_nblocks] = vmalloc ((MAX_SAMPLE + 1) * sizeof (*samples)));
- sound_mem_sizes[sound_nblocks] = (MAX_SAMPLE + 1) * sizeof (*samples);
- if (sound_nblocks < 1024)
- sound_nblocks++;;
- if (samples == NULL)
- {
- printk ("GUS Error: Cant allocate memory for instrument tables\n");
- return;
- }
-
- conf_printf (tmp2, hw_config);
- tmp2[sizeof (gus_info.name) - 1] = 0;
- strcpy (gus_info.name, tmp2);
-
- if (num_synths >= MAX_SYNTH_DEV)
- printk ("GUS Error: Too many synthesizers\n");
- else
- {
- voice_alloc = &guswave_operations.alloc;
- if (iw_mode)
- guswave_operations.id = "IWAVE";
- synth_devs[num_synths++] = &guswave_operations;
- sequencer_init ();
-#ifdef CONFIG_SEQUENCER
- gus_tmr_install (gus_base + 8);
+ if ((sdev = sound_alloc_synthdev()) == -1)
+ printk(KERN_WARNING "gus_init: Too many synthesizers\n");
+ else
+ {
+ voice_alloc = &guswave_operations.alloc;
+ if (iw_mode)
+ guswave_operations.id = "IWAVE";
+ hw_config->slots[0] = sdev;
+ synth_devs[sdev] = &guswave_operations;
+ sequencer_init();
+#if defined(CONFIG_SEQUENCER) || defined(MODULE)
+ gus_tmr_install(gus_base + 8);
#endif
- }
-
- reset_sample_memory ();
-
- gus_initialize ();
-
- if (gus_mem_size > 0)
- if (num_audiodevs < MAX_AUDIO_DEV)
- {
-
- if ((gus_devnum = sound_install_audiodrv (AUDIO_DRIVER_VERSION,
- "Ultrasound",
- &gus_audio_driver,
- sizeof (struct audio_driver),
- NEEDS_RESTART |
- ((!iw_mode && dma2 != dma && dma2 != -1) ?
- DMA_DUPLEX : 0),
- AFMT_U8 | AFMT_S16_LE,
- NULL,
- dma,
- dma2)) < 0)
- return;
-
- audio_devs[gus_devnum]->min_fragment = 9; /* 512k */
- audio_devs[gus_devnum]->max_fragment = 11; /* 8k (must match size of bounce_buf */
- audio_devs[gus_devnum]->mixer_dev = num_mixers; /* Next mixer# */
- audio_devs[gus_devnum]->flags |= DMA_HARDSTOP;
- }
- else
- printk ("GUS: Too many audio devices available\n");
-
- /*
- * Mixer dependent initialization.
- */
-
- switch (mixer_type)
- {
- case ICS2101:
- gus_mic_vol = gus_line_vol = gus_pcm_volume = 100;
- gus_wave_volume = 90;
- request_region (u_MixSelect, 1, "GUS mixer");
- ics2101_mixer_init ();
- return;
-
- case CS4231:
- /* Initialized elsewhere (ad1848.c) */
- default:
- gus_default_mixer_init ();
- return;
- }
+ }
+
+ reset_sample_memory();
+
+ gus_initialize();
+
+ if (gus_mem_size > 0)
+ if ((dev = sound_alloc_audiodev()) != -1)
+ {
+ hw_config->slots[4] = dev;
+ if ((gus_devnum = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
+ "Ultrasound",
+ &gus_audio_driver,
+ sizeof(struct audio_driver),
+ NEEDS_RESTART |
+ ((!iw_mode && dma2 != dma && dma2 != -1) ?
+ DMA_DUPLEX : 0),
+ AFMT_U8 | AFMT_S16_LE,
+ NULL,
+ dma,
+ dma2)) < 0)
+ return;
+
+ audio_devs[gus_devnum]->min_fragment = 9; /* 512k */
+ audio_devs[gus_devnum]->max_fragment = 11; /* 8k (must match size of bounce_buf */
+ audio_devs[gus_devnum]->mixer_dev = -1; /* Next mixer# */
+ audio_devs[gus_devnum]->flags |= DMA_HARDSTOP;
+ } else
+ printk("GUS: Too many audio devices available\n");
+
+ /*
+ * Mixer dependent initialization.
+ */
+
+ switch (mixer_type)
+ {
+ case ICS2101:
+ gus_mic_vol = gus_line_vol = gus_pcm_volume = 100;
+ gus_wave_volume = 90;
+ request_region(u_MixSelect, 1, "GUS mixer");
+ hw_config->slots[5] = ics2101_mixer_init();
+ audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */
+ return;
+
+ case CS4231:
+ /* Initialized elsewhere (ad1848.c) */
+ default:
+ hw_config->slots[5] = gus_default_mixer_init();
+ audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */
+ return;
+ }
}
void
-gus_wave_unload (void)
+gus_wave_unload(struct address_info *hw_config)
{
#ifdef CONFIG_GUSMAX
- if (have_gus_max)
- {
- ad1848_unload (gus_base + 0x10c,
- -gus_irq,
- gus_dma2, /* Playback DMA */
- gus_dma, /* Capture DMA */
- 1); /* Share DMA channels with GF1 */
- }
+ if (have_gus_max)
+ {
+ ad1848_unload(gus_base + 0x10c,
+ -gus_irq,
+ gus_dma2, /* Playback DMA */
+ gus_dma, /* Capture DMA */
+ 1); /* Share DMA channels with GF1 */
+ }
#endif
- if (mixer_type == ICS2101)
- {
- release_region (u_MixSelect, 1);
- }
+ if (mixer_type == ICS2101)
+ {
+ release_region(u_MixSelect, 1);
+ }
+ if (hw_config->slots[0] != -1)
+ sound_unload_synthdev(hw_config->slots[0]);
+ if (hw_config->slots[1] != -1)
+ sound_unload_audiodev(hw_config->slots[1]);
+ if (hw_config->slots[2] != -1)
+ sound_unload_mididev(hw_config->slots[2]);
+ if (hw_config->slots[4] != -1)
+ sound_unload_audiodev(hw_config->slots[4]);
+ if (hw_config->slots[5] != -1)
+ sound_unload_mixerdev(hw_config->slots[4]);
}
static void
-do_loop_irq (int voice)
+do_loop_irq(int voice)
{
- unsigned char tmp;
- int mode, parm;
- unsigned long flags;
+ unsigned char tmp;
+ int mode, parm;
+ unsigned long flags;
- save_flags (flags);
- cli ();
- gus_select_voice (voice);
+ save_flags(flags);
+ cli();
+ gus_select_voice(voice);
- tmp = gus_read8 (0x00);
- tmp &= ~0x20; /*
+ tmp = gus_read8(0x00);
+ tmp &= ~0x20; /*
* Disable wave IRQ for this_one voice
*/
- gus_write8 (0x00, tmp);
+ gus_write8(0x00, tmp);
- if (tmp & 0x03) /* Voice stopped */
- voice_alloc->map[voice] = 0;
+ if (tmp & 0x03) /* Voice stopped */
+ voice_alloc->map[voice] = 0;
- mode = voices[voice].loop_irq_mode;
- voices[voice].loop_irq_mode = 0;
- parm = voices[voice].loop_irq_parm;
+ mode = voices[voice].loop_irq_mode;
+ voices[voice].loop_irq_mode = 0;
+ parm = voices[voice].loop_irq_parm;
- switch (mode)
- {
+ switch (mode)
+ {
- case LMODE_FINISH: /*
+ case LMODE_FINISH: /*
* Final loop finished, shoot volume down
*/
- if ((int) (gus_read16 (0x09) >> 4) < 100) /*
- * Get current volume
- */
- {
- gus_voice_off ();
- gus_rampoff ();
- gus_voice_init (voice);
- break;
- }
- gus_ramp_range (65, 4065);
- gus_ramp_rate (0, 63); /*
- * Fastest possible rate
- */
- gus_rampon (0x20 | 0x40); /*
- * Ramp down, once, irq
- */
- voices[voice].volume_irq_mode = VMODE_HALT;
- break;
-
- case LMODE_PCM_STOP:
- pcm_active = 0; /* Signal to the play_next_pcm_block routine */
- case LMODE_PCM:
- {
-
- pcm_qlen--;
- pcm_head = (pcm_head + 1) % pcm_nblk;
- if (pcm_qlen && pcm_active)
- {
- play_next_pcm_block ();
- }
- else
- { /* Underrun. Just stop the voice */
- gus_select_voice (0); /* Left channel */
- gus_voice_off ();
- gus_rampoff ();
- gus_select_voice (1); /* Right channel */
- gus_voice_off ();
- gus_rampoff ();
- pcm_active = 0;
+ if ((int) (gus_read16(0x09) >> 4) < 100) /*
+ * Get current volume
+ */
+ {
+ gus_voice_off();
+ gus_rampoff();
+ gus_voice_init(voice);
+ break;
+ }
+ gus_ramp_range(65, 4065);
+ gus_ramp_rate(0, 63); /*
+ * Fastest possible rate
+ */
+ gus_rampon(0x20 | 0x40); /*
+ * Ramp down, once, irq
+ */
+ voices[voice].volume_irq_mode = VMODE_HALT;
+ break;
+
+ case LMODE_PCM_STOP:
+ pcm_active = 0; /* Signal to the play_next_pcm_block routine */
+ case LMODE_PCM:
+ {
+
+ pcm_qlen--;
+ pcm_head = (pcm_head + 1) % pcm_nblk;
+ if (pcm_qlen && pcm_active)
+ {
+ play_next_pcm_block();
+ } else
+ { /* Underrun. Just stop the voice */
+ gus_select_voice(0); /* Left channel */
+ gus_voice_off();
+ gus_rampoff();
+ gus_select_voice(1); /* Right channel */
+ gus_voice_off();
+ gus_rampoff();
+ pcm_active = 0;
+ }
+
+ /*
+ * If the queue was full before this interrupt, the DMA transfer was
+ * suspended. Let it continue now.
+ */
+ if (audio_devs[gus_devnum]->dmap_out->qlen > 0)
+ DMAbuf_outputintr(gus_devnum, 0);
+ }
+ break;
+
+ default:;
}
-
- /*
- * If the queue was full before this interrupt, the DMA transfer was
- * suspended. Let it continue now.
- */
- if (audio_devs[gus_devnum]->dmap_out->qlen > 0)
- DMAbuf_outputintr (gus_devnum, 0);
- }
- break;
-
- default:;
- }
- restore_flags (flags);
+ restore_flags(flags);
}
static void
-do_volume_irq (int voice)
+do_volume_irq(int voice)
{
- unsigned char tmp;
- int mode, parm;
- unsigned long flags;
+ unsigned char tmp;
+ int mode, parm;
+ unsigned long flags;
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- gus_select_voice (voice);
+ gus_select_voice(voice);
- tmp = gus_read8 (0x0d);
- tmp &= ~0x20; /*
+ tmp = gus_read8(0x0d);
+ tmp &= ~0x20; /*
* Disable volume ramp IRQ
*/
- gus_write8 (0x0d, tmp);
-
- mode = voices[voice].volume_irq_mode;
- voices[voice].volume_irq_mode = 0;
- parm = voices[voice].volume_irq_parm;
-
- switch (mode)
- {
- case VMODE_HALT: /* Decay phase finished */
- if (iw_mode)
- gus_write8 (0x15, 0x02); /* Set voice deactivate bit of SMSI */
- restore_flags (flags);
- gus_voice_init (voice);
- break;
-
- case VMODE_ENVELOPE:
- gus_rampoff ();
- restore_flags (flags);
- step_envelope (voice);
- break;
-
- case VMODE_START_NOTE:
- restore_flags (flags);
- guswave_start_note2 (voices[voice].dev_pending, voice,
- voices[voice].note_pending, voices[voice].volume_pending);
- if (voices[voice].kill_pending)
- guswave_kill_note (voices[voice].dev_pending, voice,
- voices[voice].note_pending, 0);
-
- if (voices[voice].sample_pending >= 0)
- {
- guswave_set_instr (voices[voice].dev_pending, voice,
- voices[voice].sample_pending);
- voices[voice].sample_pending = -1;
- }
- break;
-
- default:;
- }
-}
+ gus_write8(0x0d, tmp);
-void
-gus_voice_irq (void)
-{
- unsigned long wave_ignore = 0, volume_ignore = 0;
- unsigned long voice_bit;
+ mode = voices[voice].volume_irq_mode;
+ voices[voice].volume_irq_mode = 0;
+ parm = voices[voice].volume_irq_parm;
- unsigned char src, voice;
+ switch (mode)
+ {
+ case VMODE_HALT: /* Decay phase finished */
+ if (iw_mode)
+ gus_write8(0x15, 0x02); /* Set voice deactivate bit of SMSI */
+ restore_flags(flags);
+ gus_voice_init(voice);
+ break;
+
+ case VMODE_ENVELOPE:
+ gus_rampoff();
+ restore_flags(flags);
+ step_envelope(voice);
+ break;
+
+ case VMODE_START_NOTE:
+ restore_flags(flags);
+ guswave_start_note2(voices[voice].dev_pending, voice,
+ voices[voice].note_pending, voices[voice].volume_pending);
+ if (voices[voice].kill_pending)
+ guswave_kill_note(voices[voice].dev_pending, voice,
+ voices[voice].note_pending, 0);
+
+ if (voices[voice].sample_pending >= 0)
+ {
+ guswave_set_instr(voices[voice].dev_pending, voice,
+ voices[voice].sample_pending);
+ voices[voice].sample_pending = -1;
+ }
+ break;
- while (1)
- {
- src = gus_read8 (0x0f); /*
- * Get source info
- */
- voice = src & 0x1f;
- src &= 0xc0;
+ default:
+ restore_flags(flags);
+ }
+ restore_flags(flags);
+}
- if (src == (0x80 | 0x40))
- return; /*
- * No interrupt
- */
+void
+gus_voice_irq(void)
+{
+ unsigned long wave_ignore = 0, volume_ignore = 0;
+ unsigned long voice_bit;
- voice_bit = 1 << voice;
+ unsigned char src, voice;
- if (!(src & 0x80)) /*
- * Wave IRQ pending
- */
- if (!(wave_ignore & voice_bit) && (int) voice < nr_voices) /*
- * Not done
- * yet
- */
+ while (1)
{
- wave_ignore |= voice_bit;
- do_loop_irq (voice);
- }
+ src = gus_read8(0x0f); /*
+ * Get source info
+ */
+ voice = src & 0x1f;
+ src &= 0xc0;
+
+ if (src == (0x80 | 0x40))
+ return; /*
+ * No interrupt
+ */
- if (!(src & 0x40)) /*
- * Volume IRQ pending
- */
- if (!(volume_ignore & voice_bit) && (int) voice < nr_voices) /*
- * Not done
- * yet
- */
- {
- volume_ignore |= voice_bit;
- do_volume_irq (voice);
+ voice_bit = 1 << voice;
+
+ if (!(src & 0x80)) /*
+ * Wave IRQ pending
+ */
+ if (!(wave_ignore & voice_bit) && (int) voice < nr_voices) /*
+ * Not done
+ * yet
+ */
+ {
+ wave_ignore |= voice_bit;
+ do_loop_irq(voice);
+ }
+ if (!(src & 0x40)) /*
+ * Volume IRQ pending
+ */
+ if (!(volume_ignore & voice_bit) && (int) voice < nr_voices) /*
+ * Not done
+ * yet
+ */
+ {
+ volume_ignore |= voice_bit;
+ do_volume_irq(voice);
+ }
}
- }
}
void
-guswave_dma_irq (void)
-{
- unsigned char status;
-
- status = gus_look8 (0x41); /* Get DMA IRQ Status */
- if (status & 0x40) /* DMA interrupt pending */
- switch (active_device)
- {
- case GUS_DEV_WAVE:
- if ((dram_sleep_flag.opts & WK_SLEEP))
- {
- dram_sleep_flag.opts = WK_WAKEUP;
- wake_up (&dram_sleeper);
- };
- break;
-
- case GUS_DEV_PCM_CONTINUE: /* Left channel data transferred */
- gus_write8 (0x41, 0); /* Disable GF1 DMA */
- gus_transfer_output_block (pcm_current_dev, pcm_current_buf,
- pcm_current_count,
- pcm_current_intrflag, 1);
- break;
-
- case GUS_DEV_PCM_DONE: /* Right or mono channel data transferred */
- gus_write8 (0x41, 0); /* Disable GF1 DMA */
- if (pcm_qlen < pcm_nblk)
- {
- dma_active = 0;
- if (gus_busy)
- {
- if (audio_devs[gus_devnum]->dmap_out->qlen > 0)
- DMAbuf_outputintr (gus_devnum, 0);
- }
- }
- break;
-
- default:;
- }
-
- status = gus_look8 (0x49); /*
- * Get Sampling IRQ Status
- */
- if (status & 0x40) /*
+guswave_dma_irq(void)
+{
+ unsigned char status;
+
+ status = gus_look8(0x41); /* Get DMA IRQ Status */
+ if (status & 0x40) /* DMA interrupt pending */
+ switch (active_device)
+ {
+ case GUS_DEV_WAVE:
+ if ((dram_sleep_flag.opts & WK_SLEEP))
+ {
+ dram_sleep_flag.opts = WK_WAKEUP;
+ wake_up(&dram_sleeper);
+ };
+ break;
+
+ case GUS_DEV_PCM_CONTINUE: /* Left channel data transferred */
+ gus_write8(0x41, 0); /* Disable GF1 DMA */
+ gus_transfer_output_block(pcm_current_dev, pcm_current_buf,
+ pcm_current_count,
+ pcm_current_intrflag, 1);
+ break;
+
+ case GUS_DEV_PCM_DONE: /* Right or mono channel data transferred */
+ gus_write8(0x41, 0); /* Disable GF1 DMA */
+ if (pcm_qlen < pcm_nblk)
+ {
+ dma_active = 0;
+ if (gus_busy)
+ {
+ if (audio_devs[gus_devnum]->dmap_out->qlen > 0)
+ DMAbuf_outputintr(gus_devnum, 0);
+ }
+ }
+ break;
+
+ default:;
+ }
+ status = gus_look8(0x49); /*
+ * Get Sampling IRQ Status
+ */
+ if (status & 0x40) /*
* Sampling Irq pending
*/
- {
- DMAbuf_inputintr (gus_devnum);
- }
-
+ {
+ DMAbuf_inputintr(gus_devnum);
+ }
}
-#ifdef CONFIG_SEQUENCER
+#if defined(CONFIG_SEQUENCER) || defined(MODULE)
+
/*
* Timer stuff
*/
@@ -3585,105 +3527,103 @@ static volatile int select_addr, data_addr;
static volatile int curr_timer = 0;
void
-gus_timer_command (unsigned int addr, unsigned int val)
+gus_timer_command(unsigned int addr, unsigned int val)
{
- int i;
+ int i;
- outb (((unsigned char) (addr & 0xff)), select_addr);
+ outb(((unsigned char) (addr & 0xff)), select_addr);
- for (i = 0; i < 2; i++)
- inb (select_addr);
+ for (i = 0; i < 2; i++)
+ inb(select_addr);
- outb (((unsigned char) (val & 0xff)), data_addr);
+ outb(((unsigned char) (val & 0xff)), data_addr);
- for (i = 0; i < 2; i++)
- inb (select_addr);
+ for (i = 0; i < 2; i++)
+ inb(select_addr);
}
static void
-arm_timer (int timer, unsigned int interval)
+arm_timer(int timer, unsigned int interval)
{
- curr_timer = timer;
+ curr_timer = timer;
- if (timer == 1)
- {
- gus_write8 (0x46, 256 - interval); /* Set counter for timer 1 */
- gus_write8 (0x45, 0x04); /* Enable timer 1 IRQ */
- gus_timer_command (0x04, 0x01); /* Start timer 1 */
- }
- else
- {
- gus_write8 (0x47, 256 - interval); /* Set counter for timer 2 */
- gus_write8 (0x45, 0x08); /* Enable timer 2 IRQ */
- gus_timer_command (0x04, 0x02); /* Start timer 2 */
- }
+ if (timer == 1)
+ {
+ gus_write8(0x46, 256 - interval); /* Set counter for timer 1 */
+ gus_write8(0x45, 0x04); /* Enable timer 1 IRQ */
+ gus_timer_command(0x04, 0x01); /* Start timer 1 */
+ } else
+ {
+ gus_write8(0x47, 256 - interval); /* Set counter for timer 2 */
+ gus_write8(0x45, 0x08); /* Enable timer 2 IRQ */
+ gus_timer_command(0x04, 0x02); /* Start timer 2 */
+ }
- gus_timer_enabled = 1;
+ gus_timer_enabled = 1;
}
static unsigned int
-gus_tmr_start (int dev, unsigned int usecs_per_tick)
+gus_tmr_start(int dev, unsigned int usecs_per_tick)
{
- int timer_no, resolution;
- int divisor;
+ int timer_no, resolution;
+ int divisor;
- if (usecs_per_tick > (256 * 80))
- {
- timer_no = 2;
- resolution = 320; /* usec */
- }
- else
- {
- timer_no = 1;
- resolution = 80; /* usec */
- }
+ if (usecs_per_tick > (256 * 80))
+ {
+ timer_no = 2;
+ resolution = 320; /* usec */
+ } else
+ {
+ timer_no = 1;
+ resolution = 80; /* usec */
+ }
- divisor = (usecs_per_tick + (resolution / 2)) / resolution;
+ divisor = (usecs_per_tick + (resolution / 2)) / resolution;
- arm_timer (timer_no, divisor);
+ arm_timer(timer_no, divisor);
- return divisor * resolution;
+ return divisor * resolution;
}
static void
-gus_tmr_disable (int dev)
+gus_tmr_disable(int dev)
{
- gus_write8 (0x45, 0); /* Disable both timers */
- gus_timer_enabled = 0;
+ gus_write8(0x45, 0); /* Disable both timers */
+ gus_timer_enabled = 0;
}
static void
-gus_tmr_restart (int dev)
+gus_tmr_restart(int dev)
{
- if (curr_timer == 1)
- gus_write8 (0x45, 0x04); /* Start timer 1 again */
- else
- gus_write8 (0x45, 0x08); /* Start timer 2 again */
- gus_timer_enabled = 1;
+ if (curr_timer == 1)
+ gus_write8(0x45, 0x04); /* Start timer 1 again */
+ else
+ gus_write8(0x45, 0x08); /* Start timer 2 again */
+ gus_timer_enabled = 1;
}
static struct sound_lowlev_timer gus_tmr =
{
- 0,
- 1,
- gus_tmr_start,
- gus_tmr_disable,
- gus_tmr_restart
+ 0,
+ 1,
+ gus_tmr_start,
+ gus_tmr_disable,
+ gus_tmr_restart
};
static void
-gus_tmr_install (int io_base)
+gus_tmr_install(int io_base)
{
- struct sound_lowlev_timer *tmr;
+ struct sound_lowlev_timer *tmr;
- select_addr = io_base;
- data_addr = io_base + 1;
+ select_addr = io_base;
+ data_addr = io_base + 1;
- tmr = &gus_tmr;
+ tmr = &gus_tmr;
#ifdef THIS_GETS_FIXED
- sound_timer_init (&gus_tmr, "GUS");
+ sound_timer_init(&gus_tmr, "GUS");
#endif
}
#endif
diff --git a/drivers/sound/ics2101.c b/drivers/sound/ics2101.c
index 07666ec29..3c47f5a50 100644
--- a/drivers/sound/ics2101.c
+++ b/drivers/sound/ics2101.c
@@ -14,7 +14,7 @@
#include "sound_config.h"
-#if defined(CONFIG_GUSHW)
+#if defined(CONFIG_GUSHW) || defined(MODULE)
#include <linux/ultrasound.h>
#include "gus_hw.h"
@@ -31,222 +31,206 @@ static int left_fix[ICS_MIXDEVS] =
static int right_fix[ICS_MIXDEVS] =
{2, 2, 2, 1, 2, 1};
-static int
-scale_vol (int vol)
+static int scale_vol(int vol)
{
- /*
- * Experimental volume scaling by Risto Kankkunen.
- * This should give smoother volume response than just
- * a plain multiplication.
- */
- int e;
-
- if (vol < 0)
- vol = 0;
- if (vol > 100)
- vol = 100;
- vol = (31 * vol + 50) / 100;
- e = 0;
- if (vol)
- {
- while (vol < 16)
+ /*
+ * Experimental volume scaling by Risto Kankkunen.
+ * This should give smoother volume response than just
+ * a plain multiplication.
+ */
+
+ int e;
+
+ if (vol < 0)
+ vol = 0;
+ if (vol > 100)
+ vol = 100;
+ vol = (31 * vol + 50) / 100;
+ e = 0;
+ if (vol)
{
- vol <<= 1;
- e--;
+ while (vol < 16)
+ {
+ vol <<= 1;
+ e--;
+ }
+ vol -= 16;
+ e += 7;
}
- vol -= 16;
- e += 7;
- }
- return ((e << 4) + vol);
+ return ((e << 4) + vol);
}
-static void
-write_mix (int dev, int chn, int vol)
+static void write_mix(int dev, int chn, int vol)
{
- int *selector;
- unsigned long flags;
- int ctrl_addr = dev << 3;
- int attn_addr = dev << 3;
-
- vol = scale_vol (vol);
-
- if (chn == CHN_LEFT)
- {
- selector = left_fix;
- ctrl_addr |= 0x00;
- attn_addr |= 0x02;
- }
- else
- {
- selector = right_fix;
- ctrl_addr |= 0x01;
- attn_addr |= 0x03;
- }
-
- save_flags (flags);
- cli ();
- outb ((ctrl_addr), u_MixSelect);
- outb ((selector[dev]), u_MixData);
- outb ((attn_addr), u_MixSelect);
- outb (((unsigned char) vol), u_MixData);
- restore_flags (flags);
+ int *selector;
+ unsigned long flags;
+ int ctrl_addr = dev << 3;
+ int attn_addr = dev << 3;
+
+ vol = scale_vol(vol);
+
+ if (chn == CHN_LEFT)
+ {
+ selector = left_fix;
+ ctrl_addr |= 0x00;
+ attn_addr |= 0x02;
+ }
+ else
+ {
+ selector = right_fix;
+ ctrl_addr |= 0x01;
+ attn_addr |= 0x03;
+ }
+
+ save_flags(flags);
+ cli();
+ outb((ctrl_addr), u_MixSelect);
+ outb((selector[dev]), u_MixData);
+ outb((attn_addr), u_MixSelect);
+ outb(((unsigned char) vol), u_MixData);
+ restore_flags(flags);
}
-static int
-set_volumes (int dev, int vol)
+static int set_volumes(int dev, int vol)
{
- int left = vol & 0x00ff;
- int right = (vol >> 8) & 0x00ff;
-
- if (left < 0)
- left = 0;
- if (left > 100)
- left = 100;
- if (right < 0)
- right = 0;
- if (right > 100)
- right = 100;
-
- write_mix (dev, CHN_LEFT, left);
- write_mix (dev, CHN_RIGHT, right);
-
- vol = left + (right << 8);
- volumes[dev] = vol;
- return vol;
+ int left = vol & 0x00ff;
+ int right = (vol >> 8) & 0x00ff;
+
+ if (left < 0)
+ left = 0;
+ if (left > 100)
+ left = 100;
+ if (right < 0)
+ right = 0;
+ if (right > 100)
+ right = 100;
+
+ write_mix(dev, CHN_LEFT, left);
+ write_mix(dev, CHN_RIGHT, right);
+
+ vol = left + (right << 8);
+ volumes[dev] = vol;
+ return vol;
}
-static int
-ics2101_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
+static int ics2101_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
{
- if (((cmd >> 8) & 0xff) == 'M')
- {
- if (_SIOC_DIR (cmd) & _SIOC_WRITE)
+ if (((cmd >> 8) & 0xff) == 'M')
{
- int val;
+ if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+ {
+ int val;
+
+ val = *(int *) arg;
+
+ switch (cmd & 0xff)
+ {
+ case SOUND_MIXER_RECSRC:
+ return gus_default_mixer_ioctl(dev, cmd, arg);
+
+ case SOUND_MIXER_MIC:
+ return (*(int *) arg = set_volumes(DEV_MIC, val));
+
+ case SOUND_MIXER_CD:
+ return (*(int *) arg = set_volumes(DEV_CD, val));
- val = *(int *) arg;
+ case SOUND_MIXER_LINE:
+ return (*(int *) arg = set_volumes(DEV_LINE, val));
- switch (cmd & 0xff)
- {
- case SOUND_MIXER_RECSRC:
- return gus_default_mixer_ioctl (dev, cmd, arg);
- break;
+ case SOUND_MIXER_SYNTH:
+ return (*(int *) arg = set_volumes(DEV_GF1, val));
- case SOUND_MIXER_MIC:
- return (*(int *) arg = set_volumes (DEV_MIC, val));
- break;
+ case SOUND_MIXER_VOLUME:
+ return (*(int *) arg = set_volumes(DEV_VOL, val));
- case SOUND_MIXER_CD:
- return (*(int *) arg = set_volumes (DEV_CD, val));
- break;
+ default:
+ return -EINVAL;
+ }
+ }
+ else
+ {
+ switch (cmd & 0xff) /*
+ * Return parameters
+ */
+ {
- case SOUND_MIXER_LINE:
- return (*(int *) arg = set_volumes (DEV_LINE, val));
- break;
+ case SOUND_MIXER_RECSRC:
+ return gus_default_mixer_ioctl(dev, cmd, arg);
- case SOUND_MIXER_SYNTH:
- return (*(int *) arg = set_volumes (DEV_GF1, val));
- break;
+ case SOUND_MIXER_DEVMASK:
+ return (*(int *) arg = MIX_DEVS);
- case SOUND_MIXER_VOLUME:
- return (*(int *) arg = set_volumes (DEV_VOL, val));
- break;
+ case SOUND_MIXER_STEREODEVS:
+ return (*(int *) arg = SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC);
- default:
- return -EINVAL;
- }
+ case SOUND_MIXER_RECMASK:
+ return (*(int *) arg = SOUND_MASK_MIC | SOUND_MASK_LINE);
+
+ case SOUND_MIXER_CAPS:
+ return (*(int *) arg = 0);
+ break;
+
+ case SOUND_MIXER_MIC:
+ return (*(int *) arg = volumes[DEV_MIC]);
+
+ case SOUND_MIXER_LINE:
+ return (*(int *) arg = volumes[DEV_LINE]);
+
+ case SOUND_MIXER_CD:
+ return (*(int *) arg = volumes[DEV_CD]);
+
+ case SOUND_MIXER_VOLUME:
+ return (*(int *) arg = volumes[DEV_VOL]);
+
+ case SOUND_MIXER_SYNTH:
+ return (*(int *) arg = volumes[DEV_GF1]);
+
+ default:
+ return -EINVAL;
+ }
+ }
}
- else
- switch (cmd & 0xff) /*
- * Return parameters
- */
- {
-
- case SOUND_MIXER_RECSRC:
- return gus_default_mixer_ioctl (dev, cmd, arg);
- break;
-
- case SOUND_MIXER_DEVMASK:
- return (*(int *) arg = MIX_DEVS);
- break;
-
- case SOUND_MIXER_STEREODEVS:
- return (*(int *) arg = SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC);
- break;
-
- case SOUND_MIXER_RECMASK:
- return (*(int *) arg = SOUND_MASK_MIC | SOUND_MASK_LINE);
- break;
-
- case SOUND_MIXER_CAPS:
- return (*(int *) arg = 0);
- break;
-
- case SOUND_MIXER_MIC:
- return (*(int *) arg = volumes[DEV_MIC]);
- break;
-
- case SOUND_MIXER_LINE:
- return (*(int *) arg = volumes[DEV_LINE]);
- break;
-
- case SOUND_MIXER_CD:
- return (*(int *) arg = volumes[DEV_CD]);
- break;
-
- case SOUND_MIXER_VOLUME:
- return (*(int *) arg = volumes[DEV_VOL]);
- break;
-
- case SOUND_MIXER_SYNTH:
- return (*(int *) arg = volumes[DEV_GF1]);
- break;
-
- default:
- return -EINVAL;
- }
- }
-
- return -EINVAL;
+ return -EINVAL;
}
static struct mixer_operations ics2101_mixer_operations =
{
- "ICS2101",
- "ICS2101 Multimedia Mixer",
- ics2101_mixer_ioctl
+ "ICS2101",
+ "ICS2101 Multimedia Mixer",
+ ics2101_mixer_ioctl
};
-void
-ics2101_mixer_init (void)
+int
+ics2101_mixer_init(void)
{
- int i;
-
- if (num_mixers < MAX_MIXER_DEV)
- {
- mixer_devs[num_mixers++] = &ics2101_mixer_operations;
-
- /*
- * Some GUS v3.7 cards had some channels flipped. Disable
- * the flipping feature if the model id is other than 5.
- */
+ int i;
+ int n;
- if (inb (u_MixSelect) != 5)
+ if ((n = sound_alloc_mixerdev()) != -1)
{
- for (i = 0; i < ICS_MIXDEVS; i++)
- left_fix[i] = 1;
- for (i = 0; i < ICS_MIXDEVS; i++)
- right_fix[i] = 2;
+ n = num_mixers;
+ mixer_devs[n] = &ics2101_mixer_operations;
+
+ /*
+ * Some GUS v3.7 cards had some channels flipped. Disable
+ * the flipping feature if the model id is other than 5.
+ */
+
+ if (inb(u_MixSelect) != 5)
+ {
+ for (i = 0; i < ICS_MIXDEVS; i++)
+ left_fix[i] = 1;
+ for (i = 0; i < ICS_MIXDEVS; i++)
+ right_fix[i] = 2;
+ }
+ set_volumes(DEV_GF1, 0x5a5a);
+ set_volumes(DEV_CD, 0x5a5a);
+ set_volumes(DEV_MIC, 0x0000);
+ set_volumes(DEV_LINE, 0x5a5a);
+ set_volumes(DEV_VOL, 0x5a5a);
+ set_volumes(DEV_UNUSED, 0x0000);
}
-
- set_volumes (DEV_GF1, 0x5a5a);
- set_volumes (DEV_CD, 0x5a5a);
- set_volumes (DEV_MIC, 0x0000);
- set_volumes (DEV_LINE, 0x5a5a);
- set_volumes (DEV_VOL, 0x5a5a);
- set_volumes (DEV_UNUSED, 0x0000);
- }
-
+ return n;
}
#endif
diff --git a/drivers/sound/local.h.master b/drivers/sound/local.h.master
new file mode 100644
index 000000000..91ad38d1d
--- /dev/null
+++ b/drivers/sound/local.h.master
@@ -0,0 +1,228 @@
+/* Computer generated file. Please don't edit! */
+
+#define KERNEL_COMPATIBLE_CONFIG
+
+#define SELECTED_SOUND_OPTIONS 0x00000000
+
+#if \
+ defined(CONFIG_PSS) || defined(CONFIG_SSCAPE) || \
+ defined(CONFIG_CS4232) || defined(CONFIG_MAUI) || \
+ defined(CONFIG_PSS_MODULE) || defined(CONFIG_SSCAPE_MODULE) || \
+ defined(CONFIG_CS4232_MODULE) || defined(CONFIG_MAUI_MODULE)
+# define CONFIG_MPU_EMU
+#endif
+
+#if \
+ defined(CONFIG_PSS) || defined(CONFIG_GUS16) || \
+ defined(CONFIG_GUSMAX) || defined(CONFIG_MSS) || \
+ defined(CONFIG_SSCAPE) || defined(CONFIG_TRIX) || \
+ defined(CONFIG_MAD16) || defined(CONFIG_CS4232) || \
+ defined(CONFIG_PSS_MODULE) || defined(CONFIG_GUS16_MODULE) || \
+ defined(CONFIG_GUSMAX_MODULE) || defined(CONFIG_MSS_MODULE) || \
+ defined(CONFIG_SSCAPE_MODULE) || defined(CONFIG_TRIX_MODULE) || \
+ defined(CONFIG_MAD16_MODULE) || defined(CONFIG_CS4232_MODULE)
+# define CONFIG_AD1848
+#endif
+
+#if \
+ defined(CONFIG_PAS) || defined(CONFIG_SB) || \
+ defined(CONFIG_GUS) || defined(CONFIG_PSS) || \
+ defined(CONFIG_GUS16) || defined(CONFIG_GUSMAX) || \
+ defined(CONFIG_MSS) || defined(CONFIG_SSCAPE) || \
+ defined(CONFIG_TRIX) || defined(CONFIG_MAD16) || \
+ defined(CONFIG_CS4232) || defined(CONFIG_OPL3SA1) || \
+ defined(CONFIG_SOFTOSS) || \
+ defined(CONFIG_PAS_MODULE) || defined(CONFIG_SB_MODULE) || \
+ defined(CONFIG_GUS_MODULE) || defined(CONFIG_PSS_MODULE) || \
+ defined(CONFIG_GUS16_MODULE) || defined(CONFIG_GUSMAX_MODULE) || \
+ defined(CONFIG_MSS_MODULE) || defined(CONFIG_SSCAPE_MODULE) || \
+ defined(CONFIG_TRIX_MODULE) || defined(CONFIG_MAD16_MODULE) || \
+ defined(CONFIG_CS4232_MODULE) || defined(CONFIG_OPL3SA1_MODULE) || \
+ defined(CONFIG_SOFTOSS_MODULE)
+# define CONFIG_AUDIO
+#endif
+
+#if \
+ defined(CONFIG_PAS) || defined(CONFIG_SB) || \
+ defined(CONFIG_GUS) || defined(CONFIG_MPU401) || \
+ defined(CONFIG_PSS) || defined(CONFIG_GUS16) || \
+ defined(CONFIG_GUSMAX) || defined(CONFIG_SSCAPE) || \
+ defined(CONFIG_TRIX) || defined(CONFIG_MAD16) || \
+ defined(CONFIG_CS4232) || defined(CONFIG_MAUI) || \
+ defined(CONFIG_OPL3SA1) || defined(CONFIG_SOFTOSS) || \
+ defined(CONFIG_PAS_MODULE) || defined(CONFIG_SB_MODULE) || \
+ defined(CONFIG_GUS_MODULE) || defined(CONFIG_MPU401_MODULE) || \
+ defined(CONFIG_PSS_MODULE) || defined(CONFIG_GUS16_MODULE) || \
+ defined(CONFIG_GUSMAX_MODULE) || defined(CONFIG_SSCAPE_MODULE) || \
+ defined(CONFIG_TRIX_MODULE) || defined(CONFIG_MAD16_MODULE) || \
+ defined(CONFIG_CS4232_MODULE) || defined(CONFIG_MAUI_MODULE) || \
+ defined(CONFIG_OPL3SA1_MODULE) || defined(CONFIG_SOFTOSS_MODULE)
+# define CONFIG_MIDI
+#endif
+
+#if \
+ defined(CONFIG_SB) || defined(CONFIG_TRIX) || \
+ defined(CONFIG_MAD16) || \
+ defined(CONFIG_SB_MODULE) || defined(CONFIG_TRIX_MODULE) || \
+ defined(CONFIG_MAD16_MODULE)
+# define CONFIG_SBDSP
+#endif
+#if \
+ defined(CONFIG_SB_MODULE) || defined(CONFIG_TRIX_MODULE) || \
+ defined(CONFIG_MAD16_MODULE)
+# define CONFIG_SBDSP_MODULE
+#endif
+
+#if \
+ defined(CONFIG_SB) || defined(CONFIG_TRIX) || \
+ defined(CONFIG_MAD16) || defined(CONFIG_SB_MODULE) || \
+ defined(CONFIG_TRIX_MODULE) || defined(CONFIG_MAD16_MODULE)
+# define CONFIG_UART401
+#endif
+
+#if \
+ defined(CONFIG_SB_MODULE) || defined(CONFIG_TRIX_MODULE) || \
+ defined(CONFIG_MAD16_MODULE)
+#ifndef CONFIG_UART401_MODULE
+#define CONFIG_UART401_MODULE
+#endif
+#endif
+
+#if \
+ defined(CONFIG_PAS) || defined(CONFIG_SB) || \
+ defined(CONFIG_ADLIB) || defined(CONFIG_GUS) || \
+ defined(CONFIG_MPU401) || defined(CONFIG_PSS) || \
+ defined(CONFIG_SSCAPE) || defined(CONFIG_TRIX) || \
+ defined(CONFIG_MAD16) || defined(CONFIG_CS4232) || \
+ defined(CONFIG_MAUI) || \
+ defined(CONFIG_PAS_MODULE) || defined(CONFIG_SB_MODULE) || \
+ defined(CONFIG_ADLIB_MODULE) || defined(CONFIG_GUS_MODULE) || \
+ defined(CONFIG_MPU401_MODULE) || defined(CONFIG_PSS_MODULE) || \
+ defined(CONFIG_SSCAPE_MODULE) || defined(CONFIG_TRIX_MODULE) || \
+ defined(CONFIG_MAD16_MODULE) || defined(CONFIG_CS4232_MODULE) || \
+ defined(CONFIG_MAUI_MODULE)
+# define CONFIG_SEQUENCER
+#endif
+
+/*
+ * Force on additional support
+ */
+
+#define SM_WAVE
+#define __SGNXPRO__
+#define SM_GAMES
+#define DESKPROXL
+/* Computer generated file. Please don't edit! */
+
+#define KERNEL_COMPATIBLE_CONFIG
+
+#define SELECTED_SOUND_OPTIONS 0x00000000
+
+#if \
+ defined(CONFIG_PSS) || defined(CONFIG_SSCAPE) || \
+ defined(CONFIG_CS4232) || defined(CONFIG_MAUI) || \
+ defined(CONFIG_PSS_MODULE) || defined(CONFIG_SSCAPE_MODULE) || \
+ defined(CONFIG_CS4232_MODULE) || defined(CONFIG_MAUI_MODULE)
+# define CONFIG_MPU_EMU
+#endif
+
+#if \
+ defined(CONFIG_PSS) || defined(CONFIG_GUS16) || \
+ defined(CONFIG_GUSMAX) || defined(CONFIG_MSS) || \
+ defined(CONFIG_SSCAPE) || defined(CONFIG_TRIX) || \
+ defined(CONFIG_MAD16) || defined(CONFIG_CS4232) || \
+ defined(CONFIG_PSS_MODULE) || defined(CONFIG_GUS16_MODULE) || \
+ defined(CONFIG_GUSMAX_MODULE) || defined(CONFIG_MSS_MODULE) || \
+ defined(CONFIG_SSCAPE_MODULE) || defined(CONFIG_TRIX_MODULE) || \
+ defined(CONFIG_MAD16_MODULE) || defined(CONFIG_CS4232_MODULE)
+# define CONFIG_AD1848
+#endif
+
+#if \
+ defined(CONFIG_PAS) || defined(CONFIG_SB) || \
+ defined(CONFIG_GUS) || defined(CONFIG_PSS) || \
+ defined(CONFIG_GUS16) || defined(CONFIG_GUSMAX) || \
+ defined(CONFIG_MSS) || defined(CONFIG_SSCAPE) || \
+ defined(CONFIG_TRIX) || defined(CONFIG_MAD16) || \
+ defined(CONFIG_CS4232) || defined(CONFIG_OPL3SA1) || \
+ defined(CONFIG_SOFTOSS) || \
+ defined(CONFIG_PAS_MODULE) || defined(CONFIG_SB_MODULE) || \
+ defined(CONFIG_GUS_MODULE) || defined(CONFIG_PSS_MODULE) || \
+ defined(CONFIG_GUS16_MODULE) || defined(CONFIG_GUSMAX_MODULE) || \
+ defined(CONFIG_MSS_MODULE) || defined(CONFIG_SSCAPE_MODULE) || \
+ defined(CONFIG_TRIX_MODULE) || defined(CONFIG_MAD16_MODULE) || \
+ defined(CONFIG_CS4232_MODULE) || defined(CONFIG_OPL3SA1_MODULE) || \
+ defined(CONFIG_SOFTOSS_MODULE)
+# define CONFIG_AUDIO
+#endif
+
+#if \
+ defined(CONFIG_PAS) || defined(CONFIG_SB) || \
+ defined(CONFIG_GUS) || defined(CONFIG_MPU401) || \
+ defined(CONFIG_PSS) || defined(CONFIG_GUS16) || \
+ defined(CONFIG_GUSMAX) || defined(CONFIG_SSCAPE) || \
+ defined(CONFIG_TRIX) || defined(CONFIG_MAD16) || \
+ defined(CONFIG_CS4232) || defined(CONFIG_MAUI) || \
+ defined(CONFIG_OPL3SA1) || defined(CONFIG_SOFTOSS) || \
+ defined(CONFIG_PAS_MODULE) || defined(CONFIG_SB_MODULE) || \
+ defined(CONFIG_GUS_MODULE) || defined(CONFIG_MPU401_MODULE) || \
+ defined(CONFIG_PSS_MODULE) || defined(CONFIG_GUS16_MODULE) || \
+ defined(CONFIG_GUSMAX_MODULE) || defined(CONFIG_SSCAPE_MODULE) || \
+ defined(CONFIG_TRIX_MODULE) || defined(CONFIG_MAD16_MODULE) || \
+ defined(CONFIG_CS4232_MODULE) || defined(CONFIG_MAUI_MODULE) || \
+ defined(CONFIG_OPL3SA1_MODULE) || defined(CONFIG_SOFTOSS_MODULE)
+# define CONFIG_MIDI
+#endif
+
+#if \
+ defined(CONFIG_SB) || defined(CONFIG_TRIX) || \
+ defined(CONFIG_MAD16) || \
+ defined(CONFIG_SB_MODULE) || defined(CONFIG_TRIX_MODULE) || \
+ defined(CONFIG_MAD16_MODULE)
+# define CONFIG_SBDSP
+#endif
+#if \
+ defined(CONFIG_SB_MODULE) || defined(CONFIG_TRIX_MODULE) || \
+ defined(CONFIG_MAD16_MODULE)
+# define CONFIG_SBDSP_MODULE
+#endif
+
+#if \
+ defined(CONFIG_SB) || defined(CONFIG_TRIX) || \
+ defined(CONFIG_MAD16) || defined(CONFIG_SB_MODULE) || \
+ defined(CONFIG_TRIX_MODULE) || defined(CONFIG_MAD16_MODULE)
+# define CONFIG_UART401
+#endif
+
+#if \
+ defined(CONFIG_SB_MODULE) || defined(CONFIG_TRIX_MODULE) || \
+ defined(CONFIG_MAD16_MODULE)
+#ifndef CONFIG_UART401_MODULE
+#define CONFIG_UART401_MODULE
+#endif
+#endif
+
+#if \
+ defined(CONFIG_PAS) || defined(CONFIG_SB) || \
+ defined(CONFIG_ADLIB) || defined(CONFIG_GUS) || \
+ defined(CONFIG_MPU401) || defined(CONFIG_PSS) || \
+ defined(CONFIG_SSCAPE) || defined(CONFIG_TRIX) || \
+ defined(CONFIG_MAD16) || defined(CONFIG_CS4232) || \
+ defined(CONFIG_MAUI) || \
+ defined(CONFIG_PAS_MODULE) || defined(CONFIG_SB_MODULE) || \
+ defined(CONFIG_ADLIB_MODULE) || defined(CONFIG_GUS_MODULE) || \
+ defined(CONFIG_MPU401_MODULE) || defined(CONFIG_PSS_MODULE) || \
+ defined(CONFIG_SSCAPE_MODULE) || defined(CONFIG_TRIX_MODULE) || \
+ defined(CONFIG_MAD16_MODULE) || defined(CONFIG_CS4232_MODULE) || \
+ defined(CONFIG_MAUI_MODULE)
+# define CONFIG_SEQUENCER
+#endif
+
+/*
+ * Force on additional support
+ */
+
+#define SM_WAVE
+#define __SGNXPRO__
+#define SM_GAMES
+#define DESKPROXL
diff --git a/drivers/sound/lowlevel/ChangeLog.awe b/drivers/sound/lowlevel/ChangeLog.awe
index 9ee76e4b6..196da64d4 100644
--- a/drivers/sound/lowlevel/ChangeLog.awe
+++ b/drivers/sound/lowlevel/ChangeLog.awe
@@ -1,3 +1,85 @@
+ver.0.4.2c
+ - Add a mode to enable drum channel toggle via bank number
+ change.
+
+ver.0.4.2b
+ - Clear voice position after note on
+ - Change nrvoices according to the current playing mode
+
+ver.0.4.2a
+ - Fix a bug in pitch calculation with scale parameter
+ - Change default chorus & reverb modes
+
+ver.0.4.2
+ - Use indirect voice allocation mode; used as default mode
+ - Add preset mapping
+ - Free buffers when resetting samples
+ - Set default preset/bank/drumset as variable
+ - Fix a bug in exclusive note-off
+ - Add channel reset control macro
+ - Change modwheel sensitivity as variable
+ - Add lock option in open_patch
+ - Add channel priority mode macro, and disable it as default
+ - Add unset effect macro
+ - Add user defined chorus/reverb modes
+ - Do not initialize effect parameters when allocating voices
+ - Accept realtime filter-Q parameter change
+ - Check value range of set/add effects
+ - Change drum flags automatically when receiving bank #128
+
+ver.0.4.1 development versions
+
+ver.0.4.0c
+ - Fix kernel oops when setting AWE_FX_ATTEN
+
+ver.0.4.0b
+ - Do not kill_note in start_note when velocity is zero
+
+ver.0.4.0a
+ - Fix a bug in channel pressure effects
+
+ver.0.4.0
+ - Support dynamic buffer allocation
+ - Add functions to open/close/unload a patch
+ - Change from pointer to integer index in voice/sample lists
+ - Support for Linux/Alpha-AXP
+ - Fix for FreeBSD
+ - Add sostenuto control
+ - Add midi channel priority
+ - Fix a bug in all notes off control
+ - Use AWE_DEFAULT_MEMSIZE always if defined
+ - Fix a bug in awe_reset causes seg fault when no DRAM onboard
+ - Use awe_mem_start variable instead of constant
+
+ver.0.3.3c
+ - Fix IOCTL_TO_USER for OSS-3.8 (on Linux-2.1.25)
+ - Fix i/o macros for mixer controls
+
+ver.0.3.3b
+ - Fix version number in awe_version.h
+ - Fix a small bug in noteoff/relese all
+
+ver.0.3.3a
+ - Fix all notes/sounds off
+ - Add layer effect control
+ - Add misc mode controls; realtime pan, version number, etc.
+ - Move gus bank control in misc mode control
+ - Modify awe_operations for OSS3.8b5
+ - Fix installation script
+
+ver.0.3.3
+ - Add bass/treble control in Emu8000 chip
+ - Add mixer device
+ - Fix sustain on to value 127
+
+ver.0.3.2
+ - Refuse linux-2.0.0 at installation
+ - Move awe_voice.h to /usr/include/linux
+
+ver.0.3.1b (not released)
+ - Rewrite chorus/reverb mode change functions
+ - Rewrite awe_detect & awe_check_dram routines
+
ver.0.3.1a
- Fix a bug to reset voice counter in awe_reset
- Fix voice balance on GUS mode
diff --git a/drivers/sound/lowlevel/Makefile b/drivers/sound/lowlevel/Makefile
index d4687017f..1a4c6a396 100644
--- a/drivers/sound/lowlevel/Makefile
+++ b/drivers/sound/lowlevel/Makefile
@@ -3,18 +3,29 @@ all: lowlevel.o
ALLOBJS = init.o aci.o awe_wave.o aedsp16.o
OBJS = init.o
-ifdef CONFIG_LOWLEVEL_SOUND
-ifdef CONFIG_ACI_MIXER
-OBJS := $(OBJS) aci.o
+ifeq ($(CONFIG_LOWLEVEL_SOUND),y)
+ifeq ($(CONFIG_ACI_MIXER),y)
+ OBJS := $(OBJS) aci.o
endif
-ifdef CONFIG_AWE32_SYNTH
+ifeq ($(CONFIG_AWE32_SYNTH),y)
OBJS := $(OBJS) awe_wave.o
+else
+ ifeq ($(CONFIG_AWE32_SYNTH),m)
+ MX_OBJS := $(MX_OBJS) awe_wave.o
+ endif
endif
-ifdef CONFIG_AEDSP16
-OBJS := $(OBJS) aedsp16.o
+ifeq ($(CONFIG_AEDSP16),y)
+ OBJS := $(OBJS) aedsp16.o
endif
endif
+ifndef TOPDIR
+TOPDIR=/usr/src/linux
+endif
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
lowlevel.o: $(OBJS)
$(LD) -r -o lowlevel.o $(OBJS)
@@ -35,10 +46,16 @@ clean:
dep:
$(CPP) -M $(CFLAGS) -I. *.c > .depend
-ifdef HOSTCC
-include $(TOPDIR)/Rules.make
-else
+ifndef HOSTCC
+#
+# Running outside the kernel build.
+#
+CC = gcc
+HOSTCC = gcc
+CFLAGS = -O2 -D__KERNEL__ -DMODULE -I/usr/src/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -pipe -m486
USE_DEPEND=y
+else
+include $(TOPDIR)/Rules.make
endif
ifdef USE_DEPEND
diff --git a/drivers/sound/lowlevel/README.awe b/drivers/sound/lowlevel/README.awe
index db082bf52..a0281710a 100644
--- a/drivers/sound/lowlevel/README.awe
+++ b/drivers/sound/lowlevel/README.awe
@@ -1,6 +1,6 @@
================================================================
AWE32 Sound Driver for Linux / FreeBSD
- version 0.3.1; Jan. 11, 1997
+ version 0.4.2c; Oct. 7, 1997
================================================================
* GENERAL NOTES
@@ -8,9 +8,9 @@
This is a sound driver extension for SoundBlaster AWE32 and other
compatible cards (AWE32-PnP, SB32, SB32-PnP, AWE64 & etc) to enable
the wave synth operations. The driver is provided for both Linux
-1.2.x and 2.[01].x kernels, and also FreeBSD. Consult the
-installation document for installation on the original sound driver
-package.
+1.2.x and 2.[01].x kernels, and also FreeBSD on Intel x86 and DEC
+Alpha systems. See INSTALL.awe (or INSTALL.fbsd) document for
+installation of the driver package.
This driver was written by Takashi Iwai (iwai@dragon.mm.t.u-tokyo.ac.jp)
who also maintains the code. Please forward any questions, bug fixes
@@ -18,47 +18,80 @@ and suggestions directly to Iwai (_NOT_ to Linus Torvalds or Hannu
Savolainen).
-* CAUTION
-
-- On ver.0.3.1, some zero size array entries are removed from
-awe_voice.h to avoid compile error in some non-ANSI compilers.
-Due to this fix, the size of awe_voice_rec structure is changed from
-older versions. Use a constant AWE_VOICE_REC_SIZE instead of
-sizeof(awe_voice_rec).
-You can still have a compatibility by defining AWE_COMPAT_030=1, but
-this feature will be omitted in the future release.
-
-
* NOTE TO LINUX USERS
To enable this driver on linux-2.[01].x kernels, you need turn on both
"lowlevel drivers support" and "AWE32 synth support" options in sound
menu when configure your linux kernel and modules. For more details,
see the installation document in the original driver package
-(awedrv-0.x.x.tar.gz) available at the web page:
+(awedrv-0.4.2.tar.gz) available at the web page:
http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/
If you're using PnP cards, the card must be initialized before loading
the sound driver. There're several options to do this:
- Initialize the card via ISA PnP tools, and load the sound module.
- Initialize the card on DOS, and load linux by loadlin.exe
- - Use PnP driver (for Linux-2.0.x)
+ - Use PnP driver (for Linux-2.x.x)
See the FAQ list on the URL above.
+* USING THE DRIVER
+
+The GM and GS sounds include multiple instrument layers.
+The current version supports this type of sounds with a special
+extension, but it uses a non-standard way of sequencer calls. Then,
+so far, only drvmidi and playmidi can play the multiple instruments
+and stereo sounds properly as MIDI sequencers.
+
+To load SoundFont files, sfxload utility is required.
+All AWE32 driver and utilities can be downloaded from:
+ http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/
+
+The sfxload is included in the package awesfx-0.4.2.tgz. Binary
+packages are available there, too. See the instruction in each
+package for installation.
+
+Sfxload reads a SoundFont file and transfers it to the sound driver.
+Note that new sfxload no longer requires -i option.
+
+ % sfxload synthgm.sbk
+
+You can tune up the sound via some new options, -A, -a and -d.
+
+ % sfxload -A2 synthgm.sbk
+
+See the manual of sfxload for more details.
+
+Now you can hear midi musics by supported midi players (drvmidi or
+playmidi-2.5).
+
+ % drvmidi foo.mid
+
+If you have only 512kb on the sound card, I recommend to use dynamic
+sample loading via -L option of drvmidi. 2MB GM/GS soundfont file is
+available in most midi files.
+
+ % sfxload synthgm
+ % drvmidi -L 2mbgmgs foo.mid
+
+Enjoy.
+
+
* COMPILE FLAGS
Compile conditions are defined in awe_config.h.
+[Compatibility Conditions]
+The following flags are defined automatically when using installation
+shell script.
+
- AWE_OBSOLETE_VOXWARE (default: not defined)
indicates the system is VoxWare-3.0.x (with linux 1.2.x or
- FreeBSD) if defined. This option will be set automatically when
- you use installation script.
+ FreeBSD) if defined.
- AWE_NEW_KERNEL_INTERFACE (default: not defined)
indicates the system is OSSLite on Linux 2.1.6 or later if
- defined. This option will be set automatically when you use
- installation script.
+ defined.
- HAS_LOWLEVEL_H (default: not defined)
indicates the system has "lowlevel.h" in the sound/lowlevel
@@ -68,27 +101,32 @@ Compile conditions are defined in awe_config.h.
indicates the sound driver has no patch manager function (for
OSS-3.707 (in Linux-2.1.13) or newer).
+- AWE_OSS38 (default: not defined)
+ indicates the sound driver has an additional parameter in
+ operation table (for OSS-3.8b5 in Linux-2.1.25 or newer).
+
+
+[Hardware Conditions]
+You don't have to define the following two values.
+Define them only when the driver couldn't detect the card properly.
+
- AWE_DEFAULT_BASE_ADDR (default: not defined)
specifies the base port address of your AWE32 card.
- Define this only when the driver couldn't detect your card
- properly.
- AWE_DEFAULT_MEM_SIZE (default: not defined)
- specifies the memory size of your AWE32 card by kilo bytes.
- Define this only when the driver couldn't detect memory size
- properly.
+ specifies the memory size of your AWE32 card in kilo bytes.
-- AWE_MAX_SAMPLES (default: 400)
- specifies the maximum number of wave samples.
- The default size is set for the original GM and GS presets from
- CreativeLab. If you have a large set of samples (eg 2MB & 8MB GM
- presets), increase this value to appropriate size.
-
-- AWE_MAX_INFOS (default: 900)
- specifies the maximum number of instruments.
- The default size is set for the original GM and GS presets from
- CreativeLab. If you have a large set of samples (eg 2MB & 8MB GM
- presets), increase this value to appropriate size.
+
+[Sample Table Size]
+From ver.0.4.0, sample tables are allocated dynamically (except
+Linux-1.2.x system), so you need NOT to touch these parameters.
+Linux-1.2.x users may need to increase these values to apropriate size
+if larger DRAM is equipped with the soundcard.
+
+- AWE_MAX_SF_LISTS, AWE_MAX_SAMPLES, AWE_MAX_INFOS
+
+
+[Other Conditions]
- AWE_ALWAYS_INIT_FM (default: not defined)
indicates the AWE driver always initialize FM passthrough even
@@ -99,22 +137,24 @@ Compile conditions are defined in awe_config.h.
- AWE_DEBUG_ON (default: defined)
turns on debuggin messages if defined.
-- AWE_CHECKSUM_DATA (default: defined)
- verifies check sum of sample data with the transferred data if
- defined.
-
-- AWE_CHECKSUM_MEMORY (default: defined)
- Verifies check sum of sample data with the written data on DRAM.
-
- AWE_HAS_GUS_COMPATIBILITY (default: defined)
Enables GUS compatibility mode if defined, reading GUS patches and
GUS control commands. Define this option to use GMOD or other
GUS module players.
-- AWE_ACCEPT_ALL_SOUNDS_CONTROL (default: not defined)
+- AWE_ACCEPT_ALL_SOUNDS_CONTROL (default: defined)
Enables MIDI control #120 and #123 as "all notes off" and "all
sounds off" events, respectively.
+- CONFIG_AWE32_MIXER (default: defined)
+ Adds a mixer device for AWE32 bass/treble equalizer control.
+ You can access this device using /dev/mixer?? (usually mixer01).
+
+- AWE_LOOKUP_MIDI_PRIORIITY (default: defined)
+ Allocates voices according to MIDI channel priority.
+ Drum channels have the highest priorit, followed by #1, #2, and
+ so on.
+
- DEF_FM_CHORUS_DEPTH (default: 0x10)
The default strength to be sent to the chorus effect engine.
From 0 to 0xff. Larger numbers may often cause weird sounds.
@@ -124,43 +164,12 @@ Compile conditions are defined in awe_config.h.
From 0 to 0xff. Larger numbers may often cause weird sounds.
-* USING THE DRIVER
-
-To load SoundFont files, sfxload utility is required.
-All AWE32 driver and utilities can be downloaded from:
- http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/
-
-The GM and GS sounds include multiple instrument layers. The older
-driver couldn't handle multiple instruments. The current version
-supports this type of sounds with a special extension, but so far only
-drvmidi and playmidi can play the multiple instruments and stereo
-sounds.
-
-To play drvmidi, load the SoundFont file directly uing sfxload utility.
-
- % sfxload -i synthgm.sf2
-
-To use other sequencers like musserver, some sounds may become
-inaudible unless converting to SFX file. Follow the instruction in
-awesfx package to make patched GM and GS presets. Then, load the SFX
-file on driver by sfxload utility.
-
- % sfxload -i gm.sfx
-
-Now you can hear midi musics by supported midi players
-(awemidi-0.1.x.tar.gz or patch file to playmidi-2.3).
-
- % drvmidi foo.mid
-
-Enjoy.
-
-
* ACKNOWLEDGMENTS
Thanks to Witold Jachimczyk (witek@xfactor.wpi.edu) for many advices
to programming of AWE32. Many codes are brought from his AWE32-native
MOD player, ALMP.
-The port of awedrv0.1.6 to FreeBSD is done by Randall Hopper
+The port of awedrv to FreeBSD is done by Randall Hopper
(rhh@ct.picker.com).
I also thank linux-awe-ml members for their efforts
to reboot their system many times :-)
@@ -168,9 +177,9 @@ to reboot their system many times :-)
* BUGS & TODO'S
+- Can't detect DRAM size on some card
- More smart patch management
- More smart DRAM memory control
-- Dynamic buffer allocation
- etc, etc, etc.
diff --git a/drivers/sound/lowlevel/aedsp16.c b/drivers/sound/lowlevel/aedsp16.c
index beb1695a6..93db242a1 100644
--- a/drivers/sound/lowlevel/aedsp16.c
+++ b/drivers/sound/lowlevel/aedsp16.c
@@ -250,7 +250,7 @@
#undef AEDSP16_INFO 1 /* Define this to enable info code */
#if defined(AEDSP16_DEBUG)
-# define DBG(x) printk x
+# define DBG(x) printk x
# if defined(AEDSP16_DEBUG_MORE)
# define DBG1(x) printk x
# else
diff --git a/drivers/sound/lowlevel/awe_compat.h b/drivers/sound/lowlevel/awe_compat.h
new file mode 100644
index 000000000..d0c34a1fa
--- /dev/null
+++ b/drivers/sound/lowlevel/awe_compat.h
@@ -0,0 +1,190 @@
+/*----------------------------------------------------------------
+ * compatibility macros for AWE32 driver
+ *----------------------------------------------------------------*/
+
+/* redefine following macros */
+#undef IOCTL_IN
+#undef IOCTL_OUT
+#undef OUTW
+#undef COPY_FROM_USER
+#undef COPY_TO_USER
+#undef GET_BYTE_FROM_USER
+#undef GET_SHORT_FROM_USER
+#undef IOCTL_TO_USER
+
+#ifdef linux
+
+/*================================================================
+ * Linux macros
+ *================================================================*/
+
+/* use inline prefix */
+#define INLINE inline
+
+/*----------------------------------------------------------------
+ * memory management for linux
+ *----------------------------------------------------------------*/
+
+#ifdef AWE_OBSOLETE_VOXWARE
+/* old type linux system */
+
+/* i/o requests; nothing */
+#define awe_check_port() 0 /* always false */
+#define awe_request_region() /* nothing */
+#define awe_release_region() /* nothing */
+
+static int _mem_start; /* memory pointer for permanent buffers */
+
+#define my_malloc_init(memptr) _mem_start = (memptr)
+#define my_malloc_memptr() _mem_start
+#define my_free(ptr) /* do nothing */
+#define my_realloc(buf,oldsize,size) NULL /* no realloc */
+
+static void *my_malloc(int size)
+{
+ char *ptr;
+ PERMANENT_MALLOC(ptr, char*, size, _mem_start);
+ return (void*)ptr;
+}
+
+/* allocate buffer only once */
+#define INIT_TABLE(buffer,index,nums,type) {\
+buffer = my_malloc(sizeof(type) * (nums)); index = (nums);\
+}
+
+#else
+
+#define AWE_DYNAMIC_BUFFER
+
+#define my_malloc_init(ptr) /* nothing */
+#define my_malloc_memptr() 0
+#define my_malloc(size) vmalloc(size)
+#define my_free(ptr) if (ptr) {vfree(ptr);}
+
+static void *my_realloc(void *buf, int oldsize, int size)
+{
+ void *ptr;
+ if ((ptr = vmalloc(size)) == NULL)
+ return NULL;
+ memcpy(ptr, buf, ((oldsize < size) ? oldsize : size) );
+ vfree(buf);
+ return ptr;
+}
+
+/* do not allocate buffer at beginning */
+#define INIT_TABLE(buffer,index,nums,type) {buffer=NULL; index=0;}
+
+/* old type macro */
+#define RET_ERROR(err) -err
+
+#endif
+
+/*----------------------------------------------------------------
+ * i/o interfaces for linux
+ *----------------------------------------------------------------*/
+
+#define OUTW(data,addr) outw(data, addr)
+
+#ifdef AWE_NEW_KERNEL_INTERFACE
+#define COPY_FROM_USER(target,source,offs,count) \
+ copy_from_user(target, (source)+(offs), count)
+#define GET_BYTE_FROM_USER(target,addr,offs) \
+ get_user(target, (unsigned char*)&((addr)[offs]))
+#define GET_SHORT_FROM_USER(target,addr,offs) \
+ get_user(target, (unsigned short*)&((addr)[offs]))
+#ifdef AWE_OSS38
+#define IOCTL_TO_USER(target,offs,source,count) \
+ memcpy(target, (source)+(offs), count)
+#define IO_WRITE_CHECK(cmd) (_SIOC_DIR(cmd) & _IOC_WRITE)
+#else
+#define IOCTL_TO_USER(target,offs,source,count) \
+ copy_to_user(target, (source)+(offs), count)
+#define IO_WRITE_CHECK(cmd) (_IOC_DIR(cmd) & _IOC_WRITE)
+#endif /* AWE_OSS38 */
+#define COPY_TO_USER IOCTL_TO_USER
+#define IOCTL_IN(arg) (*(int*)(arg))
+#define IOCTL_OUT(arg,val) (*(int*)(arg) = (val))
+
+#else /* old type i/o */
+#define COPY_FROM_USER(target,source,offs,count) \
+ memcpy_fromfs(target, (source)+(offs), (count))
+#define GET_BYTE_FROM_USER(target,addr,offs) \
+ *((char *)&(target)) = get_fs_byte((addr)+(offs))
+#define GET_SHORT_FROM_USER(target,addr,offs) \
+ *((short *)&(target)) = get_fs_word((addr)+(offs))
+#define IOCTL_TO_USER(target,offs,source,count) \
+ memcpy_tofs(target, (source)+(offs), (count))
+#define COPY_TO_USER IOCTL_TO_USER
+#define IO_WRITE_CHECK(cmd) (cmd & IOC_IN)
+#define IOCTL_IN(arg) get_fs_long((long *)(arg))
+#define IOCTL_OUT(arg,ret) snd_ioctl_return((int *)arg, ret)
+
+#endif /* AWE_NEW_KERNEL_INTERFACE */
+
+#define BZERO(target,len) memset(target, 0, len)
+#define MEMCPY(dst,src,len) memcpy(dst, src, len)
+
+
+#elif defined(__FreeBSD__)
+
+/*================================================================
+ * FreeBSD macros
+ *================================================================*/
+
+/* inline is not checked yet.. maybe it'll work */
+#define INLINE /*inline*/
+
+/*----------------------------------------------------------------
+ * memory management for freebsd
+ *----------------------------------------------------------------*/
+
+/* i/o requests; nothing */
+#define awe_check_port() 0 /* always false */
+#define awe_request_region() /* nothing */
+#define awe_release_region() /* nothing */
+
+#define AWE_DYNAMIC_BUFFER
+
+#define my_malloc_init(ptr) /* nothing */
+#define my_malloc_memptr() 0
+#define my_malloc(size) malloc(size, M_TEMP, M_WAITOK)
+#define my_free(ptr) if (ptr) {free(ptr, M_TEMP);}
+
+#define INIT_TABLE(buffer,index,nums,type) {buffer=NULL; index=0;}
+
+/* it should be realloc? */
+static void *my_realloc(void *buf, int oldsize, int size)
+{
+ void *ptr;
+ if ((ptr = my_malloc(size)) == NULL)
+ return NULL;
+ memcpy(ptr, buf, ((oldsize < size) ? oldsize : size) );
+ my_free(buf);
+ return ptr;
+}
+
+/*----------------------------------------------------------------
+ * i/o interfaces for freebsd
+ *----------------------------------------------------------------*/
+
+/* according to linux rule; the arguments are swapped */
+#define OUTW(data,addr) outw(addr, data)
+
+#define COPY_FROM_USER(target,source,offs,count) \
+ uiomove(((caddr_t)(target)),(count),((struct uio *)(source)))
+#define COPY_TO_USER(target,source,offs,count) \
+ uiomove(((caddr_t)(source)),(count),((struct uio *)(target)))
+#define GET_BYTE_FROM_USER(target,addr,offs) \
+ uiomove(((char*)&(target)), 1, ((struct uio *)(addr)))
+#define GET_SHORT_FROM_USER(target,addr,offs) \
+ uiomove(((char*)&(target)), 2, ((struct uio *)(addr)))
+#define IOCTL_TO_USER(target,offs,source,count) \
+ memcpy(&((target)[offs]), (source), (count))
+#define IO_WRITE_CHECK(cmd) (cmd & IOC_IN)
+#define IOCTL_IN(arg) (*(int*)(arg))
+#define IOCTL_OUT(arg,val) (*(int*)(arg) = (val))
+#define BZERO(target,len) bzero((caddr_t)target, len)
+#define MEMCPY(dst,src,len) bcopy((caddr_t)src, (caddr_t)dst, len)
+
+#endif
+
diff --git a/drivers/sound/lowlevel/awe_config.h b/drivers/sound/lowlevel/awe_config.h
index c80eb3f35..c6b0dae6d 100644
--- a/drivers/sound/lowlevel/awe_config.h
+++ b/drivers/sound/lowlevel/awe_config.h
@@ -2,9 +2,9 @@
* sound/awe_config.h
*
* Configuration of AWE32 sound driver
- * version 0.2.99e; Dec. 10, 1997
+ * version 0.4.2; Sep. 15, 1997
*
- * Copyright (C) 1996,1997 Takashi Iwai
+ * Copyright (C) 1996 Takashi Iwai
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -52,6 +52,10 @@
*/
#define AWE_NO_PATCHMGR
+/* if your system has an additional parameter (OSS 3.8b5 or newer),
+ * define this.
+ */
+#define AWE_OSS38
/*----------------------------------------------------------------
* AWE32 card configuration:
@@ -64,13 +68,20 @@
/*----------------------------------------------------------------
- * maximum size of sample table:
- * the followings are for ROM GM and 512k GS samples. if your have
- * additional DRAM and SoundFonts, increase these values.
+ * maximum size of soundfont list table:
+ * you usually don't need to touch this value.
+ *----------------------------------------------------------------*/
+
+#define AWE_MAX_SF_LISTS 16
+
+
+/*----------------------------------------------------------------
+ * chunk size of sample and voice tables:
+ * you usually don't need to touch these values.
*----------------------------------------------------------------*/
#define AWE_MAX_SAMPLES 400
-#define AWE_MAX_INFOS 1500
+#define AWE_MAX_INFOS 800
/*----------------------------------------------------------------
@@ -92,24 +103,17 @@
/* debug on */
#define AWE_DEBUG_ON
-/* verify checksum for uploading samples */
-#define AWE_CHECKSUM_DATA
-#define AWE_CHECKSUM_MEMORY
-
/* GUS compatible mode */
#define AWE_HAS_GUS_COMPATIBILITY
/* accept all notes/sounds off controls */
-#undef AWE_ACCEPT_ALL_SOUNDS_CONTROL
-
+#define AWE_ACCEPT_ALL_SOUNDS_CONTROL
-#ifdef linux
-/* i tested this only on my linux */
-#define INLINE __inline__
-#else
-#define INLINE /**/
-#endif
+/* add mixer control of emu8000 equalizer */
+#define CONFIG_AWE32_MIXER
+/* look up voices according to MIDI channel priority */
+#define AWE_LOOKUP_MIDI_PRIORITY
/*----------------------------------------------------------------*/
diff --git a/drivers/sound/lowlevel/awe_hw.h b/drivers/sound/lowlevel/awe_hw.h
index 6aa151fc1..7d0d88e77 100644
--- a/drivers/sound/lowlevel/awe_hw.h
+++ b/drivers/sound/lowlevel/awe_hw.h
@@ -3,7 +3,7 @@
*
* Access routines and definitions for the low level driver for the
* AWE32/Sound Blaster 32 wave table synth.
- * version 0.3.1; Jan. 21, 1997
+ * version 0.4.2; Sep. 15, 1997
*
* Copyright (C) 1996,1997 Takashi Iwai
*
@@ -89,10 +89,12 @@
#define AWE_NORMAL_VOICES 30 /*30&31 are reserved for DRAM refresh*/
#define AWE_MAX_CHANNELS 32 /* max midi channels (must >= voices) */
+#define AWE_MAX_LAYERS AWE_MAX_VOICES /* maximum number of multiple layers */
#define AWE_DRAM_OFFSET 0x200000
#define AWE_MAX_DRAM_SIZE (28 * 1024) /* 28 MB is max onboard memory */
#define AWE_DEFAULT_ATTENUATION 32 /* 12dB below */
+#define AWE_DEFAULT_MOD_SENSE 18
#endif
diff --git a/drivers/sound/lowlevel/awe_version.h b/drivers/sound/lowlevel/awe_version.h
new file mode 100644
index 000000000..a579b09ef
--- /dev/null
+++ b/drivers/sound/lowlevel/awe_version.h
@@ -0,0 +1,24 @@
+/* AWE32 driver version number */
+
+#ifndef AWE_VERSION_H_DEF
+#define AWE_VERSION_H_DEF
+
+#define AWE_VERSION_NUMBER 0x00040203
+#define AWEDRV_VERSION "0.4.2c"
+#define AWE_MAJOR_VERSION(id) (((id) >> 16) & 0xff)
+#define AWE_MINOR_VERSION(id) (((id) >> 8) & 0xff)
+#define AWE_TINY_VERSION(id) ((id) & 0xff)
+
+#endif
+/* AWE32 driver version number */
+
+#ifndef AWE_VERSION_H_DEF
+#define AWE_VERSION_H_DEF
+
+#define AWE_VERSION_NUMBER 0x00040202
+#define AWEDRV_VERSION "0.4.2b"
+#define AWE_MAJOR_VERSION(id) (((id) >> 16) & 0xff)
+#define AWE_MINOR_VERSION(id) (((id) >> 8) & 0xff)
+#define AWE_TINY_VERSION(id) ((id) & 0xff)
+
+#endif
diff --git a/drivers/sound/lowlevel/awe_voice.h b/drivers/sound/lowlevel/awe_voice.h
new file mode 100644
index 000000000..aa1313131
--- /dev/null
+++ b/drivers/sound/lowlevel/awe_voice.h
@@ -0,0 +1,490 @@
+/*
+ * sound/awe_voice.h
+ *
+ * Voice information definitions for the low level driver for the
+ * AWE32/Sound Blaster 32 wave table synth.
+ * version 0.4.2c; Oct. 7, 1997
+ *
+ * Copyright (C) 1996,1997 Takashi Iwai
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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.
+ */
+
+#ifndef AWE_VOICE_H
+#define AWE_VOICE_H
+
+#ifndef SAMPLE_TYPE_AWE32
+#define SAMPLE_TYPE_AWE32 0x20
+#endif
+
+#ifndef _PATCHKEY
+#define _PATCHKEY(id) ((id<<8)|0xfd)
+#endif
+
+/*----------------------------------------------------------------
+ * patch information record
+ *----------------------------------------------------------------*/
+
+/* patch interface header: 16 bytes */
+typedef struct awe_patch_info {
+ short key; /* use AWE_PATCH here */
+#define AWE_PATCH _PATCHKEY(0x07)
+
+ short device_no; /* synthesizer number */
+ unsigned short sf_id; /* file id (should be zero) */
+ short optarg; /* optional argument */
+ int len; /* data length (without this header) */
+
+ short type; /* patch operation type */
+#define AWE_LOAD_INFO 0 /* awe_voice_rec */
+#define AWE_LOAD_DATA 1 /* awe_sample_info */
+#define AWE_OPEN_PATCH 2 /* awe_open_parm */
+#define AWE_CLOSE_PATCH 3 /* none */
+#define AWE_UNLOAD_PATCH 4 /* none */
+#define AWE_REPLACE_DATA 5 /* awe_sample_info (optarg=#channels)*/
+#define AWE_MAP_PRESET 6 /* awe_voice_map */
+#define AWE_LOAD_CHORUS_FX 0x10 /* awe_chorus_fx_rec (optarg=mode) */
+#define AWE_LOAD_REVERB_FX 0x11 /* awe_reverb_fx_rec (optarg=mode) */
+
+ short reserved; /* word alignment data */
+
+ /* the actual patch data begins after this */
+#if defined(AWE_COMPAT_030) && AWE_COMPAT_030
+ char data[0];
+#endif
+} awe_patch_info;
+
+/*#define AWE_PATCH_INFO_SIZE 16*/
+#define AWE_PATCH_INFO_SIZE sizeof(awe_patch_info)
+
+
+/*----------------------------------------------------------------
+ * open patch
+ *----------------------------------------------------------------*/
+
+#define AWE_PATCH_NAME_LEN 32
+
+typedef struct _awe_open_parm {
+ unsigned short type; /* sample type */
+#define AWE_PAT_TYPE_MISC 0
+#define AWE_PAT_TYPE_GM 1
+#define AWE_PAT_TYPE_GS 2
+#define AWE_PAT_TYPE_MT32 3
+#define AWE_PAT_TYPE_XG 4
+#define AWE_PAT_TYPE_SFX 5
+#define AWE_PAT_TYPE_GUS 6
+#define AWE_PAT_TYPE_MAP 7
+
+#define AWE_PAT_LOCKED 0x100 /* lock the samples */
+
+ short reserved;
+ char name[AWE_PATCH_NAME_LEN];
+} awe_open_parm;
+
+/*#define AWE_OPEN_PARM_SIZE 28*/
+#define AWE_OPEN_PARM_SIZE sizeof(awe_open_parm)
+
+
+/*----------------------------------------------------------------
+ * raw voice information record
+ *----------------------------------------------------------------*/
+
+/* wave table envelope & effect parameters to control EMU8000 */
+typedef struct _awe_voice_parm {
+ unsigned short moddelay; /* modulation delay (0x8000) */
+ unsigned short modatkhld; /* modulation attack & hold time (0x7f7f) */
+ unsigned short moddcysus; /* modulation decay & sustain (0x7f7f) */
+ unsigned short modrelease; /* modulation release time (0x807f) */
+ short modkeyhold, modkeydecay; /* envelope change per key (not used) */
+ unsigned short voldelay; /* volume delay (0x8000) */
+ unsigned short volatkhld; /* volume attack & hold time (0x7f7f) */
+ unsigned short voldcysus; /* volume decay & sustain (0x7f7f) */
+ unsigned short volrelease; /* volume release time (0x807f) */
+ short volkeyhold, volkeydecay; /* envelope change per key (not used) */
+ unsigned short lfo1delay; /* LFO1 delay (0x8000) */
+ unsigned short lfo2delay; /* LFO2 delay (0x8000) */
+ unsigned short pefe; /* modulation pitch & cutoff (0x0000) */
+ unsigned short fmmod; /* LFO1 pitch & cutoff (0x0000) */
+ unsigned short tremfrq; /* LFO1 volume & freq (0x0000) */
+ unsigned short fm2frq2; /* LFO2 pitch & freq (0x0000) */
+ unsigned char cutoff; /* initial cutoff (0xff) */
+ unsigned char filterQ; /* initial filter Q [0-15] (0x0) */
+ unsigned char chorus; /* chorus send (0x00) */
+ unsigned char reverb; /* reverb send (0x00) */
+ unsigned short reserved[4]; /* not used */
+} awe_voice_parm;
+
+#define AWE_VOICE_PARM_SIZE 48
+
+
+/* wave table parameters: 92 bytes */
+typedef struct _awe_voice_info {
+ unsigned short sf_id; /* file id (should be zero) */
+ unsigned short sample; /* sample id */
+ int start, end; /* sample offset correction */
+ int loopstart, loopend; /* loop offset correction */
+ short rate_offset; /* sample rate pitch offset */
+ unsigned short mode; /* sample mode */
+#define AWE_MODE_ROMSOUND 0x8000
+#define AWE_MODE_STEREO 1
+#define AWE_MODE_LOOPING 2
+#define AWE_MODE_NORELEASE 4 /* obsolete */
+#define AWE_MODE_INIT_PARM 8
+
+ short root; /* midi root key */
+ short tune; /* pitch tuning (in cents) */
+ char low, high; /* key note range */
+ char vellow, velhigh; /* velocity range */
+ char fixkey, fixvel; /* fixed key, velocity */
+ char pan, fixpan; /* panning, fixed panning */
+ short exclusiveClass; /* exclusive class (0 = none) */
+ unsigned char amplitude; /* sample volume (127 max) */
+ unsigned char attenuation; /* attenuation (0.375dB) */
+ short scaleTuning; /* pitch scale tuning(%), normally 100 */
+ awe_voice_parm parm; /* voice envelope parameters */
+ short index; /* internal index (set by driver) */
+} awe_voice_info;
+
+/*#define AWE_VOICE_INFO_SIZE 92*/
+#define AWE_VOICE_INFO_SIZE sizeof(awe_voice_info)
+
+/*----------------------------------------------------------------*/
+
+/* The info entry of awe_voice_rec is changed from 0 to 1
+ * for some compilers refusing zero size array.
+ * Due to this change, sizeof(awe_voice_rec) becomes different
+ * from older versions.
+ * Use AWE_VOICE_REC_SIZE instead.
+ */
+
+/* instrument info header: 4 bytes */
+typedef struct _awe_voice_rec_hdr {
+ unsigned char bank; /* midi bank number */
+ unsigned char instr; /* midi preset number */
+ char nvoices; /* number of voices */
+ char write_mode; /* write mode; normally 0 */
+#define AWE_WR_APPEND 0 /* append anyway */
+#define AWE_WR_EXCLUSIVE 1 /* skip if already exists */
+#define AWE_WR_REPLACE 2 /* replace if already exists */
+} awe_voice_rec_hdr;
+
+/*#define AWE_VOICE_REC_SIZE 4*/
+#define AWE_VOICE_REC_SIZE sizeof(awe_voice_rec_hdr)
+
+/* the standard patch structure for one sample */
+typedef struct _awe_voice_rec_patch {
+ awe_patch_info patch;
+ awe_voice_rec_hdr hdr;
+ awe_voice_info info;
+} awe_voice_rec_patch;
+
+
+/* obsolete data type */
+#if defined(AWE_COMPAT_030) && AWE_COMPAT_030
+#define AWE_INFOARRAY_SIZE 0
+#else
+#define AWE_INFOARRAY_SIZE 1
+#endif
+
+typedef struct _awe_voice_rec {
+ unsigned char bank; /* midi bank number */
+ unsigned char instr; /* midi preset number */
+ short nvoices; /* number of voices */
+ /* voice information follows here */
+ awe_voice_info info[AWE_INFOARRAY_SIZE];
+} awe_voice_rec;
+
+
+/*----------------------------------------------------------------
+ * sample wave information
+ *----------------------------------------------------------------*/
+
+/* wave table sample header: 32 bytes */
+typedef struct awe_sample_info {
+ unsigned short sf_id; /* file id (should be zero) */
+ unsigned short sample; /* sample id */
+ int start, end; /* start & end offset */
+ int loopstart, loopend; /* loop start & end offset */
+ int size; /* size (0 = ROM) */
+ short checksum_flag; /* use check sum = 1 */
+ unsigned short mode_flags; /* mode flags */
+#define AWE_SAMPLE_8BITS 1 /* wave data is 8bits */
+#define AWE_SAMPLE_UNSIGNED 2 /* wave data is unsigned */
+#define AWE_SAMPLE_NO_BLANK 4 /* no blank loop is attached */
+#define AWE_SAMPLE_SINGLESHOT 8 /* single-shot w/o loop */
+#define AWE_SAMPLE_BIDIR_LOOP 16 /* bidirectional looping */
+#define AWE_SAMPLE_STEREO_LEFT 32 /* stereo left sound */
+#define AWE_SAMPLE_STEREO_RIGHT 64 /* stereo right sound */
+#define AWE_SAMPLE_REVERSE_LOOP 128 /* reverse looping */
+ unsigned int checksum; /* check sum */
+#if defined(AWE_COMPAT_030) && AWE_COMPAT_030
+ unsigned short data[0]; /* sample data follows here */
+#endif
+} awe_sample_info;
+
+/*#define AWE_SAMPLE_INFO_SIZE 32*/
+#define AWE_SAMPLE_INFO_SIZE sizeof(awe_sample_info)
+
+
+/*----------------------------------------------------------------
+ * voice preset mapping
+ *----------------------------------------------------------------*/
+
+typedef struct awe_voice_map {
+ int map_bank, map_instr, map_key; /* key = -1 means all keys */
+ int src_bank, src_instr, src_key;
+} awe_voice_map;
+
+#define AWE_VOICE_MAP_SIZE sizeof(awe_voice_map)
+
+
+/*----------------------------------------------------------------
+ * awe hardware controls
+ *----------------------------------------------------------------*/
+
+#define _AWE_DEBUG_MODE 0x00
+#define _AWE_REVERB_MODE 0x01
+#define _AWE_CHORUS_MODE 0x02
+#define _AWE_REMOVE_LAST_SAMPLES 0x03
+#define _AWE_INITIALIZE_CHIP 0x04
+#define _AWE_SEND_EFFECT 0x05
+#define _AWE_TERMINATE_CHANNEL 0x06
+#define _AWE_TERMINATE_ALL 0x07
+#define _AWE_INITIAL_VOLUME 0x08
+#define _AWE_INITIAL_ATTEN _AWE_INITIAL_VOLUME
+#define _AWE_RESET_CHANNEL 0x09
+#define _AWE_CHANNEL_MODE 0x0a
+#define _AWE_DRUM_CHANNELS 0x0b
+#define _AWE_MISC_MODE 0x0c
+#define _AWE_RELEASE_ALL 0x0d
+#define _AWE_NOTEOFF_ALL 0x0e
+#define _AWE_CHN_PRESSURE 0x0f
+/*#define _AWE_GET_CURRENT_MODE 0x10*/
+#define _AWE_EQUALIZER 0x11
+/*#define _AWE_GET_MISC_MODE 0x12*/
+/*#define _AWE_GET_FONTINFO 0x13*/
+
+#define _AWE_MODE_FLAG 0x80
+#define _AWE_COOKED_FLAG 0x40 /* not supported */
+#define _AWE_MODE_VALUE_MASK 0x3F
+
+/*----------------------------------------------------------------*/
+
+#define _AWE_SET_CMD(p,dev,voice,cmd,p1,p2) \
+{((char*)(p))[0] = SEQ_PRIVATE;\
+ ((char*)(p))[1] = dev;\
+ ((char*)(p))[2] = _AWE_MODE_FLAG|(cmd);\
+ ((char*)(p))[3] = voice;\
+ ((unsigned short*)(p))[2] = p1;\
+ ((unsigned short*)(p))[3] = p2;}
+
+/* buffered access */
+#define _AWE_CMD(dev, voice, cmd, p1, p2) \
+{_SEQ_NEEDBUF(8);\
+ _AWE_SET_CMD(_seqbuf + _seqbufptr, dev, voice, cmd, p1, p2);\
+ _SEQ_ADVBUF(8);}
+
+/* direct access */
+#define _AWE_CMD_NOW(seqfd,dev,voice,cmd,p1,p2) \
+{struct seq_event_rec tmp;\
+ _AWE_SET_CMD(&tmp, dev, voice, cmd, p1, p2);\
+ ioctl(seqfd, SNDCTL_SEQ_OUTOFBAND, &tmp);}
+
+/*----------------------------------------------------------------*/
+
+/* set debugging mode */
+#define AWE_DEBUG_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_DEBUG_MODE, p1, 0)
+/* set reverb mode; from 0 to 7 */
+#define AWE_REVERB_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_REVERB_MODE, p1, 0)
+/* set chorus mode; from 0 to 7 */
+#define AWE_CHORUS_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_CHORUS_MODE, p1, 0)
+
+/* reset channel */
+#define AWE_RESET_CHANNEL(dev,ch) _AWE_CMD(dev, ch, _AWE_RESET_CHANNEL, 0, 0)
+#define AWE_RESET_CONTROL(dev,ch) _AWE_CMD(dev, ch, _AWE_RESET_CHANNEL, 1, 0)
+
+/* send an effect to all layers */
+#define AWE_SEND_EFFECT(dev,voice,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,type,value)
+#define AWE_ADD_EFFECT(dev,voice,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((type)|0x80),value)
+#define AWE_UNSET_EFFECT(dev,voice,type) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((type)|0x40),0)
+/* send an effect to a layer */
+#define AWE_SEND_LAYER_EFFECT(dev,voice,layer,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)),value)
+#define AWE_ADD_LAYER_EFFECT(dev,voice,layer,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)|0x80),value)
+#define AWE_UNSET_LAYER_EFFECT(dev,voice,layer,type) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)|0x40),0)
+
+/* terminate sound on the channel/voice */
+#define AWE_TERMINATE_CHANNEL(dev,voice) _AWE_CMD(dev,voice,_AWE_TERMINATE_CHANNEL,0,0)
+/* terminate all sounds */
+#define AWE_TERMINATE_ALL(dev) _AWE_CMD(dev, 0, _AWE_TERMINATE_ALL, 0, 0)
+/* release all sounds (w/o sustain effect) */
+#define AWE_RELEASE_ALL(dev) _AWE_CMD(dev, 0, _AWE_RELEASE_ALL, 0, 0)
+/* note off all sounds (w sustain effect) */
+#define AWE_NOTEOFF_ALL(dev) _AWE_CMD(dev, 0, _AWE_NOTEOFF_ALL, 0, 0)
+
+/* set initial attenuation */
+#define AWE_INITIAL_VOLUME(dev,atten) _AWE_CMD(dev, 0, _AWE_INITIAL_VOLUME, atten, 0)
+#define AWE_INITIAL_ATTEN AWE_INITIAL_VOLUME
+/* relative attenuation */
+#define AWE_SET_ATTEN(dev,atten) _AWE_CMD(dev, 0, _AWE_INITIAL_VOLUME, atten, 1)
+
+/* set channel playing mode; mode=0/1/2 */
+#define AWE_SET_CHANNEL_MODE(dev,mode) _AWE_CMD(dev, 0, _AWE_CHANNEL_MODE, mode, 0)
+#define AWE_PLAY_INDIRECT 0 /* indirect voice mode (default) */
+#define AWE_PLAY_MULTI 1 /* multi note voice mode */
+#define AWE_PLAY_DIRECT 2 /* direct single voice mode */
+#define AWE_PLAY_MULTI2 3 /* sequencer2 mode; used internally */
+
+/* set drum channel mask; channels is 32bit long value */
+#define AWE_DRUM_CHANNELS(dev,channels) _AWE_CMD(dev, 0, _AWE_DRUM_CHANNELS, ((channels) & 0xffff), ((channels) >> 16))
+
+/* set bass and treble control; values are from 0 to 11 */
+#define AWE_EQUALIZER(dev,bass,treble) _AWE_CMD(dev, 0, _AWE_EQUALIZER, bass, treble)
+
+/* remove last loaded samples */
+#define AWE_REMOVE_LAST_SAMPLES(seqfd,dev) _AWE_CMD_NOW(seqfd, dev, 0, _AWE_REMOVE_LAST_SAMPLES, 0, 0)
+/* initialize emu8000 chip */
+#define AWE_INITIALIZE_CHIP(seqfd,dev) _AWE_CMD_NOW(seqfd, dev, 0, _AWE_INITIALIZE_CHIP, 0, 0)
+
+/* set miscellaneous modes; meta command */
+#define AWE_MISC_MODE(dev,mode,value) _AWE_CMD(dev, 0, _AWE_MISC_MODE, mode, value)
+/* exclusive sound off; 1=off */
+#define AWE_EXCLUSIVE_SOUND(dev,mode) AWE_MISC_MODE(dev,AWE_MD_EXCLUSIVE_SOUND,mode)
+/* default GUS bank number */
+#define AWE_SET_GUS_BANK(dev,bank) AWE_MISC_MODE(dev,AWE_MD_GUS_BANK,bank)
+/* change panning position in realtime; 0=don't 1=do */
+#define AWE_REALTIME_PAN(dev,mode) AWE_MISC_MODE(dev,AWE_MD_REALTIME_PAN,mode)
+
+/* extended pressure controls; not portable with other sound drivers */
+#define AWE_KEY_PRESSURE(dev,ch,note,vel) SEQ_START_NOTE(dev,ch,(note)+128,vel)
+#define AWE_CHN_PRESSURE(dev,ch,vel) _AWE_CMD(dev,ch,_AWE_CHN_PRESSURE,vel,0)
+
+/*----------------------------------------------------------------*/
+
+/* reverb mode parameters */
+#define AWE_REVERB_ROOM1 0
+#define AWE_REVERB_ROOM2 1
+#define AWE_REVERB_ROOM3 2
+#define AWE_REVERB_HALL1 3
+#define AWE_REVERB_HALL2 4
+#define AWE_REVERB_PLATE 5
+#define AWE_REVERB_DELAY 6
+#define AWE_REVERB_PANNINGDELAY 7
+#define AWE_REVERB_PREDEFINED 8
+/* user can define reverb modes up to 32 */
+#define AWE_REVERB_NUMBERS 32
+
+typedef struct awe_reverb_fx_rec {
+ unsigned short parms[28];
+} awe_reverb_fx_rec;
+
+/*----------------------------------------------------------------*/
+
+/* chorus mode parameters */
+#define AWE_CHORUS_1 0
+#define AWE_CHORUS_2 1
+#define AWE_CHORUS_3 2
+#define AWE_CHORUS_4 3
+#define AWE_CHORUS_FEEDBACK 4
+#define AWE_CHORUS_FLANGER 5
+#define AWE_CHORUS_SHORTDELAY 6
+#define AWE_CHORUS_SHORTDELAY2 7
+#define AWE_CHORUS_PREDEFINED 8
+/* user can define chorus modes up to 32 */
+#define AWE_CHORUS_NUMBERS 32
+
+typedef struct awe_chorus_fx_rec {
+ unsigned short feedback; /* feedback level (0xE600-0xE6FF) */
+ unsigned short delay_offset; /* delay (0-0x0DA3) [1/44100 sec] */
+ unsigned short lfo_depth; /* LFO depth (0xBC00-0xBCFF) */
+ unsigned int delay; /* right delay (0-0xFFFFFFFF) [1/256/44100 sec] */
+ unsigned int lfo_freq; /* LFO freq LFO freq (0-0xFFFFFFFF) */
+} awe_chorus_fx_rec;
+
+/*----------------------------------------------------------------*/
+
+/* misc mode types */
+enum {
+/* 0*/ AWE_MD_EXCLUSIVE_OFF, /* obsolete */
+/* 1*/ AWE_MD_EXCLUSIVE_ON, /* obsolete */
+/* 2*/ AWE_MD_VERSION, /* read only */
+/* 3*/ AWE_MD_EXCLUSIVE_SOUND, /* ignored */
+/* 4*/ AWE_MD_REALTIME_PAN, /* 0/1: do realtime pan change (default=1) */
+/* 5*/ AWE_MD_GUS_BANK, /* bank number for GUS patches (default=0) */
+/* 6*/ AWE_MD_KEEP_EFFECT, /* 0/1: keep effect values, (default=0) */
+/* 7*/ AWE_MD_ZERO_ATTEN, /* attenuation of max volume (default=32) */
+/* 8*/ AWE_MD_CHN_PRIOR, /* 0/1: set MIDI channel priority mode (default=1) */
+/* 9*/ AWE_MD_MOD_SENSE, /* integer: modwheel sensitivity (def=18) */
+/*10*/ AWE_MD_DEF_PRESET, /* integer: default preset number (def=0) */
+/*11*/ AWE_MD_DEF_BANK, /* integer: default bank number (def=0) */
+/*12*/ AWE_MD_DEF_DRUM, /* integer: default drumset number (def=0) */
+/*13*/ AWE_MD_TOGGLE_DRUM_BANK, /* 0/1: toggle drum flag with bank# (def=0) */
+ AWE_MD_END,
+};
+
+/*----------------------------------------------------------------*/
+
+/* effect parameters */
+enum {
+
+/* modulation envelope parameters */
+/* 0*/ AWE_FX_ENV1_DELAY, /* WORD: ENVVAL */
+/* 1*/ AWE_FX_ENV1_ATTACK, /* BYTE: up ATKHLD */
+/* 2*/ AWE_FX_ENV1_HOLD, /* BYTE: lw ATKHLD */
+/* 3*/ AWE_FX_ENV1_DECAY, /* BYTE: lw DCYSUS */
+/* 4*/ AWE_FX_ENV1_RELEASE, /* BYTE: lw DCYSUS */
+/* 5*/ AWE_FX_ENV1_SUSTAIN, /* BYTE: up DCYSUS */
+/* 6*/ AWE_FX_ENV1_PITCH, /* BYTE: up PEFE */
+/* 7*/ AWE_FX_ENV1_CUTOFF, /* BYTE: lw PEFE */
+
+/* volume envelope parameters */
+/* 8*/ AWE_FX_ENV2_DELAY, /* WORD: ENVVOL */
+/* 9*/ AWE_FX_ENV2_ATTACK, /* BYTE: up ATKHLDV */
+/*10*/ AWE_FX_ENV2_HOLD, /* BYTE: lw ATKHLDV */
+/*11*/ AWE_FX_ENV2_DECAY, /* BYTE: lw DCYSUSV */
+/*12*/ AWE_FX_ENV2_RELEASE, /* BYTE: lw DCYSUSV */
+/*13*/ AWE_FX_ENV2_SUSTAIN, /* BYTE: up DCYSUSV */
+
+/* LFO1 (tremolo & vibrato) parameters */
+/*14*/ AWE_FX_LFO1_DELAY, /* WORD: LFO1VAL */
+/*15*/ AWE_FX_LFO1_FREQ, /* BYTE: lo TREMFRQ */
+/*16*/ AWE_FX_LFO1_VOLUME, /* BYTE: up TREMFRQ */
+/*17*/ AWE_FX_LFO1_PITCH, /* BYTE: up FMMOD */
+/*18*/ AWE_FX_LFO1_CUTOFF, /* BYTE: lo FMMOD */
+
+/* LFO2 (vibrato) parameters */
+/*19*/ AWE_FX_LFO2_DELAY, /* WORD: LFO2VAL */
+/*20*/ AWE_FX_LFO2_FREQ, /* BYTE: lo FM2FRQ2 */
+/*21*/ AWE_FX_LFO2_PITCH, /* BYTE: up FM2FRQ2 */
+
+/* Other overall effect parameters */
+/*22*/ AWE_FX_INIT_PITCH, /* SHORT: pitch offset */
+/*23*/ AWE_FX_CHORUS, /* BYTE: chorus effects send (0-255) */
+/*24*/ AWE_FX_REVERB, /* BYTE: reverb effects send (0-255) */
+/*25*/ AWE_FX_CUTOFF, /* BYTE: up IFATN */
+/*26*/ AWE_FX_FILTERQ, /* BYTE: up CCCA */
+
+/* Sample / loop offset changes */
+/*27*/ AWE_FX_SAMPLE_START, /* SHORT: offset */
+/*28*/ AWE_FX_LOOP_START, /* SHORT: offset */
+/*29*/ AWE_FX_LOOP_END, /* SHORT: offset */
+/*30*/ AWE_FX_COARSE_SAMPLE_START, /* SHORT: upper word offset */
+/*31*/ AWE_FX_COARSE_LOOP_START, /* SHORT: upper word offset */
+/*32*/ AWE_FX_COARSE_LOOP_END, /* SHORT: upper word offset */
+/*33*/ AWE_FX_ATTEN, /* BYTE: lo IFATN */
+
+ AWE_FX_END,
+};
+
+#endif /* AWE_VOICE_H */
diff --git a/drivers/sound/lowlevel/awe_wave.c b/drivers/sound/lowlevel/awe_wave.c
index 740ecb94b..196882423 100644
--- a/drivers/sound/lowlevel/awe_wave.c
+++ b/drivers/sound/lowlevel/awe_wave.c
@@ -2,7 +2,7 @@
* sound/awe_wave.c
*
* The low level driver for the AWE32/Sound Blaster 32 wave table synth.
- * version 0.3.1b; Jan. 21, 1997
+ * version 0.4.2c; Oct. 7, 1997
*
* Copyright (C) 1996,1997 Takashi Iwai
*
@@ -21,25 +21,34 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#define AWEDRV_VERSION "0.3.1b"
#ifdef __FreeBSD__
# include <i386/isa/sound/awe_config.h>
#else
+#ifdef MODULE
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/module.h>
+# include "../soundmodule.h"
+#endif
# include "awe_config.h"
#endif
/*----------------------------------------------------------------*/
-#ifdef CONFIG_AWE32_SYNTH
+#if defined(CONFIG_AWE32_SYNTH) || defined(CONFIG_AWE32_SYNTH_MODULE)
#ifdef __FreeBSD__
# include <i386/isa/sound/awe_hw.h>
+# include <i386/isa/sound/awe_version.h>
# include <i386/isa/sound/awe_voice.h>
#else
# include "awe_hw.h"
+# include "awe_version.h"
# include <linux/awe_voice.h>
#endif
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+/* include finetune table */
#ifdef AWE_OBSOLETE_VOXWARE
# ifdef __FreeBSD__
# define SEQUENCER_C
@@ -57,6 +66,8 @@
# include <machine/ultrasound.h>
#endif
+#endif /* AWE_HAS_GUS_COMPATIBILITY */
+
/*----------------------------------------------------------------
* debug message
@@ -77,39 +88,79 @@ static int debug_mode = 0;
* bank and voice record
*----------------------------------------------------------------*/
+/* soundfont record */
+typedef struct _sf_list {
+ unsigned short sf_id;
+ unsigned short type;
+ int num_info; /* current info table index */
+ int num_sample; /* current sample table index */
+ int mem_ptr; /* current word byte pointer */
+ int infos;
+ int samples;
+ /*char name[AWE_PATCH_NAME_LEN];*/
+} sf_list;
+
/* bank record */
typedef struct _awe_voice_list {
- unsigned char bank, instr;
- awe_voice_info v;
- struct _awe_voice_list *next_instr;
- struct _awe_voice_list *next_bank;
+ int next; /* linked list with same sf_id */
+ unsigned char bank, instr; /* preset number information */
+ char type, disabled; /* type=normal/mapped, disabled=boolean */
+ awe_voice_info v; /* voice information */
+ int next_instr; /* preset table list */
+ int next_bank; /* preset table list */
} awe_voice_list;
+/* voice list type */
+#define V_ST_NORMAL 0
+#define V_ST_MAPPED 1
+
+typedef struct _awe_sample_list {
+ int next; /* linked list with same sf_id */
+ awe_sample_info v; /* sample information */
+} awe_sample_list;
+
/* sample and information table */
-static awe_sample_info *samples;
-static awe_voice_list *infos;
+static int current_sf_id = 0;
+static int locked_sf_id = 0;
+static int max_sfs;
+static sf_list *sflists = NULL;
+
+#define awe_free_mem_ptr() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].mem_ptr)
+#define awe_free_info() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].num_info)
+#define awe_free_sample() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].num_sample)
+
+static int max_samples;
+static awe_sample_list *samples = NULL;
+
+static int max_infos;
+static awe_voice_list *infos = NULL;
+
#define AWE_MAX_PRESETS 256
+#define AWE_DEFAULT_PRESET 0
#define AWE_DEFAULT_BANK 0
+#define AWE_DEFAULT_DRUM 0
#define AWE_DRUM_BANK 128
+#define MAX_LAYERS AWE_MAX_VOICES
+
/* preset table index */
-static awe_voice_list *preset_table[AWE_MAX_PRESETS];
+static int preset_table[AWE_MAX_PRESETS];
/*----------------------------------------------------------------
* voice table
*----------------------------------------------------------------*/
/* effects table */
-#define AWE_FX_NBYTES ((AWE_FX_END+7)/8)
typedef struct FX_Rec { /* channel effects */
- unsigned char flags[AWE_FX_NBYTES];
+ unsigned char flags[AWE_FX_END];
short val[AWE_FX_END];
} FX_Rec;
/* channel parameters */
typedef struct _awe_chan_info {
+ int channel; /* channel number */
int bank; /* current tone bank */
int instr; /* current program */
int bender; /* midi pitchbend (-8192 - 8192) */
@@ -117,28 +168,35 @@ typedef struct _awe_chan_info {
int panning; /* panning (0-127) */
int main_vol; /* channel volume (0-127) */
int expression_vol; /* midi expression (0-127) */
- awe_voice_list *vrec; /* instrument list */
- awe_voice_list *def_vrec; /* default instrument list */
- FX_Rec fx; /* effects */
+ int chan_press; /* channel pressure */
+ int vrec; /* instrument list */
+ int def_vrec; /* default instrument list */
int sustained; /* sustain status in MIDI */
+ FX_Rec fx; /* effects */
+ FX_Rec fx_layer[MAX_LAYERS]; /* layer effects */
} awe_chan_info;
/* voice parameters */
typedef struct _voice_info {
int state;
-#define AWE_ST_OFF 0 /* no sound */
-#define AWE_ST_ON 1 /* playing */
-#define AWE_ST_STANDBY 2 /* stand by for playing */
-#define AWE_ST_SUSTAINED 3 /* sustained */
-#define AWE_ST_MARK 4 /* marked for allocation */
+#define AWE_ST_OFF (1<<0) /* no sound */
+#define AWE_ST_ON (1<<1) /* playing */
+#define AWE_ST_STANDBY (1<<2) /* stand by for playing */
+#define AWE_ST_SUSTAINED (1<<3) /* sustained */
+#define AWE_ST_MARK (1<<4) /* marked for allocation */
+#define AWE_ST_DRAM (1<<5) /* DRAM read/write */
+#define AWE_ST_FM (1<<6) /* reserved for FM */
+#define AWE_ST_RELEASED (1<<7) /* released */
int ch; /* midi channel */
int key; /* internal key for search */
+ int layer; /* layer number (for channel mode only) */
int time; /* allocated time */
awe_chan_info *cinfo; /* channel info */
int note; /* midi key (0-127) */
int velocity; /* midi velocity (0-127) */
+ int sostenuto; /* sostenuto on/off */
awe_voice_info *sample; /* assigned voice */
/* EMU8000 parameters */
@@ -148,58 +206,87 @@ typedef struct _voice_info {
} voice_info;
/* voice information */
-static voice_info voices[AWE_MAX_VOICES];
+static voice_info *voices;
-#define IS_NO_SOUND(v) (voices[v].state == AWE_ST_OFF || voices[v].state == AWE_ST_STANDBY)
+#define IS_NO_SOUND(v) (voices[v].state & (AWE_ST_OFF|AWE_ST_RELEASED|AWE_ST_STANDBY|AWE_ST_SUSTAINED))
#define IS_NO_EFFECT(v) (voices[v].state != AWE_ST_ON)
-#define IS_PLAYING(v) (!IS_NO_SOUND(v))
+#define IS_PLAYING(v) (voices[v].state & (AWE_ST_ON|AWE_ST_SUSTAINED|AWE_ST_RELEASED))
+#define IS_EMPTY(v) (voices[v].state & (AWE_ST_OFF|AWE_ST_MARK|AWE_ST_DRAM|AWE_ST_FM))
/* MIDI channel effects information (for hw control) */
-#if AWE_MAX_CHANNELS < AWE_MAX_VOICES
-static awe_chan_info channels[AWE_MAX_VOICES];
-#else
-static awe_chan_info channels[AWE_MAX_CHANNELS];
-#endif
+static awe_chan_info *channels;
/*----------------------------------------------------------------
* global variables
*----------------------------------------------------------------*/
+#ifndef AWE_DEFAULT_BASE_ADDR
+#define AWE_DEFAULT_BASE_ADDR 0 /* autodetect */
+#endif
+
+#ifndef AWE_DEFAULT_MEM_SIZE
+#define AWE_DEFAULT_MEM_SIZE 0 /* autodetect */
+#endif
+
/* awe32 base address (overwritten at initialization) */
-static int awe_base = 0;
-/* memory byte size (overwritten at initialization) */
-static long awe_mem_size = 0;
+static int awe_base = AWE_DEFAULT_BASE_ADDR;
+/* memory byte size */
+static int awe_mem_size = AWE_DEFAULT_MEM_SIZE;
+/* DRAM start offset */
+static int awe_mem_start = AWE_DRAM_OFFSET;
/* maximum channels for playing */
static int awe_max_voices = AWE_MAX_VOICES;
-static long free_mem_ptr = 0; /* free word byte size */
-static int free_info = 0; /* free info tables */
-static int last_info = 0; /* last loaded info index */
-static int free_sample = 0; /* free sample tables */
-static int last_sample = 0; /* last loaded sample index */
-static int loaded_once = 0; /* samples are loaded after init? */
-static unsigned short current_sf_id = 0; /* internal id */
+static int patch_opened = 0; /* sample already loaded? */
-static int reverb_mode = 0; /* reverb mode */
-static int chorus_mode = 0; /* chorus mode */
-static unsigned short init_atten = AWE_DEFAULT_ATTENUATION; /* 12dB below */
+static int reverb_mode = 4; /* reverb mode */
+static int chorus_mode = 2; /* chorus mode */
+static short init_atten = AWE_DEFAULT_ATTENUATION; /* 12dB below */
static int awe_present = FALSE; /* awe device present? */
static int awe_busy = FALSE; /* awe device opened? */
-#define DEFAULT_DRUM_FLAGS (1 << 9)
-#define IS_DRUM_CHANNEL(c) (drum_flags & (1 << (c)))
-static unsigned long drum_flags = DEFAULT_DRUM_FLAGS; /* channel flags */
+static int my_dev = -1;
+static int my_mixerdev = -1 ;
-static int awe_channel_mode = 0; /* channel control mode */
+#define DEFAULT_DRUM_FLAGS ((1 << 9) | (1 << 25))
+#define IS_DRUM_CHANNEL(c) (drum_flags & (1 << (c)))
+#define DRUM_CHANNEL_ON(c) (drum_flags |= (1 << (c)))
+#define DRUM_CHANNEL_OFF(c) (drum_flags &= ~(1 << (c)))
+static unsigned int drum_flags = DEFAULT_DRUM_FLAGS; /* channel flags */
+
+static int playing_mode = AWE_PLAY_INDIRECT;
+#define SINGLE_LAYER_MODE() (playing_mode == AWE_PLAY_INDIRECT || playing_mode == AWE_PLAY_DIRECT)
+#define MULTI_LAYER_MODE() (playing_mode == AWE_PLAY_MULTI || playing_mode == AWE_PLAY_MULTI2)
+
+static int current_alloc_time = 0; /* voice allocation index for channel mode */
+
+static struct MiscModeDef {
+ int value;
+ int init_each_time;
+} misc_modes_default[AWE_MD_END] = {
+ {0,0}, {0,0}, /* <-- not used */
+ {AWE_VERSION_NUMBER, FALSE},
+ {TRUE, TRUE}, /* exclusive */
+ {TRUE, TRUE}, /* realpan */
+ {AWE_DEFAULT_BANK, TRUE}, /* gusbank */
+ {FALSE, TRUE}, /* keep effect */
+ {AWE_DEFAULT_ATTENUATION, FALSE}, /* zero_atten */
+ {FALSE, TRUE}, /* chn_prior */
+ {AWE_DEFAULT_MOD_SENSE, TRUE}, /* modwheel sense */
+ {AWE_DEFAULT_PRESET, TRUE}, /* def_preset */
+ {AWE_DEFAULT_BANK, TRUE}, /* def_bank */
+ {AWE_DEFAULT_DRUM, TRUE}, /* def_drum */
+ {FALSE, TRUE}, /* toggle_drum_bank */
+};
-static int current_alloc_time = 0; /* voice allocation time */
+static int misc_modes[AWE_MD_END];
-static int awe_gus_bank = AWE_DEFAULT_BANK; /* GUS default bank number */
-static int awe_exclusive_sound = TRUE; /* exclusive sound on */
+static int awe_bass_level = 5;
+static int awe_treble_level = 9;
static struct synth_info awe_info = {
@@ -230,15 +317,16 @@ static void awe_release_region(void);
static void awe_reset_samples(void);
/* emu8000 chip i/o access */
static void awe_poke(unsigned short cmd, unsigned short port, unsigned short data);
-static void awe_poke_dw(unsigned short cmd, unsigned short port, unsigned long data);
+static void awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data);
static unsigned short awe_peek(unsigned short cmd, unsigned short port);
-static unsigned long awe_peek_dw(unsigned short cmd, unsigned short port);
+static unsigned int awe_peek_dw(unsigned short cmd, unsigned short port);
static void awe_wait(unsigned short delay);
/* initialize emu8000 chip */
static void awe_initialize(void);
/* set voice parameters */
+static void awe_init_misc_modes(int init_all);
static void awe_init_voice_info(awe_voice_info *vp);
static void awe_init_voice_parm(awe_voice_parm *pp);
#ifdef AWE_HAS_GUS_COMPATIBILITY
@@ -255,7 +343,8 @@ static int calc_parm_search(int msec, short *table);
static void awe_note_on(int voice);
static void awe_note_off(int voice);
static void awe_terminate(int voice);
-static void awe_exclusive_off(int voice, int exclass);
+static void awe_exclusive_off(int voice);
+static void awe_note_off_all(int do_sustain);
/* calculate voice parameters */
typedef void (*fx_affect_func)(int voice, int forced);
@@ -267,13 +356,15 @@ static void awe_set_pan(int voice, int forced);
static void awe_fx_fmmod(int voice, int forced);
static void awe_fx_tremfrq(int voice, int forced);
static void awe_fx_fm2frq2(int voice, int forced);
-static void awe_fx_cutoff(int voice, int forced);
+static void awe_fx_filterQ(int voice, int forced);
static void awe_calc_pitch(int voice);
#ifdef AWE_HAS_GUS_COMPATIBILITY
static void awe_calc_pitch_from_freq(int voice, int freq);
#endif
static void awe_calc_volume(int voice);
-static void awe_voice_init(int voice);
+static void awe_voice_init(int voice, int init_all);
+static void awe_channel_init(int ch, int init_all);
+static void awe_fx_init(int ch);
/* sequencer interface */
static int awe_open(int dev, int mode);
@@ -304,25 +395,36 @@ static void awe_hw_gus_control(int dev, int cmd, unsigned char *event);
#endif
static void awe_hw_awe_control(int dev, int cmd, unsigned char *event);
static void awe_voice_change(int voice, fx_affect_func func);
+static void awe_sostenuto_on(int voice, int forced);
static void awe_sustain_off(int voice, int forced);
+static void awe_terminate_and_init(int voice, int forced);
/* voice search */
-static awe_voice_list *awe_search_instr(int bank, int preset);
-static int awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, awe_voice_info **vlist);
-static void awe_alloc_multi_voices(int ch, int note, int velocity);
+static int awe_search_instr(int bank, int preset);
+static int awe_search_multi_voices(int rec, int note, int velocity, awe_voice_info **vlist);
+static void awe_alloc_multi_voices(int ch, int note, int velocity, int key);
static void awe_alloc_one_voice(int voice, int note, int velocity);
static int awe_clear_voice(void);
/* load / remove patches */
-static void awe_check_loaded(void);
-static int awe_load_info(awe_patch_info *patch, const char *addr);
-static int awe_load_data(awe_patch_info *patch, const char *addr);
+static int awe_open_patch(awe_patch_info *patch, const char *addr, int count);
+static int awe_close_patch(awe_patch_info *patch, const char *addr, int count);
+static int awe_unload_patch(awe_patch_info *patch, const char *addr, int count);
+static int awe_load_info(awe_patch_info *patch, const char *addr, int count);
+static int awe_load_data(awe_patch_info *patch, const char *addr, int count);
+static int awe_replace_data(awe_patch_info *patch, const char *addr, int count);
+static int awe_load_map(awe_patch_info *patch, const char *addr, int count);
#ifdef AWE_HAS_GUS_COMPATIBILITY
static int awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag);
#endif
-static int awe_write_wave_data(const char *addr, long offset, int size);
-static awe_voice_list *awe_get_removed_list(awe_voice_list *curp);
-static void awe_remove_samples(void);
+static int check_patch_opened(int type, char *name);
+static int awe_write_wave_data(const char *addr, int offset, awe_sample_info *sp, int channels);
+static void add_sf_info(int rec);
+static void add_sf_sample(int rec);
+static void purge_old_list(int rec, int next);
+static void add_info_list(int rec);
+static void awe_remove_samples(int sf_id);
+static void rebuild_preset_list(void);
static short awe_set_sample(awe_voice_info *vp);
/* lowlevel functions */
@@ -333,89 +435,28 @@ static void awe_send_array(unsigned short *data);
static void awe_tweak_voice(int voice);
static void awe_tweak(void);
static void awe_init_fm(void);
-static int awe_open_dram_for_write(int offset);
-static int awe_open_dram_for_read(int offset);
+static int awe_open_dram_for_write(int offset, int channels);
static void awe_open_dram_for_check(void);
static void awe_close_dram(void);
-static void awe_close_dram_for_read(void);
static void awe_write_dram(unsigned short c);
static int awe_detect_base(int addr);
static int awe_detect(void);
static int awe_check_dram(void);
+static int awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count);
static void awe_set_chorus_mode(int mode);
+static int awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count);
static void awe_set_reverb_mode(int mode);
-
-#ifdef AWE_OBSOLETE_VOXWARE
-
-#define awe_check_port() 0 /* always false */
-#define awe_request_region() /* nothing */
-#define awe_release_region() /* nothing */
-
-#else /* AWE_OBSOLETE_VOXWARE */
-
-/* the following macros are osbolete */
-
-#define PERMANENT_MALLOC(type,var,size,memptr) \
- var = (type)(sound_mem_blocks[sound_nblocks++] = vmalloc(size))
-#define RET_ERROR(err) -err
-
-#endif /* AWE_OBSOLETE_VOXWARE */
-
-
-/* macros for Linux and FreeBSD compatibility */
-
-#undef OUTW
-#undef COPY_FROM_USER
-#undef GET_BYTE_FROM_USER
-#undef GET_SHORT_FROM_USER
-#undef IOCTL_TO_USER
-
-#ifdef linux
-# define NO_DATA_ERR ENODATA
-# define OUTW(data, addr) outw(data, addr)
-
-#ifdef AWE_NEW_KERNEL_INTERFACE
-# define COPY_FROM_USER(target, source, offs, count) \
- copy_from_user( ((caddr_t)(target)),(source)+(offs),(count) )
-# define GET_BYTE_FROM_USER(target, addr, offs) \
- get_user(target, (unsigned char*)&((addr)[offs]))
-# define GET_SHORT_FROM_USER(target, addr, offs) \
- get_user(target, (unsigned short*)&((addr)[offs]))
-# define IOCTL_TO_USER(target, offs, source, count) \
- copy_to_user ( ((caddr_t)(target)),(source)+(offs),(count) )
-#else /* AWE_NEW_KERNEL_INTERFACE */
-# define COPY_FROM_USER(target, source, offs, count) \
- memcpy_fromfs( ((caddr_t)(target)),(source)+(offs),(count) )
-# define GET_BYTE_FROM_USER(target, addr, offs) \
- *((char *)&(target)) = get_fs_byte( (addr)+(offs) )
-# define GET_SHORT_FROM_USER(target, addr, offs) \
- *((short *)&(target)) = get_fs_word( (addr)+(offs) )
-# define IOCTL_TO_USER(target, offs, source, count) \
- memcpy_tofs ( ((caddr_t)(target)),(source)+(offs),(count) )
-#endif /* AWE_NEW_KERNEL_INTERFACE */
-
-# define BZERO(target,len) \
- memset( (caddr_t)target, '\0', len )
-# define MEMCPY(dst,src,len) \
- memcpy((caddr_t)dst, (caddr_t)src, len)
-
-#elif defined(__FreeBSD__)
-# define NO_DATA_ERR EINVAL
-# define OUTW(data, addr) outw(addr, data)
-# define COPY_FROM_USER(target, source, offs, count) \
- uiomove( ((caddr_t)(target)),(count),((struct uio *)(source)) )
-# define GET_BYTE_FROM_USER(target, addr, offs) \
- uiomove( ((char*)&(target)), 1, ((struct uio *)(addr)) )
-# define GET_SHORT_FROM_USER(target, addr, offs) \
- uiomove( ((char*)&(target)), 2, ((struct uio *)(addr)) )
-# define IOCTL_TO_USER(target, offs, source, count) \
- memcpy( &((target)[offs]), (source), (count) )
-# define BZERO(target,len) \
- bzero( (caddr_t)target, len )
-# define MEMCPY(dst,src,len) \
- bcopy((caddr_t)src, (caddr_t)dst, len)
+static void awe_equalizer(int bass, int treble);
+#ifdef CONFIG_AWE32_MIXER
+static int awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg);
#endif
+/* define macros for compatibility */
+#ifdef __FreeBSD__
+# include <i386/isa/sound/awe_compat.h>
+#else
+# include "awe_compat.h"
+#endif
/*----------------------------------------------------------------
* synth operation table
@@ -423,7 +464,9 @@ static void awe_set_reverb_mode(int mode);
static struct synth_operations awe_operations =
{
+#ifdef AWE_OSS38
"EMU8K",
+#endif
&awe_info,
0,
SYNTH_TYPE_SAMPLE,
@@ -449,6 +492,15 @@ static struct synth_operations awe_operations =
awe_setup_voice
};
+#ifdef CONFIG_AWE32_MIXER
+static struct mixer_operations awe_mixer_operations = {
+#ifndef __FreeBSD__
+ "AWE32",
+#endif
+ "AWE32 Equalizer",
+ awe_mixer_ioctl,
+};
+#endif
/*================================================================
@@ -456,10 +508,13 @@ static struct synth_operations awe_operations =
*================================================================*/
#ifdef AWE_OBSOLETE_VOXWARE
-long attach_awe_obsolete(long mem_start, struct address_info *hw_config)
+#define ATTACH_DECL static
#else
-int attach_awe(void)
+#define ATTACH_DECL /**/
#endif
+
+ATTACH_DECL
+int attach_awe(void)
{
/* check presence of AWE32 card */
if (! awe_detect()) {
@@ -473,24 +528,43 @@ int attach_awe(void)
return 0;
}
- /* allocate sample tables */
- PERMANENT_MALLOC(awe_sample_info *, samples,
- AWE_MAX_SAMPLES * AWE_SAMPLE_INFO_SIZE, mem_start);
- PERMANENT_MALLOC(awe_voice_list *, infos,
- AWE_MAX_INFOS * sizeof(awe_voice_list), mem_start);
- if (samples == NULL || infos == NULL) {
+ /* set buffers to NULL */
+ voices = NULL;
+ channels = NULL;
+ sflists = NULL;
+ samples = NULL;
+ infos = NULL;
+
+ /* voice & channel info */
+ voices = (voice_info*)my_malloc(AWE_MAX_VOICES * sizeof(voice_info));
+ channels = (awe_chan_info*)my_malloc(AWE_MAX_CHANNELS * sizeof(awe_chan_info));
+
+ if (voices == NULL || channels == NULL) {
+ my_free(voices);
+ my_free(channels);
printk("AWE32: can't allocate sample tables\n");
return 0;
}
- if (num_synths >= MAX_SYNTH_DEV)
- printk("AWE32 Error: too many synthesizers\n");
+ /* allocate sample tables */
+ INIT_TABLE(sflists, max_sfs, AWE_MAX_SF_LISTS, sf_list);
+ INIT_TABLE(samples, max_samples, AWE_MAX_SAMPLES, awe_sample_list);
+ INIT_TABLE(infos, max_infos, AWE_MAX_INFOS, awe_voice_list);
+
+ if (my_dev=sound_alloc_synthdev())
+ printk(KERN_WARNING "AWE32 Error: too many synthesizers\n");
else {
voice_alloc = &awe_operations.alloc;
voice_alloc->max_voice = awe_max_voices;
- synth_devs[num_synths++] = &awe_operations;
+ synth_devs[my_dev] = &awe_operations;
}
+#ifdef CONFIG_AWE32_MIXER
+ if (my_mixerdev=sound_alloc_mixerdev()) {
+ mixer_devs[my_mixerdev] = &awe_mixer_operations;
+ }
+#endif
+
/* reserve I/O ports for awedrv */
awe_request_region();
@@ -500,15 +574,16 @@ int attach_awe(void)
/* intialize AWE32 hardware */
awe_initialize();
-#ifndef __FreeBSD__
- printk("AWE32 Sound Driver v%s (DRAM %dk)\n",
- AWEDRV_VERSION, (int)awe_mem_size/1024);
-#else
- DEBUG(0,printk("AWE32 Sound Driver v%s (DRAM %dk)\n",
- AWEDRV_VERSION, (int)awe_mem_size/1024));
+ sprintf(awe_info.name, "AWE32-%s (RAM%dk)",
+ AWEDRV_VERSION, awe_mem_size/1024);
+#ifdef __FreeBSD__
+ printk("awe0: <SoundBlaster EMU8000 MIDI (RAM%dk)>", awe_mem_size/1024);
+#elif defined(AWE_DEBUG_ON)
+ printk("%s\n", awe_info.name);
#endif
- sprintf(awe_info.name, "AWE32 Driver v%s (DRAM %dk)",
- AWEDRV_VERSION, (int)awe_mem_size/1024);
+
+ /* set default values */
+ awe_init_misc_modes(TRUE);
/* set reverb & chorus modes */
awe_set_reverb_mode(reverb_mode);
@@ -516,24 +591,59 @@ int attach_awe(void)
awe_present = TRUE;
-#ifdef AWE_OBSOLETE_VOXWARE
- return mem_start;
-#else
return 1;
-#endif
}
+#ifdef AWE_DYNAMIC_BUFFER
+static void free_tables(void)
+{
+ my_free(sflists);
+ sflists = NULL; max_sfs = 0;
+ my_free(samples);
+ samples = NULL; max_samples = 0;
+ my_free(infos);
+ infos = NULL; max_infos = 0;
+}
+#else
+#define free_buffers() /**/
+#endif
+
+
+ATTACH_DECL
void unload_awe(void)
{
if (awe_present) {
awe_reset_samples();
awe_release_region();
+ my_free(voices);
+ my_free(channels);
+ free_tables();
+ sound_unload_mixerdev(my_mixerdev);
+ sound_unload_synthdev(my_dev);
+ awe_present = FALSE;
}
}
+/*----------------------------------------------------------------
+ * old type interface
+ *----------------------------------------------------------------*/
+
#ifdef AWE_OBSOLETE_VOXWARE
+
+#ifdef __FreeBSD__
+long attach_awe_obsolete(long mem_start, struct address_info *hw_config)
+#else
+int attach_awe_obsolete(int mem_start, struct address_info *hw_config)
+#endif
+{
+ my_malloc_init(mem_start);
+ if (! attach_awe())
+ return 0;
+ return my_malloc_memptr();
+}
+
int probe_awe_obsolete(struct address_info *hw_config)
{
return 1;
@@ -542,6 +652,7 @@ int probe_awe_obsolete(struct address_info *hw_config)
#endif
+
/*================================================================
* clear sample tables
*================================================================*/
@@ -552,15 +663,14 @@ awe_reset_samples(void)
int i;
/* free all bank tables */
- for (i = 0; i < AWE_MAX_PRESETS; i++) {
- preset_table[i] = NULL;
- }
+ for (i = 0; i < AWE_MAX_PRESETS; i++)
+ preset_table[i] = -1;
+
+ free_tables();
- free_mem_ptr = 0;
- last_sample = free_sample = 0;
- last_info = free_info = 0;
current_sf_id = 0;
- loaded_once = 0;
+ locked_sf_id = 0;
+ patch_opened = 0;
}
@@ -584,7 +694,7 @@ awe_poke(unsigned short cmd, unsigned short port, unsigned short data)
/* write 32bit data */
INLINE static void
-awe_poke_dw(unsigned short cmd, unsigned short port, unsigned long data)
+awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data)
{
awe_set_cmd(cmd);
OUTW(data, awe_port(port)); /* write lower 16 bits */
@@ -602,10 +712,10 @@ awe_peek(unsigned short cmd, unsigned short port)
}
/* read 32bit data */
-INLINE static unsigned long
+INLINE static unsigned int
awe_peek_dw(unsigned short cmd, unsigned short port)
{
- unsigned long k1, k2;
+ unsigned int k1, k2;
awe_set_cmd(cmd);
k1 = inw(awe_port(port));
k2 = inw(awe_port(port)+2);
@@ -636,6 +746,13 @@ awe_wait(unsigned short delay)
break;
}
+/* write a word data */
+INLINE static void
+awe_write_dram(unsigned short c)
+{
+ awe_poke(AWE_SMLD, c);
+}
+
#ifndef AWE_OBSOLETE_VOXWARE
@@ -706,6 +823,9 @@ awe_initialize(void)
/* enable audio */
awe_poke(AWE_HWCF3, 0x0004);
+
+ /* set equalizer */
+ awe_equalizer(5, 9);
}
@@ -717,7 +837,7 @@ awe_initialize(void)
static void
awe_init_voice_info(awe_voice_info *vp)
{
- vp->sf_id = 0;
+ vp->sf_id = 0; /* normal mode */
vp->sample = 0;
vp->rate_offset = 0;
@@ -792,10 +912,10 @@ static int
freq_to_note(int mHz)
{
/* abscents = log(mHz/8176) / log(2) * 1200 */
- unsigned long max_val = (unsigned long)0xffffffff / 10000;
+ unsigned int max_val = (unsigned int)0xffffffff / 10000;
int i, times;
- unsigned long base;
- unsigned long freq;
+ unsigned int base;
+ unsigned int freq;
int note, tune;
if (mHz == 0)
@@ -913,7 +1033,7 @@ calc_parm_delay(int msec)
static int
calc_parm_hold(int msec)
{
- int val = 0x7f - (unsigned char)(msec / 92);
+ int val = (0x7f * 92 - msec) / 92;
if (val < 1) val = 1;
if (val > 127) val = 127;
return val;
@@ -955,46 +1075,137 @@ calc_parm_search(int msec, short *table)
*================================================================*/
/* set an effect value */
+#define FX_FLAG_OFF 0
+#define FX_FLAG_SET 1
+#define FX_FLAG_ADD 2
+
#define FX_SET(rec,type,value) \
- ((rec)->flags[(type)/8] |= (1 << ((type) % 8)), \
- (rec)->val[type] = (value))
+ ((rec)->flags[type] = FX_FLAG_SET, (rec)->val[type] = (value))
+#define FX_ADD(rec,type,value) \
+ ((rec)->flags[type] = FX_FLAG_ADD, (rec)->val[type] = (value))
+#define FX_UNSET(rec,type) \
+ ((rec)->flags[type] = FX_FLAG_OFF, (rec)->val[type] = 0)
/* check the effect value is set */
-#define FX_ON(rec,type) ((rec)->flags[(type)/8] & (1<<((type)%8)))
+#define FX_ON(rec,type) ((rec)->flags[type])
+
+#define PARM_BYTE 0
+#define PARM_WORD 1
+
+static struct PARM_DEFS {
+ int type; /* byte or word */
+ int low, high; /* value range */
+ fx_affect_func realtime; /* realtime paramater change */
+} parm_defs[] = {
+ {PARM_WORD, 0, 0x8000, NULL}, /* env1 delay */
+ {PARM_BYTE, 1, 0x7f, NULL}, /* env1 attack */
+ {PARM_BYTE, 0, 0x7e, NULL}, /* env1 hold */
+ {PARM_BYTE, 1, 0x7f, NULL}, /* env1 decay */
+ {PARM_BYTE, 1, 0x7f, NULL}, /* env1 release */
+ {PARM_BYTE, 0, 0x7f, NULL}, /* env1 sustain */
+ {PARM_BYTE, 0, 0xff, NULL}, /* env1 pitch */
+ {PARM_BYTE, 0, 0xff, NULL}, /* env1 cutoff */
+
+ {PARM_WORD, 0, 0x8000, NULL}, /* env2 delay */
+ {PARM_BYTE, 1, 0x7f, NULL}, /* env2 attack */
+ {PARM_BYTE, 0, 0x7e, NULL}, /* env2 hold */
+ {PARM_BYTE, 1, 0x7f, NULL}, /* env2 decay */
+ {PARM_BYTE, 1, 0x7f, NULL}, /* env2 release */
+ {PARM_BYTE, 0, 0x7f, NULL}, /* env2 sustain */
+
+ {PARM_WORD, 0, 0x8000, NULL}, /* lfo1 delay */
+ {PARM_BYTE, 0, 0xff, awe_fx_tremfrq}, /* lfo1 freq */
+ {PARM_BYTE, 0, 0x7f, awe_fx_tremfrq}, /* lfo1 volume (positive only)*/
+ {PARM_BYTE, 0, 0x7f, awe_fx_fmmod}, /* lfo1 pitch (positive only)*/
+ {PARM_BYTE, 0, 0xff, awe_fx_fmmod}, /* lfo1 cutoff (positive only)*/
+
+ {PARM_WORD, 0, 0x8000, NULL}, /* lfo2 delay */
+ {PARM_BYTE, 0, 0xff, awe_fx_fm2frq2}, /* lfo2 freq */
+ {PARM_BYTE, 0, 0x7f, awe_fx_fm2frq2}, /* lfo2 pitch (positive only)*/
+
+ {PARM_WORD, 0, 0xffff, awe_set_voice_pitch}, /* initial pitch */
+ {PARM_BYTE, 0, 0xff, NULL}, /* chorus */
+ {PARM_BYTE, 0, 0xff, NULL}, /* reverb */
+ {PARM_BYTE, 0, 0xff, awe_set_volume}, /* initial cutoff */
+ {PARM_BYTE, 0, 15, awe_fx_filterQ}, /* initial resonance */
+
+ {PARM_WORD, 0, 0xffff, NULL}, /* sample start */
+ {PARM_WORD, 0, 0xffff, NULL}, /* loop start */
+ {PARM_WORD, 0, 0xffff, NULL}, /* loop end */
+ {PARM_WORD, 0, 0xffff, NULL}, /* coarse sample start */
+ {PARM_WORD, 0, 0xffff, NULL}, /* coarse loop start */
+ {PARM_WORD, 0, 0xffff, NULL}, /* coarse loop end */
+ {PARM_BYTE, 0, 0xff, awe_set_volume}, /* initial attenuation */
+};
+
+
+static unsigned char
+FX_BYTE(FX_Rec *rec, FX_Rec *lay, int type, unsigned char value)
+{
+ int effect = 0;
+ int on = 0;
+ if (lay && (on = FX_ON(lay, type)) != 0)
+ effect = lay->val[type];
+ if (!on && (on = FX_ON(rec, type)) != 0)
+ effect = rec->val[type];
+ if (on == FX_FLAG_ADD)
+ effect += (int)value;
+ if (on) {
+ if (effect < parm_defs[type].low)
+ effect = parm_defs[type].low;
+ else if (effect > parm_defs[type].high)
+ effect = parm_defs[type].high;
+ return (unsigned char)effect;
+ }
+ return value;
+}
-/* get byte effect value */
-#define FX_BYTE(rec,type,value) \
- (unsigned char)(FX_ON(rec,type) ? (rec)->val[type] : (value))
/* get word effect value */
-#define FX_WORD(rec,type,value) \
- (unsigned short)(FX_ON(rec,type) ? (rec)->val[type] : (value))
+static unsigned short
+FX_WORD(FX_Rec *rec, FX_Rec *lay, int type, unsigned short value)
+{
+ int effect = 0;
+ int on = 0;
+ if (lay && (on = FX_ON(lay, type)) != 0)
+ effect = lay->val[type];
+ if (!on && (on = FX_ON(rec, type)) != 0)
+ effect = rec->val[type];
+ if (on == FX_FLAG_ADD)
+ effect += (int)value;
+ if (on) {
+ if (effect < parm_defs[type].low)
+ effect = parm_defs[type].low;
+ else if (effect > parm_defs[type].high)
+ effect = parm_defs[type].high;
+ return (unsigned short)effect;
+ }
+ return value;
+}
/* get word (upper=type1/lower=type2) effect value */
static unsigned short
-FX_COMB(FX_Rec *rec, int type1, int type2, unsigned short value)
+FX_COMB(FX_Rec *rec, FX_Rec *lay, int type1, int type2, unsigned short value)
{
unsigned short tmp;
- if (FX_ON(rec, type1))
- tmp = (unsigned short)(rec->val[type1]) << 8;
- else
- tmp = value & 0xff00;
- if (FX_ON(rec, type2))
- tmp |= (unsigned short)(rec->val[type2]) & 0xff;
- else
- tmp |= value & 0xff;
+ tmp = FX_BYTE(rec, lay, type1, (unsigned char)(value >> 8));
+ tmp <<= 8;
+ tmp |= FX_BYTE(rec, lay, type2, (unsigned char)(value & 0xff));
return tmp;
}
/* address offset */
-static long
-FX_OFFSET(FX_Rec *rec, int lo, int hi, int mode)
+static int
+FX_OFFSET(FX_Rec *rec, FX_Rec *lay, int lo, int hi, int mode)
{
- long addr = 0;
- if (FX_ON(rec, hi)) {
+ int addr = 0;
+ if (lay && FX_ON(lay, hi))
+ addr = (short)lay->val[hi];
+ else if (FX_ON(rec, hi))
addr = (short)rec->val[hi];
- addr = addr << 15;
- }
- if (FX_ON(rec, lo))
+ addr = addr << 15;
+ if (lay && FX_ON(lay, lo))
+ addr += (short)lay->val[lo];
+ else if (FX_ON(rec, lo))
addr += (short)rec->val[lo];
if (!(mode & AWE_SAMPLE_8BITS))
addr /= 2;
@@ -1009,10 +1220,13 @@ FX_OFFSET(FX_Rec *rec, int lo, int hi, int mode)
static void
awe_note_on(int voice)
{
- unsigned long temp;
- long addr;
+ unsigned int temp;
+ int addr;
awe_voice_info *vp;
FX_Rec *fx = &voices[voice].cinfo->fx;
+ FX_Rec *fx_lay = NULL;
+ if (voices[voice].layer < MAX_LAYERS)
+ fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
/* A voice sample must assigned before calling */
if ((vp = voices[voice].sample) == NULL || vp->index < 0)
@@ -1027,17 +1241,17 @@ awe_note_on(int voice)
/* modulation & volume envelope */
awe_poke(AWE_ENVVAL(voice),
- FX_WORD(fx, AWE_FX_ENV1_DELAY, vp->parm.moddelay));
+ FX_WORD(fx, fx_lay, AWE_FX_ENV1_DELAY, vp->parm.moddelay));
awe_poke(AWE_ATKHLD(voice),
- FX_COMB(fx, AWE_FX_ENV1_HOLD, AWE_FX_ENV1_ATTACK,
+ FX_COMB(fx, fx_lay, AWE_FX_ENV1_HOLD, AWE_FX_ENV1_ATTACK,
vp->parm.modatkhld));
awe_poke(AWE_DCYSUS(voice),
- FX_COMB(fx, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY,
+ FX_COMB(fx, fx_lay, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY,
vp->parm.moddcysus));
awe_poke(AWE_ENVVOL(voice),
- FX_WORD(fx, AWE_FX_ENV2_DELAY, vp->parm.voldelay));
+ FX_WORD(fx, fx_lay, AWE_FX_ENV2_DELAY, vp->parm.voldelay));
awe_poke(AWE_ATKHLDV(voice),
- FX_COMB(fx, AWE_FX_ENV2_HOLD, AWE_FX_ENV2_ATTACK,
+ FX_COMB(fx, fx_lay, AWE_FX_ENV2_HOLD, AWE_FX_ENV2_ATTACK,
vp->parm.volatkhld));
/* decay/sustain parameter for volume envelope must be set at last */
@@ -1049,14 +1263,14 @@ awe_note_on(int voice)
/* modulation envelope heights */
awe_poke(AWE_PEFE(voice),
- FX_COMB(fx, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF,
+ FX_COMB(fx, fx_lay, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF,
vp->parm.pefe));
/* lfo1/2 delay */
awe_poke(AWE_LFO1VAL(voice),
- FX_WORD(fx, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay));
+ FX_WORD(fx, fx_lay, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay));
awe_poke(AWE_LFO2VAL(voice),
- FX_WORD(fx, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay));
+ FX_WORD(fx, fx_lay, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay));
/* lfo1 pitch & cutoff shift */
awe_fx_fmmod(voice, TRUE);
@@ -1065,27 +1279,25 @@ awe_note_on(int voice)
/* lfo2 pitch & freq */
awe_fx_fm2frq2(voice, TRUE);
/* pan & loop start */
- awe_set_pan(voice, 1);
+ awe_set_pan(voice, TRUE);
/* chorus & loop end (chorus 8bit, MSB) */
addr = vp->loopend - 1;
- addr += FX_OFFSET(fx, AWE_FX_LOOP_END,
+ addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_END,
AWE_FX_COARSE_LOOP_END, vp->mode);
- temp = FX_BYTE(fx, AWE_FX_CHORUS, vp->parm.chorus);
- temp = (temp <<24) | (unsigned long)addr;
+ temp = FX_BYTE(fx, fx_lay, AWE_FX_CHORUS, vp->parm.chorus);
+ temp = (temp <<24) | (unsigned int)addr;
awe_poke_dw(AWE_CSL(voice), temp);
- DEBUG(4,printk("AWE32: [-- loopend=%x/%x]\n",
- (int)vp->loopend, (int)addr));
+ DEBUG(4,printk("AWE32: [-- loopend=%x/%x]\n", vp->loopend, addr));
/* Q & current address (Q 4bit value, MSB) */
addr = vp->start - 1;
- addr += FX_OFFSET(fx, AWE_FX_SAMPLE_START,
+ addr += FX_OFFSET(fx, fx_lay, AWE_FX_SAMPLE_START,
AWE_FX_COARSE_SAMPLE_START, vp->mode);
- temp = FX_BYTE(fx, AWE_FX_FILTERQ, vp->parm.filterQ);
- temp = (temp<<28) | (unsigned long)addr;
+ temp = FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ);
+ temp = (temp<<28) | (unsigned int)addr;
awe_poke_dw(AWE_CCCA(voice), temp);
- DEBUG(4,printk("AWE32: [-- startaddr=%x/%x]\n",
- (int)vp->start, (int)addr));
+ DEBUG(4,printk("AWE32: [-- startaddr=%x/%x]\n", vp->start, addr));
/* reset volume */
awe_poke_dw(AWE_VTFT(voice), 0x0000FFFF);
@@ -1093,13 +1305,21 @@ awe_note_on(int voice)
/* turn on envelope */
awe_poke(AWE_DCYSUSV(voice),
- FX_COMB(fx, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY,
+ FX_COMB(fx, fx_lay, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY,
vp->parm.voldcysus));
- /* set chorus */
- temp = FX_BYTE(fx, AWE_FX_REVERB, vp->parm.reverb);
+ /* set reverb */
+ temp = FX_BYTE(fx, fx_lay, AWE_FX_REVERB, vp->parm.reverb);
temp = (awe_peek_dw(AWE_PTRX(voice)) & 0xffff0000) | (temp<<8);
awe_poke_dw(AWE_PTRX(voice), temp);
awe_poke_dw(AWE_CPF(voice), 0x40000000);
+
+ voices[voice].state = AWE_ST_ON;
+
+ /* clear voice position for the next note on this channel */
+ if (SINGLE_LAYER_MODE()) {
+ FX_UNSET(fx, AWE_FX_SAMPLE_START);
+ FX_UNSET(fx, AWE_FX_COARSE_SAMPLE_START);
+ }
}
@@ -1110,24 +1330,22 @@ awe_note_off(int voice)
awe_voice_info *vp;
unsigned short tmp;
FX_Rec *fx = &voices[voice].cinfo->fx;
+ FX_Rec *fx_lay = NULL;
+ if (voices[voice].layer < MAX_LAYERS)
+ fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
if ((vp = voices[voice].sample) == NULL) {
- awe_voice_init(voice);
+ voices[voice].state = AWE_ST_OFF;
return;
}
- if (FX_ON(fx, AWE_FX_ENV1_RELEASE))
- tmp = 0x8000 | fx->val[AWE_FX_ENV1_RELEASE];
- else
- tmp = vp->parm.modrelease;
+ tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV1_RELEASE,
+ (unsigned char)vp->parm.modrelease);
awe_poke(AWE_DCYSUS(voice), tmp);
- if (FX_ON(fx, AWE_FX_ENV2_RELEASE))
- tmp = 0x8000 | fx->val[AWE_FX_ENV2_RELEASE];
- else
- tmp = vp->parm.volrelease;
+ tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV2_RELEASE,
+ (unsigned char)vp->parm.volrelease);
awe_poke(AWE_DCYSUSV(voice), tmp);
- voices[voice].state = AWE_ST_OFF;
-
- awe_voice_init(voice);
+ voices[voice].state = AWE_ST_RELEASED;
}
/* force to terminate the voice (no releasing echo) */
@@ -1136,26 +1354,28 @@ awe_terminate(int voice)
{
awe_poke(AWE_DCYSUSV(voice), 0x807F);
awe_tweak_voice(voice);
- awe_voice_init(voice);
+ voices[voice].state = AWE_ST_OFF;
}
-
/* turn off other voices with the same exclusive class (for drums) */
static void
-awe_exclusive_off(int voice, int exclass)
+awe_exclusive_off(int voice)
{
- int i;
+ int i, exclass;
- if (exclass == 0) /* not exclusive */
+ if (voices[voice].sample == NULL)
return;
+ if ((exclass = voices[voice].sample->exclusiveClass) == 0)
+ return; /* not exclusive */
/* turn off voices with the same class */
for (i = 0; i < awe_max_voices; i++) {
- if (i != voice && IS_PLAYING(voice) &&
- voices[i].sample &&
+ if (i != voice && IS_PLAYING(i) &&
+ voices[i].sample && voices[i].ch == voices[voice].ch &&
voices[i].sample->exclusiveClass == exclass) {
DEBUG(4,printk("AWE32: [exoff(%d)]\n", i));
- awe_note_off(i);
+ awe_terminate(i);
+ awe_voice_init(i, TRUE);
}
}
}
@@ -1182,20 +1402,25 @@ awe_set_voice_pitch(int voice, int forced)
awe_set_pitch(voice, forced);
}
-/* change volume */
+/* change volume & cutoff */
static void
awe_set_volume(int voice, int forced)
{
awe_voice_info *vp;
unsigned short tmp2;
FX_Rec *fx = &voices[voice].cinfo->fx;
+ FX_Rec *fx_lay = NULL;
+ if (voices[voice].layer < MAX_LAYERS)
+ fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
- if (IS_NO_EFFECT(voice) && !forced) return;
+ if (!IS_PLAYING(voice) && !forced) return;
if ((vp = voices[voice].sample) == NULL || vp->index < 0)
return;
- tmp2 = FX_BYTE(fx, AWE_FX_CUTOFF, vp->parm.cutoff);
- tmp2 = (tmp2 << 8) | voices[voice].avol;
+ tmp2 = FX_BYTE(fx, fx_lay, AWE_FX_CUTOFF, vp->parm.cutoff);
+ tmp2 = (tmp2 << 8);
+ tmp2 |= FX_BYTE(fx, fx_lay, AWE_FX_ATTEN,
+ (unsigned char)voices[voice].avol);
awe_poke(AWE_IFATN(voice), tmp2);
}
@@ -1203,6 +1428,8 @@ awe_set_volume(int voice, int forced)
static void
awe_set_voice_vol(int voice, int forced)
{
+ if (IS_EMPTY(voice))
+ return;
awe_calc_volume(voice);
awe_set_volume(voice, forced);
}
@@ -1212,10 +1439,13 @@ awe_set_voice_vol(int voice, int forced)
static void
awe_set_pan(int voice, int forced)
{
- unsigned long temp;
- long addr;
+ unsigned int temp;
+ int addr;
awe_voice_info *vp;
FX_Rec *fx = &voices[voice].cinfo->fx;
+ FX_Rec *fx_lay = NULL;
+ if (voices[voice].layer < MAX_LAYERS)
+ fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
if (IS_NO_EFFECT(voice) && !forced) return;
if ((vp = voices[voice].sample) == NULL || vp->index < 0)
@@ -1239,13 +1469,12 @@ awe_set_pan(int voice, int forced)
}
if (forced || temp != voices[voice].apan) {
addr = vp->loopstart - 1;
- addr += FX_OFFSET(fx, AWE_FX_LOOP_START,
+ addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_START,
AWE_FX_COARSE_LOOP_START, vp->mode);
- temp = (temp<<24) | (unsigned long)addr;
+ temp = (temp<<24) | (unsigned int)addr;
awe_poke_dw(AWE_PSST(voice), temp);
voices[voice].apan = temp;
- DEBUG(4,printk("AWE32: [-- loopstart=%x/%x]\n",
- (int)vp->loopstart, (int)addr));
+ DEBUG(4,printk("AWE32: [-- loopstart=%x/%x]\n", vp->loopstart, addr));
}
}
@@ -1255,11 +1484,15 @@ awe_fx_fmmod(int voice, int forced)
{
awe_voice_info *vp;
FX_Rec *fx = &voices[voice].cinfo->fx;
+ FX_Rec *fx_lay = NULL;
+ if (voices[voice].layer < MAX_LAYERS)
+ fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
if (IS_NO_EFFECT(voice) && !forced) return;
if ((vp = voices[voice].sample) == NULL || vp->index < 0)
return;
awe_poke(AWE_FMMOD(voice),
- FX_COMB(fx, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF,
+ FX_COMB(fx, fx_lay, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF,
vp->parm.fmmod));
}
@@ -1269,11 +1502,15 @@ awe_fx_tremfrq(int voice, int forced)
{
awe_voice_info *vp;
FX_Rec *fx = &voices[voice].cinfo->fx;
+ FX_Rec *fx_lay = NULL;
+ if (voices[voice].layer < MAX_LAYERS)
+ fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
if (IS_NO_EFFECT(voice) && !forced) return;
if ((vp = voices[voice].sample) == NULL || vp->index < 0)
return;
awe_poke(AWE_TREMFRQ(voice),
- FX_COMB(fx, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ,
+ FX_COMB(fx, fx_lay, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ,
vp->parm.tremfrq));
}
@@ -1283,29 +1520,38 @@ awe_fx_fm2frq2(int voice, int forced)
{
awe_voice_info *vp;
FX_Rec *fx = &voices[voice].cinfo->fx;
+ FX_Rec *fx_lay = NULL;
+ if (voices[voice].layer < MAX_LAYERS)
+ fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
if (IS_NO_EFFECT(voice) && !forced) return;
if ((vp = voices[voice].sample) == NULL || vp->index < 0)
return;
awe_poke(AWE_FM2FRQ2(voice),
- FX_COMB(fx, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ,
+ FX_COMB(fx, fx_lay, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ,
vp->parm.fm2frq2));
}
-/* set total cutoff & current pitch */
+
+/* Q & current address (Q 4bit value, MSB) */
static void
-awe_fx_cutoff(int voice, int forced)
+awe_fx_filterQ(int voice, int forced)
{
- unsigned short tmp2;
+ unsigned int addr;
awe_voice_info *vp;
FX_Rec *fx = &voices[voice].cinfo->fx;
- if (IS_NO_EFFECT(voice)) return;
+ FX_Rec *fx_lay = NULL;
+ if (voices[voice].layer < MAX_LAYERS)
+ fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
+ if (IS_NO_EFFECT(voice) && !forced) return;
if ((vp = voices[voice].sample) == NULL || vp->index < 0)
return;
- tmp2 = FX_BYTE(fx, AWE_FX_CUTOFF, vp->parm.cutoff);
- tmp2 = (tmp2 << 8) | voices[voice].avol;
- awe_poke(AWE_IFATN(voice), tmp2);
-}
+ addr = awe_peek_dw(AWE_CCCA(voice)) & 0xffffff;
+ addr |= (FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ) << 28);
+ awe_poke_dw(AWE_CCCA(voice), addr);
+}
/*================================================================
* calculate pitch offset
@@ -1326,6 +1572,7 @@ awe_calc_pitch(int voice)
if ((ap = vp->sample) == NULL)
return;
if (ap->index < 0) {
+ DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample));
if (awe_set_sample(ap) < 0)
return;
}
@@ -1339,6 +1586,8 @@ awe_calc_pitch(int voice)
offset = (vp->note - ap->root) * 4096 / 12;
DEBUG(4,printk("AWE32: p-> ofs=%d\n", offset));
}
+ offset = (offset * ap->scaleTuning) / 100;
+ DEBUG(4,printk("AWE32: p-> scale* ofs=%d\n", offset));
offset += ap->tune * 4096 / 1200;
DEBUG(4,printk("AWE32: p-> tune+ ofs=%d\n", offset));
if (cp->bender != 0) {
@@ -1346,14 +1595,12 @@ awe_calc_pitch(int voice)
/* (819200: 1 semitone) ==> (4096: 12 semitones) */
offset += cp->bender * cp->bender_range / 2400;
}
- offset = (offset * ap->scaleTuning) / 100;
- DEBUG(4,printk("AWE32: p-> scale* ofs=%d\n", offset));
/* add initial pitch correction */
- if (FX_ON(&cp->fx, AWE_FX_INIT_PITCH)) {
- DEBUG(3,printk("AWE32: fx_pitch(%d) %d\n", voice, cp->fx.val[AWE_FX_INIT_PITCH]));
+ if (FX_ON(&cp->fx_layer[vp->layer], AWE_FX_INIT_PITCH))
+ offset += cp->fx_layer[vp->layer].val[AWE_FX_INIT_PITCH];
+ else if (FX_ON(&cp->fx, AWE_FX_INIT_PITCH))
offset += cp->fx.val[AWE_FX_INIT_PITCH];
- }
/* 0xe000: root pitch */
vp->apitch = 0xe000 + ap->rate_offset + offset;
@@ -1373,20 +1620,27 @@ awe_calc_pitch_from_freq(int voice, int freq)
voice_info *vp = &voices[voice];
awe_voice_info *ap;
FX_Rec *fx = &voices[voice].cinfo->fx;
+ FX_Rec *fx_lay = NULL;
int offset;
int note;
+ if (voices[voice].layer < MAX_LAYERS)
+ fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer];
+
/* search voice information */
if ((ap = vp->sample) == NULL)
return;
if (ap->index < 0) {
+ DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample));
if (awe_set_sample(ap) < 0)
return;
}
note = freq_to_note(freq);
offset = (note - ap->root * 100 + ap->tune) * 4096 / 1200;
offset = (offset * ap->scaleTuning) / 100;
- if (FX_ON(fx, AWE_FX_INIT_PITCH))
+ if (fx_lay && FX_ON(fx_lay, AWE_FX_INIT_PITCH))
+ offset += fx_lay->val[AWE_FX_INIT_PITCH];
+ else if (FX_ON(fx, AWE_FX_INIT_PITCH))
offset += fx->val[AWE_FX_INIT_PITCH];
vp->apitch = 0xe000 + ap->rate_offset + offset;
if (vp->apitch > 0xffff)
@@ -1430,6 +1684,7 @@ awe_calc_volume(int voice)
ap = vp->sample;
if (ap->index < 0) {
+ DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample));
if (awe_set_sample(ap) < 0)
return;
}
@@ -1437,6 +1692,7 @@ awe_calc_volume(int voice)
/* 0 - 127 */
vol = (vp->velocity * cp->main_vol * cp->expression_vol) / (127*127);
vol = vol * ap->amplitude / 127;
+
if (vol < 0) vol = 0;
if (vol > 127) vol = 127;
@@ -1450,56 +1706,138 @@ awe_calc_volume(int voice)
}
+/* set sostenuto on */
+static void awe_sostenuto_on(int voice, int forced)
+{
+ if (IS_NO_EFFECT(voice) && !forced) return;
+ voices[voice].sostenuto = 127;
+}
+
+
+/* drop sustain */
+static void awe_sustain_off(int voice, int forced)
+{
+ if (voices[voice].state == AWE_ST_SUSTAINED) {
+ awe_note_off(voice);
+ awe_fx_init(voices[voice].ch);
+ awe_voice_init(voice, FALSE);
+ }
+}
+
+
+/* terminate and initialize voice */
+static void awe_terminate_and_init(int voice, int forced)
+{
+ awe_terminate(voice);
+ awe_fx_init(voices[voice].ch);
+ awe_voice_init(voice, TRUE);
+}
+
+
/*================================================================
* synth operation routines
*================================================================*/
#define AWE_VOICE_KEY(v) (0x8000 | (v))
#define AWE_CHAN_KEY(c,n) (((c) << 8) | ((n) + 1))
+#define KEY_CHAN_MATCH(key,c) (((key) >> 8) == (c))
/* initialize the voice */
static void
-awe_voice_init(int voice)
+awe_voice_init(int voice, int init_all)
{
- voices[voice].note = -1;
- voices[voice].velocity = 0;
- voices[voice].sample = NULL;
- voices[voice].state = AWE_ST_OFF;
- voices[voice].cinfo = &channels[voice];
- voices[voice].ch = -1;
- voices[voice].key = AWE_VOICE_KEY(voice);
- voices[voice].time = current_alloc_time;
+ voice_info *vp = &voices[voice];
+
+ /* reset voice search key */
+ if (playing_mode == AWE_PLAY_DIRECT)
+ vp->key = AWE_VOICE_KEY(voice);
+ else
+ vp->key = 0;
/* clear voice mapping */
voice_alloc->map[voice] = 0;
- /* emu8000 parameters */
- voices[voice].apitch = 0;
- voices[voice].avol = 255;
- voices[voice].apan = -1;
+ /* touch the timing flag */
+ vp->time = current_alloc_time;
- /* clear effects */
- if (! awe_channel_mode)
- BZERO(&voices[voice].cinfo->fx, sizeof(FX_Rec));
+ /* initialize other parameters if necessary */
+ if (init_all) {
+ vp->note = -1;
+ vp->velocity = 0;
+ vp->sostenuto = 0;
+
+ vp->sample = NULL;
+ vp->cinfo = &channels[voice];
+ vp->ch = voice;
+ vp->state = AWE_ST_OFF;
+
+ /* emu8000 parameters */
+ vp->apitch = 0;
+ vp->avol = 255;
+ vp->apan = -1;
+ }
+}
+
+/* clear effects */
+static void awe_fx_init(int ch)
+{
+ if (SINGLE_LAYER_MODE() && !misc_modes[AWE_MD_KEEP_EFFECT]) {
+ BZERO(&channels[ch].fx, sizeof(channels[ch].fx));
+ BZERO(&channels[ch].fx_layer, sizeof(&channels[ch].fx_layer));
+ }
}
/* initialize channel info */
-static void awe_channel_init(int ch)
-{
- channels[ch].panning = 0; /* zero center */
- channels[ch].bender = 0; /* zero tune skew */
- channels[ch].bender_range = 200; /* sense * 100 */
- channels[ch].main_vol = 127;
- channels[ch].expression_vol = 127;
- if (awe_channel_mode && IS_DRUM_CHANNEL(ch))
- channels[ch].bank = AWE_DRUM_BANK;
- else
- channels[ch].bank = AWE_DEFAULT_BANK;
- channels[ch].instr = 0;
- channels[ch].vrec = NULL;
- channels[ch].def_vrec = NULL;
- channels[ch].sustained = 0;
- BZERO(&channels[ch].fx, sizeof(FX_Rec));
+static void awe_channel_init(int ch, int init_all)
+{
+ awe_chan_info *cp = &channels[ch];
+ cp->channel = ch;
+ if (init_all) {
+ cp->panning = 0; /* zero center */
+ cp->bender_range = 200; /* sense * 100 */
+ cp->main_vol = 127;
+ if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(ch)) {
+ cp->instr = misc_modes[AWE_MD_DEF_DRUM];
+ cp->bank = AWE_DRUM_BANK;
+ } else {
+ cp->instr = misc_modes[AWE_MD_DEF_PRESET];
+ cp->bank = misc_modes[AWE_MD_DEF_BANK];
+ }
+ cp->vrec = -1;
+ cp->def_vrec = -1;
+ }
+
+ cp->bender = 0; /* zero tune skew */
+ cp->expression_vol = 127;
+ cp->chan_press = 0;
+ cp->sustained = 0;
+
+ if (! misc_modes[AWE_MD_KEEP_EFFECT]) {
+ BZERO(&cp->fx, sizeof(cp->fx));
+ BZERO(&cp->fx_layer, sizeof(cp->fx_layer));
+ }
+}
+
+
+/* change the voice parameters; voice = channel */
+static void awe_voice_change(int voice, fx_affect_func func)
+{
+ int i;
+ switch (playing_mode) {
+ case AWE_PLAY_DIRECT:
+ func(voice, FALSE);
+ break;
+ case AWE_PLAY_INDIRECT:
+ for (i = 0; i < awe_max_voices; i++)
+ if (voices[i].key == AWE_VOICE_KEY(voice))
+ func(i, FALSE);
+ break;
+ default:
+ for (i = 0; i < awe_max_voices; i++)
+ if (KEY_CHAN_MATCH(voices[i].key, voice))
+ func(i, FALSE);
+ break;
+ }
}
@@ -1517,17 +1855,17 @@ awe_open(int dev, int mode)
return RET_ERROR(EBUSY);
awe_busy = TRUE;
- awe_reset(dev);
-
- /* clear sample position flag */
- loaded_once = 0;
/* set default mode */
- init_atten = AWE_DEFAULT_ATTENUATION; /* 12dB below */
- awe_gus_bank = AWE_DEFAULT_BANK;
+ awe_init_misc_modes(FALSE);
+ init_atten = misc_modes[AWE_MD_ZERO_ATTEN];
drum_flags = DEFAULT_DRUM_FLAGS;
- awe_exclusive_sound = TRUE;
- awe_channel_mode = 0;
+ playing_mode = AWE_PLAY_INDIRECT;
+
+ /* reset voices & channels */
+ awe_reset(dev);
+
+ patch_opened = 0;
return 0;
}
@@ -1544,6 +1882,19 @@ awe_close(int dev)
}
+/* set miscellaneous mode parameters
+ */
+static void
+awe_init_misc_modes(int init_all)
+{
+ int i;
+ for (i = 0; i < AWE_MD_END; i++) {
+ if (init_all || misc_modes_default[i].init_each_time)
+ misc_modes[i] = misc_modes_default[i].value;
+ }
+}
+
+
/* sequencer I/O control:
*/
static int
@@ -1551,7 +1902,10 @@ awe_ioctl(int dev, unsigned int cmd, caddr_t arg)
{
switch (cmd) {
case SNDCTL_SYNTH_INFO:
- awe_info.nr_voices = awe_max_voices;
+ if (playing_mode == AWE_PLAY_DIRECT)
+ awe_info.nr_voices = awe_max_voices;
+ else
+ awe_info.nr_voices = AWE_MAX_CHANNELS;
IOCTL_TO_USER((char*)arg, 0, &awe_info, sizeof(awe_info));
return 0;
break;
@@ -1568,62 +1922,107 @@ awe_ioctl(int dev, unsigned int cmd, caddr_t arg)
break;
case SNDCTL_SYNTH_MEMAVL:
- DEBUG(0,printk("AWE32: [ioctl memavl = %d]\n", (int)free_mem_ptr));
- return awe_mem_size - free_mem_ptr*2;
+ return awe_mem_size - awe_free_mem_ptr() * 2;
default:
- ERRMSG(printk("AWE32: unsupported ioctl %d\n", cmd));
+ printk("AWE32: unsupported ioctl %d\n", cmd);
return RET_ERROR(EINVAL);
}
}
+static int voice_in_range(int voice)
+{
+ if (playing_mode == AWE_PLAY_DIRECT) {
+ if (voice < 0 || voice >= awe_max_voices)
+ return FALSE;
+ } else {
+ if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void release_voice(int voice, int do_sustain)
+{
+ if (IS_NO_SOUND(voice))
+ return;
+ if (do_sustain && (voices[voice].cinfo->sustained == 127 ||
+ voices[voice].sostenuto == 127))
+ voices[voice].state = AWE_ST_SUSTAINED;
+ else {
+ awe_note_off(voice);
+ awe_fx_init(voices[voice].ch);
+ awe_voice_init(voice, FALSE);
+ }
+}
+
+/* release all notes */
+static void awe_note_off_all(int do_sustain)
+{
+ int i;
+ for (i = 0; i < awe_max_voices; i++)
+ release_voice(i, do_sustain);
+}
+
/* kill a voice:
* not terminate, just release the voice.
*/
static int
awe_kill_note(int dev, int voice, int note, int velocity)
{
- int i, key, besttime;
+ int i, v2, key;
DEBUG(2,printk("AWE32: [off(%d) nt=%d vl=%d]\n", voice, note, velocity));
- if (awe_channel_mode) {
- if (awe_channel_mode == 2) { /* get channel */
- int v2 = voice_alloc->map[voice] >> 8;
- voice_alloc->map[voice] = 0;
- voice = v2;
- }
+ if (! voice_in_range(voice))
+ return RET_ERROR(EINVAL);
+
+ switch (playing_mode) {
+ case AWE_PLAY_DIRECT:
+ case AWE_PLAY_INDIRECT:
+ key = AWE_VOICE_KEY(voice);
+ break;
+
+ case AWE_PLAY_MULTI2:
+ v2 = voice_alloc->map[voice] >> 8;
+ voice_alloc->map[voice] = 0;
+ voice = v2;
if (voice < 0 || voice >= AWE_MAX_CHANNELS)
return RET_ERROR(EINVAL);
- if (channels[voice].instr > 128)
- note = channels[voice].instr - 128;
+ /* continue to below */
+ default:
key = AWE_CHAN_KEY(voice, note);
- } else {
- if (voice < 0 || voice >= awe_max_voices)
- return RET_ERROR(EINVAL);
- key = AWE_VOICE_KEY(voice);
+ break;
}
- besttime = current_alloc_time + 1;
for (i = 0; i < awe_max_voices; i++) {
- if (voices[i].key == key) {
- if (voices[i].time < besttime)
- besttime = voices[i].time;
- }
- }
- for (i = 0; i < awe_max_voices; i++) {
- if (voices[i].key == key &&
- (!awe_exclusive_sound || voices[i].time == besttime)) {
- if (voices[i].cinfo->sustained)
- voices[i].state = AWE_ST_SUSTAINED;
- else
- awe_note_off(i);
- }
+ if (voices[i].key == key)
+ release_voice(i, TRUE);
}
return 0;
}
+static void start_or_volume_change(int voice, int velocity)
+{
+ voices[voice].velocity = velocity;
+ awe_calc_volume(voice);
+ if (voices[voice].state == AWE_ST_STANDBY)
+ awe_note_on(voice);
+ else if (voices[voice].state == AWE_ST_ON)
+ awe_set_volume(voice, FALSE);
+}
+
+static void set_and_start_voice(int voice, int state)
+{
+ /* calculate pitch & volume parameters */
+ voices[voice].state = state;
+ awe_calc_pitch(voice);
+ awe_calc_volume(voice);
+ if (state == AWE_ST_ON)
+ awe_note_on(voice);
+}
+
/* start a voice:
* if note is 255, identical with aftertouch function.
* Otherwise, start a voice with specified not and volume.
@@ -1634,107 +2033,88 @@ awe_start_note(int dev, int voice, int note, int velocity)
int i, key, state, volonly;
DEBUG(2,printk("AWE32: [on(%d) nt=%d vl=%d]\n", voice, note, velocity));
- volonly = 0;
- if (awe_channel_mode) {
- if (awe_channel_mode == 2) /* get channel */
- voice = voice_alloc->map[voice] >> 8;
- else if (voice & 0x80) { /* channel volume mode */
- voice &= ~0x80;
- volonly = 2;
- }
+ if (! voice_in_range(voice))
+ return RET_ERROR(EINVAL);
+
+ if (velocity == 0)
+ state = AWE_ST_STANDBY; /* stand by for playing */
+ else
+ state = AWE_ST_ON; /* really play */
+ volonly = FALSE;
+
+ switch (playing_mode) {
+ case AWE_PLAY_DIRECT:
+ case AWE_PLAY_INDIRECT:
+ key = AWE_VOICE_KEY(voice);
+ if (note == 255)
+ volonly = TRUE;
+ break;
+
+ case AWE_PLAY_MULTI2:
+ voice = voice_alloc->map[voice] >> 8;
if (voice < 0 || voice >= AWE_MAX_CHANNELS)
return RET_ERROR(EINVAL);
- if (channels[voice].instr > 128)
- note = channels[voice].instr - 128;
+ /* continue to below */
+ default:
if (note >= 128) { /* key volume mode */
note -= 128;
- volonly = 1;
+ volonly = TRUE;
}
key = AWE_CHAN_KEY(voice, note);
- } else {
- if (voice < 0 || voice >= awe_max_voices)
- return RET_ERROR(EINVAL);
- if (voices[voice].cinfo->instr > 128)
- note = voices[voice].cinfo->instr - 128;
- key = AWE_VOICE_KEY(voice);
- if (note == 255)
- volonly = 1;
+ break;
}
/* dynamic volume change */
if (volonly) {
for (i = 0; i < awe_max_voices; i++) {
- if ((volonly == 2 && voices[i].ch == voice) ||
- voices[i].key == key) {
- voices[i].velocity = velocity;
- if (velocity == 0) /* for GUS compatibility */
- return awe_kill_note(dev, voice, note, velocity);
- awe_calc_volume(i);
- state = voices[i].state;
- voices[i].state = AWE_ST_ON;
- if (state == AWE_ST_STANDBY)
- awe_note_on(i);
- else
- awe_set_volume(i, FALSE);
- }
+ if (voices[i].key == key)
+ start_or_volume_change(i, velocity);
}
return 0;
}
- /* stop the sound if still playing */
- if (awe_exclusive_sound) {
- for (i = 0; i < awe_max_voices; i++)
- if (voices[i].key == key &&
- voices[i].state != AWE_ST_OFF)
+ /* if the same note still playing, stop it */
+ for (i = 0; i < awe_max_voices; i++)
+ if (voices[i].key == key) {
+ if (voices[i].state == AWE_ST_ON) {
awe_note_off(i);
- }
+ awe_voice_init(i, FALSE);
+ } else if (voices[i].state == AWE_ST_STANDBY)
+ awe_voice_init(i, TRUE);
+ }
/* allocate voices */
- if (awe_channel_mode)
- awe_alloc_multi_voices(voice, note, velocity);
- else
+ if (playing_mode == AWE_PLAY_DIRECT)
awe_alloc_one_voice(voice, note, velocity);
+ else
+ awe_alloc_multi_voices(voice, note, velocity, key);
- /* turn off other voices (for drums) */
+ /* turn off other voices exlusively (for drums) */
for (i = 0; i < awe_max_voices; i++)
- if (voices[i].key == key && voices[i].sample)
- awe_exclusive_off(i, voices[i].sample->exclusiveClass);
-
- if (velocity == 0)
- state = AWE_ST_STANDBY; /* stand by for playing */
- else
- state = AWE_ST_ON; /* really play */
+ if (voices[i].key == key)
+ awe_exclusive_off(i);
/* set up pitch and volume parameters */
- for (i = 0; i < awe_max_voices; i++)
- if (voices[i].key == key) {
- /* calculate pitch & volume parameters */
- voices[i].state = state;
- voices[i].note = note;
- voices[i].velocity = velocity;
- awe_calc_pitch(i);
- awe_calc_volume(i);
- if (state == AWE_ST_ON)
- awe_note_on(i);
- }
+ for (i = 0; i < awe_max_voices; i++) {
+ if (voices[i].key == key && voices[i].state == AWE_ST_OFF)
+ set_and_start_voice(i, state);
+ }
return 0;
}
/* search instrument from preset table with the specified bank */
-static awe_voice_list *
+static int
awe_search_instr(int bank, int preset)
{
- awe_voice_list *p;
- int maxc;
+ int i;
- for (maxc = AWE_MAX_INFOS, p = preset_table[preset];
- p && maxc; p = p->next_bank, maxc--) {
- if (p->bank == bank)
- return p;
+ for (i = preset_table[preset]; i >= 0; i = infos[i].next_bank) {
+ if (infos[i].bank == bank)
+ return i;
}
- return NULL;
+ return -1;
}
@@ -1742,53 +2122,44 @@ awe_search_instr(int bank, int preset)
static int
awe_set_instr_2(int dev, int voice, int instr_no)
{
- if (awe_channel_mode == 2)
+ if (playing_mode == AWE_PLAY_MULTI2) {
voice = voice_alloc->map[voice] >> 8;
+ if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+ return RET_ERROR(EINVAL);
+ }
return awe_set_instr(dev, voice, instr_no);
}
-/* assign the instrument to a voice */
+/* assign the instrument to a channel; voice is the channel number */
static int
awe_set_instr(int dev, int voice, int instr_no)
{
awe_chan_info *cinfo;
int def_bank;
- if (awe_channel_mode) {
- /* skip the percussion instr in SEQ2 mode */
- if (voice < 0 || voice >= AWE_MAX_CHANNELS)
- return RET_ERROR(EINVAL);
- cinfo = &channels[voice];
- if (IS_DRUM_CHANNEL(voice))
- def_bank = AWE_DRUM_BANK;
- else
- def_bank = cinfo->bank;
- } else {
- if (voice < 0 || voice >= awe_max_voices)
- return RET_ERROR(EINVAL);
- cinfo = voices[voice].cinfo;
- def_bank = cinfo->bank;
- }
+ if (! voice_in_range(voice))
+ return RET_ERROR(EINVAL);
if (instr_no < 0 || instr_no >= AWE_MAX_PRESETS)
return RET_ERROR(EINVAL);
- cinfo->vrec = NULL;
- cinfo->def_vrec = NULL;
- if (instr_no > 128) {
- cinfo->vrec = awe_search_instr(128, cinfo->bank);
- if (cinfo->bank != 0)
- cinfo->def_vrec = awe_search_instr(128, 0);
- } else {
- cinfo->vrec = awe_search_instr(def_bank, instr_no);
- if (def_bank == AWE_DRUM_BANK)
- cinfo->def_vrec = awe_search_instr(def_bank, 0);
- else
- cinfo->def_vrec = awe_search_instr(AWE_DEFAULT_BANK, instr_no);
- }
- if (cinfo->vrec == NULL && cinfo->def_vrec == NULL) {
+ cinfo = &channels[voice];
+
+ if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice))
+ def_bank = AWE_DRUM_BANK; /* always search drumset */
+ else
+ def_bank = cinfo->bank;
+
+ cinfo->vrec = -1;
+ cinfo->def_vrec = -1;
+ cinfo->vrec = awe_search_instr(def_bank, instr_no);
+ if (def_bank == AWE_DRUM_BANK) /* search default drumset */
+ cinfo->def_vrec = awe_search_instr(def_bank, misc_modes[AWE_MD_DEF_DRUM]);
+ else /* search default preset */
+ cinfo->def_vrec = awe_search_instr(misc_modes[AWE_MD_DEF_BANK], instr_no);
+
+ if (cinfo->vrec < 0 && cinfo->def_vrec < 0) {
DEBUG(1,printk("AWE32 Warning: can't find instrument %d\n", instr_no));
- return 0;
}
cinfo->instr = instr_no;
@@ -1804,10 +2175,12 @@ awe_reset(int dev)
int i;
current_alloc_time = 0;
/* don't turn off voice 31 and 32. they are used also for FM voices */
- for (i = 0; i < AWE_NORMAL_VOICES; i++)
+ for (i = 0; i < awe_max_voices; i++) {
awe_terminate(i);
+ awe_voice_init(i, TRUE);
+ }
for (i = 0; i < AWE_MAX_CHANNELS; i++)
- awe_channel_init(i);
+ awe_channel_init(i, TRUE);
for (i = 0; i < 16; i++) {
awe_operations.chn_info[i].controllers[CTL_MAIN_VOLUME] = 127;
awe_operations.chn_info[i].controllers[CTL_EXPRESSION] = 127;
@@ -1839,55 +2212,38 @@ awe_hw_control(int dev, unsigned char *event)
static void
awe_hw_gus_control(int dev, int cmd, unsigned char *event)
{
- int voice;
+ int voice, i, key;
unsigned short p1;
short p2;
int plong;
+ if (MULTI_LAYER_MODE())
+ return;
+ if (cmd == _GUS_NUMVOICES)
+ return;
+
voice = event[3];
+ if (! voice_in_range(voice))
+ return;
+
p1 = *(unsigned short *) &event[4];
p2 = *(short *) &event[6];
plong = *(int*) &event[4];
switch (cmd) {
- case _GUS_NUMVOICES:
- if (p1 >= awe_max_voices)
- DEBUG(0,printk("AWE32: num_voices: voices out of range %d\n", p1));
- break;
case _GUS_VOICESAMPLE:
- if (voice < awe_max_voices)
- awe_set_instr(dev, voice, p1);
- break;
-
- case _GUS_VOICEON:
- if (voice < awe_max_voices)
- awe_note_on(voice);
- break;
-
- case _GUS_VOICEOFF:
- if (voice < awe_max_voices)
- awe_note_off(voice);
- break;
-
- case _GUS_VOICEMODE:
- /* not supported */
- break;
+ awe_set_instr(dev, voice, p1);
+ return;
case _GUS_VOICEBALA:
/* 0 to 15 --> -128 to 127 */
- if (voice < awe_max_voices)
- awe_panning(dev, voice, ((int)p1 << 4) - 128);
- break;
+ awe_panning(dev, voice, ((int)p1 << 4) - 128);
+ return;
- case _GUS_VOICEFREQ:
- if (voice < awe_max_voices)
- awe_calc_pitch_from_freq(voice, plong);
- break;
-
case _GUS_VOICEVOL:
case _GUS_VOICEVOL2:
/* not supported yet */
- break;
+ return;
case _GUS_RAMPRANGE:
case _GUS_RAMPRATE:
@@ -1895,43 +2251,50 @@ awe_hw_gus_control(int dev, int cmd, unsigned char *event)
case _GUS_RAMPON:
case _GUS_RAMPOFF:
/* volume ramping not supported */
- break;
+ return;
case _GUS_VOLUME_SCALE:
- break;
+ return;
case _GUS_VOICE_POS:
- if (voice < awe_max_voices) {
- FX_SET(&voices[voice].cinfo->fx, AWE_FX_SAMPLE_START,
- (short)(plong & 0x7fff));
- FX_SET(&voices[voice].cinfo->fx, AWE_FX_COARSE_SAMPLE_START,
- (plong >> 15) & 0xffff);
+ FX_SET(&channels[voice].fx, AWE_FX_SAMPLE_START,
+ (short)(plong & 0x7fff));
+ FX_SET(&channels[voice].fx, AWE_FX_COARSE_SAMPLE_START,
+ (plong >> 15) & 0xffff);
+ return;
+ }
+
+ key = AWE_VOICE_KEY(voice);
+ for (i = 0; i < awe_max_voices; i++) {
+ if (voices[i].key == key) {
+ switch (cmd) {
+ case _GUS_VOICEON:
+ awe_note_on(i);
+ break;
+
+ case _GUS_VOICEOFF:
+ awe_terminate(i);
+ awe_fx_init(voices[i].ch);
+ awe_voice_init(i, TRUE);
+ break;
+
+ case _GUS_VOICEFADE:
+ awe_note_off(i);
+ awe_fx_init(voices[i].ch);
+ awe_voice_init(i, FALSE);
+ break;
+
+ case _GUS_VOICEFREQ:
+ awe_calc_pitch_from_freq(i, plong);
+ break;
+ }
}
- break;
}
}
#endif
-/* converter function table for realtime paramter change */
-
-static fx_affect_func fx_realtime[] = {
- /* env1: delay, attack, hold, decay, release, sustain, pitch, cutoff*/
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- /* env2: delay, attack, hold, decay, release, sustain */
- NULL, NULL, NULL, NULL, NULL, NULL,
- /* lfo1: delay, freq, volume, pitch, cutoff */
- NULL, awe_fx_tremfrq, awe_fx_tremfrq, awe_fx_fmmod, awe_fx_fmmod,
- /* lfo2: delay, freq, pitch */
- NULL, awe_fx_fm2frq2, awe_fx_fm2frq2,
- /* global: initpitch, chorus, reverb, cutoff, filterQ */
- awe_set_voice_pitch, NULL, NULL, awe_fx_cutoff, NULL,
- /* sample: start, loopstart, loopend */
- NULL, NULL, NULL,
-};
-
-
/* AWE32 specific controls */
static void
awe_hw_awe_control(int dev, int cmd, unsigned char *event)
@@ -1939,49 +2302,41 @@ awe_hw_awe_control(int dev, int cmd, unsigned char *event)
int voice;
unsigned short p1;
short p2;
- int chn;
awe_chan_info *cinfo;
+ FX_Rec *fx;
int i;
- chn = event[1];
voice = event[3];
- p1 = *(unsigned short *) &event[4];
- p2 = *(short *) &event[6];
-
- if (awe_channel_mode) {
+ if (! voice_in_range(voice))
+ return;
+
+ if (playing_mode == AWE_PLAY_MULTI2) {
+ voice = voice_alloc->map[voice] >> 8;
if (voice < 0 || voice >= AWE_MAX_CHANNELS)
return;
- cinfo = &channels[voice];
- } else {
- if (voice < 0 || voice >= awe_max_voices)
- return;
- cinfo = voices[voice].cinfo;
}
+ p1 = *(unsigned short *) &event[4];
+ p2 = *(short *) &event[6];
+ cinfo = &channels[voice];
+
switch (cmd) {
case _AWE_DEBUG_MODE:
debug_mode = p1;
printk("AWE32: debug mode = %d\n", debug_mode);
break;
case _AWE_REVERB_MODE:
- if (p1 <= 7) {
- reverb_mode = p1;
- DEBUG(0,printk("AWE32: reverb mode %d\n", reverb_mode));
- awe_set_reverb_mode(reverb_mode);
- }
+ awe_set_reverb_mode(p1);
break;
case _AWE_CHORUS_MODE:
- if (p1 <= 7) {
- chorus_mode = p1;
- DEBUG(0,printk("AWE32: chorus mode %d\n", chorus_mode));
- awe_set_chorus_mode(chorus_mode);
- }
+ awe_set_chorus_mode(p1);
break;
case _AWE_REMOVE_LAST_SAMPLES:
DEBUG(0,printk("AWE32: remove last samples\n"));
- awe_remove_samples();
+ if (locked_sf_id > 0)
+ awe_remove_samples(locked_sf_id);
break;
case _AWE_INITIALIZE_CHIP:
@@ -1989,83 +2344,92 @@ awe_hw_awe_control(int dev, int cmd, unsigned char *event)
break;
case _AWE_SEND_EFFECT:
+ fx = &cinfo->fx;
+ i = FX_FLAG_SET;
+ if (p1 >= 0x100) {
+ int layer = (p1 >> 8);
+ if (layer >= 0 && layer < MAX_LAYERS)
+ fx = &cinfo->fx_layer[layer];
+ p1 &= 0xff;
+ }
+ if (p1 & 0x40) i = FX_FLAG_OFF;
+ if (p1 & 0x80) i = FX_FLAG_ADD;
+ p1 &= 0x3f;
if (p1 < AWE_FX_END) {
- FX_SET(&cinfo->fx, p1, p2);
- DEBUG(0,printk("AWE32: effects (%d) %d %d\n", voice, p1, cinfo->fx.val[p1]));
- FX_SET(&cinfo->fx, p1, p2);
- if (fx_realtime[p1]) {
+ DEBUG(0,printk("AWE32: effects (%d) %d %d\n", voice, p1, p2));
+ if (i == FX_FLAG_SET)
+ FX_SET(fx, p1, p2);
+ else if (i == FX_FLAG_ADD)
+ FX_ADD(fx, p1, p2);
+ else
+ FX_UNSET(fx, p1);
+ if (i != FX_FLAG_OFF && parm_defs[p1].realtime) {
DEBUG(0,printk("AWE32: fx_realtime (%d)\n", voice));
- awe_voice_change(voice, fx_realtime[p1]);
+ awe_voice_change(voice, parm_defs[p1].realtime);
}
}
break;
+ case _AWE_RESET_CHANNEL:
+ awe_channel_init(voice, !p1);
+ break;
+
case _AWE_TERMINATE_ALL:
- DEBUG(0,printk("AWE32: terminate all\n"));
awe_reset(0);
break;
case _AWE_TERMINATE_CHANNEL:
- awe_voice_change(voice, (fx_affect_func)awe_terminate);
+ awe_voice_change(voice, awe_terminate_and_init);
break;
+ case _AWE_RELEASE_ALL:
+ awe_note_off_all(FALSE);
+ break;
case _AWE_NOTEOFF_ALL:
- for (i = 0; i < awe_max_voices; i++)
- awe_note_off(i);
+ awe_note_off_all(TRUE);
break;
case _AWE_INITIAL_VOLUME:
DEBUG(0,printk("AWE32: init attenuation %d\n", p1));
- init_atten = p1;
+ if (p2 == 0) /* absolute value */
+ init_atten = (short)p1;
+ else /* relative value */
+ init_atten = misc_modes[AWE_MD_ZERO_ATTEN] + (short)p1;
+ if (init_atten < 0) init_atten = 0;
for (i = 0; i < awe_max_voices; i++)
- awe_set_voice_vol(i, FALSE);
+ awe_set_voice_vol(i, TRUE);
break;
- case _AWE_SET_GUS_BANK:
- DEBUG(0,printk("AWE32: set gus bank %d\n", p1));
- awe_gus_bank = p1;
+ case _AWE_CHN_PRESSURE:
+ cinfo->chan_press = p1;
+ p1 = p1 * misc_modes[AWE_MD_MOD_SENSE] / 1200;
+ FX_ADD(&cinfo->fx, AWE_FX_LFO1_PITCH, p1);
+ awe_voice_change(voice, awe_fx_fmmod);
+ FX_ADD(&cinfo->fx, AWE_FX_LFO2_PITCH, p1);
+ awe_voice_change(voice, awe_fx_fm2frq2);
break;
-
- /* v0.3 stuffs */
+
case _AWE_CHANNEL_MODE:
DEBUG(0,printk("AWE32: channel mode = %d\n", p1));
- awe_channel_mode = p1;
+ playing_mode = p1;
awe_reset(0);
break;
case _AWE_DRUM_CHANNELS:
DEBUG(0,printk("AWE32: drum flags = %x\n", p1));
- drum_flags = p1;
+ drum_flags = *(unsigned int*)&event[4];
break;
- case _AWE_EXCLUSIVE_SOUND:
- DEBUG(0,printk("AWE32: exclusive mode = %d\n", p1));
- awe_exclusive_sound = p1;
+ case _AWE_MISC_MODE:
+ DEBUG(0,printk("AWE32: misc mode = %d %d\n", p1, p2));
+ if (p1 > AWE_MD_VERSION && p1 < AWE_MD_END)
+ misc_modes[p1] = p2;
+ break;
+
+ case _AWE_EQUALIZER:
+ awe_equalizer((int)p1, (int)p2);
break;
- case _AWE_GET_CURRENT_MODE:
- {
- awe_mode_rec tmprec;
- tmprec.base_addr = awe_base;
- tmprec.mem_size = awe_mem_size / 2;
- tmprec.max_voices = awe_max_voices;
- tmprec.max_infos = AWE_MAX_INFOS;
- tmprec.max_samples = AWE_MAX_SAMPLES;
- tmprec.current_sf_id = current_sf_id;
- tmprec.free_mem = free_mem_ptr;
- tmprec.free_info = free_info;
- tmprec.free_sample = free_sample;
- tmprec.reverb_mode = reverb_mode;
- tmprec.chorus_mode = chorus_mode;
- tmprec.init_atten = init_atten;
- tmprec.channel_mode = awe_channel_mode;
- tmprec.gus_bank = awe_gus_bank;
- tmprec.exclusive_sound = awe_exclusive_sound;
- tmprec.drum_flags = drum_flags;
- tmprec.debug_mode = debug_mode;
- IOCTL_TO_USER(*(awe_mode_rec**)&event[4], 0, &tmprec, sizeof(tmprec));
- break;
- }
default:
DEBUG(0,printk("AWE32: hw control cmd=%d voice=%d\n", cmd, voice));
break;
@@ -2073,6 +2437,218 @@ awe_hw_awe_control(int dev, int cmd, unsigned char *event)
}
+/* voice pressure change */
+static void
+awe_aftertouch(int dev, int voice, int pressure)
+{
+ int note;
+
+ DEBUG(2,printk("AWE32: [after(%d) %d]\n", voice, pressure));
+ if (! voice_in_range(voice))
+ return;
+
+ switch (playing_mode) {
+ case AWE_PLAY_DIRECT:
+ case AWE_PLAY_INDIRECT:
+ awe_start_note(dev, voice, 255, pressure);
+ break;
+ case AWE_PLAY_MULTI2:
+ note = (voice_alloc->map[voice] & 0xff) - 1;
+ awe_start_note(dev, voice, note + 0x80, pressure);
+ break;
+ }
+}
+
+
+/* voice control change */
+static void
+awe_controller(int dev, int voice, int ctrl_num, int value)
+{
+ int i;
+ awe_chan_info *cinfo;
+
+ if (! voice_in_range(voice))
+ return;
+
+ if (playing_mode == AWE_PLAY_MULTI2) {
+ voice = voice_alloc->map[voice] >> 8;
+ if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+ return;
+ }
+
+ cinfo = &channels[voice];
+
+ switch (ctrl_num) {
+ case CTL_BANK_SELECT: /* MIDI control #0 */
+ DEBUG(2,printk("AWE32: [bank(%d) %d]\n", voice, value));
+ if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(voice) &&
+ !misc_modes[AWE_MD_TOGGLE_DRUM_BANK])
+ break;
+ cinfo->bank = value;
+ if (cinfo->bank == AWE_DRUM_BANK)
+ DRUM_CHANNEL_ON(cinfo->channel);
+ else
+ DRUM_CHANNEL_OFF(cinfo->channel);
+ awe_set_instr(dev, voice, cinfo->instr);
+ break;
+
+ case CTL_MODWHEEL: /* MIDI control #1 */
+ DEBUG(2,printk("AWE32: [modwheel(%d) %d]\n", voice, value));
+ i = value * misc_modes[AWE_MD_MOD_SENSE] / 1200;
+ FX_ADD(&cinfo->fx, AWE_FX_LFO1_PITCH, i);
+ awe_voice_change(voice, awe_fx_fmmod);
+ FX_ADD(&cinfo->fx, AWE_FX_LFO2_PITCH, i);
+ awe_voice_change(voice, awe_fx_fm2frq2);
+ break;
+
+ case CTRL_PITCH_BENDER: /* SEQ1 V2 contorl */
+ DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, value));
+ /* zero centered */
+ cinfo->bender = value;
+ awe_voice_change(voice, awe_set_voice_pitch);
+ break;
+
+ case CTRL_PITCH_BENDER_RANGE: /* SEQ1 V2 control */
+ DEBUG(2,printk("AWE32: [range(%d) %d]\n", voice, value));
+ /* value = sense x 100 */
+ cinfo->bender_range = value;
+ /* no audible pitch change yet.. */
+ break;
+
+ case CTL_EXPRESSION: /* MIDI control #11 */
+ if (SINGLE_LAYER_MODE())
+ value /= 128;
+ case CTRL_EXPRESSION: /* SEQ1 V2 control */
+ DEBUG(2,printk("AWE32: [expr(%d) %d]\n", voice, value));
+ /* 0 - 127 */
+ cinfo->expression_vol = value;
+ awe_voice_change(voice, awe_set_voice_vol);
+ break;
+
+ case CTL_PAN: /* MIDI control #10 */
+ DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, value));
+ /* (0-127) -> signed 8bit */
+ cinfo->panning = value * 2 - 128;
+ if (misc_modes[AWE_MD_REALTIME_PAN])
+ awe_voice_change(voice, awe_set_pan);
+ break;
+
+ case CTL_MAIN_VOLUME: /* MIDI control #7 */
+ if (SINGLE_LAYER_MODE())
+ value = (value * 100) / 16383;
+ case CTRL_MAIN_VOLUME: /* SEQ1 V2 control */
+ DEBUG(2,printk("AWE32: [mainvol(%d) %d]\n", voice, value));
+ /* 0 - 127 */
+ cinfo->main_vol = value;
+ awe_voice_change(voice, awe_set_voice_vol);
+ break;
+
+ case CTL_EXT_EFF_DEPTH: /* reverb effects: 0-127 */
+ DEBUG(2,printk("AWE32: [reverb(%d) %d]\n", voice, value));
+ FX_SET(&cinfo->fx, AWE_FX_REVERB, value * 2);
+ break;
+
+ case CTL_CHORUS_DEPTH: /* chorus effects: 0-127 */
+ DEBUG(2,printk("AWE32: [chorus(%d) %d]\n", voice, value));
+ FX_SET(&cinfo->fx, AWE_FX_CHORUS, value * 2);
+ break;
+
+#ifdef AWE_ACCEPT_ALL_SOUNDS_CONTROLL
+ case 120: /* all sounds off */
+ awe_note_off_all(FALSE);
+ break;
+ case 123: /* all notes off */
+ awe_note_off_all(TRUE);
+ break;
+#endif
+
+ case CTL_SUSTAIN: /* MIDI control #64 */
+ cinfo->sustained = value;
+ if (value != 127)
+ awe_voice_change(voice, awe_sustain_off);
+ break;
+
+ case CTL_SOSTENUTO: /* MIDI control #66 */
+ if (value == 127)
+ awe_voice_change(voice, awe_sostenuto_on);
+ else
+ awe_voice_change(voice, awe_sustain_off);
+ break;
+
+ default:
+ DEBUG(0,printk("AWE32: [control(%d) ctrl=%d val=%d]\n",
+ voice, ctrl_num, value));
+ break;
+ }
+}
+
+
+/* voice pan change (value = -128 - 127) */
+static void
+awe_panning(int dev, int voice, int value)
+{
+ awe_chan_info *cinfo;
+
+ if (! voice_in_range(voice))
+ return;
+
+ if (playing_mode == AWE_PLAY_MULTI2) {
+ voice = voice_alloc->map[voice] >> 8;
+ if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+ return;
+ }
+
+ cinfo = &channels[voice];
+ cinfo->panning = value;
+ DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, cinfo->panning));
+ if (misc_modes[AWE_MD_REALTIME_PAN])
+ awe_voice_change(voice, awe_set_pan);
+}
+
+
+/* volume mode change */
+static void
+awe_volume_method(int dev, int mode)
+{
+ /* not impremented */
+ DEBUG(0,printk("AWE32: [volmethod mode=%d]\n", mode));
+}
+
+
+#ifndef AWE_NO_PATCHMGR
+/* patch manager */
+static int
+awe_patchmgr(int dev, struct patmgr_info *rec)
+{
+ printk("AWE32 Warning: patch manager control not supported\n");
+ return 0;
+}
+#endif
+
+
+/* pitch wheel change: 0-16384 */
+static void
+awe_bender(int dev, int voice, int value)
+{
+ awe_chan_info *cinfo;
+
+ if (! voice_in_range(voice))
+ return;
+
+ if (playing_mode == AWE_PLAY_MULTI2) {
+ voice = voice_alloc->map[voice] >> 8;
+ if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+ return;
+ }
+
+ /* convert to zero centered value */
+ cinfo = &channels[voice];
+ cinfo->bender = value - 8192;
+ DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, cinfo->bender));
+ awe_voice_change(voice, awe_set_voice_pitch);
+}
+
+
/*----------------------------------------------------------------
* load a sound patch:
* three types of patches are accepted: AWE, GUS, and SYSEX.
@@ -2094,12 +2670,12 @@ awe_load_patch(int dev, int format, const char *addr,
/* no system exclusive message supported yet */
return 0;
} else if (format != AWE_PATCH) {
- FATALERR(printk("AWE32 Error: Invalid patch format (key) 0x%x\n", format));
+ printk("AWE32 Error: Invalid patch format (key) 0x%x\n", format);
return RET_ERROR(EINVAL);
}
if (count < AWE_PATCH_INFO_SIZE) {
- FATALERR(printk("AWE32 Error: Patch header too short\n"));
+ printk("AWE32 Error: Patch header too short\n");
return RET_ERROR(EINVAL);
}
COPY_FROM_USER(((char*)&patch) + offs, addr, offs,
@@ -2107,27 +2683,43 @@ awe_load_patch(int dev, int format, const char *addr,
count -= AWE_PATCH_INFO_SIZE;
if (count < patch.len) {
- FATALERR(printk("AWE32 Warning: Patch record too short (%d<%d)\n",
- count, (int)patch.len));
- patch.len = count;
+ printk("AWE32: sample: Patch record too short (%d<%d)\n",
+ count, patch.len);
+ return RET_ERROR(EINVAL);
}
switch (patch.type) {
case AWE_LOAD_INFO:
- rc = awe_load_info(&patch, addr);
+ rc = awe_load_info(&patch, addr, count);
break;
-
case AWE_LOAD_DATA:
- rc = awe_load_data(&patch, addr);
- /*
- if (!pmgr_flag && rc == 0)
- pmgr_inform(dev, PM_E_PATCH_LOADED, instr, free_sample, 0, 0);
- */
+ rc = awe_load_data(&patch, addr, count);
+ break;
+ case AWE_OPEN_PATCH:
+ rc = awe_open_patch(&patch, addr, count);
+ break;
+ case AWE_CLOSE_PATCH:
+ rc = awe_close_patch(&patch, addr, count);
+ break;
+ case AWE_UNLOAD_PATCH:
+ rc = awe_unload_patch(&patch, addr, count);
+ break;
+ case AWE_REPLACE_DATA:
+ rc = awe_replace_data(&patch, addr, count);
+ break;
+ case AWE_MAP_PRESET:
+ rc = awe_load_map(&patch, addr, count);
+ break;
+ case AWE_LOAD_CHORUS_FX:
+ rc = awe_load_chorus_fx(&patch, addr, count);
+ break;
+ case AWE_LOAD_REVERB_FX:
+ rc = awe_load_reverb_fx(&patch, addr, count);
break;
default:
- FATALERR(printk("AWE32 Error: unknown patch format type %d\n",
- patch.type));
+ printk("AWE32 Error: unknown patch format type %d\n",
+ patch.type);
rc = RET_ERROR(EINVAL);
}
@@ -2135,140 +2727,384 @@ awe_load_patch(int dev, int format, const char *addr,
}
+/* create an sflist record */
+static int
+awe_create_sf(int type, char *name)
+{
+ sf_list *rec;
+
+ /* terminate sounds */
+ awe_reset(0);
+ if (current_sf_id >= max_sfs) {
+ int newsize = max_sfs + AWE_MAX_SF_LISTS;
+ sf_list *newlist = my_realloc(sflists, sizeof(sf_list)*max_sfs,
+ sizeof(sf_list)*newsize);
+ if (newlist == NULL)
+ return 1;
+ sflists = newlist;
+ max_sfs = newsize;
+ }
+ rec = &sflists[current_sf_id];
+ rec->sf_id = current_sf_id + 1;
+ rec->type = type;
+ if (current_sf_id == 0 || (type & AWE_PAT_LOCKED) != 0)
+ locked_sf_id = current_sf_id + 1;
+ /*
+ if (name)
+ MEMCPY(rec->name, name, AWE_PATCH_NAME_LEN);
+ else
+ BZERO(rec->name, AWE_PATCH_NAME_LEN);
+ */
+ rec->num_info = awe_free_info();
+ rec->num_sample = awe_free_sample();
+ rec->mem_ptr = awe_free_mem_ptr();
+ rec->infos = -1;
+ rec->samples = -1;
+
+ current_sf_id++;
+ return 0;
+}
+
+
+/* open patch; create sf list and set opened flag */
+static int
+awe_open_patch(awe_patch_info *patch, const char *addr, int count)
+{
+ awe_open_parm parm;
+ COPY_FROM_USER(&parm, addr, AWE_PATCH_INFO_SIZE, sizeof(parm));
+ if (awe_create_sf(parm.type, parm.name)) {
+ printk("AWE32: can't open: failed to alloc new list\n");
+ return RET_ERROR(ENOSPC);
+ }
+ patch_opened = TRUE;
+ return current_sf_id;
+}
+
+/* check if the patch is already opened */
+static int
+check_patch_opened(int type, char *name)
+{
+ if (! patch_opened) {
+ if (awe_create_sf(type, name)) {
+ printk("AWE32: failed to alloc new list\n");
+ return RET_ERROR(ENOSPC);
+ }
+ patch_opened = TRUE;
+ return current_sf_id;
+ }
+ return current_sf_id;
+}
+
+/* close the patch; if no voice is loaded, remove the patch */
+static int
+awe_close_patch(awe_patch_info *patch, const char *addr, int count)
+{
+ if (patch_opened && current_sf_id > 0) {
+ /* if no voice is loaded, release the current patch */
+ if (sflists[current_sf_id-1].infos == -1)
+ awe_remove_samples(current_sf_id - 1);
+ }
+ patch_opened = 0;
+ return 0;
+}
+
+
+/* remove the latest patch */
+static int
+awe_unload_patch(awe_patch_info *patch, const char *addr, int count)
+{
+ if (current_sf_id > 0)
+ awe_remove_samples(current_sf_id - 1);
+ return 0;
+}
+
+/* allocate voice info list records */
+static int alloc_new_info(int nvoices)
+{
+ int newsize, free_info;
+ awe_voice_list *newlist;
+ free_info = awe_free_info();
+ if (free_info + nvoices >= max_infos) {
+ do {
+ newsize = max_infos + AWE_MAX_INFOS;
+ } while (free_info + nvoices >= newsize);
+ newlist = my_realloc(infos, sizeof(awe_voice_list)*max_infos,
+ sizeof(awe_voice_list)*newsize);
+ if (newlist == NULL) {
+ printk("AWE32: can't alloc info table\n");
+ return RET_ERROR(ENOSPC);
+ }
+ infos = newlist;
+ max_infos = newsize;
+ }
+ return 0;
+}
+
+/* allocate sample info list records */
+static int alloc_new_sample(void)
+{
+ int newsize, free_sample;
+ awe_sample_list *newlist;
+ free_sample = awe_free_sample();
+ if (free_sample >= max_samples) {
+ newsize = max_samples + AWE_MAX_SAMPLES;
+ newlist = my_realloc(samples,
+ sizeof(awe_sample_list)*max_samples,
+ sizeof(awe_sample_list)*newsize);
+ if (newlist == NULL) {
+ printk("AWE32: can't alloc sample table\n");
+ return RET_ERROR(ENOSPC);
+ }
+ samples = newlist;
+ max_samples = newsize;
+ }
+ return 0;
+}
+
+/* load voice map */
+static int
+awe_load_map(awe_patch_info *patch, const char *addr, int count)
+{
+ awe_voice_map map;
+ awe_voice_list *rec;
+ int free_info;
+
+ if (check_patch_opened(AWE_PAT_TYPE_MAP, NULL) < 0)
+ return RET_ERROR(ENOSPC);
+ if (alloc_new_info(1) < 0)
+ return RET_ERROR(ENOSPC);
+
+ COPY_FROM_USER(&map, addr, AWE_PATCH_INFO_SIZE, sizeof(map));
+
+ free_info = awe_free_info();
+ rec = &infos[free_info];
+ rec->bank = map.map_bank;
+ rec->instr = map.map_instr;
+ rec->type = V_ST_MAPPED;
+ rec->disabled = FALSE;
+ awe_init_voice_info(&rec->v);
+ if (map.map_key >= 0) {
+ rec->v.low = map.map_key;
+ rec->v.high = map.map_key;
+ }
+ rec->v.start = map.src_instr;
+ rec->v.end = map.src_bank;
+ rec->v.fixkey = map.src_key;
+ rec->v.sf_id = current_sf_id;
+ add_info_list(free_info);
+ add_sf_info(free_info);
+
+ return 0;
+}
+
/* load voice information data */
static int
-awe_load_info(awe_patch_info *patch, const char *addr)
+awe_load_info(awe_patch_info *patch, const char *addr, int count)
{
- awe_voice_list *rec, *curp;
- long offset;
- short i, nvoices;
- unsigned char bank, instr;
+ int offset;
+ awe_voice_rec_hdr hdr;
+ int i;
int total_size;
- if (patch->len < AWE_VOICE_REC_SIZE) {
- FATALERR(printk("AWE32 Error: invalid patch info length\n"));
+ if (count < AWE_VOICE_REC_SIZE) {
+ printk("AWE32 Error: invalid patch info length\n");
return RET_ERROR(EINVAL);
}
offset = AWE_PATCH_INFO_SIZE;
- GET_BYTE_FROM_USER(bank, addr, offset); offset++;
- GET_BYTE_FROM_USER(instr, addr, offset); offset++;
- GET_SHORT_FROM_USER(nvoices, addr, offset); offset+=2;
+ COPY_FROM_USER((char*)&hdr, addr, offset, AWE_VOICE_REC_SIZE);
+ offset += AWE_VOICE_REC_SIZE;
- if (nvoices <= 0 || nvoices >= 100) {
- FATALERR(printk("AWE32 Error: Illegal voice number %d\n", nvoices));
+ if (hdr.nvoices <= 0 || hdr.nvoices >= 100) {
+ printk("AWE32 Error: Illegal voice number %d\n", hdr.nvoices);
return RET_ERROR(EINVAL);
}
- if (free_info + nvoices > AWE_MAX_INFOS) {
- ERRMSG(printk("AWE32 Error: Too many voice informations\n"));
- return RET_ERROR(ENOSPC);
+ total_size = AWE_VOICE_REC_SIZE + AWE_VOICE_INFO_SIZE * hdr.nvoices;
+ if (count < total_size) {
+ printk("AWE32 Error: patch length(%d) is smaller than nvoices(%d)\n",
+ count, hdr.nvoices);
+ return RET_ERROR(EINVAL);
}
- total_size = AWE_VOICE_REC_SIZE + AWE_VOICE_INFO_SIZE * nvoices;
- if (patch->len < total_size) {
- ERRMSG(printk("AWE32 Error: patch length(%d) is smaller than nvoices(%d)\n",
- (int)patch->len, nvoices));
- return RET_ERROR(EINVAL);
+ if (check_patch_opened(AWE_PAT_TYPE_MISC, NULL) < 0)
+ return RET_ERROR(ENOSPC);
+
+#if 0 /* it looks like not so useful.. */
+ /* check if the same preset already exists in the info list */
+ for (i = sflists[current_sf_id-1].infos; i >= 0; i = infos[i].next) {
+ if (infos[i].disabled) continue;
+ if (infos[i].bank == hdr.bank && infos[i].instr == hdr.instr) {
+ /* in exclusive mode, do skip loading this */
+ if (hdr.write_mode == AWE_WR_EXCLUSIVE)
+ return 0;
+ /* in replace mode, disable the old data */
+ else if (hdr.write_mode == AWE_WR_REPLACE)
+ infos[i].disabled = TRUE;
+ }
}
+ if (hdr.write_mode == AWE_WR_REPLACE)
+ rebuild_preset_list();
+#endif
- curp = awe_search_instr(bank, instr);
- for (i = 0; i < nvoices; i++) {
- rec = &infos[free_info + i];
+ if (alloc_new_info(hdr.nvoices) < 0)
+ return RET_ERROR(ENOSPC);
- rec->bank = bank;
- rec->instr = instr;
- if (i < nvoices - 1)
- rec->next_instr = rec + 1;
- else
- rec->next_instr = curp;
- rec->next_bank = NULL;
+ for (i = 0; i < hdr.nvoices; i++) {
+ int rec = awe_free_info();
+
+ infos[rec].bank = hdr.bank;
+ infos[rec].instr = hdr.instr;
+ infos[rec].type = V_ST_NORMAL;
+ infos[rec].disabled = FALSE;
/* copy awe_voice_info parameters */
- COPY_FROM_USER(&rec->v, addr, offset, AWE_VOICE_INFO_SIZE);
+ COPY_FROM_USER(&infos[rec].v, addr, offset, AWE_VOICE_INFO_SIZE);
offset += AWE_VOICE_INFO_SIZE;
- rec->v.sf_id = current_sf_id;
- if (rec->v.mode & AWE_MODE_INIT_PARM)
- awe_init_voice_parm(&rec->v.parm);
- awe_set_sample(&rec->v);
+ infos[rec].v.sf_id = current_sf_id;
+ if (infos[rec].v.mode & AWE_MODE_INIT_PARM)
+ awe_init_voice_parm(&infos[rec].v.parm);
+ awe_set_sample(&infos[rec].v);
+ add_info_list(rec);
+ add_sf_info(rec);
}
- /* prepend to top of the list */
- infos[free_info].next_bank = preset_table[instr];
- preset_table[instr] = &infos[free_info];
- free_info += nvoices;
-
return 0;
}
-
/* load wave sample data */
static int
-awe_load_data(awe_patch_info *patch, const char *addr)
+awe_load_data(awe_patch_info *patch, const char *addr, int count)
{
- long offset;
- int size;
- int rc;
+ int offset, size;
+ int rc, free_sample;
+ awe_sample_info *rec;
- if (free_sample >= AWE_MAX_SAMPLES) {
- ERRMSG(printk("AWE32 Error: Sample table full\n"));
+ if (check_patch_opened(AWE_PAT_TYPE_MISC, NULL) < 0)
return RET_ERROR(ENOSPC);
- }
- size = (patch->len - AWE_SAMPLE_INFO_SIZE) / 2;
+ if (alloc_new_sample() < 0)
+ return RET_ERROR(ENOSPC);
+
+ free_sample = awe_free_sample();
+ rec = &samples[free_sample].v;
+
+ size = (count - AWE_SAMPLE_INFO_SIZE) / 2;
offset = AWE_PATCH_INFO_SIZE;
- COPY_FROM_USER(&samples[free_sample], addr, offset,
- AWE_SAMPLE_INFO_SIZE);
+ COPY_FROM_USER(rec, addr, offset, AWE_SAMPLE_INFO_SIZE);
offset += AWE_SAMPLE_INFO_SIZE;
- if (size != samples[free_sample].size) {
- ERRMSG(printk("AWE32 Warning: sample size differed (%d != %d)\n",
- (int)samples[free_sample].size, (int)size));
- samples[free_sample].size = size;
+ if (size != rec->size) {
+ printk("AWE32: load: sample size differed (%d != %d)\n",
+ rec->size, size);
+ return RET_ERROR(EINVAL);
}
- if (samples[free_sample].size > 0)
- if ((rc = awe_write_wave_data(addr, offset, size)) != 0)
+ if (rec->size > 0)
+ if ((rc = awe_write_wave_data(addr, offset, rec, -1)) != 0)
return rc;
- awe_check_loaded();
- samples[free_sample].sf_id = current_sf_id;
+ rec->sf_id = current_sf_id;
+
+ add_sf_sample(free_sample);
- free_sample++;
return 0;
}
-/* check the other samples are already loaded */
-static void
-awe_check_loaded(void)
+
+/* replace wave sample data */
+static int
+awe_replace_data(awe_patch_info *patch, const char *addr, int count)
{
- if (!loaded_once) {
- /* it's the first time */
- if (current_sf_id == 1) {
- last_sample = free_sample;
- last_info = free_info;
- }
- current_sf_id++;
- loaded_once = 1;
+ int offset;
+ int size;
+ int rc, i;
+ int channels;
+ awe_sample_info cursmp;
+ int save_mem_ptr;
+
+ if (! patch_opened) {
+ printk("AWE32: replace: patch not opened\n");
+ return RET_ERROR(EINVAL);
+ }
+
+ size = (count - AWE_SAMPLE_INFO_SIZE) / 2;
+ offset = AWE_PATCH_INFO_SIZE;
+ COPY_FROM_USER(&cursmp, addr, offset, AWE_SAMPLE_INFO_SIZE);
+ offset += AWE_SAMPLE_INFO_SIZE;
+ if (cursmp.size == 0 || size != cursmp.size) {
+ printk("AWE32: replace: illegal sample size (%d!=%d)\n",
+ cursmp.size, size);
+ return RET_ERROR(EINVAL);
+ }
+ channels = patch->optarg;
+ if (channels <= 0 || channels > AWE_NORMAL_VOICES) {
+ printk("AWE32: replace: illegal channels %d\n", channels);
+ return RET_ERROR(EINVAL);
+ }
+
+ for (i = sflists[current_sf_id-1].samples;
+ i >= 0; i = samples[i].next) {
+ if (samples[i].v.sample == cursmp.sample)
+ break;
+ }
+ if (i < 0) {
+ printk("AWE32: replace: cannot find existing sample data %d\n",
+ cursmp.sample);
+ return RET_ERROR(EINVAL);
}
+
+ if (samples[i].v.size != cursmp.size) {
+ printk("AWE32: replace: exiting size differed (%d!=%d)\n",
+ samples[i].v.size, cursmp.size);
+ return RET_ERROR(EINVAL);
+ }
+
+ save_mem_ptr = awe_free_mem_ptr();
+ sflists[current_sf_id-1].mem_ptr = samples[i].v.start - awe_mem_start;
+ MEMCPY(&samples[i].v, &cursmp, sizeof(cursmp));
+ if ((rc = awe_write_wave_data(addr, offset, &samples[i].v, channels)) != 0)
+ return rc;
+ sflists[current_sf_id-1].mem_ptr = save_mem_ptr;
+ samples[i].v.sf_id = current_sf_id;
+
+ return 0;
}
/*----------------------------------------------------------------*/
static const char *readbuf_addr;
-static long readbuf_offs;
+static int readbuf_offs;
static int readbuf_flags;
+#ifdef __FreeBSD__
+static unsigned short *readbuf_loop;
+static int readbuf_loopstart, readbuf_loopend;
+#endif
/* initialize read buffer */
-static void
-awe_init_readbuf(const char *addr, long offset, int size, int mode_flags)
+static int
+readbuf_init(const char *addr, int offset, awe_sample_info *sp)
{
+#ifdef __FreeBSD__
+ readbuf_loop = NULL;
+ readbuf_loopstart = sp->loopstart;
+ readbuf_loopend = sp->loopend;
+ if (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP)) {
+ int looplen = sp->loopend - sp->loopstart;
+ readbuf_loop = my_malloc(looplen * 2);
+ if (readbuf_loop == NULL) {
+ printk("AWE32: can't malloc temp buffer\n");
+ return RET_ERROR(ENOSPC);
+ }
+ }
+#endif
readbuf_addr = addr;
readbuf_offs = offset;
- readbuf_flags = mode_flags;
+ readbuf_flags = sp->mode_flags;
+ return 0;
}
/* read directly from user buffer */
static unsigned short
-awe_read_word(int pos)
+readbuf_word(int pos)
{
unsigned short c;
/* read from user buffer */
@@ -2281,10 +3117,43 @@ awe_read_word(int pos)
}
if (readbuf_flags & AWE_SAMPLE_UNSIGNED)
c ^= 0x8000; /* unsigned -> signed */
+#ifdef __FreeBSD__
+ /* write on cache for reverse loop */
+ if (readbuf_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP)) {
+ if (pos >= readbuf_loopstart && pos < readbuf_loopend)
+ readbuf_loop[pos - readbuf_loopstart] = c;
+ }
+#endif
return c;
}
+#ifdef __FreeBSD__
+/* read from cache */
+static unsigned short
+readbuf_word_cache(int pos)
+{
+ if (pos >= readbuf_loopstart && pos < readbuf_loopend)
+ return readbuf_loop[pos - readbuf_loopstart];
+ return 0;
+}
+
+static void
+readbuf_end(void)
+{
+ if (readbuf_loop) {
+ my_free(readbuf_loop);
+ }
+ readbuf_loop = NULL;
+}
+#else
+
+#define readbuf_word_cache readbuf_word
+#define readbuf_end() /**/
+
+#endif
+
+/*----------------------------------------------------------------*/
#define BLANK_LOOP_START 8
#define BLANK_LOOP_END 40
@@ -2292,28 +3161,26 @@ awe_read_word(int pos)
/* loading onto memory */
static int
-awe_write_wave_data(const char *addr, long offset, int size)
+awe_write_wave_data(const char *addr, int offset, awe_sample_info *sp, int channels)
{
- awe_sample_info *sp = &samples[free_sample];
- int i, truesize;
+ int i, truesize, dram_offset;
int rc;
- unsigned long csum1, csum2;
/* be sure loop points start < end */
if (sp->loopstart > sp->loopend) {
- long tmp = sp->loopstart;
+ int tmp = sp->loopstart;
sp->loopstart = sp->loopend;
sp->loopend = tmp;
}
/* compute true data size to be loaded */
- truesize = size;
+ truesize = sp->size;
if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP)
truesize += sp->loopend - sp->loopstart;
if (sp->mode_flags & AWE_SAMPLE_NO_BLANK)
truesize += BLANK_LOOP_SIZE;
- if (size > 0 && free_mem_ptr + truesize >= awe_mem_size/2) {
- ERRMSG(printk("AWE32 Error: Sample memory full\n"));
+ if (awe_free_mem_ptr() + truesize >= awe_mem_size/2) {
+ printk("AWE32 Error: Sample memory full\n");
return RET_ERROR(ENOSPC);
}
@@ -2321,32 +3188,36 @@ awe_write_wave_data(const char *addr, long offset, int size)
sp->end -= sp->start;
sp->loopstart -= sp->start;
sp->loopend -= sp->start;
- sp->size = truesize;
- sp->start = free_mem_ptr + AWE_DRAM_OFFSET;
- sp->end += free_mem_ptr + AWE_DRAM_OFFSET;
- sp->loopstart += free_mem_ptr + AWE_DRAM_OFFSET;
- sp->loopend += free_mem_ptr + AWE_DRAM_OFFSET;
+ dram_offset = awe_free_mem_ptr() + awe_mem_start;
+ sp->start = dram_offset;
+ sp->end += dram_offset;
+ sp->loopstart += dram_offset;
+ sp->loopend += dram_offset;
- if ((rc = awe_open_dram_for_write(free_mem_ptr)) != 0) {
+ /* set the total size (store onto obsolete checksum value) */
+ if (sp->size == 0)
+ sp->checksum = 0;
+ else
+ sp->checksum = truesize;
+
+ if ((rc = awe_open_dram_for_write(dram_offset, channels)) != 0)
return rc;
- }
- awe_init_readbuf(addr, offset, size, sp->mode_flags);
- csum1 = 0;
- for (i = 0; i < size; i++) {
+ if (readbuf_init(addr, offset, sp) < 0)
+ return RET_ERROR(ENOSPC);
+
+ for (i = 0; i < sp->size; i++) {
unsigned short c;
- c = awe_read_word(i);
- csum1 += c;
+ c = readbuf_word(i);
awe_write_dram(c);
if (i == sp->loopend &&
(sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP))) {
int looplen = sp->loopend - sp->loopstart;
/* copy reverse loop */
int k;
- for (k = 0; k < looplen; k++) {
- /* non-buffered data */
- c = awe_read_word(i - k);
+ for (k = 1; k <= looplen; k++) {
+ c = readbuf_word_cache(i - k);
awe_write_dram(c);
}
if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP) {
@@ -2357,6 +3228,7 @@ awe_write_wave_data(const char *addr, long offset, int size)
}
}
}
+ readbuf_end();
/* if no blank loop is attached in the sample, add it */
if (sp->mode_flags & AWE_SAMPLE_NO_BLANK) {
@@ -2366,40 +3238,13 @@ awe_write_wave_data(const char *addr, long offset, int size)
sp->loopstart = sp->end + BLANK_LOOP_START;
sp->loopend = sp->end + BLANK_LOOP_END;
}
- sp->size += BLANK_LOOP_SIZE;
}
+ sflists[current_sf_id-1].mem_ptr += truesize;
awe_close_dram();
- if (sp->checksum_flag) {
-#ifdef AWE_CHECKSUM_DATA
- if (sp->checksum_flag != 2 && csum1 != sp->checksum) {
- ERRMSG(printk("AWE32: [%d] checksum mismatch on data %x:%x\n",
- free_sample, (int)sp->checksum, (int)csum1));
- return RET_ERROR(NO_DATA_ERR);
- }
-#endif /* AWE_CHECKSUM_DATA */
-#ifdef AWE_CHECKSUM_MEMORY
- if (awe_open_dram_for_read(free_mem_ptr) == 0) {
- csum2 = 0;
- for (i = 0; i < size; i++) {
- unsigned short c;
- c = awe_peek(AWE_SMLD);
- csum2 += c;
- }
- awe_close_dram_for_read();
- if (csum1 != csum2) {
- ERRMSG(printk("AWE32: [%d] checksum mismatch on DRAM %x:%x\n",
- free_sample, (int)csum1, (int)csum2));
- return RET_ERROR(NO_DATA_ERR);
- }
- }
-#endif /* AWE_CHECKSUM_MEMORY */
- }
- free_mem_ptr += sp->size;
- /* re-initialize FM passthrough */
+ /* initialize FM */
awe_init_fm();
- awe_tweak();
return 0;
}
@@ -2435,91 +3280,89 @@ static int
awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag)
{
struct patch_info patch;
- awe_voice_list *rec, *curp;
- long sizeof_patch;
- int note;
+ awe_voice_info *rec;
+ awe_sample_info *smp;
+ int sizeof_patch;
+ int note, free_sample, free_info;
int rc;
- sizeof_patch = (long)&patch.data[0] - (long)&patch; /* header size */
- if (free_sample >= AWE_MAX_SAMPLES) {
- ERRMSG(printk("AWE32 Error: Sample table full\n"));
- return RET_ERROR(ENOSPC);
- }
- if (free_info >= AWE_MAX_INFOS) {
- ERRMSG(printk("AWE32 Error: Too many voice informations\n"));
- return RET_ERROR(ENOSPC);
- }
+ sizeof_patch = (int)((long)&patch.data[0] - (long)&patch); /* header size */
if (size < sizeof_patch) {
- ERRMSG(printk("AWE32 Error: Patch header too short\n"));
+ printk("AWE32 Error: Patch header too short\n");
return RET_ERROR(EINVAL);
}
COPY_FROM_USER(((char*)&patch) + offs, addr, offs, sizeof_patch - offs);
size -= sizeof_patch;
if (size < patch.len) {
- FATALERR(printk("AWE32 Warning: Patch record too short (%d<%d)\n",
- size, (int)patch.len));
- patch.len = size;
+ printk("AWE32 Warning: Patch record too short (%d<%d)\n",
+ size, patch.len);
+ return RET_ERROR(EINVAL);
}
+ if (check_patch_opened(AWE_PAT_TYPE_GUS, NULL) < 0)
+ return RET_ERROR(ENOSPC);
+ if (alloc_new_sample() < 0)
+ return RET_ERROR(ENOSPC);
+ if (alloc_new_info(1))
+ return RET_ERROR(ENOSPC);
- samples[free_sample].sf_id = 0;
- samples[free_sample].sample = free_sample;
- samples[free_sample].start = 0;
- samples[free_sample].end = patch.len;
- samples[free_sample].loopstart = patch.loop_start;
- samples[free_sample].loopend = patch.loop_end + 1;
- samples[free_sample].size = patch.len;
+ free_sample = awe_free_sample();
+ smp = &samples[free_sample].v;
+
+ smp->sample = free_sample;
+ smp->start = 0;
+ smp->end = patch.len;
+ smp->loopstart = patch.loop_start;
+ smp->loopend = patch.loop_end;
+ smp->size = patch.len;
/* set up mode flags */
- samples[free_sample].mode_flags = 0;
+ smp->mode_flags = 0;
if (!(patch.mode & WAVE_16_BITS))
- samples[free_sample].mode_flags |= AWE_SAMPLE_8BITS;
+ smp->mode_flags |= AWE_SAMPLE_8BITS;
if (patch.mode & WAVE_UNSIGNED)
- samples[free_sample].mode_flags |= AWE_SAMPLE_UNSIGNED;
- samples[free_sample].mode_flags |= AWE_SAMPLE_NO_BLANK;
+ smp->mode_flags |= AWE_SAMPLE_UNSIGNED;
+ smp->mode_flags |= AWE_SAMPLE_NO_BLANK;
if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK)))
- samples[free_sample].mode_flags |= AWE_SAMPLE_SINGLESHOT;
+ smp->mode_flags |= AWE_SAMPLE_SINGLESHOT;
if (patch.mode & WAVE_BIDIR_LOOP)
- samples[free_sample].mode_flags |= AWE_SAMPLE_BIDIR_LOOP;
+ smp->mode_flags |= AWE_SAMPLE_BIDIR_LOOP;
if (patch.mode & WAVE_LOOP_BACK)
- samples[free_sample].mode_flags |= AWE_SAMPLE_REVERSE_LOOP;
+ smp->mode_flags |= AWE_SAMPLE_REVERSE_LOOP;
- DEBUG(0,printk("AWE32: [sample %d mode %x]\n", patch.instr_no,
- samples[free_sample].mode_flags));
+ DEBUG(0,printk("AWE32: [sample %d mode %x]\n", patch.instr_no, smp->mode_flags));
if (patch.mode & WAVE_16_BITS) {
/* convert to word offsets */
- samples[free_sample].size /= 2;
- samples[free_sample].end /= 2;
- samples[free_sample].loopstart /= 2;
- samples[free_sample].loopend /= 2;
+ smp->size /= 2;
+ smp->end /= 2;
+ smp->loopstart /= 2;
+ smp->loopend /= 2;
}
- samples[free_sample].checksum_flag = 0;
- samples[free_sample].checksum = 0;
+ smp->checksum_flag = 0;
+ smp->checksum = 0;
- if ((rc = awe_write_wave_data(addr, sizeof_patch,
- samples[free_sample].size)) != 0)
+ if ((rc = awe_write_wave_data(addr, sizeof_patch, smp, -1)) != 0)
return rc;
- awe_check_loaded();
- samples[free_sample].sf_id = current_sf_id;
- free_sample++;
+ smp->sf_id = current_sf_id;
+ add_sf_sample(free_sample);
/* set up voice info */
- rec = &infos[free_info];
- awe_init_voice_info(&rec->v);
- rec->v.sf_id = current_sf_id;
- rec->v.sample = free_sample - 1; /* the last sample */
- rec->v.rate_offset = calc_rate_offset(patch.base_freq);
+ free_info = awe_free_info();
+ rec = &infos[free_info].v;
+ awe_init_voice_info(rec);
+ rec->sample = free_sample; /* the last sample */
+ rec->rate_offset = calc_rate_offset(patch.base_freq);
note = freq_to_note(patch.base_note);
- rec->v.root = note / 100;
- rec->v.tune = -(note % 100);
- rec->v.low = freq_to_note(patch.low_note) / 100;
- rec->v.high = freq_to_note(patch.high_note) / 100;
+ rec->root = note / 100;
+ rec->tune = -(note % 100);
+ rec->low = freq_to_note(patch.low_note) / 100;
+ rec->high = freq_to_note(patch.high_note) / 100;
DEBUG(1,printk("AWE32: [gus base offset=%d, note=%d, range=%d-%d(%d-%d)]\n",
- rec->v.rate_offset, note,
- rec->v.low, rec->v.high,
+ rec->rate_offset, note,
+ rec->low, rec->high,
patch.low_note, patch.high_note));
/* panning position; -128 - 127 => 0-127 */
- rec->v.pan = (patch.panning + 128) / 2;
+ rec->pan = (patch.panning + 128) / 2;
/* detuning is ignored */
/* 6points volume envelope */
@@ -2542,367 +3385,215 @@ awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag)
release += calc_gus_envelope_time
(patch.env_rate[5], patch.env_offset[4],
patch.env_offset[5]);
- rec->v.parm.volatkhld = (calc_parm_attack(attack) << 8) |
+ rec->parm.volatkhld = (calc_parm_attack(attack) << 8) |
calc_parm_hold(hold);
- rec->v.parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) |
+ rec->parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) |
calc_parm_decay(decay);
- rec->v.parm.volrelease = 0x8000 | calc_parm_decay(release);
+ rec->parm.volrelease = 0x8000 | calc_parm_decay(release);
DEBUG(2,printk("AWE32: [gusenv atk=%d, hld=%d, dcy=%d, rel=%d]\n", attack, hold, decay, release));
- rec->v.attenuation = calc_gus_attenuation(patch.env_offset[0]);
+ rec->attenuation = calc_gus_attenuation(patch.env_offset[0]);
}
/* tremolo effect */
if (patch.mode & WAVE_TREMOLO) {
int rate = (patch.tremolo_rate * 1000 / 38) / 42;
- rec->v.parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate;
+ rec->parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate;
DEBUG(2,printk("AWE32: [gusenv tremolo rate=%d, dep=%d, tremfrq=%x]\n",
patch.tremolo_rate, patch.tremolo_depth,
- rec->v.parm.tremfrq));
+ rec->parm.tremfrq));
}
/* vibrato effect */
if (patch.mode & WAVE_VIBRATO) {
int rate = (patch.vibrato_rate * 1000 / 38) / 42;
- rec->v.parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate;
+ rec->parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate;
DEBUG(2,printk("AWE32: [gusenv vibrato rate=%d, dep=%d, tremfrq=%x]\n",
patch.tremolo_rate, patch.tremolo_depth,
- rec->v.parm.tremfrq));
+ rec->parm.tremfrq));
}
/* scale_freq, scale_factor, volume, and fractions not implemented */
- /* set the voice index */
- awe_set_sample(&rec->v);
+ /* append to the tail of the list */
+ infos[free_info].bank = misc_modes[AWE_MD_GUS_BANK];
+ infos[free_info].instr = patch.instr_no;
+ infos[free_info].disabled = FALSE;
+ infos[free_info].type = V_ST_NORMAL;
+ infos[free_info].v.sf_id = current_sf_id;
+
+ add_info_list(free_info);
+ add_sf_info(free_info);
- /* prepend to top of the list */
- curp = awe_search_instr(awe_gus_bank, patch.instr_no);
- rec->bank = awe_gus_bank;
- rec->instr = patch.instr_no;
- rec->next_instr = curp;
- rec->next_bank = preset_table[rec->instr];
- preset_table[rec->instr] = rec;
- free_info++;
+ /* set the voice index */
+ awe_set_sample(rec);
return 0;
}
#endif /* AWE_HAS_GUS_COMPATIBILITY */
-/*----------------------------------------------------------------*/
+/*----------------------------------------------------------------
+ * sample and voice list handlers
+ *----------------------------------------------------------------*/
-/* remove samples with different sf_id from instrument list */
-static awe_voice_list *
-awe_get_removed_list(awe_voice_list *curp)
-{
- awe_voice_list *lastp, **prevp;
- int maxc;
- lastp = curp;
- prevp = &lastp;
- for (maxc = AWE_MAX_INFOS;
- curp && maxc; curp = curp->next_instr, maxc--) {
- if (curp->v.sf_id != 1)
- *prevp = curp->next_instr;
- else
- prevp = &curp->next_instr;
+/* append this to the sf list */
+static void add_sf_info(int rec)
+{
+ int sf_id = infos[rec].v.sf_id;
+ if (sf_id == 0) return;
+ sf_id--;
+ if (sflists[sf_id].infos < 0)
+ sflists[sf_id].infos = rec;
+ else {
+ int i, prev;
+ prev = sflists[sf_id].infos;
+ while ((i = infos[prev].next) >= 0)
+ prev = i;
+ infos[prev].next = rec;
}
- return lastp;
+ infos[rec].next = -1;
+ sflists[sf_id].num_info++;
}
-
-/* remove last loaded samples */
-static void
-awe_remove_samples(void)
+/* prepend this sample to sf list */
+static void add_sf_sample(int rec)
{
- awe_voice_list **prevp, *p, *nextp;
- int maxc;
- int i;
-
- /* no sample is loaded yet */
- if (last_sample == free_sample && last_info == free_info)
- return;
-
- /* only the primary samples are loaded */
- if (current_sf_id <= 1)
- return;
-
- /* remove the records from preset table */
- for (i = 0; i < AWE_MAX_PRESETS; i++) {
- prevp = &preset_table[i];
- for (maxc = AWE_MAX_INFOS, p = preset_table[i];
- p && maxc; p = nextp, maxc--) {
- nextp = p->next_bank;
- p = awe_get_removed_list(p);
- if (p == NULL)
- *prevp = nextp;
- else {
- *prevp = p;
- prevp = &p->next_bank;
- }
- }
- }
-
- for (i = last_sample; i < free_sample; i++)
- free_mem_ptr -= samples[i].size;
-
- free_sample = last_sample;
- free_info = last_info;
- current_sf_id = 1;
- loaded_once = 0;
+ int sf_id = samples[rec].v.sf_id;
+ if (sf_id == 0) return;
+ sf_id--;
+ samples[rec].next = sflists[sf_id].samples;
+ sflists[sf_id].samples = rec;
+ sflists[sf_id].num_sample++;
}
-
-/* search the specified sample */
-static short
-awe_set_sample(awe_voice_info *vp)
+/* purge the old records which don't belong with the same file id */
+static void purge_old_list(int rec, int next)
{
- int i;
- for (i = 0; i < free_sample; i++) {
- if (samples[i].sf_id == vp->sf_id &&
- samples[i].sample == vp->sample) {
- /* set the actual sample offsets */
- vp->start += samples[i].start;
- vp->end += samples[i].end;
- vp->loopstart += samples[i].loopstart;
- vp->loopend += samples[i].loopend;
- /* copy mode flags */
- vp->mode = samples[i].mode_flags;
- /* set index */
- vp->index = i;
- return i;
+ infos[rec].next_instr = next;
+ if (infos[rec].bank == AWE_DRUM_BANK) {
+ /* remove samples with the same note range */
+ int cur, *prevp = &infos[rec].next_instr;
+ int low = infos[rec].v.low;
+ int high = infos[rec].v.high;
+ for (cur = next; cur >= 0; cur = infos[cur].next_instr) {
+ if (infos[cur].v.low == low &&
+ infos[cur].v.high == high &&
+ infos[cur].v.sf_id != infos[rec].v.sf_id)
+ *prevp = infos[cur].next_instr;
+ prevp = &infos[cur].next_instr;
}
+ } else {
+ if (infos[next].v.sf_id != infos[rec].v.sf_id)
+ infos[rec].next_instr = -1;
}
- return -1;
}
-
-/* voice pressure change */
-static void
-awe_aftertouch(int dev, int voice, int pressure)
+/* prepend to top of the preset table */
+static void add_info_list(int rec)
{
- DEBUG(2,printk("AWE32: [after(%d) %d]\n", voice, pressure));
- if (awe_channel_mode == 2) {
- int note = (voice_alloc->map[voice] & 0xff) - 1;
- awe_start_note(dev, voice, note + 0x80, pressure);
-
- } else if (awe_channel_mode == 0) {
- if (voice < 0 || voice >= awe_max_voices)
- return;
- voices[voice].velocity = pressure;
- awe_set_voice_vol(voice, FALSE);
- }
-}
+ int *prevp, cur;
+ int instr = infos[rec].instr;
+ int bank = infos[rec].bank;
+ if (infos[rec].disabled)
+ return;
-/* voice control change */
-static void
-awe_controller(int dev, int voice, int ctrl_num, int value)
-{
- awe_chan_info *cinfo;
-
- if (awe_channel_mode) {
- if (awe_channel_mode == 2) /* get channel */
- voice = voice_alloc->map[voice] >> 8;
- if (voice < 0 || voice >= AWE_MAX_CHANNELS)
- return;
- cinfo = &channels[voice];
- } else {
- if (voice < 0 || voice >= awe_max_voices)
+ prevp = &preset_table[instr];
+ cur = *prevp;
+ while (cur >= 0) {
+ /* search the first record with the same bank number */
+ if (infos[cur].bank == bank) {
+ /* replace the list with the new record */
+ infos[rec].next_bank = infos[cur].next_bank;
+ *prevp = rec;
+ purge_old_list(rec, cur);
return;
- cinfo = voices[voice].cinfo;
- }
-
- switch (ctrl_num) {
- case CTL_BANK_SELECT: /* SEQ1 control */
- DEBUG(2,printk("AWE32: [bank(%d) %d]\n", voice, value));
- cinfo->bank = value;
- awe_set_instr(dev, voice, cinfo->instr);
- break;
-
- case CTRL_PITCH_BENDER: /* SEQ1 V2 contorl */
- DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, value));
- /* zero centered */
- cinfo->bender = value;
- awe_voice_change(voice, awe_set_voice_pitch);
- break;
-
- case CTRL_PITCH_BENDER_RANGE: /* SEQ1 V2 control */
- DEBUG(2,printk("AWE32: [range(%d) %d]\n", voice, value));
- /* value = sense x 100 */
- cinfo->bender_range = value;
- /* no audible pitch change yet.. */
- break;
-
- case CTL_EXPRESSION: /* SEQ1 control */
- if (!awe_channel_mode)
- value /= 128;
- case CTRL_EXPRESSION: /* SEQ1 V2 control */
- DEBUG(2,printk("AWE32: [expr(%d) %d]\n", voice, value));
- /* 0 - 127 */
- cinfo->expression_vol = value;
- awe_voice_change(voice, awe_set_voice_vol);
- break;
-
- case CTL_PAN: /* SEQ1 control */
- DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, value));
- /* (0-127) -> signed 8bit */
- cinfo->panning = value * 2 - 128;
- awe_voice_change(voice, awe_set_pan);
- break;
-
- case CTL_MAIN_VOLUME: /* SEQ1 control */
- if (!awe_channel_mode)
- value = (value * 100) / 16383;
- case CTRL_MAIN_VOLUME: /* SEQ1 V2 control */
- DEBUG(2,printk("AWE32: [mainvol(%d) %d]\n", voice, value));
- /* 0 - 127 */
- cinfo->main_vol = value;
- awe_voice_change(voice, awe_set_voice_vol);
- break;
-
- case CTL_EXT_EFF_DEPTH: /* reverb effects: 0-127 */
- DEBUG(2,printk("AWE32: [reverb(%d) %d]\n", voice, value));
- FX_SET(&cinfo->fx, AWE_FX_REVERB, value * 2);
- break;
-
- case CTL_CHORUS_DEPTH: /* chorus effects: 0-127 */
- DEBUG(2,printk("AWE32: [chorus(%d) %d]\n", voice, value));
- FX_SET(&cinfo->fx, AWE_FX_CHORUS, value * 2);
- break;
-
-#ifdef AWE_ACCEPT_ALL_SOUNDS_CONTROLL
- case 120: /* all sounds off */
- {int i; for (i = 0; i < AWE_NORMAL_VOICES; i++)
- awe_terminate(i);
- }
- /*awe_reset(0);*/
- break;
- case 123: /* all notes off */
- {int i;
- for (i = 0; i < awe_max_voices; i++)
- awe_note_off(i);
}
- break;
-#endif
-
- case CTL_SUSTAIN: /* sustain the channel */
- cinfo->sustained = value;
- if (value == 0)
- awe_voice_change(voice, awe_sustain_off);
- break;
-
- default:
- DEBUG(0,printk("AWE32: [control(%d) ctrl=%d val=%d]\n",
- voice, ctrl_num, value));
- break;
+ prevp = &infos[cur].next_bank;
+ cur = infos[cur].next_bank;
}
-}
-
+ /* this is the first bank record.. just add this */
+ infos[rec].next_instr = -1;
+ infos[rec].next_bank = preset_table[instr];
+ preset_table[instr] = rec;
+}
-/* change the voice parameters */
-static void awe_voice_change(int voice, fx_affect_func func)
+/* remove samples later than the specified sf_id */
+static void
+awe_remove_samples(int sf_id)
{
- int i;
- if (! awe_channel_mode) {
- func(voice, FALSE);
+ if (sf_id <= 0) {
+ awe_reset_samples();
return;
}
+ /* already removed? */
+ if (current_sf_id <= sf_id)
+ return;
- for (i = 0; i < awe_max_voices; i++)
- if (voices[i].ch == voice)
- func(i, FALSE);
-}
-
+ current_sf_id = sf_id;
+ if (locked_sf_id > sf_id)
+ locked_sf_id = sf_id;
-/* drop sustain */
-static void awe_sustain_off(int voice, int forced)
-{
- if (voices[voice].state == AWE_ST_SUSTAINED)
- awe_note_off(voice);
+ rebuild_preset_list();
}
-
-/* voice pan change (value = -128 - 127) */
-static void
-awe_panning(int dev, int voice, int value)
+/* rebuild preset search list */
+static void rebuild_preset_list(void)
{
- awe_chan_info *cinfo;
- if (awe_channel_mode) {
- if (voice < 0 || voice >= AWE_MAX_CHANNELS)
- return;
- cinfo = &channels[voice];
- } else {
- if (voice < 0 || voice >= awe_max_voices)
- return;
- cinfo = voices[voice].cinfo;
- }
-
- cinfo->panning = value;
- DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, cinfo->panning));
- awe_voice_change(voice, awe_set_pan);
-}
+ int i, j;
+ for (i = 0; i < AWE_MAX_PRESETS; i++)
+ preset_table[i] = -1;
-/* volume mode change */
-static void
-awe_volume_method(int dev, int mode)
-{
- /* not impremented */
- DEBUG(0,printk("AWE32: [volmethod mode=%d]\n", mode));
+ for (i = 0; i < current_sf_id; i++) {
+ for (j = sflists[i].infos; j >= 0; j = infos[j].next)
+ add_info_list(j);
+ }
}
-
-#ifndef AWE_NO_PATCHMGR
-/* patch manager */
-static int
-awe_patchmgr(int dev, struct patmgr_info *rec)
+/* search the specified sample */
+static short
+awe_set_sample(awe_voice_info *vp)
{
- FATALERR(printk("AWE32 Warning: patch manager control not supported\n"));
- return 0;
+ int i;
+ vp->index = -1;
+ for (i = sflists[vp->sf_id-1].samples; i >= 0; i = samples[i].next) {
+ if (samples[i].v.sample == vp->sample) {
+ /* set the actual sample offsets */
+ vp->start += samples[i].v.start;
+ vp->end += samples[i].v.end;
+ vp->loopstart += samples[i].v.loopstart;
+ vp->loopend += samples[i].v.loopend;
+ /* copy mode flags */
+ vp->mode = samples[i].v.mode_flags;
+ /* set index */
+ vp->index = i;
+ return i;
+ }
+ }
+ return -1;
}
-#endif
-/* pitch wheel change: 0-16384 */
-static void
-awe_bender(int dev, int voice, int value)
-{
- awe_chan_info *cinfo;
- if (awe_channel_mode) {
- if (awe_channel_mode == 2)
- voice = voice_alloc->map[voice] >> 8;
- if (voice < 0 || voice >= AWE_MAX_CHANNELS)
- return;
- cinfo = &channels[voice];
- } else {
- if (voice < 0 || voice >= awe_max_voices)
- return;
- cinfo = voices[voice].cinfo;
- }
- /* convert to zero centered value */
- cinfo->bender = value - 8192;
- DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, cinfo->bender));
- awe_voice_change(voice, awe_set_voice_pitch);
-}
+/*----------------------------------------------------------------
+ * voice allocation
+ *----------------------------------------------------------------*/
-/* calculate the number of voices for this note & velocity */
+/* look for all voices associated with the specified note & velocity */
static int
-awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, awe_voice_info **vlist)
+awe_search_multi_voices(int rec, int note, int velocity, awe_voice_info **vlist)
{
- int maxc;
int nvoices;
- unsigned short sf_id;
- sf_id = current_sf_id;
nvoices = 0;
- for (maxc = AWE_MAX_INFOS;
- rec && maxc; rec = rec->next_instr, maxc--) {
- if (rec->v.low <= note && note <= rec->v.high &&
- velocity >= rec->v.vellow && velocity <= rec->v.velhigh) {
- if (nvoices == 0)
- sf_id = rec->v.sf_id;
- else if (rec->v.sf_id != sf_id)
- continue;
- vlist[nvoices] = &rec->v;
+ for (; rec >= 0; rec = infos[rec].next_instr) {
+ if (note >= infos[rec].v.low &&
+ note <= infos[rec].v.high &&
+ velocity >= infos[rec].v.vellow &&
+ velocity <= infos[rec].v.velhigh) {
+ vlist[nvoices] = &infos[rec].v;
+ if (infos[rec].type == V_ST_MAPPED) /* mapper */
+ return -1;
nvoices++;
if (nvoices >= AWE_MAX_VOICES)
break;
@@ -2911,112 +3602,182 @@ awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, awe_voice_i
return nvoices;
}
+/* store the voice list from the specified note and velocity.
+ if the preset is mapped, seek for the destination preset, and rewrite
+ the note number if necessary.
+ */
+static int
+really_alloc_voices(int vrec, int def_vrec, int *note, int velocity, awe_voice_info **vlist, int level)
+{
+ int nvoices;
+
+ nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist);
+ if (nvoices == 0)
+ nvoices = awe_search_multi_voices(def_vrec, *note, velocity, vlist);
+ if (nvoices < 0) { /* mapping */
+ int preset = vlist[0]->start;
+ int bank = vlist[0]->end;
+ int key = vlist[0]->fixkey;
+ if (level > 5) {
+ printk("AWE32: too deep mapping level\n");
+ return 0;
+ }
+ vrec = awe_search_instr(bank, preset);
+ if (bank == AWE_DRUM_BANK)
+ def_vrec = awe_search_instr(bank, 0);
+ else
+ def_vrec = awe_search_instr(0, preset);
+ if (key >= 0)
+ *note = key;
+ return really_alloc_voices(vrec, def_vrec, note, velocity, vlist, level+1);
+ }
+
+ return nvoices;
+}
+
/* allocate voices corresponding note and velocity; supports multiple insts. */
static void
-awe_alloc_multi_voices(int ch, int note, int velocity)
+awe_alloc_multi_voices(int ch, int note, int velocity, int key)
{
int i, v, nvoices;
awe_voice_info *vlist[AWE_MAX_VOICES];
- if (channels[ch].vrec == NULL && channels[ch].def_vrec == NULL)
+ if (channels[ch].vrec < 0 && channels[ch].def_vrec < 0)
awe_set_instr(0, ch, channels[ch].instr);
- nvoices = awe_search_multi_voices(channels[ch].vrec, note, velocity, vlist);
- if (nvoices == 0)
- nvoices = awe_search_multi_voices(channels[ch].def_vrec, note, velocity, vlist);
+ /* check the possible voices; note may be changeable if mapped */
+ nvoices = really_alloc_voices(channels[ch].vrec, channels[ch].def_vrec,
+ &note, velocity, vlist, 0);
- /* allocate the voices */
+ /* set the voices */
current_alloc_time++;
for (i = 0; i < nvoices; i++) {
v = awe_clear_voice();
- voices[v].key = AWE_CHAN_KEY(ch, note);
+ voices[v].key = key;
voices[v].ch = ch;
+ voices[v].note = note;
+ voices[v].velocity = velocity;
voices[v].time = current_alloc_time;
voices[v].cinfo = &channels[ch];
voices[v].sample = vlist[i];
voices[v].state = AWE_ST_MARK;
+ voices[v].layer = nvoices - i - 1; /* in reverse order */
}
/* clear the mark in allocated voices */
- for (i = 0; i < awe_max_voices; i++)
+ for (i = 0; i < awe_max_voices; i++) {
if (voices[i].state == AWE_ST_MARK)
voices[i].state = AWE_ST_OFF;
+
+ }
}
-/* search an empty voice; used internally */
+/* search the best voice from the specified status condition */
static int
-awe_clear_voice(void)
+search_best_voice(int condition)
{
int i, time, best;
-
- /* looking for the oldest empty voice */
best = -1;
- time = 0x7fffffff;
+ time = current_alloc_time + 1;
for (i = 0; i < awe_max_voices; i++) {
- if (voices[i].state == AWE_ST_OFF && voices[i].time < time) {
+ if ((voices[i].state & condition) &&
+ (best < 0 || voices[i].time < time)) {
best = i;
time = voices[i].time;
}
}
- if (best >= 0)
- return best;
+ /* clear voice */
+ if (best >= 0) {
+ if (voices[best].state != AWE_ST_OFF)
+ awe_terminate(best);
+ awe_voice_init(best, TRUE);
+ }
+
+ return best;
+}
+
+/* search an empty voice.
+ if no empty voice is found, at least terminate a voice
+ */
+static int
+awe_clear_voice(void)
+{
+ int best;
+ /* looking for the oldest empty voice */
+ if ((best = search_best_voice(AWE_ST_OFF)) >= 0)
+ return best;
+ if ((best = search_best_voice(AWE_ST_RELEASED)) >= 0)
+ return best;
/* looking for the oldest sustained voice */
- time = 0x7fffffff;
- for (i = 0; i < awe_max_voices; i++) {
- if (voices[i].state == AWE_ST_SUSTAINED &&
- voices[i].time < time) {
- best = i;
- time = voices[i].time;
- }
- }
- if (best >= 0) {
- awe_note_off(best);
+ if ((best = search_best_voice(AWE_ST_SUSTAINED)) >= 0)
return best;
- }
- /* looking for the oldest voice not marked */
- time = 0x7fffffff;
- best = 0;
- for (i = 0; i < awe_max_voices; i++) {
- if (voices[i].state != AWE_ST_MARK && voices[i].time < time) {
- best = i;
- time = voices[i].time;
+#ifdef AWE_LOOKUP_MIDI_PRIORITY
+ if (MULTI_LAYER_MODE() && misc_modes[AWE_MD_CHN_PRIOR]) {
+ int ch = -1;
+ int time = current_alloc_time + 1;
+ int i;
+ /* looking for the voices from high channel (except drum ch) */
+ for (i = 0; i < awe_max_voices; i++) {
+ if (IS_DRUM_CHANNEL(voices[i].ch)) continue;
+ if (voices[i].ch < ch) continue;
+ if (voices[i].state != AWE_ST_MARK &&
+ (voices[i].ch > ch || voices[i].time < time)) {
+ best = i;
+ time = voices[i].time;
+ ch = voices[i].ch;
+ }
}
}
- /*awe_terminate(best);*/
- awe_note_off(best);
- return best;
+#endif
+ if (best < 0)
+ best = search_best_voice(~AWE_ST_MARK);
+
+ if (best >= 0)
+ return best;
+
+ return 0;
}
-/* allocate a voice corresponding note and velocity; single instrument */
+/* search sample for the specified note & velocity and set it on the voice;
+ * note that voice is the voice index (not channel index)
+ */
static void
awe_alloc_one_voice(int voice, int note, int velocity)
{
- int nvoices;
+ int ch, nvoices;
awe_voice_info *vlist[AWE_MAX_VOICES];
- if (voices[voice].cinfo->vrec == NULL && voices[voice].cinfo->def_vrec == NULL)
- awe_set_instr(0, voice, voices[voice].cinfo->instr);
-
- nvoices = awe_search_multi_voices(voices[voice].cinfo->vrec, note, velocity, vlist);
- if (nvoices == 0)
- nvoices = awe_search_multi_voices(voices[voice].cinfo->def_vrec, note, velocity, vlist);
+ ch = voices[voice].ch;
+ if (channels[ch].vrec < 0 && channels[ch].def_vrec < 0)
+ awe_set_instr(0, ch, channels[ch].instr);
+ nvoices = really_alloc_voices(voices[voice].cinfo->vrec,
+ voices[voice].cinfo->def_vrec,
+ &note, velocity, vlist, 0);
if (nvoices > 0) {
voices[voice].time = ++current_alloc_time;
voices[voice].sample = vlist[0]; /* use the first one */
+ voices[voice].layer = 0;
+ voices[voice].note = note;
+ voices[voice].velocity = velocity;
}
}
+/*----------------------------------------------------------------
+ * sequencer2 functions
+ *----------------------------------------------------------------*/
+
/* search an empty voice; used by sequencer2 */
static int
awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc)
{
- awe_channel_mode = 2;
+ playing_mode = AWE_PLAY_MULTI2;
+ awe_info.nr_voices = AWE_MAX_CHANNELS;
return awe_clear_voice();
}
@@ -3053,6 +3814,75 @@ awe_setup_voice(int dev, int voice, int chn)
}
+#ifdef CONFIG_AWE32_MIXER
+/*================================================================
+ * AWE32 mixer device control
+ *================================================================*/
+
+static int
+awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+ int i, level;
+
+ if (((cmd >> 8) & 0xff) != 'M')
+ return RET_ERROR(EINVAL);
+
+ level = (int)IOCTL_IN(arg);
+ level = ((level & 0xff) + (level >> 8)) / 2;
+ DEBUG(0,printk("AWEMix: cmd=%x val=%d\n", cmd & 0xff, level));
+
+ if (IO_WRITE_CHECK(cmd)) {
+ switch (cmd & 0xff) {
+ case SOUND_MIXER_BASS:
+ awe_bass_level = level * 12 / 100;
+ if (awe_bass_level >= 12)
+ awe_bass_level = 11;
+ awe_equalizer(awe_bass_level, awe_treble_level);
+ break;
+ case SOUND_MIXER_TREBLE:
+ awe_treble_level = level * 12 / 100;
+ if (awe_treble_level >= 12)
+ awe_treble_level = 11;
+ awe_equalizer(awe_bass_level, awe_treble_level);
+ break;
+ case SOUND_MIXER_VOLUME:
+ level = level * 127 / 100;
+ if (level >= 128) level = 127;
+ init_atten = vol_table[level];
+ for (i = 0; i < awe_max_voices; i++)
+ awe_set_voice_vol(i, TRUE);
+ break;
+ }
+ }
+ switch (cmd & 0xff) {
+ case SOUND_MIXER_BASS:
+ level = awe_bass_level * 100 / 24;
+ level = (level << 8) | level;
+ break;
+ case SOUND_MIXER_TREBLE:
+ level = awe_treble_level * 100 / 24;
+ level = (level << 8) | level;
+ break;
+ case SOUND_MIXER_VOLUME:
+ for (i = 127; i > 0; i--) {
+ if (init_atten <= vol_table[i])
+ break;
+ }
+ level = i * 100 / 127;
+ level = (level << 8) | level;
+ break;
+ case SOUND_MIXER_DEVMASK:
+ level = SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_VOLUME;
+ break;
+ default:
+ level = 0;
+ break;
+ }
+ return IOCTL_OUT(arg, level);
+}
+#endif /* CONFIG_AWE32_MIXER */
+
+
/*================================================================
* initialization of AWE32
*================================================================*/
@@ -3299,16 +4129,22 @@ awe_init_fm(void)
awe_poke_dw(AWE_CSL(31), 0x00FFFFF8 |
(DEF_FM_CHORUS_DEPTH << 24));
awe_poke_dw(AWE_PTRX(31), (DEF_FM_REVERB_DEPTH << 8));
- awe_poke_dw(AWE_CPF(31), 0);
+ awe_poke_dw(AWE_CPF(31), 0x8000);
awe_poke_dw(AWE_CCCA(31), 0x00FFFFF3);
/* skew volume & cutoff */
awe_poke_dw(AWE_VTFT(30), 0x8000FFFF);
awe_poke_dw(AWE_VTFT(31), 0x8000FFFF);
+ voices[30].state = AWE_ST_FM;
+ voices[31].state = AWE_ST_FM;
+
/* change maximum channels to 30 */
awe_max_voices = AWE_NORMAL_VOICES;
- awe_info.nr_voices = awe_max_voices;
+ if (playing_mode == AWE_PLAY_DIRECT)
+ awe_info.nr_voices = awe_max_voices;
+ else
+ awe_info.nr_voices = AWE_MAX_CHANNELS;
voice_alloc->max_voice = awe_max_voices;
}
@@ -3318,20 +4154,32 @@ awe_init_fm(void)
/* open DRAM write accessing mode */
static int
-awe_open_dram_for_write(int offset)
+awe_open_dram_for_write(int offset, int channels)
{
+ int vidx[AWE_NORMAL_VOICES];
int i;
+ if (channels < 0 || channels >= AWE_NORMAL_VOICES) {
+ channels = AWE_NORMAL_VOICES;
+ for (i = 0; i < AWE_NORMAL_VOICES; i++)
+ vidx[i] = i;
+ } else {
+ for (i = 0; i < channels; i++)
+ vidx[i] = awe_clear_voice();
+ }
+
/* use all channels for DMA transfer */
- for (i = 0; i < AWE_NORMAL_VOICES; i++) {
- awe_poke(AWE_DCYSUSV(i), 0x80);
- awe_poke_dw(AWE_VTFT(i), 0);
- awe_poke_dw(AWE_CVCF(i), 0);
- awe_poke_dw(AWE_PTRX(i), 0x40000000);
- awe_poke_dw(AWE_CPF(i), 0x40000000);
- awe_poke_dw(AWE_PSST(i), 0);
- awe_poke_dw(AWE_CSL(i), 0);
- awe_poke_dw(AWE_CCCA(i), 0x06000000);
+ for (i = 0; i < channels; i++) {
+ if (vidx[i] < 0) continue;
+ awe_poke(AWE_DCYSUSV(vidx[i]), 0x80);
+ awe_poke_dw(AWE_VTFT(vidx[i]), 0);
+ awe_poke_dw(AWE_CVCF(vidx[i]), 0);
+ awe_poke_dw(AWE_PTRX(vidx[i]), 0x40000000);
+ awe_poke_dw(AWE_CPF(vidx[i]), 0x40000000);
+ awe_poke_dw(AWE_PSST(vidx[i]), 0);
+ awe_poke_dw(AWE_CSL(vidx[i]), 0);
+ awe_poke_dw(AWE_CCCA(vidx[i]), 0x06000000);
+ voices[vidx[i]].state = AWE_ST_DRAM;
}
/* point channels 31 & 32 to ROM samples for DRAM refresh */
awe_poke_dw(AWE_VTFT(30), 0);
@@ -3342,16 +4190,20 @@ awe_open_dram_for_write(int offset)
awe_poke_dw(AWE_PSST(31), 0x1d8);
awe_poke_dw(AWE_CSL(31), 0x1e0);
awe_poke_dw(AWE_CCCA(31), 0x1d8);
+ voices[30].state = AWE_ST_FM;
+ voices[31].state = AWE_ST_FM;
/* if full bit is on, not ready to write on */
if (awe_peek_dw(AWE_SMALW) & 0x80000000) {
- for (i = 0; i < AWE_NORMAL_VOICES; i++)
- awe_poke_dw(AWE_CCCA(i), 0);
+ for (i = 0; i < channels; i++) {
+ awe_poke_dw(AWE_CCCA(vidx[i]), 0);
+ voices[i].state = AWE_ST_OFF;
+ }
return RET_ERROR(ENOSPC);
}
/* set address to write */
- awe_poke_dw(AWE_SMALW, offset + AWE_DRAM_OFFSET);
+ awe_poke_dw(AWE_SMALW, offset);
return 0;
}
@@ -3373,6 +4225,7 @@ awe_open_dram_for_check(void)
awe_poke_dw(AWE_CCCA(i), 0x06000000);
else /* DMA read */
awe_poke_dw(AWE_CCCA(i), 0x04000000);
+ voices[i].state = AWE_ST_DRAM;
}
}
@@ -3390,79 +4243,14 @@ awe_close_dram(void)
}
for (i = 0; i < AWE_NORMAL_VOICES; i++) {
- awe_poke_dw(AWE_CCCA(i), 0);
- awe_poke(AWE_DCYSUSV(i), 0x807F);
- }
-}
-
-
-#ifdef AWE_CHECKSUM_MEMORY
-/* open DRAM read accessing mode */
-static int
-awe_open_dram_for_read(int offset)
-{
- int i;
-
- /* use all channels for DMA transfer */
- for (i = 0; i < AWE_NORMAL_VOICES; i++) {
- awe_poke(AWE_DCYSUSV(i), 0x80);
- awe_poke_dw(AWE_VTFT(i), 0);
- awe_poke_dw(AWE_CVCF(i), 0);
- awe_poke_dw(AWE_PTRX(i), 0x40000000);
- awe_poke_dw(AWE_CPF(i), 0x40000000);
- awe_poke_dw(AWE_PSST(i), 0);
- awe_poke_dw(AWE_CSL(i), 0);
- awe_poke_dw(AWE_CCCA(i), 0x04000000);
- }
- /* point channels 31 & 32 to ROM samples for DRAM refresh */
- awe_poke_dw(AWE_VTFT(30), 0);
- awe_poke_dw(AWE_PSST(30), 0x1d8);
- awe_poke_dw(AWE_CSL(30), 0x1e0);
- awe_poke_dw(AWE_CCCA(30), 0x1d8);
- awe_poke_dw(AWE_VTFT(31), 0);
- awe_poke_dw(AWE_PSST(31), 0x1d8);
- awe_poke_dw(AWE_CSL(31), 0x1e0);
- awe_poke_dw(AWE_CCCA(31), 0x1d8);
-
- /* if empty flag is on, not ready to read */
- if (awe_peek_dw(AWE_SMALR) & 0x80000000) {
- for (i = 0; i < AWE_NORMAL_VOICES; i++)
+ if (voices[i].state == AWE_ST_DRAM) {
awe_poke_dw(AWE_CCCA(i), 0);
- return RET_ERROR(ENOSPC);
- }
-
- /* set address to read */
- awe_poke_dw(AWE_SMALR, offset + AWE_DRAM_OFFSET);
- /* drop stale data */
- awe_peek(AWE_SMLD);
- return 0;
-}
-
-/* close dram access for read */
-static void
-awe_close_dram_for_read(void)
-{
- int i;
- /* wait until FULL bit in SMAxW register be false */
- for (i = 0; i < 10000; i++) {
- if (!(awe_peek_dw(AWE_SMALR) & 0x80000000))
- break;
- awe_wait(10);
- }
- for (i = 0; i < AWE_NORMAL_VOICES; i++) {
- awe_poke_dw(AWE_CCCA(i), 0);
- awe_poke(AWE_DCYSUSV(i), 0x807F);
+ awe_poke(AWE_DCYSUSV(i), 0x807F);
+ voices[i].state = AWE_ST_OFF;
+ }
}
}
-#endif /* AWE_CHECKSUM_MEMORY */
-
-/* write a word data */
-static void
-awe_write_dram(unsigned short c)
-{
- awe_poke(AWE_SMLD, c);
-}
/*================================================================
* detect presence of AWE32 and check memory size
@@ -3488,18 +4276,14 @@ static int
awe_detect(void)
{
int base;
-#ifdef AWE_DEFAULT_BASE_ADDR
- if (awe_detect_base(AWE_DEFAULT_BASE_ADDR))
- return 1;
-#endif
if (awe_base == 0) {
for (base = 0x620; base <= 0x680; base += 0x20)
if (awe_detect_base(base))
return 1;
+ DEBUG(0,printk("AWE32 not found\n"));
+ return 0;
}
- FATALERR(printk("AWE32 not found\n"));
- awe_base = 0;
- return 0;
+ return 1;
}
@@ -3515,14 +4299,20 @@ awe_detect(void)
static int
awe_check_dram(void)
{
+ if (awe_mem_size > 0) {
+ awe_mem_size *= 1024; /* convert to Kbytes */
+ return awe_mem_size;
+ }
+
awe_open_dram_for_check();
+ awe_mem_size = 0;
+
/* set up unique two id numbers */
awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET);
awe_poke(AWE_SMLD, UNIQUE_ID1);
awe_poke(AWE_SMLD, UNIQUE_ID2);
- awe_mem_size = 0;
while (awe_mem_size < AWE_MAX_DRAM_SIZE) {
awe_wait(2);
/* read a data on the DRAM start address */
@@ -3540,14 +4330,17 @@ awe_check_dram(void)
*/
awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET + awe_mem_size*512L);
awe_poke(AWE_SMLD, UNIQUE_ID3);
+ awe_wait(2);
+ /* read a data on the just written DRAM address */
+ awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET + awe_mem_size*512L);
+ awe_peek(AWE_SMLD); /* discard stale data */
+ if (awe_peek(AWE_SMLD) != UNIQUE_ID3)
+ break;
}
awe_close_dram();
- DEBUG(0,printk("AWE32: %d Kbytes memory detected\n", (int)awe_mem_size));
-#ifdef AWE_DEFAULT_MEM_SIZE
- if (awe_mem_size == 0)
- awe_mem_size = AWE_DEFAULT_MEM_SIZE;
-#endif
+ DEBUG(0,printk("AWE32: %d Kbytes memory detected\n", awe_mem_size));
+
/* convert to Kbytes */
awe_mem_size *= 1024;
return awe_mem_size;
@@ -3559,26 +4352,49 @@ awe_check_dram(void)
*================================================================*/
/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */
-static unsigned short chorus_parm[8][5] = {
- {0xE600, 0x03F6, 0xBC2C ,0x0000, 0x006D}, /* chorus 1 */
- {0xE608, 0x031A, 0xBC6E, 0x0000, 0x017C}, /* chorus 2 */
- {0xE610, 0x031A, 0xBC84, 0x0000, 0x0083}, /* chorus 3 */
- {0xE620, 0x0269, 0xBC6E, 0x0000, 0x017C}, /* chorus 4 */
- {0xE680, 0x04D3, 0xBCA6, 0x0000, 0x005B}, /* feedback */
- {0xE6E0, 0x044E, 0xBC37, 0x0000, 0x0026}, /* flanger */
- {0xE600, 0x0B06, 0xBC00, 0xE000, 0x0083}, /* short delay */
- {0xE6C0, 0x0B06, 0xBC00, 0xE000, 0x0083}, /* short delay + feedback */
+static char chorus_defined[AWE_CHORUS_NUMBERS];
+static awe_chorus_fx_rec chorus_parm[AWE_CHORUS_NUMBERS] = {
+ {0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */
+ {0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */
+ {0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */
+ {0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */
+ {0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */
+ {0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */
+ {0xE600, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay */
+ {0xE6C0, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay + feedback */
};
-static void awe_set_chorus_mode(int effect)
+static int
+awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count)
+{
+ if (patch->optarg < AWE_CHORUS_PREDEFINED || patch->optarg >= AWE_CHORUS_NUMBERS) {
+ printk("AWE32 Error: illegal chorus mode %d for uploading\n", patch->optarg);
+ return RET_ERROR(EINVAL);
+ }
+ if (count < sizeof(awe_chorus_fx_rec)) {
+ printk("AWE32 Error: too short chorus fx parameters\n");
+ return RET_ERROR(EINVAL);
+ }
+ COPY_FROM_USER(&chorus_parm[patch->optarg], addr, AWE_PATCH_INFO_SIZE,
+ sizeof(awe_chorus_fx_rec));
+ chorus_defined[patch->optarg] = TRUE;
+ return 0;
+}
+
+static void
+awe_set_chorus_mode(int effect)
{
- awe_poke(AWE_INIT3(9), chorus_parm[effect][0]);
- awe_poke(AWE_INIT3(12), chorus_parm[effect][1]);
- awe_poke(AWE_INIT4(3), chorus_parm[effect][2]);
- awe_poke_dw(AWE_HWCF4, (unsigned long)chorus_parm[effect][3]);
- awe_poke_dw(AWE_HWCF5, (unsigned long)chorus_parm[effect][4]);
+ if (effect < 0 || effect >= AWE_CHORUS_NUMBERS ||
+ (effect >= AWE_CHORUS_PREDEFINED && !chorus_defined[effect]))
+ return;
+ awe_poke(AWE_INIT3(9), chorus_parm[effect].feedback);
+ awe_poke(AWE_INIT3(12), chorus_parm[effect].delay_offset);
+ awe_poke(AWE_INIT4(3), chorus_parm[effect].lfo_depth);
+ awe_poke_dw(AWE_HWCF4, chorus_parm[effect].delay);
+ awe_poke_dw(AWE_HWCF5, chorus_parm[effect].lfo_freq);
awe_poke_dw(AWE_HWCF6, 0x8000);
awe_poke_dw(AWE_HWCF7, 0x0000);
+ chorus_mode = effect;
}
/*----------------------------------------------------------------*/
@@ -3586,55 +4402,56 @@ static void awe_set_chorus_mode(int effect)
/* reverb mode settings; write the following 28 data of 16 bit length
* on the corresponding ports in the reverb_cmds array
*/
-static unsigned short reverb_parm[8][28] = {
-{ /* room 1 */
+static char reverb_defined[AWE_CHORUS_NUMBERS];
+static awe_reverb_fx_rec reverb_parm[AWE_REVERB_NUMBERS] = {
+{{ /* room 1 */
0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4,
0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516,
0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
-},
-{ /* room 2 */
+}},
+{{ /* room 2 */
0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284,
0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
-},
-{ /* room 3 */
+}},
+{{ /* room 3 */
0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284,
0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516,
0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B,
0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A,
-},
-{ /* hall 1 */
+}},
+{{ /* hall 1 */
0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284,
0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A,
0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529,
-},
-{ /* hall 2 */
+}},
+{{ /* hall 2 */
0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254,
0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3,
0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
-},
-{ /* plate */
+}},
+{{ /* plate */
0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234,
0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548,
0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
-},
-{ /* delay */
+}},
+{{ /* delay */
0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204,
0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
-},
-{ /* panning delay */
+}},
+{{ /* panning delay */
0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204,
0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
-},
+}},
};
static struct ReverbCmdPair {
@@ -3649,13 +4466,112 @@ static struct ReverbCmdPair {
{AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)},
};
+static int
+awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count)
+{
+ if (patch->optarg < AWE_REVERB_PREDEFINED || patch->optarg >= AWE_REVERB_NUMBERS) {
+ printk("AWE32 Error: illegal reverb mode %d for uploading\n", patch->optarg);
+ return RET_ERROR(EINVAL);
+ }
+ if (count < sizeof(awe_reverb_fx_rec)) {
+ printk("AWE32 Error: too short reverb fx parameters\n");
+ return RET_ERROR(EINVAL);
+ }
+ COPY_FROM_USER(&reverb_parm[patch->optarg], addr, AWE_PATCH_INFO_SIZE,
+ sizeof(awe_reverb_fx_rec));
+ reverb_defined[patch->optarg] = TRUE;
+ return 0;
+}
-static void awe_set_reverb_mode(int effect)
+static void
+awe_set_reverb_mode(int effect)
{
int i;
+ if (effect < 0 || effect >= AWE_REVERB_NUMBERS ||
+ (effect >= AWE_REVERB_PREDEFINED && !reverb_defined[effect]))
+ return;
for (i = 0; i < 28; i++)
awe_poke(reverb_cmds[i].cmd, reverb_cmds[i].port,
- reverb_parm[effect][i]);
+ reverb_parm[effect].parms[i]);
+ reverb_mode = effect;
}
+/*================================================================
+ * treble/bass equalizer control
+ *================================================================*/
+
+static unsigned short bass_parm[12][3] = {
+ {0xD26A, 0xD36A, 0x0000}, /* -12 dB */
+ {0xD25B, 0xD35B, 0x0000}, /* -8 */
+ {0xD24C, 0xD34C, 0x0000}, /* -6 */
+ {0xD23D, 0xD33D, 0x0000}, /* -4 */
+ {0xD21F, 0xD31F, 0x0000}, /* -2 */
+ {0xC208, 0xC308, 0x0001}, /* 0 (HW default) */
+ {0xC219, 0xC319, 0x0001}, /* +2 */
+ {0xC22A, 0xC32A, 0x0001}, /* +4 */
+ {0xC24C, 0xC34C, 0x0001}, /* +6 */
+ {0xC26E, 0xC36E, 0x0001}, /* +8 */
+ {0xC248, 0xC348, 0x0002}, /* +10 */
+ {0xC26A, 0xC36A, 0x0002}, /* +12 dB */
+};
+
+static unsigned short treble_parm[12][9] = {
+ {0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */
+ {0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+ {0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+ {0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+ {0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+ {0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002},
+ {0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002},
+ {0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002},
+ {0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002},
+ {0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */
+ {0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002},
+ {0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +12 dB */
+};
+
+
+/*
+ * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB]
+ */
+static void
+awe_equalizer(int bass, int treble)
+{
+ unsigned short w;
+
+ if (bass < 0 || bass > 11 || treble < 0 || treble > 11)
+ return;
+ awe_bass_level = bass;
+ awe_treble_level = treble;
+ awe_poke(AWE_INIT4(0x01), bass_parm[bass][0]);
+ awe_poke(AWE_INIT4(0x11), bass_parm[bass][1]);
+ awe_poke(AWE_INIT3(0x11), treble_parm[treble][0]);
+ awe_poke(AWE_INIT3(0x13), treble_parm[treble][1]);
+ awe_poke(AWE_INIT3(0x1B), treble_parm[treble][2]);
+ awe_poke(AWE_INIT4(0x07), treble_parm[treble][3]);
+ awe_poke(AWE_INIT4(0x0B), treble_parm[treble][4]);
+ awe_poke(AWE_INIT4(0x0D), treble_parm[treble][5]);
+ awe_poke(AWE_INIT4(0x17), treble_parm[treble][6]);
+ awe_poke(AWE_INIT4(0x19), treble_parm[treble][7]);
+ w = bass_parm[bass][2] + treble_parm[treble][8];
+ awe_poke(AWE_INIT4(0x15), (unsigned short)(w + 0x0262));
+ awe_poke(AWE_INIT4(0x1D), (unsigned short)(w + 0x8362));
+}
+
+
#endif /* CONFIG_AWE32_SYNTH */
+
+#ifdef MODULE
+int init_module(void)
+{
+ attach_awe();
+ SOUND_LOCK;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ unload_awe();
+ SOUND_LOCK_END;
+}
+#endif
diff --git a/drivers/sound/mad16.c b/drivers/sound/mad16.c
index f4f86ff57..f1f6559c4 100644
--- a/drivers/sound/mad16.c
+++ b/drivers/sound/mad16.c
@@ -6,7 +6,7 @@
* for more info.
*/
#include <linux/config.h>
-
+#include <linux/module.h>
/*
* sound/mad16.c
*
@@ -59,8 +59,18 @@
*/
#include "sound_config.h"
+#include "soundmodule.h"
+
+#ifdef MODULE
+#define MAD16_CDSEL mad16_cdsel
+#define MAD16_CONF mad16_conf
+
+static int mad16_conf;
+static int mad16_cdsel;
+
+#endif
-#if defined(CONFIG_MAD16)
+#if defined(CONFIG_MAD16) || defined(MODULE)
#include "sb.h"
@@ -108,127 +118,121 @@ static int *mad16_osp;
#endif
static unsigned char
-mad_read (int port)
+mad_read(int port)
{
- unsigned long flags;
- unsigned char tmp;
-
- save_flags (flags);
- cli ();
-
- switch (board_type) /* Output password */
- {
- case C928:
- case MOZART:
- outb ((0xE2), PASSWD_REG);
- break;
-
- case C929:
- outb ((0xE3), PASSWD_REG);
- break;
-
- case C930:
- /* outb(( 0xE4), PASSWD_REG); */
- break;
-
- case C924:
- outb ((0xE5), PASSWD_REG);
- break;
- }
-
- if (board_type == C930)
- {
- outb ((port - MC0_PORT), 0xe0e); /* Write to index reg */
- tmp = inb (0xe0f); /* Read from data reg */
- }
- else
- tmp = inb (port);
- restore_flags (flags);
-
- return tmp;
+ unsigned long flags;
+ unsigned char tmp;
+
+ save_flags(flags);
+ cli();
+
+ switch (board_type) /* Output password */
+ {
+ case C928:
+ case MOZART:
+ outb((0xE2), PASSWD_REG);
+ break;
+
+ case C929:
+ outb((0xE3), PASSWD_REG);
+ break;
+
+ case C930:
+ /* outb(( 0xE4), PASSWD_REG); */
+ break;
+
+ case C924:
+ outb((0xE5), PASSWD_REG);
+ break;
+ }
+
+ if (board_type == C930)
+ {
+ outb((port - MC0_PORT), 0xe0e); /* Write to index reg */
+ tmp = inb(0xe0f); /* Read from data reg */
+ } else
+ tmp = inb(port);
+ restore_flags(flags);
+
+ return tmp;
}
static void
-mad_write (int port, int value)
+mad_write(int port, int value)
{
- unsigned long flags;
-
- save_flags (flags);
- cli ();
-
- switch (board_type) /* Output password */
- {
- case C928:
- case MOZART:
- outb ((0xE2), PASSWD_REG);
- break;
-
- case C929:
- outb ((0xE3), PASSWD_REG);
- break;
-
- case C930:
- /* outb(( 0xE4), PASSWD_REG); */
- break;
-
- case C924:
- outb ((0xE5), PASSWD_REG);
- break;
- }
-
- if (board_type == C930)
- {
- outb ((port - MC0_PORT), 0xe0e); /* Write to index reg */
- outb (((unsigned char) (value & 0xff)), 0xe0f);
- }
- else
- outb (((unsigned char) (value & 0xff)), port);
- restore_flags (flags);
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ switch (board_type) /* Output password */
+ {
+ case C928:
+ case MOZART:
+ outb((0xE2), PASSWD_REG);
+ break;
+
+ case C929:
+ outb((0xE3), PASSWD_REG);
+ break;
+
+ case C930:
+ /* outb(( 0xE4), PASSWD_REG); */
+ break;
+
+ case C924:
+ outb((0xE5), PASSWD_REG);
+ break;
+ }
+
+ if (board_type == C930)
+ {
+ outb((port - MC0_PORT), 0xe0e); /* Write to index reg */
+ outb(((unsigned char) (value & 0xff)), 0xe0f);
+ } else
+ outb(((unsigned char) (value & 0xff)), port);
+ restore_flags(flags);
}
static int
-detect_c930 (void)
+detect_c930(void)
{
- unsigned char tmp = mad_read (MC1_PORT);
-
- if ((tmp & 0x06) != 0x06)
- {
- DDB (printk ("Wrong C930 signature (%x)\n", tmp));
- /* return 0; */
- }
-
- mad_write (MC1_PORT, 0);
-
- if (mad_read (MC1_PORT) != 0x06)
- {
- DDB (printk ("Wrong C930 signature2 (%x)\n", tmp));
- /* return 0; */
- }
-
- mad_write (MC1_PORT, tmp); /* Restore bits */
-
- mad_write (MC7_PORT, 0);
- if ((tmp = mad_read (MC7_PORT)) != 0)
- {
- DDB (printk ("MC7 not writable (%x)\n", tmp));
- return 0;
- }
-
- mad_write (MC7_PORT, 0xcb);
- if ((tmp = mad_read (MC7_PORT)) != 0xcb)
- {
- DDB (printk ("MC7 not writable2 (%x)\n", tmp));
- return 0;
- }
-
- return 1;
+ unsigned char tmp = mad_read(MC1_PORT);
+
+ if ((tmp & 0x06) != 0x06)
+ {
+ DDB(printk("Wrong C930 signature (%x)\n", tmp));
+ /* return 0; */
+ }
+ mad_write(MC1_PORT, 0);
+
+ if (mad_read(MC1_PORT) != 0x06)
+ {
+ DDB(printk("Wrong C930 signature2 (%x)\n", tmp));
+ /* return 0; */
+ }
+ mad_write(MC1_PORT, tmp); /* Restore bits */
+
+ mad_write(MC7_PORT, 0);
+ if ((tmp = mad_read(MC7_PORT)) != 0)
+ {
+ DDB(printk("MC7 not writable (%x)\n", tmp));
+ return 0;
+ }
+ mad_write(MC7_PORT, 0xcb);
+ if ((tmp = mad_read(MC7_PORT)) != 0xcb)
+ {
+ DDB(printk("MC7 not writable2 (%x)\n", tmp));
+ return 0;
+ }
+ return 1;
}
static int
-detect_mad16 (void)
+detect_mad16(void)
{
- unsigned char tmp, tmp2;
- int i;
+ unsigned char tmp, tmp2;
+ int i;
/*
* Check that reading a register doesn't return bus float (0xff)
@@ -236,570 +240,716 @@ detect_mad16 (void)
* the card is in low power mode. Normally at least the power saving mode
* bit should be 0.
*/
- if ((tmp = mad_read (MC1_PORT)) == 0xff)
- {
- DDB (printk ("MC1_PORT returned 0xff\n"));
- return 0;
- }
-
- for (i = 0xf8d; i <= 0xf98; i++)
- DDB (printk ("Port %0x (init value) = %0x\n", i, mad_read (i)));
-
- if (board_type == C930)
- return detect_c930 ();
+ if ((tmp = mad_read(MC1_PORT)) == 0xff)
+ {
+ DDB(printk("MC1_PORT returned 0xff\n"));
+ return 0;
+ }
+ for (i = 0xf8d; i <= 0xf98; i++)
+ DDB(printk("Port %0x (init value) = %0x\n", i, mad_read(i)));
+
+ if (board_type == C930)
+ return detect_c930();
/*
* Now check that the gate is closed on first I/O after writing
* the password. (This is how a MAD16 compatible card works).
*/
- if ((tmp2 = inb (MC1_PORT)) == tmp) /* It didn't close */
- {
- DDB (printk ("MC1_PORT didn't close after read (0x%02x)\n", tmp2));
- return 0;
- }
-
- mad_write (MC1_PORT, tmp ^ 0x80); /* Toggle a bit */
- if ((tmp2 = mad_read (MC1_PORT)) != (tmp ^ 0x80)) /* Compare the bit */
- {
- mad_write (MC1_PORT, tmp); /* Restore */
- DDB (printk ("Bit revert test failed (0x%02x, 0x%02x)\n", tmp, tmp2));
- return 0;
- }
-
- mad_write (MC1_PORT, tmp); /* Restore */
- return 1; /* Bingo */
+ if ((tmp2 = inb(MC1_PORT)) == tmp) /* It didn't close */
+ {
+ DDB(printk("MC1_PORT didn't close after read (0x%02x)\n", tmp2));
+ return 0;
+ }
+ mad_write(MC1_PORT, tmp ^ 0x80); /* Toggle a bit */
+ if ((tmp2 = mad_read(MC1_PORT)) != (tmp ^ 0x80)) /* Compare the bit */
+ {
+ mad_write(MC1_PORT, tmp); /* Restore */
+ DDB(printk("Bit revert test failed (0x%02x, 0x%02x)\n", tmp, tmp2));
+ return 0;
+ }
+ mad_write(MC1_PORT, tmp); /* Restore */
+ return 1; /* Bingo */
}
static int
-wss_init (struct address_info *hw_config)
+wss_init(struct address_info *hw_config)
{
- int ad_flags = 0;
+ int ad_flags = 0;
/*
* Verify the WSS parameters
*/
- if (check_region (hw_config->io_base, 8))
- {
- printk ("MSS: I/O port conflict\n");
- return 0;
- }
-
- if (!ad1848_detect (hw_config->io_base + 4, &ad_flags, mad16_osp))
- return 0;
- /*
- * Check if the IO port returns valid signature. The original MS Sound
- * system returns 0x04 while some cards (AudioTrix Pro for example)
- * return 0x00.
- */
-
- if ((inb (hw_config->io_base + 3) & 0x3f) != 0x04 &&
- (inb (hw_config->io_base + 3) & 0x3f) != 0x00)
- {
- DDB (printk ("No MSS signature detected on port 0x%x (0x%x)\n",
- hw_config->io_base, inb (hw_config->io_base + 3)));
- return 0;
- }
-
- if (hw_config->irq > 11)
- {
- printk ("MSS: Bad IRQ %d\n", hw_config->irq);
- return 0;
- }
-
- if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
- {
- printk ("MSS: Bad DMA %d\n", hw_config->dma);
- return 0;
- }
-
- /*
- * Check that DMA0 is not in use with a 8 bit board.
- */
-
- if (hw_config->dma == 0 && inb (hw_config->io_base + 3) & 0x80)
- {
- printk ("MSS: Can't use DMA0 with a 8 bit card/slot\n");
- return 0;
- }
-
- if (hw_config->irq > 7 && hw_config->irq != 9 && inb (hw_config->io_base + 3) & 0x80)
- {
- printk ("MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq);
- }
-
- return 1;
+ if (check_region(hw_config->io_base, 8))
+ {
+ printk("MSS: I/O port conflict\n");
+ return 0;
+ }
+ if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp))
+ return 0;
+ /*
+ * Check if the IO port returns valid signature. The original MS Sound
+ * system returns 0x04 while some cards (AudioTrix Pro for example)
+ * return 0x00.
+ */
+
+ if ((inb(hw_config->io_base + 3) & 0x3f) != 0x04 &&
+ (inb(hw_config->io_base + 3) & 0x3f) != 0x00)
+ {
+ DDB(printk("No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, inb(hw_config->io_base + 3)));
+ return 0;
+ }
+ if (hw_config->irq > 11)
+ {
+ printk("MSS: Bad IRQ %d\n", hw_config->irq);
+ return 0;
+ }
+ if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
+ {
+ printk("MSS: Bad DMA %d\n", hw_config->dma);
+ return 0;
+ }
+ /*
+ * Check that DMA0 is not in use with a 8 bit board.
+ */
+
+ if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80)
+ {
+ printk("MSS: Can't use DMA0 with a 8 bit card/slot\n");
+ return 0;
+ }
+ if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80)
+ {
+ printk("MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq);
+ }
+ return 1;
}
static int
-init_c930 (struct address_info *hw_config)
+init_c930(struct address_info *hw_config)
{
- unsigned char cfg;
-
- cfg = (mad_read (MC1_PORT) & ~0x30);
- /* mad_write(MC1_PORT, 0); */
-
- switch (hw_config->io_base)
- {
- case 0x530:
- cfg |= 0x00;
- break;
- case 0xe80:
- cfg |= 0x10;
- break;
- case 0xf40:
- cfg |= 0x20;
- break;
- case 0x604:
- cfg |= 0x30;
- break;
-
- default:
- printk ("MAD16: Invalid codec port %x\n", hw_config->io_base);
- return 0;
- }
- mad_write (MC1_PORT, cfg);
-
- /* MC2 is CD configuration. Don't touch it. */
-
- mad_write (MC3_PORT, 0); /* Disable SB mode IRQ and DMA */
- mad_write (MC4_PORT, 0x52); /* ??? */
- mad_write (MC5_PORT, 0x3C); /* Init it into mode2 */
- mad_write (MC6_PORT, 0x02); /* Enable WSS, Disable MPU and SB */
- mad_write (MC7_PORT, 0xCB);
- mad_write (MC10_PORT, 0x11);
-
- return wss_init (hw_config);
+ unsigned char cfg;
+
+ cfg = (mad_read(MC1_PORT) & ~0x30);
+ /* mad_write(MC1_PORT, 0); */
+
+ switch (hw_config->io_base)
+ {
+ case 0x530:
+ cfg |= 0x00;
+ break;
+ case 0xe80:
+ cfg |= 0x10;
+ break;
+ case 0xf40:
+ cfg |= 0x20;
+ break;
+ case 0x604:
+ cfg |= 0x30;
+ break;
+
+ default:
+ printk("MAD16: Invalid codec port %x\n", hw_config->io_base);
+ return 0;
+ }
+ mad_write(MC1_PORT, cfg);
+
+ /* MC2 is CD configuration. Don't touch it. */
+
+ mad_write(MC3_PORT, 0); /* Disable SB mode IRQ and DMA */
+ mad_write(MC4_PORT, 0x52); /* ??? */
+ mad_write(MC5_PORT, 0x3C); /* Init it into mode2 */
+ mad_write(MC6_PORT, 0x02); /* Enable WSS, Disable MPU and SB */
+ mad_write(MC7_PORT, 0xCB);
+ mad_write(MC10_PORT, 0x11);
+
+ return wss_init(hw_config);
}
static int
-chip_detect (void)
+chip_detect(void)
{
- int i;
+ int i;
/*
* Then try to detect with the old password
*/
- board_type = C924;
+ board_type = C924;
- DDB (printk ("Detect using password = 0xE5\n"));
+ DDB(printk("Detect using password = 0xE5\n"));
- if (!detect_mad16 ()) /* No luck. Try different model */
- {
- board_type = C928;
+ if (!detect_mad16()) /* No luck. Try different model */
+ {
+ board_type = C928;
- DDB (printk ("Detect using password = 0xE2\n"));
+ DDB(printk("Detect using password = 0xE2\n"));
- if (!detect_mad16 ())
- {
- board_type = C929;
+ if (!detect_mad16())
+ {
+ board_type = C929;
- DDB (printk ("Detect using password = 0xE3\n"));
+ DDB(printk("Detect using password = 0xE3\n"));
- if (!detect_mad16 ())
- {
- if (inb (PASSWD_REG) != 0xff)
- return 0;
+ if (!detect_mad16())
+ {
+ if (inb(PASSWD_REG) != 0xff)
+ return 0;
/*
* First relocate MC# registers to 0xe0e/0xe0f, disable password
*/
- outb ((0xE4), PASSWD_REG);
- outb ((0x80), PASSWD_REG);
-
- board_type = C930;
-
- DDB (printk ("Detect using password = 0xE4\n"));
-
- for (i = 0xf8d; i <= 0xf93; i++)
- DDB (printk ("port %03x = %02x\n", i, mad_read (i)));
-
- if (!detect_mad16 ())
- return 0;
-
- DDB (printk ("mad16.c: 82C930 detected\n"));
- }
- else
- {
- DDB (printk ("mad16.c: 82C929 detected\n"));
- }
- }
- else
- {
- unsigned char model;
-
- if (((model = mad_read (MC3_PORT)) & 0x03) == 0x03)
- {
- DDB (printk ("mad16.c: Mozart detected\n"));
- board_type = MOZART;
- }
- else
- {
- DDB (printk ("mad16.c: 82C928 detected???\n"));
- board_type = C928;
- }
- }
- }
-
- return 1;
+ outb((0xE4), PASSWD_REG);
+ outb((0x80), PASSWD_REG);
+
+ board_type = C930;
+
+ DDB(printk("Detect using password = 0xE4\n"));
+
+ for (i = 0xf8d; i <= 0xf93; i++)
+ DDB(printk("port %03x = %02x\n", i, mad_read(i)));
+
+ if (!detect_mad16())
+ return 0;
+
+ DDB(printk("mad16.c: 82C930 detected\n"));
+ } else
+ {
+ DDB(printk("mad16.c: 82C929 detected\n"));
+ }
+ } else
+ {
+ unsigned char model;
+
+ if (((model = mad_read(MC3_PORT)) & 0x03) == 0x03)
+ {
+ DDB(printk("mad16.c: Mozart detected\n"));
+ board_type = MOZART;
+ } else
+ {
+ DDB(printk("mad16.c: 82C928 detected???\n"));
+ board_type = C928;
+ }
+ }
+ }
+ return 1;
}
int
-probe_mad16 (struct address_info *hw_config)
+probe_mad16(struct address_info *hw_config)
{
- int i;
- static int valid_ports[] =
- {0x530, 0xe80, 0xf40, 0x604};
- unsigned char tmp;
- unsigned char cs4231_mode = 0;
+ int i;
+ static int valid_ports[] =
+ {0x530, 0xe80, 0xf40, 0x604};
+ unsigned char tmp;
+ unsigned char cs4231_mode = 0;
- int ad_flags = 0;
+ int ad_flags = 0;
- if (already_initialized)
- return 0;
+ if (already_initialized)
+ return 0;
- mad16_osp = hw_config->osp;
+ mad16_osp = hw_config->osp;
/*
* Check that all ports return 0xff (bus float) when no password
* is written to the password register.
*/
- DDB (printk ("--- Detecting MAD16 / Mozart ---\n"));
- if (!chip_detect ())
- return 0;
+ DDB(printk("--- Detecting MAD16 / Mozart ---\n"));
+ if (!chip_detect())
+ return 0;
- if (board_type == C930)
- return init_c930 (hw_config);
+ if (board_type == C930)
+ return init_c930(hw_config);
- for (i = 0xf8d; i <= 0xf93; i++)
- DDB (printk ("port %03x = %02x\n", i, mad_read (i)));
+ for (i = 0xf8d; i <= 0xf93; i++)
+ DDB(printk("port %03x = %02x\n", i, mad_read(i)));
/*
* Set the WSS address
*/
- tmp = (mad_read (MC1_PORT) & 0x0f) | 0x80; /* Enable WSS, Disable SB */
-
- for (i = 0; i < 5; i++)
- {
- if (i > 3) /* Not a valid port */
- {
- printk ("MAD16/Mozart: Bad WSS base address 0x%x\n", hw_config->io_base);
- return 0;
- }
-
- if (valid_ports[i] == hw_config->io_base)
- {
- tmp |= i << 4; /* WSS port select bits */
- break;
- }
- }
+ tmp = (mad_read(MC1_PORT) & 0x0f) | 0x80; /* Enable WSS, Disable SB */
+
+ for (i = 0; i < 5; i++)
+ {
+ if (i > 3) /* Not a valid port */
+ {
+ printk("MAD16/Mozart: Bad WSS base address 0x%x\n", hw_config->io_base);
+ return 0;
+ }
+ if (valid_ports[i] == hw_config->io_base)
+ {
+ tmp |= i << 4; /* WSS port select bits */
+ break;
+ }
+ }
/*
* Set optional CD-ROM and joystick settings.
*/
#ifdef MAD16_CONF
- tmp &= ~0x0f;
- tmp |= ((MAD16_CONF) & 0x0f); /* CD-ROM and joystick bits */
+ tmp &= ~0x0f;
+ tmp |= ((MAD16_CONF) & 0x0f); /* CD-ROM and joystick bits */
#endif
- mad_write (MC1_PORT, tmp);
+ mad_write(MC1_PORT, tmp);
#if defined(MAD16_CONF) && defined(MAD16_CDSEL)
- tmp = MAD16_CDSEL;
+ tmp = MAD16_CDSEL;
#else
- tmp = mad_read (MC2_PORT);
+ tmp = mad_read(MC2_PORT);
#endif
#ifdef MAD16_OPL4
- tmp |= 0x20; /* Enable OPL4 access */
+ tmp |= 0x20; /* Enable OPL4 access */
#endif
- mad_write (MC2_PORT, tmp);
- mad_write (MC3_PORT, 0xf0); /* Disable SB */
+ mad_write(MC2_PORT, tmp);
+ mad_write(MC3_PORT, 0xf0); /* Disable SB */
- if (board_type == C924) /* Specific C924 init values */
- {
- mad_write (MC4_PORT, 0xA0);
- mad_write (MC5_PORT, 0x05);
- mad_write (MC6_PORT, 0x03);
- }
-
- if (!ad1848_detect (hw_config->io_base + 4, &ad_flags, mad16_osp))
- return 0;
+ if (board_type == C924) /* Specific C924 init values */
+ {
+ mad_write(MC4_PORT, 0xA0);
+ mad_write(MC5_PORT, 0x05);
+ mad_write(MC6_PORT, 0x03);
+ }
+ if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp))
+ return 0;
- if (ad_flags & (AD_F_CS4231 | AD_F_CS4248))
- cs4231_mode = 0x02; /* CS4248/CS4231 sync delay switch */
+ if (ad_flags & (AD_F_CS4231 | AD_F_CS4248))
+ cs4231_mode = 0x02; /* CS4248/CS4231 sync delay switch */
- if (board_type == C929)
- {
- mad_write (MC4_PORT, 0xa2);
- mad_write (MC5_PORT, 0xA5 | cs4231_mode);
- mad_write (MC6_PORT, 0x03); /* Disable MPU401 */
- }
- else
- {
- mad_write (MC4_PORT, 0x02);
- mad_write (MC5_PORT, 0x30 | cs4231_mode);
- }
+ if (board_type == C929)
+ {
+ mad_write(MC4_PORT, 0xa2);
+ mad_write(MC5_PORT, 0xA5 | cs4231_mode);
+ mad_write(MC6_PORT, 0x03); /* Disable MPU401 */
+ } else
+ {
+ mad_write(MC4_PORT, 0x02);
+ mad_write(MC5_PORT, 0x30 | cs4231_mode);
+ }
- for (i = 0xf8d; i <= 0xf93; i++)
- DDB (printk ("port %03x after init = %02x\n", i, mad_read (i)));
+ for (i = 0xf8d; i <= 0xf93; i++)
+ DDB(printk("port %03x after init = %02x\n", i, mad_read(i)));
- wss_init (hw_config);
+ wss_init(hw_config);
- return 1;
+ return 1;
}
void
-attach_mad16 (struct address_info *hw_config)
+attach_mad16(struct address_info *hw_config)
{
- static char interrupt_bits[12] =
- {
- -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20
- };
- char bits;
+ static char interrupt_bits[12] =
+ {
+ -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20
+ };
+ char bits;
- static char dma_bits[4] =
- {
- 1, 2, 0, 3
- };
+ static char dma_bits[4] =
+ {
+ 1, 2, 0, 3
+ };
- int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3;
- int ad_flags = 0, dma = hw_config->dma, dma2 = hw_config->dma2;
- unsigned char dma2_bit = 0;
+ int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3;
+ int ad_flags = 0, dma = hw_config->dma, dma2 = hw_config->dma2;
+ unsigned char dma2_bit = 0;
- already_initialized = 1;
+ already_initialized = 1;
- if (!ad1848_detect (hw_config->io_base + 4, &ad_flags, mad16_osp))
- return;
+ if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp))
+ return;
- /*
- * Set the IRQ and DMA addresses.
- */
- if (board_type == C930)
- interrupt_bits[5] = 0x28; /* Also IRQ5 is possible on C930 */
+ /*
+ * Set the IRQ and DMA addresses.
+ */
+ if (board_type == C930)
+ interrupt_bits[5] = 0x28; /* Also IRQ5 is possible on C930 */
- bits = interrupt_bits[hw_config->irq];
- if (bits == -1)
- return;
+ bits = interrupt_bits[hw_config->irq];
+ if (bits == -1)
+ return;
- outb ((bits | 0x40), config_port);
- if ((inb (version_port) & 0x40) == 0)
- printk ("[IRQ Conflict?]");
+ outb((bits | 0x40), config_port);
+ if ((inb(version_port) & 0x40) == 0)
+ printk("[IRQ Conflict?]");
/*
* Handle the capture DMA channel
*/
- if (ad_flags & AD_F_CS4231 && dma2 != -1 && dma2 != dma)
- {
- if (!((dma == 0 && dma2 == 1) ||
- (dma == 1 && dma2 == 0) ||
- (dma == 3 && dma2 == 0)))
- { /* Unsupported combination. Try to swap channels */
- int tmp = dma;
-
- dma = dma2;
- dma2 = tmp;
- }
-
- if ((dma == 0 && dma2 == 1) ||
- (dma == 1 && dma2 == 0) ||
- (dma == 3 && dma2 == 0))
- {
- dma2_bit = 0x04; /* Enable capture DMA */
- }
- else
- {
- printk ("MAD16: Invalid capture DMA\n");
- dma2 = dma;
- }
- }
- else
- dma2 = dma;
-
- outb ((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */
-
- ad1848_init ("MAD16 WSS", hw_config->io_base + 4,
- hw_config->irq,
- dma,
- dma2, 0,
- hw_config->osp);
- request_region (hw_config->io_base, 4, "MAD16 WSS config");
+ if (ad_flags & AD_F_CS4231 && dma2 != -1 && dma2 != dma)
+ {
+ if (!((dma == 0 && dma2 == 1) ||
+ (dma == 1 && dma2 == 0) ||
+ (dma == 3 && dma2 == 0)))
+ { /* Unsupported combination. Try to swap channels */
+ int tmp = dma;
+
+ dma = dma2;
+ dma2 = tmp;
+ }
+ if ((dma == 0 && dma2 == 1) ||
+ (dma == 1 && dma2 == 0) ||
+ (dma == 3 && dma2 == 0))
+ {
+ dma2_bit = 0x04; /* Enable capture DMA */
+ } else
+ {
+ printk("MAD16: Invalid capture DMA\n");
+ dma2 = dma;
+ }
+ } else
+ dma2 = dma;
+
+ outb((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */
+
+ hw_config->slots[0] = ad1848_init("MAD16 WSS", hw_config->io_base + 4,
+ hw_config->irq,
+ dma,
+ dma2, 0,
+ hw_config->osp);
+ request_region(hw_config->io_base, 4, "MAD16 WSS config");
}
void
-attach_mad16_mpu (struct address_info *hw_config)
+attach_mad16_mpu(struct address_info *hw_config)
{
- if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */
- {
-#ifdef CONFIG_MIDI
+ if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */
+ {
+#if defined(CONFIG_MIDI)
- if (mad_read (MC1_PORT) & 0x20)
- hw_config->io_base = 0x240;
- else
- hw_config->io_base = 0x220;
+ if (mad_read(MC1_PORT) & 0x20)
+ hw_config->io_base = 0x240;
+ else
+ hw_config->io_base = 0x220;
- hw_config->name = "Mad16/Mozart";
- sb_dsp_init (hw_config);
+ hw_config->name = "Mad16/Mozart";
+ sb_dsp_init(hw_config);
#endif
- return;
- }
-
+ return;
+ }
#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
- if (!already_initialized)
- return;
+ if (!already_initialized)
+ return;
- hw_config->driver_use_1 = SB_MIDI_ONLY;
- hw_config->name = "Mad16/Mozart";
- attach_uart401 (hw_config);
+ hw_config->driver_use_1 = SB_MIDI_ONLY;
+ hw_config->name = "Mad16/Mozart";
+ attach_uart401(hw_config);
#endif
}
int
-probe_mad16_mpu (struct address_info *hw_config)
+probe_mad16_mpu(struct address_info *hw_config)
{
#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
- static int mpu_attached = 0;
- static int valid_ports[] =
- {0x330, 0x320, 0x310, 0x300};
- static short valid_irqs[] =
- {9, 10, 5, 7};
- unsigned char tmp;
-
- int i; /* A variable with secret power */
-
- if (!already_initialized) /* The MSS port must be initialized first */
- return 0;
-
- if (mpu_attached) /* Don't let them call this twice */
- return 0;
- mpu_attached = 1;
+ static int mpu_attached = 0;
+ static int valid_ports[] =
+ {0x330, 0x320, 0x310, 0x300};
+ static short valid_irqs[] =
+ {9, 10, 5, 7};
+ unsigned char tmp;
- if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */
- {
+ int i; /* A variable with secret power */
-#ifdef CONFIG_MIDI
- unsigned char tmp;
-
- tmp = mad_read (MC3_PORT);
-
- /*
- * MAD16 SB base is defined by the WSS base. It cannot be changed
- * alone.
- * Ignore configured I/O base. Use the active setting.
- */
-
- if (mad_read (MC1_PORT) & 0x20)
- hw_config->io_base = 0x240;
- else
- hw_config->io_base = 0x220;
+ if (!already_initialized) /* The MSS port must be initialized first */
+ return 0;
- switch (hw_config->irq)
- {
- case 5:
- tmp = (tmp & 0x3f) | 0x80;
- break;
- case 7:
- tmp = (tmp & 0x3f);
- break;
- case 11:
- tmp = (tmp & 0x3f) | 0x40;
- break;
- default:
- printk ("mad16/Mozart: Invalid MIDI IRQ\n");
- return 0;
- }
-
- mad_write (MC3_PORT, tmp | 0x04);
- hw_config->driver_use_1 = SB_MIDI_ONLY;
- return sb_dsp_detect (hw_config);
+ if (mpu_attached) /* Don't let them call this twice */
+ return 0;
+ mpu_attached = 1;
+
+ if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */
+ {
+
+#if defined(CONFIG_MIDI)
+ unsigned char tmp;
+
+ tmp = mad_read(MC3_PORT);
+
+ /*
+ * MAD16 SB base is defined by the WSS base. It cannot be changed
+ * alone.
+ * Ignore configured I/O base. Use the active setting.
+ */
+
+ if (mad_read(MC1_PORT) & 0x20)
+ hw_config->io_base = 0x240;
+ else
+ hw_config->io_base = 0x220;
+
+ switch (hw_config->irq)
+ {
+ case 5:
+ tmp = (tmp & 0x3f) | 0x80;
+ break;
+ case 7:
+ tmp = (tmp & 0x3f);
+ break;
+ case 11:
+ tmp = (tmp & 0x3f) | 0x40;
+ break;
+ default:
+ printk("mad16/Mozart: Invalid MIDI IRQ\n");
+ return 0;
+ }
+
+ mad_write(MC3_PORT, tmp | 0x04);
+ hw_config->driver_use_1 = SB_MIDI_ONLY;
+ return sb_dsp_detect(hw_config);
#else
- return 0;
+ return 0;
#endif
- }
-
- tmp = mad_read (MC6_PORT) & 0x83;
- tmp |= 0x80; /* MPU-401 enable */
+ }
+ tmp = mad_read(MC6_PORT) & 0x83;
+ tmp |= 0x80; /* MPU-401 enable */
/*
* Set the MPU base bits
*/
- for (i = 0; i < 5; i++)
- {
- if (i > 3) /* Out of array bounds */
- {
- printk ("MAD16 / Mozart: Invalid MIDI port 0x%x\n", hw_config->io_base);
- return 0;
- }
-
- if (valid_ports[i] == hw_config->io_base)
- {
- tmp |= i << 5;
- break;
- }
- }
+ for (i = 0; i < 5; i++)
+ {
+ if (i > 3) /* Out of array bounds */
+ {
+ printk("MAD16 / Mozart: Invalid MIDI port 0x%x\n", hw_config->io_base);
+ return 0;
+ }
+ if (valid_ports[i] == hw_config->io_base)
+ {
+ tmp |= i << 5;
+ break;
+ }
+ }
/*
* Set the MPU IRQ bits
*/
- for (i = 0; i < 5; i++)
- {
- if (i > 3) /* Out of array bounds */
- {
- printk ("MAD16 / Mozart: Invalid MIDI IRQ %d\n", hw_config->irq);
- return 0;
- }
-
- if (valid_irqs[i] == hw_config->irq)
- {
- tmp |= i << 3;
- break;
- }
- }
- mad_write (MC6_PORT, tmp); /* Write MPU401 config */
-
- return probe_uart401 (hw_config);
+ for (i = 0; i < 5; i++)
+ {
+ if (i > 3) /* Out of array bounds */
+ {
+ printk("MAD16 / Mozart: Invalid MIDI IRQ %d\n", hw_config->irq);
+ return 0;
+ }
+ if (valid_irqs[i] == hw_config->irq)
+ {
+ tmp |= i << 3;
+ break;
+ }
+ }
+ mad_write(MC6_PORT, tmp); /* Write MPU401 config */
+
+ return probe_uart401(hw_config);
#else
- return 0;
+ return 0;
#endif
}
void
-unload_mad16 (struct address_info *hw_config)
+unload_mad16(struct address_info *hw_config)
{
- ad1848_unload (hw_config->io_base + 4,
- hw_config->irq,
- hw_config->dma,
- hw_config->dma2, 0);
- release_region (hw_config->io_base, 4);
+ ad1848_unload(hw_config->io_base + 4,
+ hw_config->irq,
+ hw_config->dma,
+ hw_config->dma2, 0);
+ release_region(hw_config->io_base, 4);
+ sound_unload_audiodev(hw_config->slots[0]);
}
void
-unload_mad16_mpu (struct address_info *hw_config)
+unload_mad16_mpu(struct address_info *hw_config)
{
-#ifdef CONFIG_MIDI
- if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */
- {
- sb_dsp_unload (hw_config);
- return;
- }
+#if defined(CONFIG_MIDI)
+ if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */
+ {
+ sb_dsp_unload(hw_config);
+ return;
+ }
#endif
#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
- unload_uart401 (hw_config);
+ unload_uart401(hw_config);
#endif
}
+#ifdef MODULE
+
+int io = -1;
+int dma = -1;
+int dma16 = -1; /* Set this for modules that need it */
+int irq = -1;
+
+int cdtype = 0;
+int cdirq = 0;
+int cdport = 0x340;
+int cddma = 3;
+int opl4 = 0;
+int joystick = 0;
+
+static int found_mpu;
+
+
+static int dma_map[2][8] =
+{
+ {0x03, -1, -1, -1, -1, 0x00, 0x01, 0x02},
+ {0x03, -1, 0x01, 0x00, -1, -1, -1, -1}
+};
+
+static int irq_map[16] =
+{
+ 0x00, -1, -1, 0x0A,
+ -1, 0x04, -1, 0x08,
+ -1, 0x10, 0x14, 0x18,
+ -1, -1, -1, -1
+};
+
+struct address_info config;
+
+int
+init_module(void)
+{
+ int dmatype = 0;
+
+ printk("MAD16 audio driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+
+ if (io == -1 || dma == -1 || irq == -1)
+ {
+ printk("I/O, DMA and irq are mandatory\n");
+ return -EINVAL;
+ }
+ printk("CDROM ");
+ switch (cdtype)
+ {
+ case 0x00:
+ printk("Disabled");
+ cdirq = 0;
+ break;
+ case 0x02:
+ printk("Sony CDU31A");
+ dmatype = 2;
+ break;
+ case 0x04:
+ printk("Mitsumi");
+ dmatype = 1;
+ break;
+ case 0x06:
+ printk("Panasonic Lasermate");
+ dmatype = 2;
+ break;
+ case 0x08:
+ printk("Secondary IDE");
+ dmatype = 1;
+ break;
+ case 0x0A:
+ printk("Primary IDE");
+ dmatype = 1;
+ break;
+ default:
+ printk("\nInvalid CDROM type\n");
+ return -EINVAL;
+ }
+
+ if (dmatype)
+ {
+ if (cddma > 7 || cddma < 0 || dma_map[dmatype][cddma] == -1)
+ {
+ printk("\nInvalid CDROM DMA\n");
+ return -EINVAL;
+ }
+ if (cddma)
+ printk(", DMA %d", cddma);
+ else
+ printk(", no DMA");
+ }
+ if (cdtype && !cdirq)
+ printk(", no IRQ");
+ else if (cdirq < 0 || cdirq > 15 || irq_map[cdirq] == -1)
+ {
+ printk(", invalid IRQ (disabling)");
+ cdirq = 0;
+ } else
+ printk(", IRQ %d", cdirq);
+
+ printk(".\nJoystick port ");
+ if (joystick == 1)
+ printk("enabled.\n");
+ else
+ {
+ joystick = 0;
+ printk("disabled.\n");
+ }
+
+ /*
+ * Build the config words
+ */
+
+ mad16_conf = (joystick ^ 1) | cdtype;
+ mad16_cdsel = 0;
+ if (opl4)
+ mad16_cdsel |= 0x20;
+ mad16_cdsel |= dma_map[dmatype][cddma];
+
+ if (cdtype < 0x08)
+ {
+ switch (cdport)
+ {
+ case 0x340:
+ mad16_cdsel |= 0x00;
+ break;
+ case 0x330:
+ mad16_cdsel |= 0x40;
+ break;
+ case 0x360:
+ mad16_cdsel |= 0x80;
+ break;
+ case 0x320:
+ mad16_cdsel |= 0xC0;
+ break;
+ default:
+ printk("Unknown CDROM I/O base %d\n", cdport);
+ return -EINVAL;
+ }
+ }
+ mad16_cdsel |= irq_map[cdirq];
+
+ config.io_base = io;
+ config.irq = irq;
+ config.dma = dma;
+ config.dma2 = dma16;
+
+ if (!probe_mad16(&config))
+ return -ENODEV;
+ found_mpu = probe_mad16_mpu(&config);
+
+ attach_mad16(&config);
+
+ if (found_mpu)
+ attach_mad16_mpu(&config);
+
+ SOUND_LOCK;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ if (found_mpu)
+ unload_mad16_mpu(&config);
+ unload_mad16(&config);
+ SOUND_LOCK_END;
+}
+
+#endif
+
/* That's all folks */
diff --git a/drivers/sound/maui.c b/drivers/sound/maui.c
index 6fa104905..37f4966b6 100644
--- a/drivers/sound/maui.c
+++ b/drivers/sound/maui.c
@@ -11,14 +11,16 @@
* for more info.
*/
#include <linux/config.h>
-
+#include <linux/module.h>
#define USE_SEQ_MACROS
#define USE_SIMPLE_MACROS
#include "sound_config.h"
+#include "soundmodule.h"
+#include "sound_firmware.h"
-#if defined(CONFIG_MAUI)
+#if defined(CONFIG_MAUI) || defined(MODULE)
static int maui_base = 0x330;
@@ -37,7 +39,7 @@ static int *maui_osp;
#define STAT_RX_IENA 0x01
static int (*orig_load_patch) (int dev, int format, const char *addr,
- int offs, int count, int pmgr_flag) = NULL;
+ int offs, int count, int pmgr_flag) = NULL;
#ifdef HAVE_MAUI_BOOT
#include "maui_boot.h"
@@ -52,440 +54,472 @@ static volatile struct snd_wait maui_sleep_flag =
{0};
static int
-maui_wait (int mask)
+maui_wait(int mask)
{
- int i;
+ int i;
/*
* Perform a short initial wait without sleeping
*/
- for (i = 0; i < 100; i++)
- {
- if (inb (HOST_STAT_PORT) & mask)
- {
- return 1;
- }
- }
+ for (i = 0; i < 100; i++)
+ {
+ if (inb(HOST_STAT_PORT) & mask)
+ {
+ return 1;
+ }
+ }
/*
* Wait up to 15 seconds with sleeping
*/
- for (i = 0; i < 150; i++)
- {
- if (inb (HOST_STAT_PORT) & mask)
- {
- return 1;
- }
-
-
- {
- unsigned long tlimit;
-
- if (HZ / 10)
- current->timeout = tlimit = jiffies + (HZ / 10);
- else
- tlimit = (unsigned long) -1;
- maui_sleep_flag.opts = WK_SLEEP;
- interruptible_sleep_on (&maui_sleeper);
- if (!(maui_sleep_flag.opts & WK_WAKEUP))
+ for (i = 0; i < 150; i++)
{
- if (jiffies >= tlimit)
- maui_sleep_flag.opts |= WK_TIMEOUT;
+ if (inb(HOST_STAT_PORT) & mask)
+ {
+ return 1;
+ }
+ {
+ unsigned long tlimit;
+
+ if (HZ / 10)
+ current->timeout = tlimit = jiffies + (HZ / 10);
+ else
+ tlimit = (unsigned long) -1;
+ maui_sleep_flag.opts = WK_SLEEP;
+ interruptible_sleep_on(&maui_sleeper);
+ if (!(maui_sleep_flag.opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ maui_sleep_flag.opts |= WK_TIMEOUT;
+ }
+ maui_sleep_flag.opts &= ~WK_SLEEP;
+ };
+ if ((current->signal & ~current->blocked))
+ {
+ return 0;
+ }
}
- maui_sleep_flag.opts &= ~WK_SLEEP;
- };
- if ((current->signal & ~current->blocked))
- {
- return 0;
- }
- }
-
- return 0;
+
+ return 0;
}
static int
-maui_read (void)
+maui_read(void)
{
- if (maui_wait (STAT_RX_AVAIL))
- return inb (HOST_DATA_PORT);
+ if (maui_wait(STAT_RX_AVAIL))
+ return inb(HOST_DATA_PORT);
- return -1;
+ return -1;
}
static int
-maui_write (unsigned char data)
+maui_write(unsigned char data)
{
- if (maui_wait (STAT_TX_AVAIL))
- {
- outb ((data), HOST_DATA_PORT);
- return 1;
- }
- printk ("Maui: Write timeout\n");
-
- return 0;
+ if (maui_wait(STAT_TX_AVAIL))
+ {
+ outb((data), HOST_DATA_PORT);
+ return 1;
+ }
+ printk("Maui: Write timeout\n");
+
+ return 0;
}
static void
-mauiintr (int irq, void *dev_id, struct pt_regs *dummy)
+mauiintr(int irq, void *dev_id, struct pt_regs *dummy)
{
- irq_ok = 1;
+ irq_ok = 1;
}
static int
-download_code (void)
+download_code(void)
{
- int i, lines = 0;
- int eol_seen = 0, done = 0;
- int skip = 1;
+ int i, lines = 0;
+ int eol_seen = 0, done = 0;
+ int skip = 1;
- printk ("Code download (%d bytes): ", maui_osLen);
+ printk("Code download (%d bytes): ", maui_osLen);
- for (i = 0; i < maui_osLen; i++)
- {
- if (maui_os[i] != '\r')
- if (!skip || (maui_os[i] == 'S' && (i == 0 || maui_os[i - 1] == '\n')))
+ for (i = 0; i < maui_osLen; i++)
{
- skip = 0;
-
- if (maui_os[i] == '\n')
- eol_seen = skip = 1;
- else if (maui_os[i] == 'S')
- {
- if (maui_os[i + 1] == '8')
- done = 1;
- if (!maui_write (0xF1))
- goto failure;
- if (!maui_write ('S'))
- goto failure;
- }
- else
- {
- if (!maui_write (maui_os[i]))
- goto failure;
- }
-
- if (eol_seen)
- {
- int c = 0;
-
- int n;
-
- eol_seen = 0;
-
- for (n = 0; n < 2; n++)
- if (maui_wait (STAT_RX_AVAIL))
- {
- c = inb (HOST_DATA_PORT);
- break;
- }
-
- if (c != 0x80)
- {
- printk ("Download not acknowledged\n");
- return 0;
- }
- else if (!(lines++ % 10))
- printk (".");
-
- if (done)
- {
- printk ("\nDownload complete\n");
- return 1;
- }
- }
+ if (maui_os[i] != '\r')
+ if (!skip || (maui_os[i] == 'S' && (i == 0 || maui_os[i - 1] == '\n')))
+ {
+ skip = 0;
+
+ if (maui_os[i] == '\n')
+ eol_seen = skip = 1;
+ else if (maui_os[i] == 'S')
+ {
+ if (maui_os[i + 1] == '8')
+ done = 1;
+ if (!maui_write(0xF1))
+ goto failure;
+ if (!maui_write('S'))
+ goto failure;
+ } else
+ {
+ if (!maui_write(maui_os[i]))
+ goto failure;
+ }
+
+ if (eol_seen)
+ {
+ int c = 0;
+
+ int n;
+
+ eol_seen = 0;
+
+ for (n = 0; n < 2; n++)
+ if (maui_wait(STAT_RX_AVAIL))
+ {
+ c = inb(HOST_DATA_PORT);
+ break;
+ }
+ if (c != 0x80)
+ {
+ printk("Download not acknowledged\n");
+ return 0;
+ } else if (!(lines++ % 10))
+ printk(".");
+
+ if (done)
+ {
+ printk("\nDownload complete\n");
+ return 1;
+ }
+ }
+ }
}
- }
-failure:
+ failure:
- printk ("\nDownload failed!!!\n");
- return 0;
+ printk("\nDownload failed!!!\n");
+ return 0;
}
static int
-maui_init (int irq)
+maui_init(int irq)
{
- int i;
- unsigned char bits;
+ int i;
+ unsigned char bits;
- switch (irq)
- {
- case 9:
- bits = 0x00;
- break;
- case 5:
- bits = 0x08;
- break;
- case 12:
- bits = 0x10;
- break;
- case 15:
- bits = 0x18;
- break;
-
- default:
- printk ("Maui: Invalid IRQ %d\n", irq);
- return 0;
- }
-
- outb ((0x00), HOST_CTRL_PORT); /* Reset */
+ switch (irq)
+ {
+ case 9:
+ bits = 0x00;
+ break;
+ case 5:
+ bits = 0x08;
+ break;
+ case 12:
+ bits = 0x10;
+ break;
+ case 15:
+ bits = 0x18;
+ break;
+
+ default:
+ printk("Maui: Invalid IRQ %d\n", irq);
+ return 0;
+ }
- outb ((bits), HOST_DATA_PORT); /* Set the IRQ bits */
- outb ((bits | 0x80), HOST_DATA_PORT); /* Set the IRQ bits again? */
+ outb((0x00), HOST_CTRL_PORT); /* Reset */
- outb ((0x80), HOST_CTRL_PORT); /* Leave reset */
- outb ((0x80), HOST_CTRL_PORT); /* Leave reset */
+ outb((bits), HOST_DATA_PORT); /* Set the IRQ bits */
+ outb((bits | 0x80), HOST_DATA_PORT); /* Set the IRQ bits again? */
- outb ((0xD0), HOST_CTRL_PORT); /* Cause interrupt */
+ outb((0x80), HOST_CTRL_PORT); /* Leave reset */
+ outb((0x80), HOST_CTRL_PORT); /* Leave reset */
- for (i = 0; i < 1000000 && !irq_ok; i++);
+ outb((0xD0), HOST_CTRL_PORT); /* Cause interrupt */
- if (!irq_ok)
- return 0;
+ for (i = 0; i < 1000000 && !irq_ok; i++);
- outb ((0x80), HOST_CTRL_PORT); /* Leave reset */
+ if (!irq_ok)
+ return 0;
- printk ("Turtle Beach Maui initialization\n");
+ outb((0x80), HOST_CTRL_PORT); /* Leave reset */
- if (!download_code ())
- return 0;
+ printk("Turtle Beach Maui initialization\n");
- outb ((0xE0), HOST_CTRL_PORT); /* Normal operation */
+ if (!download_code())
+ return 0;
- /* Select mpu401 mode */
+ outb((0xE0), HOST_CTRL_PORT); /* Normal operation */
- maui_write (0xf0);
- maui_write (1);
- if (maui_read () != 0x80)
- {
- maui_write (0xf0);
- maui_write (1);
- if (maui_read () != 0x80)
- printk ("Maui didn't acknowledge set HW mode command\n");
- }
+ /* Select mpu401 mode */
- printk ("Maui initialized OK\n");
- return 1;
+ maui_write(0xf0);
+ maui_write(1);
+ if (maui_read() != 0x80)
+ {
+ maui_write(0xf0);
+ maui_write(1);
+ if (maui_read() != 0x80)
+ printk("Maui didn't acknowledge set HW mode command\n");
+ }
+ printk("Maui initialized OK\n");
+ return 1;
}
static int
-maui_short_wait (int mask)
+maui_short_wait(int mask)
{
- int i;
+ int i;
- for (i = 0; i < 1000; i++)
- {
- if (inb (HOST_STAT_PORT) & mask)
- {
- return 1;
- }
- }
+ for (i = 0; i < 1000; i++)
+ {
+ if (inb(HOST_STAT_PORT) & mask)
+ {
+ return 1;
+ }
+ }
- return 0;
+ return 0;
}
static int
-maui_load_patch (int dev, int format, const char *addr,
- int offs, int count, int pmgr_flag)
+maui_load_patch(int dev, int format, const char *addr,
+ int offs, int count, int pmgr_flag)
{
- struct sysex_info header;
- unsigned long left, src_offs;
- int hdr_size = (unsigned long) &header.data[0] - (unsigned long) &header;
- int i;
-
- if (format == SYSEX_PATCH) /* Handled by midi_synth.c */
- return orig_load_patch (dev, format, addr, offs, count, pmgr_flag);
-
- if (format != MAUI_PATCH)
- {
- printk ("Maui: Unknown patch format\n");
- }
+ struct sysex_info header;
+ unsigned long left, src_offs;
+ int hdr_size = (unsigned long) &header.data[0] - (unsigned long) &header;
+ int i;
- if (count < hdr_size)
- {
- printk ("Maui error: Patch header too short\n");
- return -EINVAL;
- }
+ if (format == SYSEX_PATCH) /* Handled by midi_synth.c */
+ return orig_load_patch(dev, format, addr, offs, count, pmgr_flag);
- count -= hdr_size;
-
- /*
- * Copy the header from user space but ignore the first bytes which have
- * been transferred already.
- */
-
- copy_from_user (&((char *) &header)[offs], &(addr)[offs], hdr_size - offs);
+ if (format != MAUI_PATCH)
+ {
+ printk("Maui: Unknown patch format\n");
+ }
+ if (count < hdr_size)
+ {
+ printk("Maui error: Patch header too short\n");
+ return -EINVAL;
+ }
+ count -= hdr_size;
- if (count < header.len)
- {
- printk ("Maui warning: Host command record too short (%d<%d)\n",
- count, (int) header.len);
- header.len = count;
- }
+ /*
+ * Copy the header from user space but ignore the first bytes which have
+ * been transferred already.
+ */
- left = header.len;
- src_offs = 0;
+ copy_from_user(&((char *) &header)[offs], &(addr)[offs], hdr_size - offs);
- for (i = 0; i < left; i++)
- {
- unsigned char data;
+ if (count < header.len)
+ {
+ printk("Maui warning: Host command record too short (%d<%d)\n", count, (int) header.len);
+ header.len = count;
+ }
+ left = header.len;
+ src_offs = 0;
- get_user (*(unsigned char *) &data, (unsigned char *) &((addr)[hdr_size + i]));
- if (i == 0 && !(data & 0x80))
- return -EINVAL;
+ for (i = 0; i < left; i++)
+ {
+ unsigned char data;
- if (maui_write (data) == -1)
- return -EIO;
- }
+ get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[hdr_size + i]));
+ if (i == 0 && !(data & 0x80))
+ return -EINVAL;
- if ((i = maui_read ()) != 0x80)
- {
- if (i != -1)
- printk ("Maui: Error status %02x\n", i);
+ if (maui_write(data) == -1)
+ return -EIO;
+ }
- return -EIO;
- }
+ if ((i = maui_read()) != 0x80)
+ {
+ if (i != -1)
+ printk("Maui: Error status %02x\n", i);
- return 0;
+ return -EIO;
+ }
+ return 0;
}
int
-probe_maui (struct address_info *hw_config)
+probe_maui(struct address_info *hw_config)
{
- int i;
- int tmp1, tmp2, ret;
+ int i;
+ int tmp1, tmp2, ret;
- if (check_region (hw_config->io_base, 8))
- return 0;
+ if (check_region(hw_config->io_base, 8))
+ return 0;
- maui_base = hw_config->io_base;
- maui_osp = hw_config->osp;
+ maui_base = hw_config->io_base;
+ maui_osp = hw_config->osp;
- if (snd_set_irq_handler (hw_config->irq, mauiintr, "Maui", maui_osp) < 0)
- return 0;
+ if (snd_set_irq_handler(hw_config->irq, mauiintr, "Maui", maui_osp) < 0)
+ return 0;
- maui_sleep_flag.opts = WK_NONE;
+ maui_sleep_flag.opts = WK_NONE;
/*
* Initialize the processor if necessary
*/
- if (maui_osLen > 0)
- {
- if (!(inb (HOST_STAT_PORT) & STAT_TX_AVAIL) ||
- !maui_write (0x9F) || /* Report firmware version */
- !maui_short_wait (STAT_RX_AVAIL) ||
- maui_read () == -1 || maui_read () == -1)
- if (!maui_init (hw_config->irq))
+ if (maui_osLen > 0)
+ {
+ if (!(inb(HOST_STAT_PORT) & STAT_TX_AVAIL) ||
+ !maui_write(0x9F) || /* Report firmware version */
+ !maui_short_wait(STAT_RX_AVAIL) ||
+ maui_read() == -1 || maui_read() == -1)
+ if (!maui_init(hw_config->irq))
+ {
+ snd_release_irq(hw_config->irq);
+ return 0;
+ }
+ }
+ if (!maui_write(0xCF)) /* Report hardware version */
+ {
+ printk("No WaveFront firmware detected (card uninitialized?)\n");
+ snd_release_irq(hw_config->irq);
+ return 0;
+ }
+ if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1)
+ {
+ printk("No WaveFront firmware detected (card uninitialized?)\n");
+ snd_release_irq(hw_config->irq);
+ return 0;
+ }
+ if (tmp1 == 0xff || tmp2 == 0xff)
+ {
+ snd_release_irq(hw_config->irq);
+ return 0;
+ }
+ if (trace_init)
+ printk("WaveFront hardware version %d.%d\n", tmp1, tmp2);
+
+ if (!maui_write(0x9F)) /* Report firmware version */
+ return 0;
+ if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1)
+ return 0;
+
+ if (trace_init)
+ printk("WaveFront firmware version %d.%d\n", tmp1, tmp2);
+
+ if (!maui_write(0x85)) /* Report free DRAM */
+ return 0;
+ tmp1 = 0;
+ for (i = 0; i < 4; i++)
{
- snd_release_irq (hw_config->irq);
- return 0;
+ tmp1 |= maui_read() << (7 * i);
}
- }
-
- if (!maui_write (0xCF)) /* Report hardware version */
- {
- printk ("No WaveFront firmware detected (card uninitialized?)\n");
- snd_release_irq (hw_config->irq);
- return 0;
- }
-
- if ((tmp1 = maui_read ()) == -1 || (tmp2 = maui_read ()) == -1)
- {
- printk ("No WaveFront firmware detected (card uninitialized?)\n");
- snd_release_irq (hw_config->irq);
- return 0;
- }
-
- if (tmp1 == 0xff || tmp2 == 0xff)
- {
- snd_release_irq (hw_config->irq);
- return 0;
- }
-
- if (trace_init)
- printk ("WaveFront hardware version %d.%d\n", tmp1, tmp2);
-
- if (!maui_write (0x9F)) /* Report firmware version */
- return 0;
- if ((tmp1 = maui_read ()) == -1 || (tmp2 = maui_read ()) == -1)
- return 0;
-
- if (trace_init)
- printk ("WaveFront firmware version %d.%d\n", tmp1, tmp2);
-
- if (!maui_write (0x85)) /* Report free DRAM */
- return 0;
- tmp1 = 0;
- for (i = 0; i < 4; i++)
- {
- tmp1 |= maui_read () << (7 * i);
- }
- if (trace_init)
- printk ("Available DRAM %dk\n", tmp1 / 1024);
-
- for (i = 0; i < 1000; i++)
- if (probe_mpu401 (hw_config))
- break;
-
- ret = probe_mpu401 (hw_config);
-
- if (ret)
- request_region (hw_config->io_base + 2, 6, "Maui");
-
- return ret;
+ if (trace_init)
+ printk("Available DRAM %dk\n", tmp1 / 1024);
+
+ for (i = 0; i < 1000; i++)
+ if (probe_mpu401(hw_config))
+ break;
+
+ ret = probe_mpu401(hw_config);
+
+ if (ret)
+ request_region(hw_config->io_base + 2, 6, "Maui");
+
+ return ret;
}
void
-attach_maui (struct address_info *hw_config)
+attach_maui(struct address_info *hw_config)
{
- int this_dev = num_midis;
-
- conf_printf ("Maui", hw_config);
-
- hw_config->irq *= -1;
- hw_config->name = "Maui";
- attach_mpu401 (hw_config);
-
- if (num_midis > this_dev) /* The MPU401 driver installed itself */
- {
- struct synth_operations *synth;
-
- /*
- * Intercept patch loading calls so that they can be handled
- * by the Maui driver.
- */
-
- synth = midi_devs[this_dev]->converter;
- synth->id = "MAUI";
-
- if (synth != NULL)
- {
- orig_load_patch = synth->load_patch;
- synth->load_patch = &maui_load_patch;
- }
- else
- printk ("Maui: Can't install patch loader\n");
- }
+ int this_dev;
+
+ conf_printf("Maui", hw_config);
+
+ hw_config->irq *= -1;
+ hw_config->name = "Maui";
+ attach_mpu401(hw_config);
+
+ if (hw_config->slots[1] != -1) /* The MPU401 driver installed itself */
+ {
+ struct synth_operations *synth;
+
+ this_dev = hw_config->slots[1];
+
+ /*
+ * Intercept patch loading calls so that they can be handled
+ * by the Maui driver.
+ */
+
+ synth = midi_devs[this_dev]->converter;
+ synth->id = "MAUI";
+
+ if (synth != NULL)
+ {
+ orig_load_patch = synth->load_patch;
+ synth->load_patch = &maui_load_patch;
+ } else
+ printk(KERN_ERR "Maui: Can't install patch loader\n");
+ }
}
void
-unload_maui (struct address_info *hw_config)
+unload_maui(struct address_info *hw_config)
{
- int irq = hw_config->irq;
+ int irq = hw_config->irq;
- release_region (hw_config->io_base + 2, 6);
+ release_region(hw_config->io_base + 2, 6);
- unload_mpu401 (hw_config);
+ unload_mpu401(hw_config);
- if (irq < 0)
- irq = -irq;
+ if (irq < 0)
+ irq = -irq;
- if (irq > 0)
- snd_release_irq (irq);
+ if (irq > 0)
+ snd_release_irq(irq);
}
+#ifdef MODULE
+
+int io = -1;
+int irq = -1;
+
+static int fw_load = 0;
+
+struct address_info cfg;
+
+/*
+ * Install a CS4232 based card. Need to have ad1848 and mpu401
+ * loaded ready.
+ */
+
+int
+init_module(void)
+{
+ printk("Turtle beach Maui and Tropez driver, Copyright (C) by Hannu Savolainen 1993-1996\n");
+ if (io == -1 || irq == -1)
+ {
+ printk("maui: irq and io must be set.\n");
+ return -EINVAL;
+ }
+ cfg.io_base = io;
+ cfg.irq = irq;
+
+ if (maui_os == NULL)
+ {
+ fw_load = 1;
+ maui_osLen = mod_firmware_load("/etc/sound/oswf.mot", (char **) &maui_os);
+ }
+ if (probe_maui(&cfg) == 0)
+ return -ENODEV;
+ attach_maui(&cfg);
+ SOUND_LOCK;
+ return 0;
+}
+void
+cleanup_module(void)
+{
+ if (fw_load && maui_os)
+ kfree(maui_os);
+ unload_maui(&cfg);
+ SOUND_LOCK_END;
+}
+#endif
#endif
diff --git a/drivers/sound/midi_synth.c b/drivers/sound/midi_synth.c
index 454fc7471..3885a650b 100644
--- a/drivers/sound/midi_synth.c
+++ b/drivers/sound/midi_synth.c
@@ -12,13 +12,12 @@
*/
#include <linux/config.h>
-
#define USE_SEQ_MACROS
#define USE_SIMPLE_MACROS
#include "sound_config.h"
-#if defined(CONFIG_MIDI)
+#if defined(CONFIG_MIDI) || defined (MODULE)
#define _MIDI_SYNTH_C_
@@ -33,7 +32,7 @@ static int sysex_state[MAX_MIDI_DEV] =
{0};
static unsigned char prev_out_status[MAX_MIDI_DEV];
-#ifndef CONFIG_SEQUENCER
+#if !defined(CONFIG_SEQUENCER) && !defined(MODULE)
#define STORE(cmd)
#else
#define STORE(cmd) \
@@ -50,696 +49,686 @@ static unsigned char prev_out_status[MAX_MIDI_DEV];
#define _SEQ_ADVBUF(x) len=x
void
-do_midi_msg (int synthno, unsigned char *msg, int mlen)
+do_midi_msg(int synthno, unsigned char *msg, int mlen)
{
- switch (msg[0] & 0xf0)
- {
- case 0x90:
- if (msg[2] != 0)
- {
- STORE (SEQ_START_NOTE (synthno, msg[0] & 0x0f, msg[1], msg[2]));
- break;
- }
- msg[2] = 64;
-
- case 0x80:
- STORE (SEQ_STOP_NOTE (synthno, msg[0] & 0x0f, msg[1], msg[2]));
- break;
-
- case 0xA0:
- STORE (SEQ_KEY_PRESSURE (synthno, msg[0] & 0x0f, msg[1], msg[2]));
- break;
-
- case 0xB0:
- STORE (SEQ_CONTROL (synthno, msg[0] & 0x0f,
- msg[1], msg[2]));
- break;
-
- case 0xC0:
- STORE (SEQ_SET_PATCH (synthno, msg[0] & 0x0f, msg[1]));
- break;
-
- case 0xD0:
- STORE (SEQ_CHN_PRESSURE (synthno, msg[0] & 0x0f, msg[1]));
- break;
-
- case 0xE0:
- STORE (SEQ_BENDER (synthno, msg[0] & 0x0f,
- (msg[1] % 0x7f) | ((msg[2] & 0x7f) << 7)));
- break;
-
- default:
- /* printk ("MPU: Unknown midi channel message %02x\n", msg[0]); */
- ;
- }
+ switch (msg[0] & 0xf0)
+ {
+ case 0x90:
+ if (msg[2] != 0)
+ {
+ STORE(SEQ_START_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2]));
+ break;
+ }
+ msg[2] = 64;
+
+ case 0x80:
+ STORE(SEQ_STOP_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2]));
+ break;
+
+ case 0xA0:
+ STORE(SEQ_KEY_PRESSURE(synthno, msg[0] & 0x0f, msg[1], msg[2]));
+ break;
+
+ case 0xB0:
+ STORE(SEQ_CONTROL(synthno, msg[0] & 0x0f,
+ msg[1], msg[2]));
+ break;
+
+ case 0xC0:
+ STORE(SEQ_SET_PATCH(synthno, msg[0] & 0x0f, msg[1]));
+ break;
+
+ case 0xD0:
+ STORE(SEQ_CHN_PRESSURE(synthno, msg[0] & 0x0f, msg[1]));
+ break;
+
+ case 0xE0:
+ STORE(SEQ_BENDER(synthno, msg[0] & 0x0f,
+ (msg[1] % 0x7f) | ((msg[2] & 0x7f) << 7)));
+ break;
+
+ default:
+ /* printk( "MPU: Unknown midi channel message %02x\n", msg[0]); */
+ ;
+ }
}
static void
-midi_outc (int midi_dev, int data)
+midi_outc(int midi_dev, int data)
{
- int timeout;
+ int timeout;
- for (timeout = 0; timeout < 3200; timeout++)
- if (midi_devs[midi_dev]->outputc (midi_dev, (unsigned char) (data & 0xff)))
- {
- if (data & 0x80) /*
- * Status byte
- */
- prev_out_status[midi_dev] =
- (unsigned char) (data & 0xff); /*
- * Store for running status
+ for (timeout = 0; timeout < 3200; timeout++)
+ if (midi_devs[midi_dev]->outputc(midi_dev, (unsigned char) (data & 0xff)))
+ {
+ if (data & 0x80) /*
+ * Status byte
*/
- return; /*
- * Mission complete
- */
- }
-
- /*
- * Sorry! No space on buffers.
- */
- printk ("Midi send timed out\n");
+ prev_out_status[midi_dev] =
+ (unsigned char) (data & 0xff); /*
+ * Store for running status
+ */
+ return; /*
+ * Mission complete
+ */
+ }
+ /*
+ * Sorry! No space on buffers.
+ */
+ printk("Midi send timed out\n");
}
static int
-prefix_cmd (int midi_dev, unsigned char status)
+prefix_cmd(int midi_dev, unsigned char status)
{
- if ((char *) midi_devs[midi_dev]->prefix_cmd == NULL)
- return 1;
+ if ((char *) midi_devs[midi_dev]->prefix_cmd == NULL)
+ return 1;
- return midi_devs[midi_dev]->prefix_cmd (midi_dev, status);
+ return midi_devs[midi_dev]->prefix_cmd(midi_dev, status);
}
static void
-midi_synth_input (int orig_dev, unsigned char data)
+midi_synth_input(int orig_dev, unsigned char data)
{
- int dev;
- struct midi_input_info *inc;
-
- static unsigned char len_tab[] = /* # of data bytes following a status
- */
- {
- 2, /* 8x */
- 2, /* 9x */
- 2, /* Ax */
- 2, /* Bx */
- 1, /* Cx */
- 1, /* Dx */
- 2, /* Ex */
- 0 /* Fx */
- };
-
- if (orig_dev < 0 || orig_dev > num_midis)
- return;
-
- if (data == 0xfe) /* Ignore active sensing */
- return;
-
- dev = midi2synth[orig_dev];
- inc = &midi_devs[orig_dev]->in_info;
-
- switch (inc->m_state)
- {
- case MST_INIT:
- if (data & 0x80) /* MIDI status byte */
- {
- if ((data & 0xf0) == 0xf0) /* Common message */
- {
- switch (data)
- {
- case 0xf0: /* Sysex */
- inc->m_state = MST_SYSEX;
- break; /* Sysex */
-
- case 0xf1: /* MTC quarter frame */
- case 0xf3: /* Song select */
- inc->m_state = MST_DATA;
- inc->m_ptr = 1;
- inc->m_left = 1;
- inc->m_buf[0] = data;
- break;
+ int dev;
+ struct midi_input_info *inc;
- case 0xf2: /* Song position pointer */
- inc->m_state = MST_DATA;
- inc->m_ptr = 1;
- inc->m_left = 2;
- inc->m_buf[0] = data;
- break;
-
- default:
- inc->m_buf[0] = data;
- inc->m_ptr = 1;
- do_midi_msg (dev, inc->m_buf, inc->m_ptr);
- inc->m_ptr = 0;
- inc->m_left = 0;
- }
- }
- else
- {
- inc->m_state = MST_DATA;
- inc->m_ptr = 1;
- inc->m_left = len_tab[(data >> 4) - 8];
- inc->m_buf[0] = inc->m_prev_status = data;
- }
- }
- else if (inc->m_prev_status & 0x80) /* Ignore if no previous status (yet) */
- { /* Data byte (use running status) */
- inc->m_state = MST_DATA;
- inc->m_ptr = 2;
- inc->m_left = len_tab[(data >> 4) - 8] - 1;
- inc->m_buf[0] = inc->m_prev_status;
- inc->m_buf[1] = data;
- }
- break; /* MST_INIT */
-
- case MST_DATA:
- inc->m_buf[inc->m_ptr++] = data;
- if (--inc->m_left <= 0)
- {
- inc->m_state = MST_INIT;
- do_midi_msg (dev, inc->m_buf, inc->m_ptr);
- inc->m_ptr = 0;
- }
- break; /* MST_DATA */
-
- case MST_SYSEX:
- if (data == 0xf7) /* Sysex end */
+ static unsigned char len_tab[] = /* # of data bytes following a status
+ */
{
- inc->m_state = MST_INIT;
- inc->m_left = 0;
- inc->m_ptr = 0;
- }
- break; /* MST_SYSEX */
-
- default:
- printk ("MIDI%d: Unexpected state %d (%02x)\n", orig_dev, inc->m_state,
- (int) data);
- inc->m_state = MST_INIT;
- }
+ 2, /* 8x */
+ 2, /* 9x */
+ 2, /* Ax */
+ 2, /* Bx */
+ 1, /* Cx */
+ 1, /* Dx */
+ 2, /* Ex */
+ 0 /* Fx */
+ };
+
+ if (orig_dev < 0 || orig_dev > num_midis || midi_devs[orig_dev] == NULL)
+ return;
+
+ if (data == 0xfe) /* Ignore active sensing */
+ return;
+
+ dev = midi2synth[orig_dev];
+ inc = &midi_devs[orig_dev]->in_info;
+
+ switch (inc->m_state)
+ {
+ case MST_INIT:
+ if (data & 0x80) /* MIDI status byte */
+ {
+ if ((data & 0xf0) == 0xf0) /* Common message */
+ {
+ switch (data)
+ {
+ case 0xf0: /* Sysex */
+ inc->m_state = MST_SYSEX;
+ break; /* Sysex */
+
+ case 0xf1: /* MTC quarter frame */
+ case 0xf3: /* Song select */
+ inc->m_state = MST_DATA;
+ inc->m_ptr = 1;
+ inc->m_left = 1;
+ inc->m_buf[0] = data;
+ break;
+
+ case 0xf2: /* Song position pointer */
+ inc->m_state = MST_DATA;
+ inc->m_ptr = 1;
+ inc->m_left = 2;
+ inc->m_buf[0] = data;
+ break;
+
+ default:
+ inc->m_buf[0] = data;
+ inc->m_ptr = 1;
+ do_midi_msg(dev, inc->m_buf, inc->m_ptr);
+ inc->m_ptr = 0;
+ inc->m_left = 0;
+ }
+ } else
+ {
+ inc->m_state = MST_DATA;
+ inc->m_ptr = 1;
+ inc->m_left = len_tab[(data >> 4) - 8];
+ inc->m_buf[0] = inc->m_prev_status = data;
+ }
+ } else if (inc->m_prev_status & 0x80) /* Ignore if no previous status (yet) */
+ { /* Data byte (use running status) */
+ inc->m_state = MST_DATA;
+ inc->m_ptr = 2;
+ inc->m_left = len_tab[(data >> 4) - 8] - 1;
+ inc->m_buf[0] = inc->m_prev_status;
+ inc->m_buf[1] = data;
+ }
+ break; /* MST_INIT */
+
+ case MST_DATA:
+ inc->m_buf[inc->m_ptr++] = data;
+ if (--inc->m_left <= 0)
+ {
+ inc->m_state = MST_INIT;
+ do_midi_msg(dev, inc->m_buf, inc->m_ptr);
+ inc->m_ptr = 0;
+ }
+ break; /* MST_DATA */
+
+ case MST_SYSEX:
+ if (data == 0xf7) /* Sysex end */
+ {
+ inc->m_state = MST_INIT;
+ inc->m_left = 0;
+ inc->m_ptr = 0;
+ }
+ break; /* MST_SYSEX */
+
+ default:
+ printk("MIDI%d: Unexpected state %d (%02x)\n", orig_dev, inc->m_state, (int) data);
+ inc->m_state = MST_INIT;
+ }
}
static void
-leave_sysex (int dev)
+leave_sysex(int dev)
{
- int orig_dev = synth_devs[dev]->midi_dev;
- int timeout = 0;
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int timeout = 0;
- if (!sysex_state[dev])
- return;
+ if (!sysex_state[dev])
+ return;
- sysex_state[dev] = 0;
+ sysex_state[dev] = 0;
- while (!midi_devs[orig_dev]->outputc (orig_dev, 0xf7) &&
- timeout < 1000)
- timeout++;
+ while (!midi_devs[orig_dev]->outputc(orig_dev, 0xf7) &&
+ timeout < 1000)
+ timeout++;
- sysex_state[dev] = 0;
+ sysex_state[dev] = 0;
}
static void
-midi_synth_output (int dev)
+midi_synth_output(int dev)
{
- /*
- * Currently NOP
- */
+ /*
+ * Currently NOP
+ */
}
int
-midi_synth_ioctl (int dev,
- unsigned int cmd, caddr_t arg)
+midi_synth_ioctl(int dev,
+ unsigned int cmd, caddr_t arg)
{
- /*
- * int orig_dev = synth_devs[dev]->midi_dev;
- */
+ /*
+ * int orig_dev = synth_devs[dev]->midi_dev;
+ */
- switch (cmd)
- {
+ switch (cmd)
+ {
- case SNDCTL_SYNTH_INFO:
- memcpy ((&((char *) arg)[0]), (char *) synth_devs[dev]->info, sizeof (struct synth_info));
+ case SNDCTL_SYNTH_INFO:
+ memcpy((&((char *) arg)[0]), (char *) synth_devs[dev]->info, sizeof(struct synth_info));
- return 0;
- break;
+ return 0;
+ break;
- case SNDCTL_SYNTH_MEMAVL:
- return 0x7fffffff;
- break;
+ case SNDCTL_SYNTH_MEMAVL:
+ return 0x7fffffff;
+ break;
- default:
- return -EINVAL;
- }
+ default:
+ return -EINVAL;
+ }
}
int
-midi_synth_kill_note (int dev, int channel, int note, int velocity)
+midi_synth_kill_note(int dev, int channel, int note, int velocity)
{
- int orig_dev = synth_devs[dev]->midi_dev;
- int msg, chn;
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int msg, chn;
- if (note < 0 || note > 127)
- return 0;
- if (channel < 0 || channel > 15)
- return 0;
- if (velocity < 0)
- velocity = 0;
- if (velocity > 127)
- velocity = 127;
+ if (note < 0 || note > 127)
+ return 0;
+ if (channel < 0 || channel > 15)
+ return 0;
+ if (velocity < 0)
+ velocity = 0;
+ if (velocity > 127)
+ velocity = 127;
- leave_sysex (dev);
+ leave_sysex(dev);
- msg = prev_out_status[orig_dev] & 0xf0;
- chn = prev_out_status[orig_dev] & 0x0f;
+ msg = prev_out_status[orig_dev] & 0xf0;
+ chn = prev_out_status[orig_dev] & 0x0f;
- if (chn == channel && ((msg == 0x90 && velocity == 64) || msg == 0x80))
- { /*
+ if (chn == channel && ((msg == 0x90 && velocity == 64) || msg == 0x80))
+ { /*
* Use running status
*/
- if (!prefix_cmd (orig_dev, note))
- return 0;
+ if (!prefix_cmd(orig_dev, note))
+ return 0;
- midi_outc (orig_dev, note);
+ midi_outc(orig_dev, note);
- if (msg == 0x90) /*
- * Running status = Note on
- */
- midi_outc (orig_dev, 0); /*
- * Note on with velocity 0 == note
- * off
+ if (msg == 0x90) /*
+ * Running status = Note on
*/
- else
- midi_outc (orig_dev, velocity);
- }
- else
- {
- if (velocity == 64)
- {
- if (!prefix_cmd (orig_dev, 0x90 | (channel & 0x0f)))
- return 0;
- midi_outc (orig_dev, 0x90 | (channel & 0x0f)); /*
- * Note on
- */
- midi_outc (orig_dev, note);
- midi_outc (orig_dev, 0); /*
- * Zero G
- */
- }
- else
- {
- if (!prefix_cmd (orig_dev, 0x80 | (channel & 0x0f)))
- return 0;
- midi_outc (orig_dev, 0x80 | (channel & 0x0f)); /*
- * Note off
- */
- midi_outc (orig_dev, note);
- midi_outc (orig_dev, velocity);
- }
- }
-
- return 0;
+ midi_outc(orig_dev, 0); /*
+ * Note on with velocity 0 == note
+ * off
+ */
+ else
+ midi_outc(orig_dev, velocity);
+ } else
+ {
+ if (velocity == 64)
+ {
+ if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f)))
+ return 0;
+ midi_outc(orig_dev, 0x90 | (channel & 0x0f)); /*
+ * Note on
+ */
+ midi_outc(orig_dev, note);
+ midi_outc(orig_dev, 0); /*
+ * Zero G
+ */
+ } else
+ {
+ if (!prefix_cmd(orig_dev, 0x80 | (channel & 0x0f)))
+ return 0;
+ midi_outc(orig_dev, 0x80 | (channel & 0x0f)); /*
+ * Note off
+ */
+ midi_outc(orig_dev, note);
+ midi_outc(orig_dev, velocity);
+ }
+ }
+
+ return 0;
}
int
-midi_synth_set_instr (int dev, int channel, int instr_no)
+midi_synth_set_instr(int dev, int channel, int instr_no)
{
- int orig_dev = synth_devs[dev]->midi_dev;
+ int orig_dev = synth_devs[dev]->midi_dev;
- if (instr_no < 0 || instr_no > 127)
- instr_no = 0;
- if (channel < 0 || channel > 15)
- return 0;
+ if (instr_no < 0 || instr_no > 127)
+ instr_no = 0;
+ if (channel < 0 || channel > 15)
+ return 0;
- leave_sysex (dev);
+ leave_sysex(dev);
- if (!prefix_cmd (orig_dev, 0xc0 | (channel & 0x0f)))
- return 0;
- midi_outc (orig_dev, 0xc0 | (channel & 0x0f)); /*
+ if (!prefix_cmd(orig_dev, 0xc0 | (channel & 0x0f)))
+ return 0;
+ midi_outc(orig_dev, 0xc0 | (channel & 0x0f)); /*
* Program change
*/
- midi_outc (orig_dev, instr_no);
+ midi_outc(orig_dev, instr_no);
- return 0;
+ return 0;
}
int
-midi_synth_start_note (int dev, int channel, int note, int velocity)
+midi_synth_start_note(int dev, int channel, int note, int velocity)
{
- int orig_dev = synth_devs[dev]->midi_dev;
- int msg, chn;
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int msg, chn;
- if (note < 0 || note > 127)
- return 0;
- if (channel < 0 || channel > 15)
- return 0;
- if (velocity < 0)
- velocity = 0;
- if (velocity > 127)
- velocity = 127;
+ if (note < 0 || note > 127)
+ return 0;
+ if (channel < 0 || channel > 15)
+ return 0;
+ if (velocity < 0)
+ velocity = 0;
+ if (velocity > 127)
+ velocity = 127;
- leave_sysex (dev);
+ leave_sysex(dev);
- msg = prev_out_status[orig_dev] & 0xf0;
- chn = prev_out_status[orig_dev] & 0x0f;
+ msg = prev_out_status[orig_dev] & 0xf0;
+ chn = prev_out_status[orig_dev] & 0x0f;
- if (chn == channel && msg == 0x90)
- { /*
+ if (chn == channel && msg == 0x90)
+ { /*
* Use running status
*/
- if (!prefix_cmd (orig_dev, note))
+ if (!prefix_cmd(orig_dev, note))
+ return 0;
+ midi_outc(orig_dev, note);
+ midi_outc(orig_dev, velocity);
+ } else
+ {
+ if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f)))
+ return 0;
+ midi_outc(orig_dev, 0x90 | (channel & 0x0f)); /*
+ * Note on
+ */
+ midi_outc(orig_dev, note);
+ midi_outc(orig_dev, velocity);
+ }
return 0;
- midi_outc (orig_dev, note);
- midi_outc (orig_dev, velocity);
- }
- else
- {
- if (!prefix_cmd (orig_dev, 0x90 | (channel & 0x0f)))
- return 0;
- midi_outc (orig_dev, 0x90 | (channel & 0x0f)); /*
- * Note on
- */
- midi_outc (orig_dev, note);
- midi_outc (orig_dev, velocity);
- }
- return 0;
}
void
-midi_synth_reset (int dev)
+midi_synth_reset(int dev)
{
- leave_sysex (dev);
+ leave_sysex(dev);
}
int
-midi_synth_open (int dev, int mode)
+midi_synth_open(int dev, int mode)
{
- int orig_dev = synth_devs[dev]->midi_dev;
- int err;
- unsigned long flags;
- struct midi_input_info *inc;
-
- if (orig_dev < 0 || orig_dev > num_midis)
- return -ENXIO;
-
- midi2synth[orig_dev] = dev;
- sysex_state[dev] = 0;
- prev_out_status[orig_dev] = 0;
-
- if ((err = midi_devs[orig_dev]->open (orig_dev, mode,
- midi_synth_input, midi_synth_output)) < 0)
- return err;
-
- inc = &midi_devs[orig_dev]->in_info;
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int err;
+ unsigned long flags;
+ struct midi_input_info *inc;
+
+ if (orig_dev < 0 || orig_dev > num_midis || midi_devs[orig_dev] == NULL)
+ return -ENXIO;
+
+ midi2synth[orig_dev] = dev;
+ sysex_state[dev] = 0;
+ prev_out_status[orig_dev] = 0;
+
+ if ((err = midi_devs[orig_dev]->open(orig_dev, mode,
+ midi_synth_input, midi_synth_output)) < 0)
+ return err;
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+ inc = &midi_devs[orig_dev]->in_info;
- save_flags (flags);
- cli ();
- inc->m_busy = 0;
- inc->m_state = MST_INIT;
- inc->m_ptr = 0;
- inc->m_left = 0;
- inc->m_prev_status = 0x00;
- restore_flags (flags);
+ save_flags(flags);
+ cli();
+ inc->m_busy = 0;
+ inc->m_state = MST_INIT;
+ inc->m_ptr = 0;
+ inc->m_left = 0;
+ inc->m_prev_status = 0x00;
+ restore_flags(flags);
- sysex_sleep_flag.opts = WK_NONE;
+ sysex_sleep_flag.opts = WK_NONE;
- return 1;
+ return 1;
}
void
-midi_synth_close (int dev)
+midi_synth_close(int dev)
{
- int orig_dev = synth_devs[dev]->midi_dev;
+ int orig_dev = synth_devs[dev]->midi_dev;
- leave_sysex (dev);
+ leave_sysex(dev);
- /*
- * Shut up the synths by sending just single active sensing message.
- */
- midi_devs[orig_dev]->outputc (orig_dev, 0xfe);
+ /*
+ * Shut up the synths by sending just single active sensing message.
+ */
+ midi_devs[orig_dev]->outputc(orig_dev, 0xfe);
- midi_devs[orig_dev]->close (orig_dev);
+ midi_devs[orig_dev]->close(orig_dev);
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
}
void
-midi_synth_hw_control (int dev, unsigned char *event)
+midi_synth_hw_control(int dev, unsigned char *event)
{
}
int
-midi_synth_load_patch (int dev, int format, const char *addr,
- int offs, int count, int pmgr_flag)
+midi_synth_load_patch(int dev, int format, const char *addr,
+ int offs, int count, int pmgr_flag)
{
- int orig_dev = synth_devs[dev]->midi_dev;
-
- struct sysex_info sysex;
- int i;
- unsigned long left, src_offs, eox_seen = 0;
- int first_byte = 1;
- int hdr_size = (unsigned long) &sysex.data[0] - (unsigned long) &sysex;
-
- leave_sysex (dev);
-
- if (!prefix_cmd (orig_dev, 0xf0))
- return 0;
-
- if (format != SYSEX_PATCH)
- {
- printk ("MIDI Error: Invalid patch format (key) 0x%x\n", format);
- return -EINVAL;
- }
-
- if (count < hdr_size)
- {
- printk ("MIDI Error: Patch header too short\n");
- return -EINVAL;
- }
-
- count -= hdr_size;
-
- /*
- * Copy the header from user space but ignore the first bytes which have
- * been transferred already.
- */
-
- copy_from_user (&((char *) &sysex)[offs], &(addr)[offs], hdr_size - offs);
-
- if (count < sysex.len)
- {
- printk ("MIDI Warning: Sysex record too short (%d<%d)\n",
- count, (int) sysex.len);
- sysex.len = count;
- }
-
- left = sysex.len;
- src_offs = 0;
-
- sysex_sleep_flag.opts = WK_NONE;
-
- for (i = 0; i < left && !(current->signal & ~current->blocked); i++)
- {
- unsigned char data;
-
- get_user (*(unsigned char *) &data, (unsigned char *) &((addr)[hdr_size + i]));
-
- eox_seen = (i > 0 && data & 0x80); /* End of sysex */
-
- if (eox_seen && data != 0xf7)
- data = 0xf7;
-
- if (i == 0)
- {
- if (data != 0xf0)
- {
- printk ("Error: Sysex start missing\n");
- return -EINVAL;
- }
- }
-
- while (!midi_devs[orig_dev]->outputc (orig_dev, (unsigned char) (data & 0xff)) &&
- !(current->signal & ~current->blocked))
-
- {
- unsigned long tlimit;
-
- if (1)
- current->timeout = tlimit = jiffies + (1);
- else
- tlimit = (unsigned long) -1;
- sysex_sleep_flag.opts = WK_SLEEP;
- interruptible_sleep_on (&sysex_sleeper);
- if (!(sysex_sleep_flag.opts & WK_WAKEUP))
- {
- if (jiffies >= tlimit)
- sysex_sleep_flag.opts |= WK_TIMEOUT;
- }
- sysex_sleep_flag.opts &= ~WK_SLEEP;
- }; /* Wait for timeout */
-
- if (!first_byte && data & 0x80)
+ int orig_dev = synth_devs[dev]->midi_dev;
+
+ struct sysex_info sysex;
+ int i;
+ unsigned long left, src_offs, eox_seen = 0;
+ int first_byte = 1;
+ int hdr_size = (unsigned long) &sysex.data[0] - (unsigned long) &sysex;
+
+ leave_sysex(dev);
+
+ if (!prefix_cmd(orig_dev, 0xf0))
+ return 0;
+
+ if (format != SYSEX_PATCH)
+ {
+ printk("MIDI Error: Invalid patch format (key) 0x%x\n", format);
+ return -EINVAL;
+ }
+ if (count < hdr_size)
+ {
+ printk("MIDI Error: Patch header too short\n");
+ return -EINVAL;
+ }
+ count -= hdr_size;
+
+ /*
+ * Copy the header from user space but ignore the first bytes which have
+ * been transferred already.
+ */
+
+ copy_from_user(&((char *) &sysex)[offs], &(addr)[offs], hdr_size - offs);
+
+ if (count < sysex.len)
+ {
+ printk("MIDI Warning: Sysex record too short (%d<%d)\n", count, (int) sysex.len);
+ sysex.len = count;
+ }
+ left = sysex.len;
+ src_offs = 0;
+
+ sysex_sleep_flag.opts = WK_NONE;
+
+ for (i = 0; i < left && !(current->signal & ~current->blocked); i++)
+ {
+ unsigned char data;
+
+ get_user(*(unsigned char *) &data, (unsigned char *) &((addr)[hdr_size + i]));
+
+ eox_seen = (i > 0 && data & 0x80); /* End of sysex */
+
+ if (eox_seen && data != 0xf7)
+ data = 0xf7;
+
+ if (i == 0)
+ {
+ if (data != 0xf0)
+ {
+ printk("Error: Sysex start missing\n");
+ return -EINVAL;
+ }
+ }
+ while (!midi_devs[orig_dev]->outputc(orig_dev, (unsigned char) (data & 0xff)) &&
+ !(current->signal & ~current->blocked))
+
+ {
+ unsigned long tlimit;
+
+ if (1)
+ current->timeout = tlimit = jiffies + (1);
+ else
+ tlimit = (unsigned long) -1;
+ sysex_sleep_flag.opts = WK_SLEEP;
+ interruptible_sleep_on(&sysex_sleeper);
+ if (!(sysex_sleep_flag.opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ sysex_sleep_flag.opts |= WK_TIMEOUT;
+ }
+ sysex_sleep_flag.opts &= ~WK_SLEEP;
+ }; /* Wait for timeout */
+
+ if (!first_byte && data & 0x80)
+ return 0;
+ first_byte = 0;
+ }
+
+ if (!eox_seen)
+ midi_outc(orig_dev, 0xf7);
return 0;
- first_byte = 0;
- }
-
- if (!eox_seen)
- midi_outc (orig_dev, 0xf7);
- return 0;
}
void
-midi_synth_panning (int dev, int channel, int pressure)
+midi_synth_panning(int dev, int channel, int pressure)
{
}
void
-midi_synth_aftertouch (int dev, int channel, int pressure)
+midi_synth_aftertouch(int dev, int channel, int pressure)
{
- int orig_dev = synth_devs[dev]->midi_dev;
- int msg, chn;
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int msg, chn;
- if (pressure < 0 || pressure > 127)
- return;
- if (channel < 0 || channel > 15)
- return;
+ if (pressure < 0 || pressure > 127)
+ return;
+ if (channel < 0 || channel > 15)
+ return;
- leave_sysex (dev);
+ leave_sysex(dev);
- msg = prev_out_status[orig_dev] & 0xf0;
- chn = prev_out_status[orig_dev] & 0x0f;
-
- if (msg != 0xd0 || chn != channel) /*
- * Test for running status
- */
- {
- if (!prefix_cmd (orig_dev, 0xd0 | (channel & 0x0f)))
- return;
- midi_outc (orig_dev, 0xd0 | (channel & 0x0f)); /*
- * Channel pressure
- */
- }
- else if (!prefix_cmd (orig_dev, pressure))
- return;
+ msg = prev_out_status[orig_dev] & 0xf0;
+ chn = prev_out_status[orig_dev] & 0x0f;
- midi_outc (orig_dev, pressure);
+ if (msg != 0xd0 || chn != channel) /*
+ * Test for running status
+ */
+ {
+ if (!prefix_cmd(orig_dev, 0xd0 | (channel & 0x0f)))
+ return;
+ midi_outc(orig_dev, 0xd0 | (channel & 0x0f)); /*
+ * Channel pressure
+ */
+ } else if (!prefix_cmd(orig_dev, pressure))
+ return;
+
+ midi_outc(orig_dev, pressure);
}
void
-midi_synth_controller (int dev, int channel, int ctrl_num, int value)
+midi_synth_controller(int dev, int channel, int ctrl_num, int value)
{
- int orig_dev = synth_devs[dev]->midi_dev;
- int chn, msg;
-
- if (ctrl_num < 0 || ctrl_num > 127)
- return;
- if (channel < 0 || channel > 15)
- return;
-
- leave_sysex (dev);
-
- msg = prev_out_status[orig_dev] & 0xf0;
- chn = prev_out_status[orig_dev] & 0x0f;
-
- if (msg != 0xb0 || chn != channel)
- {
- if (!prefix_cmd (orig_dev, 0xb0 | (channel & 0x0f)))
- return;
- midi_outc (orig_dev, 0xb0 | (channel & 0x0f));
- }
- else if (!prefix_cmd (orig_dev, ctrl_num))
- return;
-
- midi_outc (orig_dev, ctrl_num);
- midi_outc (orig_dev, value & 0x7f);
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int chn, msg;
+
+ if (ctrl_num < 0 || ctrl_num > 127)
+ return;
+ if (channel < 0 || channel > 15)
+ return;
+
+ leave_sysex(dev);
+
+ msg = prev_out_status[orig_dev] & 0xf0;
+ chn = prev_out_status[orig_dev] & 0x0f;
+
+ if (msg != 0xb0 || chn != channel)
+ {
+ if (!prefix_cmd(orig_dev, 0xb0 | (channel & 0x0f)))
+ return;
+ midi_outc(orig_dev, 0xb0 | (channel & 0x0f));
+ } else if (!prefix_cmd(orig_dev, ctrl_num))
+ return;
+
+ midi_outc(orig_dev, ctrl_num);
+ midi_outc(orig_dev, value & 0x7f);
}
void
-midi_synth_bender (int dev, int channel, int value)
+midi_synth_bender(int dev, int channel, int value)
{
- int orig_dev = synth_devs[dev]->midi_dev;
- int msg, prev_chn;
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int msg, prev_chn;
- if (channel < 0 || channel > 15)
- return;
+ if (channel < 0 || channel > 15)
+ return;
- if (value < 0 || value > 16383)
- return;
+ if (value < 0 || value > 16383)
+ return;
- leave_sysex (dev);
+ leave_sysex(dev);
- msg = prev_out_status[orig_dev] & 0xf0;
- prev_chn = prev_out_status[orig_dev] & 0x0f;
+ msg = prev_out_status[orig_dev] & 0xf0;
+ prev_chn = prev_out_status[orig_dev] & 0x0f;
- if (msg != 0xd0 || prev_chn != channel) /*
- * Test for running status
- */
- {
- if (!prefix_cmd (orig_dev, 0xe0 | (channel & 0x0f)))
- return;
- midi_outc (orig_dev, 0xe0 | (channel & 0x0f));
- }
- else if (!prefix_cmd (orig_dev, value & 0x7f))
- return;
-
- midi_outc (orig_dev, value & 0x7f);
- midi_outc (orig_dev, (value >> 7) & 0x7f);
+ if (msg != 0xd0 || prev_chn != channel) /*
+ * Test for running status
+ */
+ {
+ if (!prefix_cmd(orig_dev, 0xe0 | (channel & 0x0f)))
+ return;
+ midi_outc(orig_dev, 0xe0 | (channel & 0x0f));
+ } else if (!prefix_cmd(orig_dev, value & 0x7f))
+ return;
+
+ midi_outc(orig_dev, value & 0x7f);
+ midi_outc(orig_dev, (value >> 7) & 0x7f);
}
void
-midi_synth_setup_voice (int dev, int voice, int channel)
+midi_synth_setup_voice(int dev, int voice, int channel)
{
}
int
-midi_synth_send_sysex (int dev, unsigned char *bytes, int len)
+midi_synth_send_sysex(int dev, unsigned char *bytes, int len)
{
- int orig_dev = synth_devs[dev]->midi_dev;
- int i;
-
- for (i = 0; i < len; i++)
- {
- switch (bytes[i])
- {
- case 0xf0: /* Start sysex */
- if (!prefix_cmd (orig_dev, 0xf0))
- return 0;
- sysex_state[dev] = 1;
- break;
-
- case 0xf7: /* End sysex */
- if (!sysex_state[dev]) /* Orphan sysex end */
- return 0;
- sysex_state[dev] = 0;
- break;
-
- default:
- if (!sysex_state[dev])
- return 0;
-
- if (bytes[i] & 0x80) /* Error. Another message before sysex end */
- {
- bytes[i] = 0xf7; /* Sysex end */
- sysex_state[dev] = 0;
- }
- }
-
- if (!midi_devs[orig_dev]->outputc (orig_dev, bytes[i]))
- {
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int i;
+
+ for (i = 0; i < len; i++)
+ {
+ switch (bytes[i])
+ {
+ case 0xf0: /* Start sysex */
+ if (!prefix_cmd(orig_dev, 0xf0))
+ return 0;
+ sysex_state[dev] = 1;
+ break;
+
+ case 0xf7: /* End sysex */
+ if (!sysex_state[dev]) /* Orphan sysex end */
+ return 0;
+ sysex_state[dev] = 0;
+ break;
+
+ default:
+ if (!sysex_state[dev])
+ return 0;
+
+ if (bytes[i] & 0x80) /* Error. Another message before sysex end */
+ {
+ bytes[i] = 0xf7; /* Sysex end */
+ sysex_state[dev] = 0;
+ }
+ }
+
+ if (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i]))
+ {
/*
* Hardware level buffer is full. Abort the sysex message.
*/
- int timeout = 0;
+ int timeout = 0;
- bytes[i] = 0xf7;
- sysex_state[dev] = 0;
+ bytes[i] = 0xf7;
+ sysex_state[dev] = 0;
- while (!midi_devs[orig_dev]->outputc (orig_dev, bytes[i]) &&
- timeout < 1000)
- timeout++;
- }
+ while (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i]) &&
+ timeout < 1000)
+ timeout++;
+ }
+ if (!sysex_state[dev])
+ return 0;
+ }
- if (!sysex_state[dev])
return 0;
- }
-
- return 0;
}
+
#endif
diff --git a/drivers/sound/midibuf.c b/drivers/sound/midibuf.c
index c4271e87b..ad2f5780a 100644
--- a/drivers/sound/midibuf.c
+++ b/drivers/sound/midibuf.c
@@ -16,7 +16,7 @@
#include "sound_config.h"
-#if defined(CONFIG_MIDI)
+#ifdef CONFIG_MIDI
/*
* Don't make MAX_QUEUE_SIZE larger than 4000
@@ -28,24 +28,24 @@ static struct wait_queue *midi_sleeper[MAX_MIDI_DEV] =
{NULL};
static volatile struct snd_wait midi_sleep_flag[MAX_MIDI_DEV] =
{
- {0}};
+ {0}};
static struct wait_queue *input_sleeper[MAX_MIDI_DEV] =
{NULL};
static volatile struct snd_wait input_sleep_flag[MAX_MIDI_DEV] =
{
- {0}};
+ {0}};
struct midi_buf
{
- int len, head, tail;
- unsigned char queue[MAX_QUEUE_SIZE];
+ int len, head, tail;
+ unsigned char queue[MAX_QUEUE_SIZE];
};
struct midi_parms
{
- int prech_timeout; /*
- * Timeout before the first ch
- */
+ int prech_timeout; /*
+ * Timeout before the first ch
+ */
};
static struct midi_buf *midi_out_buf[MAX_MIDI_DEV] =
@@ -54,7 +54,7 @@ static struct midi_buf *midi_in_buf[MAX_MIDI_DEV] =
{NULL};
static struct midi_parms parms[MAX_MIDI_DEV];
-static void midi_poll (unsigned long dummy);
+static void midi_poll(unsigned long dummy);
static struct timer_list poll_timer =
@@ -85,458 +85,446 @@ static volatile int open_devs = 0;
}
static void
-drain_midi_queue (int dev)
+drain_midi_queue(int dev)
{
- /*
- * Give the Midi driver time to drain its output queues
- */
-
- if (midi_devs[dev]->buffer_status != NULL)
- while (!(current->signal & ~current->blocked) &&
- midi_devs[dev]->buffer_status (dev))
-
- {
- unsigned long tlimit;
-
- if (HZ / 10)
- current->timeout = tlimit = jiffies + (HZ / 10);
- else
- tlimit = (unsigned long) -1;
- midi_sleep_flag[dev].opts = WK_SLEEP;
- interruptible_sleep_on (&midi_sleeper[dev]);
- if (!(midi_sleep_flag[dev].opts & WK_WAKEUP))
- {
- if (jiffies >= tlimit)
- midi_sleep_flag[dev].opts |= WK_TIMEOUT;
- }
- midi_sleep_flag[dev].opts &= ~WK_SLEEP;
- };
+ /*
+ * Give the Midi driver time to drain its output queues
+ */
+
+ if (midi_devs[dev]->buffer_status != NULL)
+ while (!(current->signal & ~current->blocked) &&
+ midi_devs[dev]->buffer_status(dev))
+
+ {
+ unsigned long tlimit;
+
+ if (HZ / 10)
+ current->timeout = tlimit = jiffies + (HZ / 10);
+ else
+ tlimit = (unsigned long) -1;
+ midi_sleep_flag[dev].opts = WK_SLEEP;
+ interruptible_sleep_on(&midi_sleeper[dev]);
+ if (!(midi_sleep_flag[dev].opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ midi_sleep_flag[dev].opts |= WK_TIMEOUT;
+ }
+ midi_sleep_flag[dev].opts &= ~WK_SLEEP;
+ };
}
static void
-midi_input_intr (int dev, unsigned char data)
+midi_input_intr(int dev, unsigned char data)
{
- if (midi_in_buf[dev] == NULL)
- return;
+ if (midi_in_buf[dev] == NULL)
+ return;
- if (data == 0xfe) /*
+ if (data == 0xfe) /*
* Active sensing
*/
- return; /*
+ return; /*
* Ignore
*/
- if (SPACE_AVAIL (midi_in_buf[dev]))
- {
- QUEUE_BYTE (midi_in_buf[dev], data);
- if ((input_sleep_flag[dev].opts & WK_SLEEP))
- {
- input_sleep_flag[dev].opts = WK_WAKEUP;
- wake_up (&input_sleeper[dev]);
- };
- }
-
+ if (SPACE_AVAIL(midi_in_buf[dev]))
+ {
+ QUEUE_BYTE(midi_in_buf[dev], data);
+ if ((input_sleep_flag[dev].opts & WK_SLEEP))
+ {
+ input_sleep_flag[dev].opts = WK_WAKEUP;
+ wake_up(&input_sleeper[dev]);
+ };
+ }
}
static void
-midi_output_intr (int dev)
+midi_output_intr(int dev)
{
- /*
- * Currently NOP
- */
+ /*
+ * Currently NOP
+ */
}
static void
-midi_poll (unsigned long dummy)
+midi_poll(unsigned long dummy)
{
- unsigned long flags;
- int dev;
-
- save_flags (flags);
- cli ();
- if (open_devs)
- {
- for (dev = 0; dev < num_midis; dev++)
- if (midi_out_buf[dev] != NULL)
- {
- int ok = 1;
-
- while (DATA_AVAIL (midi_out_buf[dev]) && ok)
- {
- int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head];
-
- restore_flags (flags); /* Give some time to others */
- ok = midi_devs[dev]->outputc (dev, c);
- save_flags (flags);
- cli ();
- midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE;
- midi_out_buf[dev]->len--;
- }
-
- if (DATA_AVAIL (midi_out_buf[dev]) < 100 &&
- (midi_sleep_flag[dev].opts & WK_SLEEP))
- {
- midi_sleep_flag[dev].opts = WK_WAKEUP;
- wake_up (&midi_sleeper[dev]);
- };
- }
+ unsigned long flags;
+ int dev;
- {
- poll_timer.expires = (1) + jiffies;
- add_timer (&poll_timer);
- }; /*
+ save_flags(flags);
+ cli();
+ if (open_devs)
+ {
+ for (dev = 0; dev < num_midis; dev++)
+ if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL)
+ {
+ int ok = 1;
+
+ while (DATA_AVAIL(midi_out_buf[dev]) && ok)
+ {
+ int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head];
+
+ restore_flags(flags); /* Give some time to others */
+ ok = midi_devs[dev]->outputc(dev, c);
+ save_flags(flags);
+ cli();
+ midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE;
+ midi_out_buf[dev]->len--;
+ }
+
+ if (DATA_AVAIL(midi_out_buf[dev]) < 100 &&
+ (midi_sleep_flag[dev].opts & WK_SLEEP))
+ {
+ midi_sleep_flag[dev].opts = WK_WAKEUP;
+ wake_up(&midi_sleeper[dev]);
+ };
+ }
+ {
+ poll_timer.expires = (1) + jiffies;
+ add_timer(&poll_timer);
+ }; /*
* Come back later
*/
- }
- restore_flags (flags);
+ }
+ restore_flags(flags);
}
int
-MIDIbuf_open (int dev, struct fileinfo *file)
+MIDIbuf_open(int dev, struct fileinfo *file)
{
- int mode, err;
-
- dev = dev >> 4;
- mode = file->mode & O_ACCMODE;
-
- if (num_midis > MAX_MIDI_DEV)
- {
- printk ("Sound: FATAL ERROR: Too many midi interfaces\n");
- num_midis = MAX_MIDI_DEV;
- }
-
- if (dev < 0 || dev >= num_midis)
- {
- printk ("Sound: Nonexistent MIDI interface %d\n", dev);
- return -ENXIO;
- }
-
- /*
- * Interrupts disabled. Be careful
- */
-
- if ((err = midi_devs[dev]->open (dev, mode,
- midi_input_intr, midi_output_intr)) < 0)
- {
- return err;
- }
-
- parms[dev].prech_timeout = 0;
-
- midi_in_buf[dev] = (struct midi_buf *) vmalloc (sizeof (struct midi_buf));
-
- if (midi_in_buf[dev] == NULL)
- {
- printk ("midi: Can't allocate buffer\n");
- midi_devs[dev]->close (dev);
- return -EIO;
- }
- midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0;
-
- midi_out_buf[dev] = (struct midi_buf *) vmalloc (sizeof (struct midi_buf));
-
- if (midi_out_buf[dev] == NULL)
- {
- printk ("midi: Can't allocate buffer\n");
- midi_devs[dev]->close (dev);
- vfree (midi_in_buf[dev]);
- midi_in_buf[dev] = NULL;
- return -EIO;
- }
- midi_out_buf[dev]->len = midi_out_buf[dev]->head = midi_out_buf[dev]->tail = 0;
- open_devs++;
-
- midi_sleep_flag[dev].opts = WK_NONE;
- input_sleep_flag[dev].opts = WK_NONE;
-
- if (open_devs < 2) /* This was first open */
- {
- ;
-
- {
- poll_timer.expires = (1) + jiffies;
- add_timer (&poll_timer);
- }; /* Start polling */
- }
-
- return err;
+ int mode, err;
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+
+ if (num_midis > MAX_MIDI_DEV)
+ {
+ printk("Sound: FATAL ERROR: Too many midi interfaces\n");
+ num_midis = MAX_MIDI_DEV;
+ }
+ if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
+ {
+ printk("Sound: Nonexistent MIDI interface %d\n", dev);
+ return -ENXIO;
+ }
+ /*
+ * Interrupts disabled. Be careful
+ */
+
+ if ((err = midi_devs[dev]->open(dev, mode,
+ midi_input_intr, midi_output_intr)) < 0)
+ {
+ return err;
+ }
+ parms[dev].prech_timeout = 0;
+
+ midi_in_buf[dev] = (struct midi_buf *) vmalloc(sizeof(struct midi_buf));
+
+ if (midi_in_buf[dev] == NULL)
+ {
+ printk("midi: Can't allocate buffer\n");
+ midi_devs[dev]->close(dev);
+ return -EIO;
+ }
+ midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0;
+
+ midi_out_buf[dev] = (struct midi_buf *) vmalloc(sizeof(struct midi_buf));
+
+ if (midi_out_buf[dev] == NULL)
+ {
+ printk("midi: Can't allocate buffer\n");
+ midi_devs[dev]->close(dev);
+ vfree(midi_in_buf[dev]);
+ midi_in_buf[dev] = NULL;
+ return -EIO;
+ }
+ midi_out_buf[dev]->len = midi_out_buf[dev]->head = midi_out_buf[dev]->tail = 0;
+ open_devs++;
+
+ midi_sleep_flag[dev].opts = WK_NONE;
+ input_sleep_flag[dev].opts = WK_NONE;
+
+ if (open_devs < 2) /* This was first open */
+ {
+ ;
+
+ {
+ poll_timer.expires = (1) + jiffies;
+ add_timer(&poll_timer);
+ }; /* Start polling */
+ }
+ return err;
}
void
-MIDIbuf_release (int dev, struct fileinfo *file)
+MIDIbuf_release(int dev, struct fileinfo *file)
{
- int mode;
- unsigned long flags;
+ int mode;
+ unsigned long flags;
- dev = dev >> 4;
- mode = file->mode & O_ACCMODE;
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
- if (dev < 0 || dev >= num_midis)
- return;
+ if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
+ return;
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- /*
- * Wait until the queue is empty
- */
+ /*
+ * Wait until the queue is empty
+ */
- if (mode != OPEN_READ)
- {
- midi_devs[dev]->outputc (dev, 0xfe); /*
- * Active sensing to shut the
- * devices
- */
-
- while (!(current->signal & ~current->blocked) &&
- DATA_AVAIL (midi_out_buf[dev]))
-
- {
- unsigned long tlimit;
-
- if (0)
- current->timeout = tlimit = jiffies + (0);
- else
- tlimit = (unsigned long) -1;
- midi_sleep_flag[dev].opts = WK_SLEEP;
- interruptible_sleep_on (&midi_sleeper[dev]);
- if (!(midi_sleep_flag[dev].opts & WK_WAKEUP))
- {
- if (jiffies >= tlimit)
- midi_sleep_flag[dev].opts |= WK_TIMEOUT;
- }
- midi_sleep_flag[dev].opts &= ~WK_SLEEP;
- }; /*
+ if (mode != OPEN_READ)
+ {
+ midi_devs[dev]->outputc(dev, 0xfe); /*
+ * Active sensing to shut the
+ * devices
+ */
+
+ while (!(current->signal & ~current->blocked) &&
+ DATA_AVAIL(midi_out_buf[dev]))
+
+ {
+ unsigned long tlimit;
+
+ if (0)
+ current->timeout = tlimit = jiffies + (0);
+ else
+ tlimit = (unsigned long) -1;
+ midi_sleep_flag[dev].opts = WK_SLEEP;
+ interruptible_sleep_on(&midi_sleeper[dev]);
+ if (!(midi_sleep_flag[dev].opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ midi_sleep_flag[dev].opts |= WK_TIMEOUT;
+ }
+ midi_sleep_flag[dev].opts &= ~WK_SLEEP;
+ }; /*
* Sync
*/
- drain_midi_queue (dev); /*
- * Ensure the output queues are empty
- */
- }
-
- restore_flags (flags);
+ drain_midi_queue(dev); /*
+ * Ensure the output queues are empty
+ */
+ }
+ restore_flags(flags);
- midi_devs[dev]->close (dev);
+ midi_devs[dev]->close(dev);
- vfree (midi_in_buf[dev]);
- vfree (midi_out_buf[dev]);
- midi_in_buf[dev] = NULL;
- midi_out_buf[dev] = NULL;
- if (open_devs < 2)
- del_timer (&poll_timer);;
- open_devs--;
+ vfree(midi_in_buf[dev]);
+ vfree(midi_out_buf[dev]);
+ midi_in_buf[dev] = NULL;
+ midi_out_buf[dev] = NULL;
+ if (open_devs < 2)
+ del_timer(&poll_timer);;
+ open_devs--;
}
int
-MIDIbuf_write (int dev, struct fileinfo *file, const char *buf, int count)
+MIDIbuf_write(int dev, struct fileinfo *file, const char *buf, int count)
{
- unsigned long flags;
- int c, n, i;
- unsigned char tmp_data;
+ unsigned long flags;
+ int c, n, i;
+ unsigned char tmp_data;
- dev = dev >> 4;
+ dev = dev >> 4;
- if (!count)
- return 0;
+ if (!count)
+ return 0;
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- c = 0;
+ c = 0;
- while (c < count)
- {
- n = SPACE_AVAIL (midi_out_buf[dev]);
+ while (c < count)
+ {
+ n = SPACE_AVAIL(midi_out_buf[dev]);
- if (n == 0) /*
+ if (n == 0) /*
* No space just now. We have to sleep
*/
- {
-
- {
- unsigned long tlimit;
-
- if (0)
- current->timeout = tlimit = jiffies + (0);
- else
- tlimit = (unsigned long) -1;
- midi_sleep_flag[dev].opts = WK_SLEEP;
- interruptible_sleep_on (&midi_sleeper[dev]);
- if (!(midi_sleep_flag[dev].opts & WK_WAKEUP))
- {
- if (jiffies >= tlimit)
- midi_sleep_flag[dev].opts |= WK_TIMEOUT;
- }
- midi_sleep_flag[dev].opts &= ~WK_SLEEP;
- };
- if ((current->signal & ~current->blocked))
- {
- restore_flags (flags);
- return -EINTR;
- }
-
- n = SPACE_AVAIL (midi_out_buf[dev]);
- }
-
- if (n > (count - c))
- n = count - c;
-
- for (i = 0; i < n; i++)
- {
- copy_from_user ((char *) &tmp_data, &(buf)[c], 1);
- QUEUE_BYTE (midi_out_buf[dev], tmp_data);
- c++;
- }
- }
+ {
+
+ {
+ unsigned long tlimit;
+
+ if (0)
+ current->timeout = tlimit = jiffies + (0);
+ else
+ tlimit = (unsigned long) -1;
+ midi_sleep_flag[dev].opts = WK_SLEEP;
+ interruptible_sleep_on(&midi_sleeper[dev]);
+ if (!(midi_sleep_flag[dev].opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ midi_sleep_flag[dev].opts |= WK_TIMEOUT;
+ }
+ midi_sleep_flag[dev].opts &= ~WK_SLEEP;
+ };
+ if ((current->signal & ~current->blocked))
+ {
+ restore_flags(flags);
+ return -EINTR;
+ }
+ n = SPACE_AVAIL(midi_out_buf[dev]);
+ }
+ if (n > (count - c))
+ n = count - c;
+
+ for (i = 0; i < n; i++)
+ {
+ copy_from_user((char *) &tmp_data, &(buf)[c], 1);
+ QUEUE_BYTE(midi_out_buf[dev], tmp_data);
+ c++;
+ }
+ }
- restore_flags (flags);
+ restore_flags(flags);
- return c;
+ return c;
}
int
-MIDIbuf_read (int dev, struct fileinfo *file, char *buf, int count)
+MIDIbuf_read(int dev, struct fileinfo *file, char *buf, int count)
{
- int n, c = 0;
- unsigned long flags;
- unsigned char tmp_data;
+ int n, c = 0;
+ unsigned long flags;
+ unsigned char tmp_data;
- dev = dev >> 4;
+ dev = dev >> 4;
- save_flags (flags);
- cli ();
-
- if (!DATA_AVAIL (midi_in_buf[dev])) /*
- * No data yet, wait
- */
- {
-
- {
- unsigned long tlimit;
-
- if (parms[dev].prech_timeout)
- current->timeout = tlimit = jiffies + (parms[dev].prech_timeout);
- else
- tlimit = (unsigned long) -1;
- input_sleep_flag[dev].opts = WK_SLEEP;
- interruptible_sleep_on (&input_sleeper[dev]);
- if (!(input_sleep_flag[dev].opts & WK_WAKEUP))
- {
- if (jiffies >= tlimit)
- input_sleep_flag[dev].opts |= WK_TIMEOUT;
- }
- input_sleep_flag[dev].opts &= ~WK_SLEEP;
- };
- if ((current->signal & ~current->blocked))
- c = -EINTR; /*
- * The user is getting restless
- */
- }
+ save_flags(flags);
+ cli();
- if (c == 0 && DATA_AVAIL (midi_in_buf[dev])) /*
- * Got some bytes
+ if (!DATA_AVAIL(midi_in_buf[dev])) /*
+ * No data yet, wait
*/
- {
- n = DATA_AVAIL (midi_in_buf[dev]);
- if (n > count)
- n = count;
- c = 0;
-
- while (c < n)
- {
- REMOVE_BYTE (midi_in_buf[dev], tmp_data);
{
- char *fixit = (char *) &tmp_data;
-
- copy_to_user (&(buf)[c], fixit, 1);
- };
- c++;
- }
- }
- restore_flags (flags);
+ {
+ unsigned long tlimit;
+
+ if (parms[dev].prech_timeout)
+ current->timeout = tlimit = jiffies + (parms[dev].prech_timeout);
+ else
+ tlimit = (unsigned long) -1;
+ input_sleep_flag[dev].opts = WK_SLEEP;
+ interruptible_sleep_on(&input_sleeper[dev]);
+ if (!(input_sleep_flag[dev].opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ input_sleep_flag[dev].opts |= WK_TIMEOUT;
+ }
+ input_sleep_flag[dev].opts &= ~WK_SLEEP;
+ };
+ if ((current->signal & ~current->blocked))
+ c = -EINTR; /*
+ * The user is getting restless
+ */
+ }
+ if (c == 0 && DATA_AVAIL(midi_in_buf[dev])) /*
+ * Got some bytes
+ */
+ {
+ n = DATA_AVAIL(midi_in_buf[dev]);
+ if (n > count)
+ n = count;
+ c = 0;
+
+ while (c < n)
+ {
+ REMOVE_BYTE(midi_in_buf[dev], tmp_data);
+ {
+ char *fixit = (char *) &tmp_data;
+
+ copy_to_user(&(buf)[c], fixit, 1);
+ };
+ c++;
+ }
+ }
+ restore_flags(flags);
- return c;
+ return c;
}
int
-MIDIbuf_ioctl (int dev, struct fileinfo *file,
- unsigned int cmd, caddr_t arg)
+MIDIbuf_ioctl(int dev, struct fileinfo *file,
+ unsigned int cmd, caddr_t arg)
{
- int val;
-
- dev = dev >> 4;
-
- if (((cmd >> 8) & 0xff) == 'C')
- {
- if (midi_devs[dev]->coproc) /* Coprocessor ioctl */
- return midi_devs[dev]->coproc->ioctl (midi_devs[dev]->coproc->devc, cmd, arg, 0);
- else
- printk ("/dev/midi%d: No coprocessor for this device\n", dev);
-
- return -ENXIO;
- }
- else
- switch (cmd)
- {
-
- case SNDCTL_MIDI_PRETIME:
- val = *(int *) arg;
- if (val < 0)
- val = 0;
-
- val = (HZ * val) / 10;
- parms[dev].prech_timeout = val;
- return (*(int *) arg = val);
- break;
-
- default:
- return midi_devs[dev]->ioctl (dev, cmd, arg);
- }
+ int val;
+
+ dev = dev >> 4;
+
+ if (((cmd >> 8) & 0xff) == 'C')
+ {
+ if (midi_devs[dev]->coproc) /* Coprocessor ioctl */
+ return midi_devs[dev]->coproc->ioctl(midi_devs[dev]->coproc->devc, cmd, arg, 0);
+ else
+ printk("/dev/midi%d: No coprocessor for this device\n", dev);
+
+ return -ENXIO;
+ } else
+ switch (cmd)
+ {
+
+ case SNDCTL_MIDI_PRETIME:
+ val = *(int *) arg;
+ if (val < 0)
+ val = 0;
+
+ val = (HZ * val) / 10;
+ parms[dev].prech_timeout = val;
+ return (*(int *) arg = val);
+ break;
+
+ default:
+ return midi_devs[dev]->ioctl(dev, cmd, arg);
+ }
}
int
-MIDIbuf_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait)
+MIDIbuf_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait)
{
- dev = dev >> 4;
-
- switch (sel_type)
- {
- case SEL_IN:
- if (!DATA_AVAIL (midi_in_buf[dev]))
- {
-
- input_sleep_flag[dev].opts = WK_SLEEP;
- poll_wait (&input_sleeper[dev], wait);
- return 0;
- }
- return 1;
- break;
-
- case SEL_OUT:
- if (SPACE_AVAIL (midi_out_buf[dev]))
- {
+ dev = dev >> 4;
- midi_sleep_flag[dev].opts = WK_SLEEP;
- poll_wait (&midi_sleeper[dev], wait);
- return 0;
- }
- return 1;
- break;
-
- case SEL_EX:
- return 0;
- }
+ switch (sel_type)
+ {
+ case SEL_IN:
+ if (!DATA_AVAIL(midi_in_buf[dev]))
+ {
+
+ input_sleep_flag[dev].opts = WK_SLEEP;
+ poll_wait(&input_sleeper[dev], wait);
+ return 0;
+ }
+ return 1;
+ break;
+
+ case SEL_OUT:
+ if (SPACE_AVAIL(midi_out_buf[dev]))
+ {
+
+ midi_sleep_flag[dev].opts = WK_SLEEP;
+ poll_wait(&midi_sleeper[dev], wait);
+ return 0;
+ }
+ return 1;
+ break;
+
+ case SEL_EX:
+ return 0;
+ }
- return 0;
+ return 0;
}
void
-MIDIbuf_init (void)
+MIDIbuf_init(void)
{
}
diff --git a/drivers/sound/mpu401.c b/drivers/sound/mpu401.c
index 5a5e90e27..f504a5d9d 100644
--- a/drivers/sound/mpu401.c
+++ b/drivers/sound/mpu401.c
@@ -11,88 +11,89 @@
* for more info.
*/
#include <linux/config.h>
-
+#include <linux/module.h>
#define USE_SEQ_MACROS
#define USE_SIMPLE_MACROS
#include "sound_config.h"
+#include "soundmodule.h"
-#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI)
+#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) || defined(MODULE)
#include "coproc.h"
-#ifdef CONFIG_SEQUENCER
+#if defined(CONFIG_SEQUENCER) || defined(MODULE)
static int timer_mode = TMR_INTERNAL, timer_caps = TMR_INTERNAL;
#endif
struct mpu_config
{
- int base; /*
+ int base; /*
* I/O base
*/
- int irq;
- int opened; /*
- * Open mode
- */
- int devno;
- int synthno;
- int uart_mode;
- int initialized;
- int mode;
+ int irq;
+ int opened; /*
+ * Open mode
+ */
+ int devno;
+ int synthno;
+ int uart_mode;
+ int initialized;
+ int mode;
#define MODE_MIDI 1
#define MODE_SYNTH 2
- unsigned char version, revision;
- unsigned int capabilities;
+ unsigned char version, revision;
+ unsigned int capabilities;
#define MPU_CAP_INTLG 0x10000000
#define MPU_CAP_SYNC 0x00000010
#define MPU_CAP_FSK 0x00000020
#define MPU_CAP_CLS 0x00000040
#define MPU_CAP_SMPTE 0x00000080
#define MPU_CAP_2PORT 0x00000001
- int timer_flag;
+ int timer_flag;
#define MBUF_MAX 10
#define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \
- {printk("MPU: Invalid buffer pointer %d/%d, s=%d\n", dc->m_ptr, dc->m_left, dc->m_state);dc->m_ptr--;}
- int m_busy;
- unsigned char m_buf[MBUF_MAX];
- int m_ptr;
- int m_state;
- int m_left;
- unsigned char last_status;
- void (*inputintr) (int dev, unsigned char data);
- int shared_irq;
- int *osp;
+ {printk( "MPU: Invalid buffer pointer %d/%d, s=%d\n", dc->m_ptr, dc->m_left, dc->m_state);dc->m_ptr--;}
+ int m_busy;
+ unsigned char m_buf[MBUF_MAX];
+ int m_ptr;
+ int m_state;
+ int m_left;
+ unsigned char last_status;
+ void (*inputintr) (int dev, unsigned char data);
+ int shared_irq;
+ int *osp;
};
#define DATAPORT(base) (base)
#define COMDPORT(base) (base+1)
#define STATPORT(base) (base+1)
-static int
-mpu401_status (struct mpu_config *devc)
+static int
+mpu401_status(struct mpu_config *devc)
{
- return inb (STATPORT (devc->base));
+ return inb(STATPORT(devc->base));
}
#define input_avail(devc) (!(mpu401_status(devc)&INPUT_AVAIL))
#define output_ready(devc) (!(mpu401_status(devc)&OUTPUT_READY))
-static void
-write_command (struct mpu_config *devc, unsigned char cmd)
+static void
+write_command(struct mpu_config *devc, unsigned char cmd)
{
- outb ((cmd), COMDPORT (devc->base));
+ outb((cmd), COMDPORT(devc->base));
}
-static int
-read_data (struct mpu_config *devc)
+static int
+read_data(struct mpu_config *devc)
{
- return inb (DATAPORT (devc->base));
+ return inb(DATAPORT(devc->base));
}
-static void
-write_data (struct mpu_config *devc, unsigned char byte)
+static void
+write_data(struct mpu_config *devc, unsigned char byte)
{
- outb ((byte), DATAPORT (devc->base));
+ outb((byte), DATAPORT(devc->base));
}
#define OUTPUT_READY 0x40
@@ -103,19 +104,19 @@ write_data (struct mpu_config *devc, unsigned char byte)
static struct mpu_config dev_conf[MAX_MIDI_DEV] =
{
- {0}};
+ {0}};
static int n_mpu_devs = 0;
static volatile int irq2dev[17] =
{-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1};
-static int reset_mpu401 (struct mpu_config *devc);
-static void set_uart_mode (int dev, struct mpu_config *devc, int arg);
+static int reset_mpu401(struct mpu_config *devc);
+static void set_uart_mode(int dev, struct mpu_config *devc, int arg);
-static void mpu_timer_init (int midi_dev);
-static void mpu_timer_interrupt (void);
-static void timer_ext_event (struct mpu_config *devc, int event, int parm);
+static int mpu_timer_init(int midi_dev);
+static void mpu_timer_interrupt(void);
+static void timer_ext_event(struct mpu_config *devc, int event, int parm);
static struct synth_info mpu_synth_info_proto =
{"MPU-401 MIDI interface", 0, SYNTH_TYPE_MIDI, MIDI_TYPE_MPU401, 0, 128, 0, 128, SYNTH_CAP_INPUT};
@@ -139,17 +140,17 @@ static struct synth_info mpu_synth_info[MAX_MIDI_DEV];
static unsigned char len_tab[] = /* # of data bytes following a status
*/
{
- 2, /* 8x */
- 2, /* 9x */
- 2, /* Ax */
- 2, /* Bx */
- 1, /* Cx */
- 1, /* Dx */
- 2, /* Ex */
- 0 /* Fx */
+ 2, /* 8x */
+ 2, /* 9x */
+ 2, /* Ax */
+ 2, /* Bx */
+ 1, /* Cx */
+ 1, /* Dx */
+ 2, /* Ex */
+ 0 /* Fx */
};
-#ifndef CONFIG_SEQUENCER
+#if !defined(CONFIG_SEQUENCER) && !defined(MODULE)
#define STORE(cmd)
#else
#define STORE(cmd) \
@@ -166,779 +167,750 @@ static unsigned char len_tab[] = /* # of data bytes following a status
#define _SEQ_ADVBUF(x) len=x
static int
-mpu_input_scanner (struct mpu_config *devc, unsigned char midic)
+mpu_input_scanner(struct mpu_config *devc, unsigned char midic)
{
- switch (devc->m_state)
- {
- case ST_INIT:
- switch (midic)
- {
- case 0xf8:
- /* Timer overflow */
- break;
-
- case 0xfc:
- printk ("<all end>");
- break;
-
- case 0xfd:
- if (devc->timer_flag)
- mpu_timer_interrupt ();
- break;
-
- case 0xfe:
- return MPU_ACK;
- break;
-
- case 0xf0:
- case 0xf1:
- case 0xf2:
- case 0xf3:
- case 0xf4:
- case 0xf5:
- case 0xf6:
- case 0xf7:
- printk ("<Trk data rq #%d>", midic & 0x0f);
- break;
-
- case 0xf9:
- printk ("<conductor rq>");
- break;
-
- case 0xff:
- devc->m_state = ST_SYSMSG;
- break;
-
- default:
- if (midic <= 0xef)
- {
- /* printk("mpu time: %d ", midic); */
- devc->m_state = ST_TIMED;
- }
- else
- printk ("<MPU: Unknown event %02x> ", midic);
- }
- break;
-
- case ST_TIMED:
- {
- int msg = ((int) (midic & 0xf0) >> 4);
-
- devc->m_state = ST_DATABYTE;
-
- if (msg < 8) /* Data byte */
+ switch (devc->m_state)
{
- /* printk("midi msg (running status) "); */
- msg = ((int) (devc->last_status & 0xf0) >> 4);
- msg -= 8;
- devc->m_left = len_tab[msg] - 1;
-
- devc->m_ptr = 2;
- devc->m_buf[0] = devc->last_status;
- devc->m_buf[1] = midic;
-
- if (devc->m_left <= 0)
- {
- devc->m_state = ST_INIT;
- do_midi_msg (devc->synthno, devc->m_buf, devc->m_ptr);
- devc->m_ptr = 0;
- }
+ case ST_INIT:
+ switch (midic)
+ {
+ case 0xf8:
+ /* Timer overflow */
+ break;
+
+ case 0xfc:
+ printk("<all end>");
+ break;
+
+ case 0xfd:
+ if (devc->timer_flag)
+ mpu_timer_interrupt();
+ break;
+
+ case 0xfe:
+ return MPU_ACK;
+ break;
+
+ case 0xf0:
+ case 0xf1:
+ case 0xf2:
+ case 0xf3:
+ case 0xf4:
+ case 0xf5:
+ case 0xf6:
+ case 0xf7:
+ printk("<Trk data rq #%d>", midic & 0x0f);
+ break;
+
+ case 0xf9:
+ printk("<conductor rq>");
+ break;
+
+ case 0xff:
+ devc->m_state = ST_SYSMSG;
+ break;
+
+ default:
+ if (midic <= 0xef)
+ {
+ /* printk( "mpu time: %d ", midic); */
+ devc->m_state = ST_TIMED;
+ } else
+ printk("<MPU: Unknown event %02x> ", midic);
+ }
+ break;
+
+ case ST_TIMED:
+ {
+ int msg = ((int) (midic & 0xf0) >> 4);
+
+ devc->m_state = ST_DATABYTE;
+
+ if (msg < 8) /* Data byte */
+ {
+ /* printk( "midi msg (running status) "); */
+ msg = ((int) (devc->last_status & 0xf0) >> 4);
+ msg -= 8;
+ devc->m_left = len_tab[msg] - 1;
+
+ devc->m_ptr = 2;
+ devc->m_buf[0] = devc->last_status;
+ devc->m_buf[1] = midic;
+
+ if (devc->m_left <= 0)
+ {
+ devc->m_state = ST_INIT;
+ do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
+ devc->m_ptr = 0;
+ }
+ } else if (msg == 0xf) /* MPU MARK */
+ {
+ devc->m_state = ST_INIT;
+
+ switch (midic)
+ {
+ case 0xf8:
+ /* printk( "NOP "); */
+ break;
+
+ case 0xf9:
+ /* printk( "meas end "); */
+ break;
+
+ case 0xfc:
+ /* printk( "data end "); */
+ break;
+
+ default:
+ printk("Unknown MPU mark %02x\n", midic);
+ }
+ } else
+ {
+ devc->last_status = midic;
+ /* printk( "midi msg "); */
+ msg -= 8;
+ devc->m_left = len_tab[msg];
+
+ devc->m_ptr = 1;
+ devc->m_buf[0] = midic;
+
+ if (devc->m_left <= 0)
+ {
+ devc->m_state = ST_INIT;
+ do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
+ devc->m_ptr = 0;
+ }
+ }
+ }
+ break;
+
+ case ST_SYSMSG:
+ switch (midic)
+ {
+ case 0xf0:
+ printk("<SYX>");
+ devc->m_state = ST_SYSEX;
+ break;
+
+ case 0xf1:
+ devc->m_state = ST_MTC;
+ break;
+
+ case 0xf2:
+ devc->m_state = ST_SONGPOS;
+ devc->m_ptr = 0;
+ break;
+
+ case 0xf3:
+ devc->m_state = ST_SONGSEL;
+ break;
+
+ case 0xf6:
+ /* printk( "tune_request\n"); */
+ devc->m_state = ST_INIT;
+
+ /*
+ * Real time messages
+ */
+ case 0xf8:
+ /* midi clock */
+ devc->m_state = ST_INIT;
+ timer_ext_event(devc, TMR_CLOCK, 0);
+ break;
+
+ case 0xfA:
+ devc->m_state = ST_INIT;
+ timer_ext_event(devc, TMR_START, 0);
+ break;
+
+ case 0xFB:
+ devc->m_state = ST_INIT;
+ timer_ext_event(devc, TMR_CONTINUE, 0);
+ break;
+
+ case 0xFC:
+ devc->m_state = ST_INIT;
+ timer_ext_event(devc, TMR_STOP, 0);
+ break;
+
+ case 0xFE:
+ /* active sensing */
+ devc->m_state = ST_INIT;
+ break;
+
+ case 0xff:
+ /* printk( "midi hard reset"); */
+ devc->m_state = ST_INIT;
+ break;
+
+ default:
+ printk("unknown MIDI sysmsg %0x\n", midic);
+ devc->m_state = ST_INIT;
+ }
+ break;
+
+ case ST_MTC:
+ devc->m_state = ST_INIT;
+ printk("MTC frame %x02\n", midic);
+ break;
+
+ case ST_SYSEX:
+ if (midic == 0xf7)
+ {
+ printk("<EOX>");
+ devc->m_state = ST_INIT;
+ } else
+ printk("%02x ", midic);
+ break;
+
+ case ST_SONGPOS:
+ BUFTEST(devc);
+ devc->m_buf[devc->m_ptr++] = midic;
+ if (devc->m_ptr == 2)
+ {
+ devc->m_state = ST_INIT;
+ devc->m_ptr = 0;
+ timer_ext_event(devc, TMR_SPP,
+ ((devc->m_buf[1] & 0x7f) << 7) |
+ (devc->m_buf[0] & 0x7f));
+ }
+ break;
+
+ case ST_DATABYTE:
+ BUFTEST(devc);
+ devc->m_buf[devc->m_ptr++] = midic;
+ if ((--devc->m_left) <= 0)
+ {
+ devc->m_state = ST_INIT;
+ do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
+ devc->m_ptr = 0;
+ }
+ break;
+
+ default:
+ printk("Bad state %d ", devc->m_state);
+ devc->m_state = ST_INIT;
}
- else if (msg == 0xf) /* MPU MARK */
- {
- devc->m_state = ST_INIT;
-
- switch (midic)
- {
- case 0xf8:
- /* printk("NOP "); */
- break;
- case 0xf9:
- /* printk("meas end "); */
- break;
-
- case 0xfc:
- /* printk("data end "); */
- break;
-
- default:
- printk ("Unknown MPU mark %02x\n", midic);
- }
- }
- else
- {
- devc->last_status = midic;
- /* printk ("midi msg "); */
- msg -= 8;
- devc->m_left = len_tab[msg];
-
- devc->m_ptr = 1;
- devc->m_buf[0] = midic;
-
- if (devc->m_left <= 0)
- {
- devc->m_state = ST_INIT;
- do_midi_msg (devc->synthno, devc->m_buf, devc->m_ptr);
- devc->m_ptr = 0;
- }
- }
- }
- break;
-
- case ST_SYSMSG:
- switch (midic)
- {
- case 0xf0:
- printk ("<SYX>");
- devc->m_state = ST_SYSEX;
- break;
-
- case 0xf1:
- devc->m_state = ST_MTC;
- break;
-
- case 0xf2:
- devc->m_state = ST_SONGPOS;
- devc->m_ptr = 0;
- break;
-
- case 0xf3:
- devc->m_state = ST_SONGSEL;
- break;
-
- case 0xf6:
- /* printk("tune_request\n"); */
- devc->m_state = ST_INIT;
-
- /*
- * Real time messages
- */
- case 0xf8:
- /* midi clock */
- devc->m_state = ST_INIT;
- timer_ext_event (devc, TMR_CLOCK, 0);
- break;
-
- case 0xfA:
- devc->m_state = ST_INIT;
- timer_ext_event (devc, TMR_START, 0);
- break;
-
- case 0xFB:
- devc->m_state = ST_INIT;
- timer_ext_event (devc, TMR_CONTINUE, 0);
- break;
-
- case 0xFC:
- devc->m_state = ST_INIT;
- timer_ext_event (devc, TMR_STOP, 0);
- break;
-
- case 0xFE:
- /* active sensing */
- devc->m_state = ST_INIT;
- break;
-
- case 0xff:
- /* printk("midi hard reset"); */
- devc->m_state = ST_INIT;
- break;
-
- default:
- printk ("unknown MIDI sysmsg %0x\n", midic);
- devc->m_state = ST_INIT;
- }
- break;
-
- case ST_MTC:
- devc->m_state = ST_INIT;
- printk ("MTC frame %x02\n", midic);
- break;
-
- case ST_SYSEX:
- if (midic == 0xf7)
- {
- printk ("<EOX>");
- devc->m_state = ST_INIT;
- }
- else
- printk ("%02x ", midic);
- break;
-
- case ST_SONGPOS:
- BUFTEST (devc);
- devc->m_buf[devc->m_ptr++] = midic;
- if (devc->m_ptr == 2)
- {
- devc->m_state = ST_INIT;
- devc->m_ptr = 0;
- timer_ext_event (devc, TMR_SPP,
- ((devc->m_buf[1] & 0x7f) << 7) |
- (devc->m_buf[0] & 0x7f));
- }
- break;
-
- case ST_DATABYTE:
- BUFTEST (devc);
- devc->m_buf[devc->m_ptr++] = midic;
- if ((--devc->m_left) <= 0)
- {
- devc->m_state = ST_INIT;
- do_midi_msg (devc->synthno, devc->m_buf, devc->m_ptr);
- devc->m_ptr = 0;
- }
- break;
-
- default:
- printk ("Bad state %d ", devc->m_state);
- devc->m_state = ST_INIT;
- }
-
- return 1;
+ return 1;
}
static void
-mpu401_input_loop (struct mpu_config *devc)
+mpu401_input_loop(struct mpu_config *devc)
{
- unsigned long flags;
- int busy;
- int n;
+ unsigned long flags;
+ int busy;
+ int n;
- save_flags (flags);
- cli ();
- busy = devc->m_busy;
- devc->m_busy = 1;
- restore_flags (flags);
+ save_flags(flags);
+ cli();
+ busy = devc->m_busy;
+ devc->m_busy = 1;
+ restore_flags(flags);
- if (busy) /* Already inside the scanner */
- return;
+ if (busy) /* Already inside the scanner */
+ return;
- n = 50;
+ n = 50;
- while (input_avail (devc) && n-- > 0)
- {
- unsigned char c = read_data (devc);
+ while (input_avail(devc) && n-- > 0)
+ {
+ unsigned char c = read_data(devc);
- if (devc->mode == MODE_SYNTH)
- {
- mpu_input_scanner (devc, c);
- }
- else if (devc->opened & OPEN_READ && devc->inputintr != NULL)
- devc->inputintr (devc->devno, c);
- }
+ if (devc->mode == MODE_SYNTH)
+ {
+ mpu_input_scanner(devc, c);
+ } else if (devc->opened & OPEN_READ && devc->inputintr != NULL)
+ devc->inputintr(devc->devno, c);
+ }
- devc->m_busy = 0;
+ devc->m_busy = 0;
}
void
-mpuintr (int irq, void *dev_id, struct pt_regs *dummy)
+mpuintr(int irq, void *dev_id, struct pt_regs *dummy)
{
- struct mpu_config *devc;
- int dev;
+ struct mpu_config *devc;
+ int dev;
- sti ();
+ sti();
/*
* FreeBSD (and some others) pass unit number to the interrupt handler.
* In this case we have to scan the table for first handler.
*/
- if (irq < 1 || irq > 15)
- {
- dev = -1;
- }
- else
- dev = irq2dev[irq];
-
- if (dev == -1)
- {
- int origirq = irq;
-
- for (irq = 0; irq <= 16; irq++)
- if (irq2dev[irq] != -1)
- break;
- if (irq > 15)
- {
- printk ("MPU-401: Bogus interrupt #%d?\n", origirq);
- return;
- }
- dev = irq2dev[irq];
- devc = &dev_conf[dev];
- }
- else
- devc = &dev_conf[dev];
-
- if (input_avail (devc))
- if (devc->base != 0 && (devc->opened & OPEN_READ || devc->mode == MODE_SYNTH))
- mpu401_input_loop (devc);
- else
- {
- /* Dummy read (just to acknowledge the interrupt) */
- read_data (devc);
- }
+ if (irq < 1 || irq > 15)
+ {
+ dev = -1;
+ } else
+ dev = irq2dev[irq];
+ if (dev == -1)
+ {
+ int origirq = irq;
+
+ for (irq = 0; irq <= 16; irq++)
+ if (irq2dev[irq] != -1)
+ break;
+ if (irq > 15)
+ {
+ printk("MPU-401: Bogus interrupt #%d?\n", origirq);
+ return;
+ }
+ dev = irq2dev[irq];
+ devc = &dev_conf[dev];
+ } else
+ devc = &dev_conf[dev];
+
+ if (input_avail(devc))
+ if (devc->base != 0 && (devc->opened & OPEN_READ || devc->mode == MODE_SYNTH))
+ mpu401_input_loop(devc);
+ else
+ {
+ /* Dummy read (just to acknowledge the interrupt) */
+ read_data(devc);
+ }
}
static int
-mpu401_open (int dev, int mode,
- void (*input) (int dev, unsigned char data),
- void (*output) (int dev)
+mpu401_open(int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
)
{
- int err;
- struct mpu_config *devc;
-
- if (dev < 0 || dev >= num_midis)
- return -ENXIO;
-
- devc = &dev_conf[dev];
-
- if (devc->opened)
- {
- printk ("MPU-401: Midi busy\n");
- return -EBUSY;
- }
-
- /*
- * Verify that the device is really running.
- * Some devices (such as Ensoniq SoundScape don't
- * work before the on board processor (OBP) is initialized
- * by downloading its microcode.
- */
-
- if (!devc->initialized)
- {
- if (mpu401_status (devc) == 0xff) /* Bus float */
- {
- printk ("MPU-401: Device not initialized properly\n");
- return -EIO;
- }
- reset_mpu401 (devc);
- }
+ int err;
+ struct mpu_config *devc;
- irq2dev[devc->irq] = dev;
+ if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
+ return -ENXIO;
- if (midi_devs[dev]->coproc)
- if ((err = midi_devs[dev]->coproc->
- open (midi_devs[dev]->coproc->devc, COPR_MIDI)) < 0)
- {
- printk ("MPU-401: Can't access coprocessor device\n");
+ devc = &dev_conf[dev];
- return err;
- }
+ if (devc->opened)
+ {
+ printk("MPU-401: Midi busy\n");
+ return -EBUSY;
+ }
+ /*
+ * Verify that the device is really running.
+ * Some devices (such as Ensoniq SoundScape don't
+ * work before the on board processor (OBP) is initialized
+ * by downloading its microcode.
+ */
+
+ if (!devc->initialized)
+ {
+ if (mpu401_status(devc) == 0xff) /* Bus float */
+ {
+ printk("MPU-401: Device not initialized properly\n");
+ return -EIO;
+ }
+ reset_mpu401(devc);
+ }
+ irq2dev[devc->irq] = dev;
+
+ if (midi_devs[dev]->coproc)
+ if ((err = midi_devs[dev]->coproc->
+ open(midi_devs[dev]->coproc->devc, COPR_MIDI)) < 0)
+ {
+ printk("MPU-401: Can't access coprocessor device\n");
- set_uart_mode (dev, devc, 1);
- devc->mode = MODE_MIDI;
- devc->synthno = 0;
+ return err;
+ }
+ set_uart_mode(dev, devc, 1);
+ devc->mode = MODE_MIDI;
+ devc->synthno = 0;
- mpu401_input_loop (devc);
+ mpu401_input_loop(devc);
- devc->inputintr = input;
- devc->opened = mode;
+ devc->inputintr = input;
+ devc->opened = mode;
- return 0;
+ return 0;
}
static void
-mpu401_close (int dev)
+mpu401_close(int dev)
{
- struct mpu_config *devc;
+ struct mpu_config *devc;
- devc = &dev_conf[dev];
+ devc = &dev_conf[dev];
- if (devc->uart_mode)
- reset_mpu401 (devc); /*
- * This disables the UART mode
- */
- devc->mode = 0;
+ if (devc->uart_mode)
+ reset_mpu401(devc); /*
+ * This disables the UART mode
+ */
+ devc->mode = 0;
- devc->inputintr = NULL;
+ devc->inputintr = NULL;
- if (midi_devs[dev]->coproc)
- midi_devs[dev]->coproc->close (midi_devs[dev]->coproc->devc, COPR_MIDI);
- devc->opened = 0;
+ if (midi_devs[dev]->coproc)
+ midi_devs[dev]->coproc->close(midi_devs[dev]->coproc->devc, COPR_MIDI);
+ devc->opened = 0;
}
static int
-mpu401_out (int dev, unsigned char midi_byte)
+mpu401_out(int dev, unsigned char midi_byte)
{
- int timeout;
- unsigned long flags;
-
- struct mpu_config *devc;
+ int timeout;
+ unsigned long flags;
- devc = &dev_conf[dev];
+ struct mpu_config *devc;
- /*
- * Sometimes it takes about 30000 loops before the output becomes ready
- * (After reset). Normally it takes just about 10 loops.
- */
+ devc = &dev_conf[dev];
- for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--);
+ /*
+ * Sometimes it takes about 30000 loops before the output becomes ready
+ * (After reset). Normally it takes just about 10 loops.
+ */
- save_flags (flags);
- cli ();
- if (!output_ready (devc))
- {
- printk ("MPU-401: Send data timeout\n");
- restore_flags (flags);
- return 0;
- }
+ for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
- write_data (devc, midi_byte);
- restore_flags (flags);
- return 1;
+ save_flags(flags);
+ cli();
+ if (!output_ready(devc))
+ {
+ printk("MPU-401: Send data timeout\n");
+ restore_flags(flags);
+ return 0;
+ }
+ write_data(devc, midi_byte);
+ restore_flags(flags);
+ return 1;
}
static int
-mpu401_command (int dev, mpu_command_rec * cmd)
+mpu401_command(int dev, mpu_command_rec * cmd)
{
- int i, timeout, ok;
- int ret = 0;
- unsigned long flags;
- struct mpu_config *devc;
+ int i, timeout, ok;
+ int ret = 0;
+ unsigned long flags;
+ struct mpu_config *devc;
- devc = &dev_conf[dev];
+ devc = &dev_conf[dev];
- if (devc->uart_mode) /*
+ if (devc->uart_mode) /*
* Not possible in UART mode
*/
- {
- printk ("MPU-401 commands not possible in the UART mode\n");
- return -EINVAL;
- }
-
- /*
- * Test for input since pending input seems to block the output.
- */
- if (input_avail (devc))
- mpu401_input_loop (devc);
-
- /*
- * Sometimes it takes about 50000 loops before the output becomes ready
- * (After reset). Normally it takes just about 10 loops.
- */
-
- timeout = 50000;
-retry:
- if (timeout-- <= 0)
- {
- printk ("MPU-401: Command (0x%x) timeout\n", (int) cmd->cmd);
- return -EIO;
- }
-
- save_flags (flags);
- cli ();
-
- if (!output_ready (devc))
- {
- restore_flags (flags);
- goto retry;
- }
-
- write_command (devc, cmd->cmd);
-
- ok = 0;
- for (timeout = 50000; timeout > 0 && !ok; timeout--)
- if (input_avail (devc))
- {
- if (devc->opened && devc->mode == MODE_SYNTH)
{
- if (mpu_input_scanner (devc, read_data (devc)) == MPU_ACK)
- ok = 1;
+ printk("MPU-401 commands not possible in the UART mode\n");
+ return -EINVAL;
}
- else
- { /* Device is not currently open. Use simpler method */
- if (read_data (devc) == MPU_ACK)
- ok = 1;
+ /*
+ * Test for input since pending input seems to block the output.
+ */
+ if (input_avail(devc))
+ mpu401_input_loop(devc);
+
+ /*
+ * Sometimes it takes about 50000 loops before the output becomes ready
+ * (After reset). Normally it takes just about 10 loops.
+ */
+
+ timeout = 50000;
+ retry:
+ if (timeout-- <= 0)
+ {
+ printk("MPU-401: Command (0x%x) timeout\n", (int) cmd->cmd);
+ return -EIO;
}
- }
-
- if (!ok)
- {
- restore_flags (flags);
- /* printk ("MPU: No ACK to command (0x%x)\n", (int) cmd->cmd); */
- return -EIO;
- }
+ save_flags(flags);
+ cli();
- if (cmd->nr_args)
- for (i = 0; i < cmd->nr_args; i++)
- {
- for (timeout = 3000; timeout > 0 && !output_ready (devc); timeout--);
-
- if (!mpu401_out (dev, cmd->data[i]))
+ if (!output_ready(devc))
{
- restore_flags (flags);
- printk ("MPU: Command (0x%x), parm send failed.\n", (int) cmd->cmd);
- return -EIO;
+ restore_flags(flags);
+ goto retry;
}
- }
-
- ret = 0;
- cmd->data[0] = 0;
+ write_command(devc, cmd->cmd);
- if (cmd->nr_returns)
- for (i = 0; i < cmd->nr_returns; i++)
- {
ok = 0;
- for (timeout = 5000; timeout > 0 && !ok; timeout--)
- if (input_avail (devc))
- {
- cmd->data[i] = read_data (devc);
- ok = 1;
- }
-
+ for (timeout = 50000; timeout > 0 && !ok; timeout--)
+ if (input_avail(devc))
+ {
+ if (devc->opened && devc->mode == MODE_SYNTH)
+ {
+ if (mpu_input_scanner(devc, read_data(devc)) == MPU_ACK)
+ ok = 1;
+ } else
+ { /* Device is not currently open. Use simpler method */
+ if (read_data(devc) == MPU_ACK)
+ ok = 1;
+ }
+ }
if (!ok)
{
- restore_flags (flags);
- /* printk ("MPU: No response(%d) to command (0x%x)\n", i, (int) cmd->cmd); */
- return -EIO;
+ restore_flags(flags);
+ /* printk( "MPU: No ACK to command (0x%x)\n", (int) cmd->cmd); */
+ return -EIO;
}
- }
-
- restore_flags (flags);
-
- return ret;
+ if (cmd->nr_args)
+ for (i = 0; i < cmd->nr_args; i++)
+ {
+ for (timeout = 3000; timeout > 0 && !output_ready(devc); timeout--);
+
+ if (!mpu401_out(dev, cmd->data[i]))
+ {
+ restore_flags(flags);
+ printk("MPU: Command (0x%x), parm send failed.\n", (int) cmd->cmd);
+ return -EIO;
+ }
+ }
+ ret = 0;
+ cmd->data[0] = 0;
+
+ if (cmd->nr_returns)
+ for (i = 0; i < cmd->nr_returns; i++)
+ {
+ ok = 0;
+ for (timeout = 5000; timeout > 0 && !ok; timeout--)
+ if (input_avail(devc))
+ {
+ cmd->data[i] = read_data(devc);
+ ok = 1;
+ }
+ if (!ok)
+ {
+ restore_flags(flags);
+ /* printk( "MPU: No response(%d) to command (0x%x)\n", i, (int) cmd->cmd); */
+ return -EIO;
+ }
+ }
+ restore_flags(flags);
+
+ return ret;
}
static int
-mpu_cmd (int dev, int cmd, int data)
+mpu_cmd(int dev, int cmd, int data)
{
- int ret;
+ int ret;
- static mpu_command_rec rec;
+ static mpu_command_rec rec;
- rec.cmd = cmd & 0xff;
- rec.nr_args = ((cmd & 0xf0) == 0xE0);
- rec.nr_returns = ((cmd & 0xf0) == 0xA0);
- rec.data[0] = data & 0xff;
+ rec.cmd = cmd & 0xff;
+ rec.nr_args = ((cmd & 0xf0) == 0xE0);
+ rec.nr_returns = ((cmd & 0xf0) == 0xA0);
+ rec.data[0] = data & 0xff;
- if ((ret = mpu401_command (dev, &rec)) < 0)
- {
- return ret;
- }
- return (unsigned char) rec.data[0];
+ if ((ret = mpu401_command(dev, &rec)) < 0)
+ {
+ return ret;
+ }
+ return (unsigned char) rec.data[0];
}
static int
-mpu401_prefix_cmd (int dev, unsigned char status)
+mpu401_prefix_cmd(int dev, unsigned char status)
{
- struct mpu_config *devc = &dev_conf[dev];
-
- if (devc->uart_mode)
- return 1;
-
- if (status < 0xf0)
- {
- if (mpu_cmd (dev, 0xD0, 0) < 0)
- {
- return 0;
- }
+ struct mpu_config *devc = &dev_conf[dev];
- return 1;
- }
+ if (devc->uart_mode)
+ return 1;
- switch (status)
- {
- case 0xF0:
- if (mpu_cmd (dev, 0xDF, 0) < 0)
- {
- return 0;
- }
-
- return 1;
- break;
-
- default:
- return 0;
- }
+ if (status < 0xf0)
+ {
+ if (mpu_cmd(dev, 0xD0, 0) < 0)
+ {
+ return 0;
+ }
+ return 1;
+ }
+ switch (status)
+ {
+ case 0xF0:
+ if (mpu_cmd(dev, 0xDF, 0) < 0)
+ {
+ return 0;
+ }
+ return 1;
+ break;
+
+ default:
+ return 0;
+ }
}
static int
-mpu401_start_read (int dev)
+mpu401_start_read(int dev)
{
- return 0;
+ return 0;
}
static int
-mpu401_end_read (int dev)
+mpu401_end_read(int dev)
{
- return 0;
+ return 0;
}
static int
-mpu401_ioctl (int dev, unsigned cmd, caddr_t arg)
+mpu401_ioctl(int dev, unsigned cmd, caddr_t arg)
{
- struct mpu_config *devc;
- int val;
-
- devc = &dev_conf[dev];
-
- switch (cmd)
- {
- case SNDCTL_MIDI_MPUMODE:
- if (!(devc->capabilities & MPU_CAP_INTLG)) /* No intelligent mode */
- {
- printk ("MPU-401: Intelligent mode not supported by the HW\n");
- return -EINVAL;
- }
- val = *(int *) arg;
- set_uart_mode (dev, devc, !val);
- return 0;
- break;
-
- case SNDCTL_MIDI_MPUCMD:
- {
- int ret;
- mpu_command_rec rec;
-
- memcpy ((char *) &rec, (&((char *) arg)[0]), sizeof (rec));
-
- if ((ret = mpu401_command (dev, &rec)) < 0)
- return ret;
+ struct mpu_config *devc;
+ int val;
- memcpy ((&((char *) arg)[0]), (char *) &rec, sizeof (rec));
- return 0;
- }
- break;
+ devc = &dev_conf[dev];
- default:
- return -EINVAL;
- }
+ switch (cmd)
+ {
+ case SNDCTL_MIDI_MPUMODE:
+ if (!(devc->capabilities & MPU_CAP_INTLG)) /* No intelligent mode */
+ {
+ printk("MPU-401: Intelligent mode not supported by the HW\n");
+ return -EINVAL;
+ }
+ val = *(int *) arg;
+ set_uart_mode(dev, devc, !val);
+ return 0;
+ break;
+
+ case SNDCTL_MIDI_MPUCMD:
+ {
+ int ret;
+ mpu_command_rec rec;
+
+ memcpy((char *) &rec, (&((char *) arg)[0]), sizeof(rec));
+
+ if ((ret = mpu401_command(dev, &rec)) < 0)
+ return ret;
+
+ memcpy((&((char *) arg)[0]), (char *) &rec, sizeof(rec));
+ return 0;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
}
static void
-mpu401_kick (int dev)
+mpu401_kick(int dev)
{
}
static int
-mpu401_buffer_status (int dev)
+mpu401_buffer_status(int dev)
{
- return 0; /*
+ return 0; /*
* No data in buffers
*/
}
static int
-mpu_synth_ioctl (int dev,
- unsigned int cmd, caddr_t arg)
+mpu_synth_ioctl(int dev,
+ unsigned int cmd, caddr_t arg)
{
- int midi_dev;
- struct mpu_config *devc;
+ int midi_dev;
+ struct mpu_config *devc;
- midi_dev = synth_devs[dev]->midi_dev;
+ midi_dev = synth_devs[dev]->midi_dev;
- if (midi_dev < 0 || midi_dev > num_midis)
- return -ENXIO;
+ if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL)
+ return -ENXIO;
- devc = &dev_conf[midi_dev];
+ devc = &dev_conf[midi_dev];
- switch (cmd)
- {
+ switch (cmd)
+ {
- case SNDCTL_SYNTH_INFO:
- memcpy ((&((char *) arg)[0]), (char *) &mpu_synth_info[midi_dev], sizeof (struct synth_info));
+ case SNDCTL_SYNTH_INFO:
+ memcpy((&((char *) arg)[0]), (char *) &mpu_synth_info[midi_dev], sizeof(struct synth_info));
- return 0;
- break;
+ return 0;
+ break;
- case SNDCTL_SYNTH_MEMAVL:
- return 0x7fffffff;
- break;
+ case SNDCTL_SYNTH_MEMAVL:
+ return 0x7fffffff;
+ break;
- default:
- return -EINVAL;
- }
+ default:
+ return -EINVAL;
+ }
}
static int
-mpu_synth_open (int dev, int mode)
+mpu_synth_open(int dev, int mode)
{
- int midi_dev, err;
- struct mpu_config *devc;
-
- midi_dev = synth_devs[dev]->midi_dev;
-
- if (midi_dev < 0 || midi_dev > num_midis)
- {
- return -ENXIO;
- }
-
- devc = &dev_conf[midi_dev];
+ int midi_dev, err;
+ struct mpu_config *devc;
- /*
- * Verify that the device is really running.
- * Some devices (such as Ensoniq SoundScape don't
- * work before the on board processor (OBP) is initialized
- * by downloading its microcode.
- */
+ midi_dev = synth_devs[dev]->midi_dev;
- if (!devc->initialized)
- {
- if (mpu401_status (devc) == 0xff) /* Bus float */
- {
- printk ("MPU-401: Device not initialized properly\n");
- return -EIO;
- }
- reset_mpu401 (devc);
- }
-
- if (devc->opened)
- {
- printk ("MPU-401: Midi busy\n");
- return -EBUSY;
- }
-
- devc->mode = MODE_SYNTH;
- devc->synthno = dev;
+ if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL)
+ {
+ return -ENXIO;
+ }
+ devc = &dev_conf[midi_dev];
- devc->inputintr = NULL;
- irq2dev[devc->irq] = midi_dev;
+ /*
+ * Verify that the device is really running.
+ * Some devices (such as Ensoniq SoundScape don't
+ * work before the on board processor (OBP) is initialized
+ * by downloading its microcode.
+ */
- if (midi_devs[midi_dev]->coproc)
- if ((err = midi_devs[midi_dev]->coproc->
- open (midi_devs[midi_dev]->coproc->devc, COPR_MIDI)) < 0)
- {
- printk ("MPU-401: Can't access coprocessor device\n");
+ if (!devc->initialized)
+ {
+ if (mpu401_status(devc) == 0xff) /* Bus float */
+ {
+ printk("MPU-401: Device not initialized properly\n");
+ return -EIO;
+ }
+ reset_mpu401(devc);
+ }
+ if (devc->opened)
+ {
+ printk("MPU-401: Midi busy\n");
+ return -EBUSY;
+ }
+ devc->mode = MODE_SYNTH;
+ devc->synthno = dev;
- return err;
- }
+ devc->inputintr = NULL;
+ irq2dev[devc->irq] = midi_dev;
- devc->opened = mode;
- reset_mpu401 (devc);
+ if (midi_devs[midi_dev]->coproc)
+ if ((err = midi_devs[midi_dev]->coproc->
+ open(midi_devs[midi_dev]->coproc->devc, COPR_MIDI)) < 0)
+ {
+ printk("MPU-401: Can't access coprocessor device\n");
- if (mode & OPEN_READ)
- {
- mpu_cmd (midi_dev, 0x8B, 0); /* Enable data in stop mode */
- mpu_cmd (midi_dev, 0x34, 0); /* Return timing bytes in stop mode */
- mpu_cmd (midi_dev, 0x87, 0); /* Enable pitch & controller */
- }
+ return err;
+ }
+ devc->opened = mode;
+ reset_mpu401(devc);
- return 0;
+ if (mode & OPEN_READ)
+ {
+ mpu_cmd(midi_dev, 0x8B, 0); /* Enable data in stop mode */
+ mpu_cmd(midi_dev, 0x34, 0); /* Return timing bytes in stop mode */
+ mpu_cmd(midi_dev, 0x87, 0); /* Enable pitch & controller */
+ }
+ return 0;
}
static void
-mpu_synth_close (int dev)
+mpu_synth_close(int dev)
{
- int midi_dev;
- struct mpu_config *devc;
+ int midi_dev;
+ struct mpu_config *devc;
- midi_dev = synth_devs[dev]->midi_dev;
+ midi_dev = synth_devs[dev]->midi_dev;
- devc = &dev_conf[midi_dev];
- mpu_cmd (midi_dev, 0x15, 0); /* Stop recording, playback and MIDI */
- mpu_cmd (midi_dev, 0x8a, 0); /* Disable data in stopped mode */
+ devc = &dev_conf[midi_dev];
+ mpu_cmd(midi_dev, 0x15, 0); /* Stop recording, playback and MIDI */
+ mpu_cmd(midi_dev, 0x8a, 0); /* Disable data in stopped mode */
- devc->inputintr = NULL;
+ devc->inputintr = NULL;
- if (midi_devs[midi_dev]->coproc)
- midi_devs[midi_dev]->coproc->close (midi_devs[midi_dev]->coproc->devc, COPR_MIDI);
- devc->opened = 0;
- devc->mode = 0;
+ if (midi_devs[midi_dev]->coproc)
+ midi_devs[midi_dev]->coproc->close(midi_devs[midi_dev]->coproc->devc, COPR_MIDI);
+ devc->opened = 0;
+ devc->mode = 0;
}
#define MIDI_SYNTH_NAME "MPU-401 UART Midi"
@@ -947,376 +919,369 @@ mpu_synth_close (int dev)
static struct synth_operations mpu401_synth_proto =
{
- "MPU401",
- NULL,
- 0,
- SYNTH_TYPE_MIDI,
- 0,
- mpu_synth_open,
- mpu_synth_close,
- mpu_synth_ioctl,
- midi_synth_kill_note,
- midi_synth_start_note,
- midi_synth_set_instr,
- midi_synth_reset,
- midi_synth_hw_control,
- midi_synth_load_patch,
- midi_synth_aftertouch,
- midi_synth_controller,
- midi_synth_panning,
- NULL,
- midi_synth_bender,
- NULL, /* alloc */
- midi_synth_setup_voice,
- midi_synth_send_sysex
+ "MPU401",
+ NULL,
+ 0,
+ SYNTH_TYPE_MIDI,
+ 0,
+ mpu_synth_open,
+ mpu_synth_close,
+ mpu_synth_ioctl,
+ midi_synth_kill_note,
+ midi_synth_start_note,
+ midi_synth_set_instr,
+ midi_synth_reset,
+ midi_synth_hw_control,
+ midi_synth_load_patch,
+ midi_synth_aftertouch,
+ midi_synth_controller,
+ midi_synth_panning,
+ NULL,
+ midi_synth_bender,
+ NULL, /* alloc */
+ midi_synth_setup_voice,
+ midi_synth_send_sysex
};
static struct synth_operations *mpu401_synth_operations[MAX_MIDI_DEV];
static struct midi_operations mpu401_midi_proto =
{
- {"MPU-401 Midi", 0, MIDI_CAP_MPU401, SNDCARD_MPU401},
- NULL,
- {0},
- mpu401_open,
- mpu401_close,
- mpu401_ioctl,
- mpu401_out,
- mpu401_start_read,
- mpu401_end_read,
- mpu401_kick,
- NULL,
- mpu401_buffer_status,
- mpu401_prefix_cmd
+ {"MPU-401 Midi", 0, MIDI_CAP_MPU401, SNDCARD_MPU401},
+ NULL,
+ {0},
+ mpu401_open,
+ mpu401_close,
+ mpu401_ioctl,
+ mpu401_out,
+ mpu401_start_read,
+ mpu401_end_read,
+ mpu401_kick,
+ NULL,
+ mpu401_buffer_status,
+ mpu401_prefix_cmd
};
static struct midi_operations mpu401_midi_operations[MAX_MIDI_DEV];
static void
-mpu401_chk_version (struct mpu_config *devc)
+mpu401_chk_version(int n, struct mpu_config *devc)
{
- int tmp;
- unsigned long flags;
-
- devc->version = devc->revision = 0;
-
- save_flags (flags);
- cli ();
- if ((tmp = mpu_cmd (num_midis, 0xAC, 0)) < 0)
- {
- restore_flags (flags);
- return;
- }
-
- if ((tmp & 0xf0) > 0x20) /* Why it's larger than 2.x ??? */
- {
- restore_flags (flags);
- return;
- }
-
- devc->version = tmp;
-
- if ((tmp = mpu_cmd (num_midis, 0xAD, 0)) < 0)
- {
- devc->version = 0;
- restore_flags (flags);
- return;
- }
- devc->revision = tmp;
-
- restore_flags (flags);
+ int tmp;
+ unsigned long flags;
+
+ devc->version = devc->revision = 0;
+
+ save_flags(flags);
+ cli();
+ if ((tmp = mpu_cmd(n, 0xAC, 0)) < 0)
+ {
+ restore_flags(flags);
+ return;
+ }
+ if ((tmp & 0xf0) > 0x20) /* Why it's larger than 2.x ??? */
+ {
+ restore_flags(flags);
+ return;
+ }
+ devc->version = tmp;
+
+ if ((tmp = mpu_cmd(n, 0xAD, 0)) < 0)
+ {
+ devc->version = 0;
+ restore_flags(flags);
+ return;
+ }
+ devc->revision = tmp;
+
+ restore_flags(flags);
}
void
-attach_mpu401 (struct address_info *hw_config)
+attach_mpu401(struct address_info *hw_config)
{
- unsigned long flags;
- char revision_char;
-
- struct mpu_config *devc;
-
- if (num_midis >= MAX_MIDI_DEV)
- {
- printk ("MPU-401: Too many midi devices detected\n");
- return;
- }
-
- devc = &dev_conf[num_midis];
-
- devc->base = hw_config->io_base;
- devc->osp = hw_config->osp;
- devc->irq = hw_config->irq;
- devc->opened = 0;
- devc->uart_mode = 0;
- devc->initialized = 0;
- devc->version = 0;
- devc->revision = 0;
- devc->capabilities = 0;
- devc->timer_flag = 0;
- devc->m_busy = 0;
- devc->m_state = ST_INIT;
- devc->shared_irq = hw_config->always_detect;
- devc->irq = hw_config->irq;
-
- if (devc->irq < 0)
- {
- devc->irq *= -1;
- devc->shared_irq = 1;
- }
- irq2dev[devc->irq] = num_midis;
-
- if (!hw_config->always_detect)
- {
- /* Verify the hardware again */
- if (!reset_mpu401 (devc))
- {
- printk ("MPU401: Device didn't respond\n");
- return;
- }
+ unsigned long flags;
+ char revision_char;
+
+ int m;
+ struct mpu_config *devc;
- if (!devc->shared_irq)
- if (snd_set_irq_handler (devc->irq, mpuintr, "mpu401", devc->osp) < 0)
+ hw_config->slots[1] = -1;
+ m = sound_alloc_mididev();
+ if (m == -1)
{
- printk ("MPU401: Failed to allocate IRQ%d\n", devc->irq);
- return;
+ printk(KERN_WARNING "MPU-401: Too many midi devices detected\n");
+ return;
}
+ devc = &dev_conf[m];
+ devc->base = hw_config->io_base;
+ devc->osp = hw_config->osp;
+ devc->irq = hw_config->irq;
+ devc->opened = 0;
+ devc->uart_mode = 0;
+ devc->initialized = 0;
+ devc->version = 0;
+ devc->revision = 0;
+ devc->capabilities = 0;
+ devc->timer_flag = 0;
+ devc->m_busy = 0;
+ devc->m_state = ST_INIT;
+ devc->shared_irq = hw_config->always_detect;
+ devc->irq = hw_config->irq;
+
+ if (devc->irq < 0)
+ {
+ devc->irq *= -1;
+ devc->shared_irq = 1;
+ }
+ irq2dev[devc->irq] = m;
- save_flags (flags);
- cli ();
- mpu401_chk_version (devc);
- if (devc->version == 0)
- mpu401_chk_version (devc);
- restore_flags (flags);
- }
-
- request_region (hw_config->io_base, 2, "mpu401");
-
- if (devc->version != 0)
- if (mpu_cmd (num_midis, 0xC5, 0) >= 0) /* Set timebase OK */
- if (mpu_cmd (num_midis, 0xE0, 120) >= 0) /* Set tempo OK */
- devc->capabilities |= MPU_CAP_INTLG; /* Supports intelligent mode */
-
-
- mpu401_synth_operations[num_midis] = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct synth_operations)));
- sound_mem_sizes[sound_nblocks] = sizeof (struct synth_operations);
-
- if (sound_nblocks < 1024)
- sound_nblocks++;;
-
- if (mpu401_synth_operations[num_midis] == NULL)
- {
- printk ("mpu401: Can't allocate memory\n");
- return;
- }
-
- if (!(devc->capabilities & MPU_CAP_INTLG)) /* No intelligent mode */
- {
- memcpy ((char *) mpu401_synth_operations[num_midis],
- (char *) &std_midi_synth,
- sizeof (struct synth_operations));
- }
- else
- {
- memcpy ((char *) mpu401_synth_operations[num_midis],
- (char *) &mpu401_synth_proto,
- sizeof (struct synth_operations));
- }
-
- memcpy ((char *) &mpu401_midi_operations[num_midis],
- (char *) &mpu401_midi_proto,
- sizeof (struct midi_operations));
-
- mpu401_midi_operations[num_midis].converter =
- mpu401_synth_operations[num_midis];
-
- memcpy ((char *) &mpu_synth_info[num_midis],
- (char *) &mpu_synth_info_proto,
- sizeof (struct synth_info));
-
- n_mpu_devs++;
-
- if (devc->version == 0x20 && devc->revision >= 0x07) /* MusicQuest interface */
- {
- int ports = (devc->revision & 0x08) ? 32 : 16;
-
- devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE |
- MPU_CAP_CLS | MPU_CAP_2PORT;
-
- revision_char = (devc->revision == 0x7f) ? 'M' : ' ';
- sprintf (mpu_synth_info[num_midis].name,
- "MQX-%d%c MIDI Interface #%d",
- ports,
- revision_char,
- n_mpu_devs);
- }
- else
- {
-
- revision_char = devc->revision ? devc->revision + '@' : ' ';
- if ((int) devc->revision > ('Z' - '@'))
- revision_char = '+';
-
- devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK;
-
- if (hw_config->name)
- sprintf (mpu_synth_info[num_midis].name, "%s (MPU401)", hw_config->name);
- else
- sprintf (mpu_synth_info[num_midis].name,
- "MPU-401 %d.%d%c Midi interface #%d",
- (int) (devc->version & 0xf0) >> 4,
- devc->version & 0x0f,
- revision_char,
- n_mpu_devs);
- }
-
- strcpy (mpu401_midi_operations[num_midis].info.name,
- mpu_synth_info[num_midis].name);
-
- conf_printf (mpu_synth_info[num_midis].name, hw_config);
-
- mpu401_synth_operations[num_midis]->midi_dev = devc->devno = num_midis;
- mpu401_synth_operations[devc->devno]->info =
- &mpu_synth_info[devc->devno];
-
- if (devc->capabilities & MPU_CAP_INTLG) /* Intelligent mode */
- mpu_timer_init (num_midis);
-
- irq2dev[devc->irq] = num_midis;
- midi_devs[num_midis++] = &mpu401_midi_operations[devc->devno];
- sequencer_init ();
-}
+ if (!hw_config->always_detect)
+ {
+ /* Verify the hardware again */
+ if (!reset_mpu401(devc))
+ {
+ printk(KERN_WARNING "mpu401: Device didn't respond\n");
+ sound_unload_mididev(m);
+ return;
+ }
+ if (!devc->shared_irq)
+ if (snd_set_irq_handler(devc->irq, mpuintr, "mpu401", devc->osp) < 0)
+ {
+ printk(KERN_WARNING "mpu401: Failed to allocate IRQ%d\n", devc->irq);
+ sound_unload_mididev(m);
+ return;
+ }
+ save_flags(flags);
+ cli();
+ mpu401_chk_version(m, devc);
+ if (devc->version == 0)
+ mpu401_chk_version(m, devc);
+ restore_flags(flags);
+ }
+ request_region(hw_config->io_base, 2, "mpu401");
-static int
-reset_mpu401 (struct mpu_config *devc)
-{
- unsigned long flags;
- int ok, timeout, n;
- int timeout_limit;
+ if (devc->version != 0)
+ if (mpu_cmd(m, 0xC5, 0) >= 0) /* Set timebase OK */
+ if (mpu_cmd(m, 0xE0, 120) >= 0) /* Set tempo OK */
+ devc->capabilities |= MPU_CAP_INTLG; /* Supports intelligent mode */
- /*
- * Send the RESET command. Try again if no success at the first time.
- * (If the device is in the UART mode, it will not ack the reset cmd).
- */
- ok = 0;
+ mpu401_synth_operations[m] = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct synth_operations)));
+ sound_mem_sizes[sound_nblocks] = sizeof(struct synth_operations);
- timeout_limit = devc->initialized ? 30000 : 100000;
- devc->initialized = 1;
+ if (sound_nblocks < 1024)
+ sound_nblocks++;;
- for (n = 0; n < 2 && !ok; n++)
- {
- for (timeout = timeout_limit; timeout > 0 && !ok; timeout--)
- ok = output_ready (devc);
+ if (mpu401_synth_operations[m] == NULL)
+ {
+ sound_unload_mididev(m);
+ printk(KERN_ERR "mpu401: Can't allocate memory\n");
+ return;
+ }
+ if (!(devc->capabilities & MPU_CAP_INTLG)) /* No intelligent mode */
+ {
+ memcpy((char *) mpu401_synth_operations[m],
+ (char *) &std_midi_synth,
+ sizeof(struct synth_operations));
+ } else
+ {
+ memcpy((char *) mpu401_synth_operations[m],
+ (char *) &mpu401_synth_proto,
+ sizeof(struct synth_operations));
+ }
- write_command (devc, MPU_RESET); /*
- * Send MPU-401 RESET Command
- */
+ memcpy((char *) &mpu401_midi_operations[m],
+ (char *) &mpu401_midi_proto,
+ sizeof(struct midi_operations));
- /*
- * Wait at least 25 msec. This method is not accurate so let's make the
- * loop bit longer. Cannot sleep since this is called during boot.
- */
+ mpu401_midi_operations[m].converter =
+ mpu401_synth_operations[m];
- for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--)
- {
- save_flags (flags);
- cli ();
- if (input_avail (devc))
- if (read_data (devc) == MPU_ACK)
- ok = 1;
- restore_flags (flags);
- }
+ memcpy((char *) &mpu_synth_info[m],
+ (char *) &mpu_synth_info_proto,
+ sizeof(struct synth_info));
- }
+ n_mpu_devs++;
- devc->m_state = ST_INIT;
- devc->m_ptr = 0;
- devc->m_left = 0;
- devc->last_status = 0;
- devc->uart_mode = 0;
+ if (devc->version == 0x20 && devc->revision >= 0x07) /* MusicQuest interface */
+ {
+ int ports = (devc->revision & 0x08) ? 32 : 16;
+
+ devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE |
+ MPU_CAP_CLS | MPU_CAP_2PORT;
+
+ revision_char = (devc->revision == 0x7f) ? 'M' : ' ';
+ sprintf(mpu_synth_info[m].name,
+ "MQX-%d%c MIDI Interface #%d",
+ ports,
+ revision_char,
+ n_mpu_devs);
+ } else
+ {
- return ok;
+ revision_char = devc->revision ? devc->revision + '@' : ' ';
+ if ((int) devc->revision > ('Z' - '@'))
+ revision_char = '+';
+
+ devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK;
+
+ if (hw_config->name)
+ sprintf(mpu_synth_info[m].name, "%s (MPU401)", hw_config->name);
+ else
+ sprintf(mpu_synth_info[m].name,
+ "MPU-401 %d.%d%c Midi interface #%d",
+ (int) (devc->version & 0xf0) >> 4,
+ devc->version & 0x0f,
+ revision_char,
+ n_mpu_devs);
+ }
+
+ strcpy(mpu401_midi_operations[m].info.name,
+ mpu_synth_info[m].name);
+
+ conf_printf(mpu_synth_info[m].name, hw_config);
+
+ mpu401_synth_operations[m]->midi_dev = devc->devno = m;
+ mpu401_synth_operations[devc->devno]->info =
+ &mpu_synth_info[devc->devno];
+
+ if (devc->capabilities & MPU_CAP_INTLG) /* Intelligent mode */
+ hw_config->slots[2] = mpu_timer_init(m);
+
+ irq2dev[devc->irq] = m;
+ midi_devs[m] = &mpu401_midi_operations[devc->devno];
+ hw_config->slots[1] = m;
+ sequencer_init();
}
-static void
-set_uart_mode (int dev, struct mpu_config *devc, int arg)
+static int
+reset_mpu401(struct mpu_config *devc)
{
- if (!arg && (devc->capabilities & MPU_CAP_INTLG))
- {
- return;
- }
+ unsigned long flags;
+ int ok, timeout, n;
+ int timeout_limit;
+
+ /*
+ * Send the RESET command. Try again if no success at the first time.
+ * (If the device is in the UART mode, it will not ack the reset cmd).
+ */
- if ((devc->uart_mode == 0) == (arg == 0))
- {
- return; /* Already set */
- }
+ ok = 0;
- reset_mpu401 (devc); /* This exits the uart mode */
+ timeout_limit = devc->initialized ? 30000 : 100000;
+ devc->initialized = 1;
- if (arg)
- {
- if (mpu_cmd (dev, UART_MODE_ON, 0) < 0)
- {
- printk ("MPU%d: Can't enter UART mode\n", devc->devno);
- devc->uart_mode = 0;
- return;
- }
- }
- devc->uart_mode = arg;
+ for (n = 0; n < 2 && !ok; n++)
+ {
+ for (timeout = timeout_limit; timeout > 0 && !ok; timeout--)
+ ok = output_ready(devc);
+
+ write_command(devc, MPU_RESET); /*
+ * Send MPU-401 RESET Command
+ */
+
+ /*
+ * Wait at least 25 msec. This method is not accurate so let's make the
+ * loop bit longer. Cannot sleep since this is called during boot.
+ */
+
+ for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--)
+ {
+ save_flags(flags);
+ cli();
+ if (input_avail(devc))
+ if (read_data(devc) == MPU_ACK)
+ ok = 1;
+ restore_flags(flags);
+ }
+
+ }
+
+ devc->m_state = ST_INIT;
+ devc->m_ptr = 0;
+ devc->m_left = 0;
+ devc->last_status = 0;
+ devc->uart_mode = 0;
+
+ return ok;
+}
+
+static void
+set_uart_mode(int dev, struct mpu_config *devc, int arg)
+{
+ if (!arg && (devc->capabilities & MPU_CAP_INTLG))
+ {
+ return;
+ }
+ if ((devc->uart_mode == 0) == (arg == 0))
+ {
+ return; /* Already set */
+ }
+ reset_mpu401(devc); /* This exits the uart mode */
+
+ if (arg)
+ {
+ if (mpu_cmd(dev, UART_MODE_ON, 0) < 0)
+ {
+ printk("MPU%d: Can't enter UART mode\n", devc->devno);
+ devc->uart_mode = 0;
+ return;
+ }
+ }
+ devc->uart_mode = arg;
}
int
-probe_mpu401 (struct address_info *hw_config)
+probe_mpu401(struct address_info *hw_config)
{
- int ok = 0;
- struct mpu_config tmp_devc;
-
- if (check_region (hw_config->io_base, 2))
- {
- printk ("\n\nmpu401.c: I/O port %x already in use\n\n",
- hw_config->io_base);
- return 0;
- }
-
- tmp_devc.base = hw_config->io_base;
- tmp_devc.irq = hw_config->irq;
- tmp_devc.initialized = 0;
- tmp_devc.opened = 0;
- tmp_devc.osp = hw_config->osp;
-
- if (hw_config->always_detect)
- return 1;
-
- if (inb (hw_config->io_base + 1) == 0xff)
- {
- DDB (printk ("MPU401: Port %x looks dead.\n", hw_config->io_base));
- return 0; /* Just bus float? */
- }
-
- ok = reset_mpu401 (&tmp_devc);
-
- if (!ok)
- {
- DDB (printk ("MPU401: Reset failed on port %x\n", hw_config->io_base));
- }
-
- return ok;
+ int ok = 0;
+ struct mpu_config tmp_devc;
+
+ if (check_region(hw_config->io_base, 2))
+ {
+ printk("\n\nmpu401.c: I/O port %x already in use\n\n", hw_config->io_base);
+ return 0;
+ }
+ tmp_devc.base = hw_config->io_base;
+ tmp_devc.irq = hw_config->irq;
+ tmp_devc.initialized = 0;
+ tmp_devc.opened = 0;
+ tmp_devc.osp = hw_config->osp;
+
+ if (hw_config->always_detect)
+ return 1;
+
+ if (inb(hw_config->io_base + 1) == 0xff)
+ {
+ DDB(printk("MPU401: Port %x looks dead.\n", hw_config->io_base));
+ return 0; /* Just bus float? */
+ }
+ ok = reset_mpu401(&tmp_devc);
+
+ if (!ok)
+ {
+ DDB(printk("MPU401: Reset failed on port %x\n", hw_config->io_base));
+ }
+ return ok;
}
void
-unload_mpu401 (struct address_info *hw_config)
+unload_mpu401(struct address_info *hw_config)
{
- release_region (hw_config->io_base, 2);
- if (hw_config->always_detect == 0 && hw_config->irq > 0)
- snd_release_irq (hw_config->irq);
+ release_region(hw_config->io_base, 2);
+ if (hw_config->always_detect == 0 && hw_config->irq > 0)
+ snd_release_irq(hw_config->irq);
+ sound_unload_mididev(hw_config->slots[1]);
+ sound_unload_timerdev(hw_config->slots[2]);
}
/*****************************************************
* Timer stuff
****************************************************/
-#if defined(CONFIG_SEQUENCER)
+#if defined(CONFIG_SEQUENCER) || defined(MODULE)
static volatile int timer_initialized = 0, timer_open = 0, tmr_running = 0;
static volatile int curr_tempo, curr_timebase, hw_timebase;
@@ -1327,525 +1292,566 @@ static unsigned long prev_event_time;
static int metronome_mode;
static unsigned long
-clocks2ticks (unsigned long clocks)
+clocks2ticks(unsigned long clocks)
{
- /*
- * The MPU-401 supports just a limited set of possible timebase values.
- * Since the applications require more choices, the driver has to
- * program the HW to do its best and to convert between the HW and
- * actual timebases.
- */
-
- return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase;
+ /*
+ * The MPU-401 supports just a limited set of possible timebase values.
+ * Since the applications require more choices, the driver has to
+ * program the HW to do its best and to convert between the HW and
+ * actual timebases.
+ */
+
+ return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase;
}
static void
-set_timebase (int midi_dev, int val)
+set_timebase(int midi_dev, int val)
{
- int hw_val;
-
- if (val < 48)
- val = 48;
- if (val > 1000)
- val = 1000;
-
- hw_val = val;
- hw_val = (hw_val + 12) / 24;
- if (hw_val > max_timebase)
- hw_val = max_timebase;
-
- if (mpu_cmd (midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0)
- {
- printk ("MPU: Can't set HW timebase to %d\n", hw_val * 24);
- return;
- }
- hw_timebase = hw_val * 24;
- curr_timebase = val;
+ int hw_val;
+
+ if (val < 48)
+ val = 48;
+ if (val > 1000)
+ val = 1000;
+
+ hw_val = val;
+ hw_val = (hw_val + 12) / 24;
+ if (hw_val > max_timebase)
+ hw_val = max_timebase;
+
+ if (mpu_cmd(midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0)
+ {
+ printk("MPU: Can't set HW timebase to %d\n", hw_val * 24);
+ return;
+ }
+ hw_timebase = hw_val * 24;
+ curr_timebase = val;
}
static void
-tmr_reset (void)
+tmr_reset(void)
{
- unsigned long flags;
-
- save_flags (flags);
- cli ();
- next_event_time = (unsigned long) -1;
- prev_event_time = 0;
- curr_ticks = curr_clocks = 0;
- restore_flags (flags);
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ next_event_time = (unsigned long) -1;
+ prev_event_time = 0;
+ curr_ticks = curr_clocks = 0;
+ restore_flags(flags);
}
static void
-set_timer_mode (int midi_dev)
+set_timer_mode(int midi_dev)
{
- if (timer_mode & TMR_MODE_CLS)
- mpu_cmd (midi_dev, 0x3c, 0); /* Use CLS sync */
- else if (timer_mode & TMR_MODE_SMPTE)
- mpu_cmd (midi_dev, 0x3d, 0); /* Use SMPTE sync */
-
- if (timer_mode & TMR_INTERNAL)
- {
- mpu_cmd (midi_dev, 0x80, 0); /* Use MIDI sync */
- }
- else
- {
- if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS))
- {
- mpu_cmd (midi_dev, 0x82, 0); /* Use MIDI sync */
- mpu_cmd (midi_dev, 0x91, 0); /* Enable ext MIDI ctrl */
- }
- else if (timer_mode & TMR_MODE_FSK)
- mpu_cmd (midi_dev, 0x81, 0); /* Use FSK sync */
- }
+ if (timer_mode & TMR_MODE_CLS)
+ mpu_cmd(midi_dev, 0x3c, 0); /* Use CLS sync */
+ else if (timer_mode & TMR_MODE_SMPTE)
+ mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */
+
+ if (timer_mode & TMR_INTERNAL)
+ {
+ mpu_cmd(midi_dev, 0x80, 0); /* Use MIDI sync */
+ } else
+ {
+ if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS))
+ {
+ mpu_cmd(midi_dev, 0x82, 0); /* Use MIDI sync */
+ mpu_cmd(midi_dev, 0x91, 0); /* Enable ext MIDI ctrl */
+ } else if (timer_mode & TMR_MODE_FSK)
+ mpu_cmd(midi_dev, 0x81, 0); /* Use FSK sync */
+ }
}
static void
-stop_metronome (int midi_dev)
+stop_metronome(int midi_dev)
{
- mpu_cmd (midi_dev, 0x84, 0); /* Disable metronome */
+ mpu_cmd(midi_dev, 0x84, 0); /* Disable metronome */
}
static void
-setup_metronome (int midi_dev)
+setup_metronome(int midi_dev)
{
- int numerator, denominator;
- int clks_per_click, num_32nds_per_beat;
- int beats_per_measure;
-
- numerator = ((unsigned) metronome_mode >> 24) & 0xff;
- denominator = ((unsigned) metronome_mode >> 16) & 0xff;
- clks_per_click = ((unsigned) metronome_mode >> 8) & 0xff;
- num_32nds_per_beat = (unsigned) metronome_mode & 0xff;
- beats_per_measure = (numerator * 4) >> denominator;
-
- if (!metronome_mode)
- mpu_cmd (midi_dev, 0x84, 0); /* Disable metronome */
- else
- {
- mpu_cmd (midi_dev, 0xE4, clks_per_click);
- mpu_cmd (midi_dev, 0xE6, beats_per_measure);
- mpu_cmd (midi_dev, 0x83, 0); /* Enable metronome without accents */
- }
+ int numerator, denominator;
+ int clks_per_click, num_32nds_per_beat;
+ int beats_per_measure;
+
+ numerator = ((unsigned) metronome_mode >> 24) & 0xff;
+ denominator = ((unsigned) metronome_mode >> 16) & 0xff;
+ clks_per_click = ((unsigned) metronome_mode >> 8) & 0xff;
+ num_32nds_per_beat = (unsigned) metronome_mode & 0xff;
+ beats_per_measure = (numerator * 4) >> denominator;
+
+ if (!metronome_mode)
+ mpu_cmd(midi_dev, 0x84, 0); /* Disable metronome */
+ else
+ {
+ mpu_cmd(midi_dev, 0xE4, clks_per_click);
+ mpu_cmd(midi_dev, 0xE6, beats_per_measure);
+ mpu_cmd(midi_dev, 0x83, 0); /* Enable metronome without accents */
+ }
}
static int
-mpu_start_timer (int midi_dev)
+mpu_start_timer(int midi_dev)
{
- tmr_reset ();
- set_timer_mode (midi_dev);
-
- if (tmr_running)
- return TIMER_NOT_ARMED; /* Already running */
-
- if (timer_mode & TMR_INTERNAL)
- {
- mpu_cmd (midi_dev, 0x02, 0); /* Send MIDI start */
- tmr_running = 1;
- return TIMER_NOT_ARMED;
- }
- else
- {
- mpu_cmd (midi_dev, 0x35, 0); /* Enable mode messages to PC */
- mpu_cmd (midi_dev, 0x38, 0); /* Enable sys common messages to PC */
- mpu_cmd (midi_dev, 0x39, 0); /* Enable real time messages to PC */
- mpu_cmd (midi_dev, 0x97, 0); /* Enable system exclusive messages to PC */
- }
-
- return TIMER_ARMED;
+ tmr_reset();
+ set_timer_mode(midi_dev);
+
+ if (tmr_running)
+ return TIMER_NOT_ARMED; /* Already running */
+
+ if (timer_mode & TMR_INTERNAL)
+ {
+ mpu_cmd(midi_dev, 0x02, 0); /* Send MIDI start */
+ tmr_running = 1;
+ return TIMER_NOT_ARMED;
+ } else
+ {
+ mpu_cmd(midi_dev, 0x35, 0); /* Enable mode messages to PC */
+ mpu_cmd(midi_dev, 0x38, 0); /* Enable sys common messages to PC */
+ mpu_cmd(midi_dev, 0x39, 0); /* Enable real time messages to PC */
+ mpu_cmd(midi_dev, 0x97, 0); /* Enable system exclusive messages to PC */
+ }
+
+ return TIMER_ARMED;
}
static int
-mpu_timer_open (int dev, int mode)
+mpu_timer_open(int dev, int mode)
{
- int midi_dev = sound_timer_devs[dev]->devlink;
+ int midi_dev = sound_timer_devs[dev]->devlink;
- if (timer_open)
- return -EBUSY;
+ if (timer_open)
+ return -EBUSY;
- tmr_reset ();
- curr_tempo = 50;
- mpu_cmd (midi_dev, 0xE0, 50);
- curr_timebase = hw_timebase = 120;
- set_timebase (midi_dev, 120);
- timer_open = 1;
- metronome_mode = 0;
- set_timer_mode (midi_dev);
+ tmr_reset();
+ curr_tempo = 50;
+ mpu_cmd(midi_dev, 0xE0, 50);
+ curr_timebase = hw_timebase = 120;
+ set_timebase(midi_dev, 120);
+ timer_open = 1;
+ metronome_mode = 0;
+ set_timer_mode(midi_dev);
- mpu_cmd (midi_dev, 0xe7, 0x04); /* Send all clocks to host */
- mpu_cmd (midi_dev, 0x95, 0); /* Enable clock to host */
+ mpu_cmd(midi_dev, 0xe7, 0x04); /* Send all clocks to host */
+ mpu_cmd(midi_dev, 0x95, 0); /* Enable clock to host */
- return 0;
+ return 0;
}
static void
-mpu_timer_close (int dev)
+mpu_timer_close(int dev)
{
- int midi_dev = sound_timer_devs[dev]->devlink;
+ int midi_dev = sound_timer_devs[dev]->devlink;
- timer_open = tmr_running = 0;
- mpu_cmd (midi_dev, 0x15, 0); /* Stop all */
- mpu_cmd (midi_dev, 0x94, 0); /* Disable clock to host */
- mpu_cmd (midi_dev, 0x8c, 0); /* Disable measure end messages to host */
- stop_metronome (midi_dev);
+ timer_open = tmr_running = 0;
+ mpu_cmd(midi_dev, 0x15, 0); /* Stop all */
+ mpu_cmd(midi_dev, 0x94, 0); /* Disable clock to host */
+ mpu_cmd(midi_dev, 0x8c, 0); /* Disable measure end messages to host */
+ stop_metronome(midi_dev);
}
static int
-mpu_timer_event (int dev, unsigned char *event)
+mpu_timer_event(int dev, unsigned char *event)
{
- unsigned char command = event[1];
- unsigned long parm = *(unsigned int *) &event[4];
- int midi_dev = sound_timer_devs[dev]->devlink;
-
- switch (command)
- {
- case TMR_WAIT_REL:
- parm += prev_event_time;
- case TMR_WAIT_ABS:
- if (parm > 0)
- {
- long time;
-
- if (parm <= curr_ticks) /* It's the time */
- return TIMER_NOT_ARMED;
+ unsigned char command = event[1];
+ unsigned long parm = *(unsigned int *) &event[4];
+ int midi_dev = sound_timer_devs[dev]->devlink;
- time = parm;
- next_event_time = prev_event_time = time;
-
- return TIMER_ARMED;
- }
- break;
-
- case TMR_START:
- if (tmr_running)
- break;
- return mpu_start_timer (midi_dev);
- break;
-
- case TMR_STOP:
- mpu_cmd (midi_dev, 0x01, 0); /* Send MIDI stop */
- stop_metronome (midi_dev);
- tmr_running = 0;
- break;
-
- case TMR_CONTINUE:
- if (tmr_running)
- break;
- mpu_cmd (midi_dev, 0x03, 0); /* Send MIDI continue */
- setup_metronome (midi_dev);
- tmr_running = 1;
- break;
-
- case TMR_TEMPO:
- if (parm)
- {
- if (parm < 8)
- parm = 8;
- if (parm > 250)
- parm = 250;
-
- if (mpu_cmd (midi_dev, 0xE0, parm) < 0)
- printk ("MPU: Can't set tempo to %d\n", (int) parm);
- curr_tempo = parm;
- }
- break;
-
- case TMR_ECHO:
- seq_copy_to_input (event, 8);
- break;
-
- case TMR_TIMESIG:
- if (metronome_mode) /* Metronome enabled */
- {
- metronome_mode = parm;
- setup_metronome (midi_dev);
- }
- break;
-
- default:;
- }
+ switch (command)
+ {
+ case TMR_WAIT_REL:
+ parm += prev_event_time;
+ case TMR_WAIT_ABS:
+ if (parm > 0)
+ {
+ long time;
+
+ if (parm <= curr_ticks) /* It's the time */
+ return TIMER_NOT_ARMED;
+
+ time = parm;
+ next_event_time = prev_event_time = time;
+
+ return TIMER_ARMED;
+ }
+ break;
+
+ case TMR_START:
+ if (tmr_running)
+ break;
+ return mpu_start_timer(midi_dev);
+ break;
+
+ case TMR_STOP:
+ mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */
+ stop_metronome(midi_dev);
+ tmr_running = 0;
+ break;
+
+ case TMR_CONTINUE:
+ if (tmr_running)
+ break;
+ mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */
+ setup_metronome(midi_dev);
+ tmr_running = 1;
+ break;
+
+ case TMR_TEMPO:
+ if (parm)
+ {
+ if (parm < 8)
+ parm = 8;
+ if (parm > 250)
+ parm = 250;
+
+ if (mpu_cmd(midi_dev, 0xE0, parm) < 0)
+ printk("MPU: Can't set tempo to %d\n", (int) parm);
+ curr_tempo = parm;
+ }
+ break;
+
+ case TMR_ECHO:
+ seq_copy_to_input(event, 8);
+ break;
+
+ case TMR_TIMESIG:
+ if (metronome_mode) /* Metronome enabled */
+ {
+ metronome_mode = parm;
+ setup_metronome(midi_dev);
+ }
+ break;
+
+ default:;
+ }
- return TIMER_NOT_ARMED;
+ return TIMER_NOT_ARMED;
}
static unsigned long
-mpu_timer_get_time (int dev)
+mpu_timer_get_time(int dev)
{
- if (!timer_open)
- return 0;
+ if (!timer_open)
+ return 0;
- return curr_ticks;
+ return curr_ticks;
}
static int
-mpu_timer_ioctl (int dev,
- unsigned int command, caddr_t arg)
+mpu_timer_ioctl(int dev,
+ unsigned int command, caddr_t arg)
{
- int midi_dev = sound_timer_devs[dev]->devlink;
-
- switch (command)
- {
- case SNDCTL_TMR_SOURCE:
- {
- int parm;
-
- parm = *(int *) arg;
- parm &= timer_caps;
+ int midi_dev = sound_timer_devs[dev]->devlink;
- if (parm != 0)
+ switch (command)
{
- timer_mode = parm;
-
- if (timer_mode & TMR_MODE_CLS)
- mpu_cmd (midi_dev, 0x3c, 0); /* Use CLS sync */
- else if (timer_mode & TMR_MODE_SMPTE)
- mpu_cmd (midi_dev, 0x3d, 0); /* Use SMPTE sync */
+ case SNDCTL_TMR_SOURCE:
+ {
+ int parm;
+
+ parm = *(int *) arg;
+ parm &= timer_caps;
+
+ if (parm != 0)
+ {
+ timer_mode = parm;
+
+ if (timer_mode & TMR_MODE_CLS)
+ mpu_cmd(midi_dev, 0x3c, 0); /* Use CLS sync */
+ else if (timer_mode & TMR_MODE_SMPTE)
+ mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */
+ }
+ return (*(int *) arg = timer_mode);
+ }
+ break;
+
+ case SNDCTL_TMR_START:
+ mpu_start_timer(midi_dev);
+ return 0;
+ break;
+
+ case SNDCTL_TMR_STOP:
+ tmr_running = 0;
+ mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */
+ stop_metronome(midi_dev);
+ return 0;
+ break;
+
+ case SNDCTL_TMR_CONTINUE:
+ if (tmr_running)
+ return 0;
+ tmr_running = 1;
+ mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */
+ return 0;
+ break;
+
+ case SNDCTL_TMR_TIMEBASE:
+ {
+ int val;
+
+ val = *(int *) arg;
+ if (val)
+ set_timebase(midi_dev, val);
+
+ return (*(int *) arg = curr_timebase);
+ }
+ break;
+
+ case SNDCTL_TMR_TEMPO:
+ {
+ int val;
+ int ret;
+
+ val = *(int *) arg;
+
+ if (val)
+ {
+ if (val < 8)
+ val = 8;
+ if (val > 250)
+ val = 250;
+ if ((ret = mpu_cmd(midi_dev, 0xE0, val)) < 0)
+ {
+ printk("MPU: Can't set tempo to %d\n", (int) val);
+ return ret;
+ }
+ curr_tempo = val;
+ }
+ return (*(int *) arg = curr_tempo);
+ }
+ break;
+
+ case SNDCTL_SEQ_CTRLRATE:
+ {
+ int val;
+
+ val = *(int *) arg;
+ if (val != 0) /* Can't change */
+ return -EINVAL;
+
+ return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60);
+ }
+ break;
+
+ case SNDCTL_SEQ_GETTIME:
+ return (*(int *) arg = curr_ticks);
+ break;
+
+ case SNDCTL_TMR_METRONOME:
+ metronome_mode = *(int *) arg;
+ setup_metronome(midi_dev);
+ return 0;
+ break;
+
+ default:;
}
- return (*(int *) arg = timer_mode);
- }
- break;
-
- case SNDCTL_TMR_START:
- mpu_start_timer (midi_dev);
- return 0;
- break;
-
- case SNDCTL_TMR_STOP:
- tmr_running = 0;
- mpu_cmd (midi_dev, 0x01, 0); /* Send MIDI stop */
- stop_metronome (midi_dev);
- return 0;
- break;
-
- case SNDCTL_TMR_CONTINUE:
- if (tmr_running)
- return 0;
- tmr_running = 1;
- mpu_cmd (midi_dev, 0x03, 0); /* Send MIDI continue */
- return 0;
- break;
-
- case SNDCTL_TMR_TIMEBASE:
- {
- int val;
-
- val = *(int *) arg;
- if (val)
- set_timebase (midi_dev, val);
-
- return (*(int *) arg = curr_timebase);
- }
- break;
-
- case SNDCTL_TMR_TEMPO:
- {
- int val;
- int ret;
-
- val = *(int *) arg;
-
- if (val)
- {
- if (val < 8)
- val = 8;
- if (val > 250)
- val = 250;
- if ((ret = mpu_cmd (midi_dev, 0xE0, val)) < 0)
- {
- printk ("MPU: Can't set tempo to %d\n", (int) val);
- return ret;
- }
-
- curr_tempo = val;
- }
-
- return (*(int *) arg = curr_tempo);
- }
- break;
-
- case SNDCTL_SEQ_CTRLRATE:
- {
- int val;
-
- val = *(int *) arg;
- if (val != 0) /* Can't change */
- return -EINVAL;
-
- return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60);
- }
- break;
-
- case SNDCTL_SEQ_GETTIME:
- return (*(int *) arg = curr_ticks);
- break;
-
- case SNDCTL_TMR_METRONOME:
- metronome_mode = *(int *) arg;
- setup_metronome (midi_dev);
- return 0;
- break;
-
- default:;
- }
-
- return -EINVAL;
+ return -EINVAL;
}
static void
-mpu_timer_arm (int dev, long time)
+mpu_timer_arm(int dev, long time)
{
- if (time < 0)
- time = curr_ticks + 1;
- else if (time <= curr_ticks) /* It's the time */
- return;
+ if (time < 0)
+ time = curr_ticks + 1;
+ else if (time <= curr_ticks) /* It's the time */
+ return;
- next_event_time = prev_event_time = time;
+ next_event_time = prev_event_time = time;
- return;
+ return;
}
static struct sound_timer_operations mpu_timer =
{
- {"MPU-401 Timer", 0},
- 10, /* Priority */
- 0, /* Local device link */
- mpu_timer_open,
- mpu_timer_close,
- mpu_timer_event,
- mpu_timer_get_time,
- mpu_timer_ioctl,
- mpu_timer_arm
+ {"MPU-401 Timer", 0},
+ 10, /* Priority */
+ 0, /* Local device link */
+ mpu_timer_open,
+ mpu_timer_close,
+ mpu_timer_event,
+ mpu_timer_get_time,
+ mpu_timer_ioctl,
+ mpu_timer_arm
};
static void
-mpu_timer_interrupt (void)
+mpu_timer_interrupt(void)
{
- if (!timer_open)
- return;
-
- if (!tmr_running)
- return;
+ if (!timer_open)
+ return;
- curr_clocks++;
- curr_ticks = clocks2ticks (curr_clocks);
+ if (!tmr_running)
+ return;
- if (curr_ticks >= next_event_time)
- {
- next_event_time = (unsigned long) -1;
- sequencer_timer (0);
- }
-}
-
-static void
-timer_ext_event (struct mpu_config *devc, int event, int parm)
-{
- int midi_dev = devc->devno;
+ curr_clocks++;
+ curr_ticks = clocks2ticks(curr_clocks);
- if (!devc->timer_flag)
- return;
-
- switch (event)
- {
- case TMR_CLOCK:
- printk ("<MIDI clk>");
- break;
-
- case TMR_START:
- printk ("Ext MIDI start\n");
- if (!tmr_running)
- if (timer_mode & TMR_EXTERNAL)
+ if (curr_ticks >= next_event_time)
{
- tmr_running = 1;
- setup_metronome (midi_dev);
- next_event_time = 0;
- STORE (SEQ_START_TIMER ());
+ next_event_time = (unsigned long) -1;
+ sequencer_timer(0);
}
- break;
+}
- case TMR_STOP:
- printk ("Ext MIDI stop\n");
- if (timer_mode & TMR_EXTERNAL)
- {
- tmr_running = 0;
- stop_metronome (midi_dev);
- STORE (SEQ_STOP_TIMER ());
- }
- break;
+static void timer_ext_event(struct mpu_config *devc, int event, int parm)
+{
+ int midi_dev = devc->devno;
- case TMR_CONTINUE:
- printk ("Ext MIDI continue\n");
- if (timer_mode & TMR_EXTERNAL)
- {
- tmr_running = 1;
- setup_metronome (midi_dev);
- STORE (SEQ_CONTINUE_TIMER ());
- }
- break;
+ if (!devc->timer_flag)
+ return;
- case TMR_SPP:
- printk ("Songpos: %d\n", parm);
- if (timer_mode & TMR_EXTERNAL)
+ switch (event)
{
- STORE (SEQ_SONGPOS (parm));
+ case TMR_CLOCK:
+ printk("<MIDI clk>");
+ break;
+
+ case TMR_START:
+ printk("Ext MIDI start\n");
+ if (!tmr_running)
+ if (timer_mode & TMR_EXTERNAL)
+ {
+ tmr_running = 1;
+ setup_metronome(midi_dev);
+ next_event_time = 0;
+ STORE(SEQ_START_TIMER());
+ }
+ break;
+
+ case TMR_STOP:
+ printk("Ext MIDI stop\n");
+ if (timer_mode & TMR_EXTERNAL)
+ {
+ tmr_running = 0;
+ stop_metronome(midi_dev);
+ STORE(SEQ_STOP_TIMER());
+ }
+ break;
+
+ case TMR_CONTINUE:
+ printk("Ext MIDI continue\n");
+ if (timer_mode & TMR_EXTERNAL)
+ {
+ tmr_running = 1;
+ setup_metronome(midi_dev);
+ STORE(SEQ_CONTINUE_TIMER());
+ }
+ break;
+
+ case TMR_SPP:
+ printk("Songpos: %d\n", parm);
+ if (timer_mode & TMR_EXTERNAL)
+ {
+ STORE(SEQ_SONGPOS(parm));
+ }
+ break;
}
- break;
- }
}
-static void
-mpu_timer_init (int midi_dev)
+static int mpu_timer_init(int midi_dev)
{
- struct mpu_config *devc;
- int n;
+ struct mpu_config *devc;
+ int n;
- devc = &dev_conf[midi_dev];
+ devc = &dev_conf[midi_dev];
- if (timer_initialized)
- return; /* There is already a similar timer */
+ if (timer_initialized)
+ return -1; /* There is already a similar timer */
- timer_initialized = 1;
+ timer_initialized = 1;
- mpu_timer.devlink = midi_dev;
- dev_conf[midi_dev].timer_flag = 1;
+ mpu_timer.devlink = midi_dev;
+ dev_conf[midi_dev].timer_flag = 1;
- if (num_sound_timers >= MAX_TIMER_DEV)
- n = 0; /* Overwrite the system timer */
- else
- n = num_sound_timers++;
- sound_timer_devs[n] = &mpu_timer;
+ n = sound_alloc_timerdev();
+ if (n == -1)
+ n = 0;
+ sound_timer_devs[n] = &mpu_timer;
- if (devc->version < 0x20) /* Original MPU-401 */
- timer_caps = TMR_INTERNAL | TMR_EXTERNAL | TMR_MODE_FSK | TMR_MODE_MIDI;
- else
- {
- /*
- * The version number 2.0 is used (at least) by the
- * MusicQuest cards and the Roland Super-MPU.
- *
- * MusicQuest has given a special meaning to the bits of the
- * revision number. The Super-MPU returns 0.
- */
+ if (devc->version < 0x20) /* Original MPU-401 */
+ timer_caps = TMR_INTERNAL | TMR_EXTERNAL | TMR_MODE_FSK | TMR_MODE_MIDI;
+ else
+ {
+ /*
+ * The version number 2.0 is used (at least) by the
+ * MusicQuest cards and the Roland Super-MPU.
+ *
+ * MusicQuest has given a special meaning to the bits of the
+ * revision number. The Super-MPU returns 0.
+ */
- if (devc->revision)
- timer_caps |= TMR_EXTERNAL | TMR_MODE_MIDI;
+ if (devc->revision)
+ timer_caps |= TMR_EXTERNAL | TMR_MODE_MIDI;
- if (devc->revision & 0x02)
- timer_caps |= TMR_MODE_CLS;
+ if (devc->revision & 0x02)
+ timer_caps |= TMR_MODE_CLS;
- if (devc->revision & 0x40)
- max_timebase = 10; /* Has the 216 and 240 ppqn modes */
- }
+ if (devc->revision & 0x40)
+ max_timebase = 10; /* Has the 216 and 240 ppqn modes */
+ }
- timer_mode = (TMR_INTERNAL | TMR_MODE_MIDI) & timer_caps;
+ timer_mode = (TMR_INTERNAL | TMR_MODE_MIDI) & timer_caps;
+ return n;
}
#endif
+EXPORT_SYMBOL(probe_mpu401);
+EXPORT_SYMBOL(attach_mpu401);
+EXPORT_SYMBOL(unload_mpu401);
+EXPORT_SYMBOL(mpuintr);
+
+#ifdef MODULE
+
+MODULE_PARM(irq, "i");
+MODULE_PARM(io, "i");
+
+int io = -1;
+int irq = -1;
+struct address_info hw;
+
+int init_module(void)
+{
+ /* Can be loaded either for module use or to provide functions
+ to others */
+ if (io != -1 && irq != -1)
+ {
+ hw.irq = irq;
+ hw.io_base = io;
+ if (probe_mpu401(&hw) == 0)
+ return -ENODEV;
+ attach_mpu401(&hw);
+ }
+ SOUND_LOCK;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ if (io != -1 && irq != -1)
+ {
+ unload_mpu401(&hw);
+ }
+ /* FREE SYMTAB */
+ SOUND_LOCK_END;
+}
+#else
+
+void
+export_mpu401_syms(void)
+{
+ register_symtab(&mpu401_syms);
+}
+
+
+#endif
#endif
diff --git a/drivers/sound/opl3.c b/drivers/sound/opl3.c
index 5c71dc585..beb146710 100644
--- a/drivers/sound/opl3.c
+++ b/drivers/sound/opl3.c
@@ -11,7 +11,7 @@
* for more info.
*/
#include <linux/config.h>
-
+#include <linux/module.h>
/*
* Major improvements to the FM handling 30AUG92 by Rob Hooft,
@@ -21,8 +21,9 @@
*/
#include "sound_config.h"
+#include "soundmodule.h"
-#if defined(CONFIG_YM3812)
+#if defined(CONFIG_YM3812) || defined(MODULE)
#include "opl3.h"
@@ -30,265 +31,248 @@
#define OFFS_4OP 11
struct voice_info
- {
- unsigned char keyon_byte;
- long bender;
- long bender_range;
- unsigned long orig_freq;
- unsigned long current_freq;
- int volume;
- int mode;
- int panning; /* 0xffff means not set */
- };
+{
+ unsigned char keyon_byte;
+ long bender;
+ long bender_range;
+ unsigned long orig_freq;
+ unsigned long current_freq;
+ int volume;
+ int mode;
+ int panning; /* 0xffff means not set */
+};
typedef struct opl_devinfo
- {
- int base;
- int left_io, right_io;
- int nr_voice;
- int lv_map[MAX_VOICE];
+{
+ int base;
+ int left_io, right_io;
+ int nr_voice;
+ int lv_map[MAX_VOICE];
- struct voice_info voc[MAX_VOICE];
- struct voice_alloc_info *v_alloc;
- struct channel_info *chn_info;
+ struct voice_info voc[MAX_VOICE];
+ struct voice_alloc_info *v_alloc;
+ struct channel_info *chn_info;
- struct sbi_instrument i_map[SBFM_MAXINSTR];
- struct sbi_instrument *act_i[MAX_VOICE];
+ struct sbi_instrument i_map[SBFM_MAXINSTR];
+ struct sbi_instrument *act_i[MAX_VOICE];
- struct synth_info fm_info;
+ struct synth_info fm_info;
- int busy;
- int model;
- unsigned char cmask;
+ int busy;
+ int model;
+ unsigned char cmask;
- int is_opl4;
- int *osp;
- }
-opl_devinfo;
+ int is_opl4;
+ int *osp;
+} opl_devinfo;
static struct opl_devinfo *devc = NULL;
static int detected_model;
-static int store_instr (int instr_no, struct sbi_instrument *instr);
-static void freq_to_fnum (int freq, int *block, int *fnum);
-static void opl3_command (int io_addr, unsigned int addr, unsigned int val);
-static int opl3_kill_note (int dev, int voice, int note, int velocity);
+static int store_instr(int instr_no, struct sbi_instrument *instr);
+static void freq_to_fnum(int freq, int *block, int *fnum);
+static void opl3_command(int io_addr, unsigned int addr, unsigned int val);
+static int opl3_kill_note(int dev, int voice, int note, int velocity);
-static void
-enter_4op_mode (void)
+static void enter_4op_mode(void)
{
- int i;
- static int v4op[MAX_VOICE] =
- {0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17};
-
- devc->cmask = 0x3f; /* Connect all possible 4 OP voice operators */
- opl3_command (devc->right_io, CONNECTION_SELECT_REGISTER, 0x3f);
-
- for (i = 0; i < 3; i++)
- pv_map[i].voice_mode = 4;
- for (i = 3; i < 6; i++)
- pv_map[i].voice_mode = 0;
-
- for (i = 9; i < 12; i++)
- pv_map[i].voice_mode = 4;
- for (i = 12; i < 15; i++)
- pv_map[i].voice_mode = 0;
-
- for (i = 0; i < 12; i++)
- devc->lv_map[i] = v4op[i];
- devc->v_alloc->max_voice = devc->nr_voice = 12;
+ int i;
+ static int v4op[MAX_VOICE] = {
+ 0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17
+ };
+
+ devc->cmask = 0x3f; /* Connect all possible 4 OP voice operators */
+ opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x3f);
+
+ for (i = 0; i < 3; i++)
+ pv_map[i].voice_mode = 4;
+ for (i = 3; i < 6; i++)
+ pv_map[i].voice_mode = 0;
+
+ for (i = 9; i < 12; i++)
+ pv_map[i].voice_mode = 4;
+ for (i = 12; i < 15; i++)
+ pv_map[i].voice_mode = 0;
+
+ for (i = 0; i < 12; i++)
+ devc->lv_map[i] = v4op[i];
+ devc->v_alloc->max_voice = devc->nr_voice = 12;
}
-static int
-opl3_ioctl (int dev,
- unsigned int cmd, caddr_t arg)
+static int opl3_ioctl(int dev, unsigned int cmd, caddr_t arg)
{
- switch (cmd)
- {
-
- case SNDCTL_FM_LOAD_INSTR:
- {
- struct sbi_instrument ins;
-
- memcpy ((char *) &ins, (&((char *) arg)[0]), sizeof (ins));
- printk("Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n");
-
- if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
- {
- printk ("FM Error: Invalid instrument number %d\n", ins.channel);
- return -EINVAL;
- }
-
- return store_instr (ins.channel, &ins);
- }
- break;
-
- case SNDCTL_SYNTH_INFO:
- devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice;
-
- memcpy ((&((char *) arg)[0]), (char *) &devc->fm_info, sizeof (devc->fm_info));
- return 0;
- break;
-
- case SNDCTL_SYNTH_MEMAVL:
- return 0x7fffffff;
- break;
-
- case SNDCTL_FM_4OP_ENABLE:
- if (devc->model == 2)
- enter_4op_mode ();
- return 0;
- break;
-
- default:
- return -EINVAL;
- }
+ switch (cmd)
+ {
+ case SNDCTL_FM_LOAD_INSTR:
+ {
+ struct sbi_instrument ins;
+
+ printk(KERN_WARNING "Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n");
+ memcpy((char *) &ins, (&((char *) arg)[0]), sizeof(ins));
+
+ if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
+ {
+ printk("FM Error: Invalid instrument number %d\n", ins.channel);
+ return -EINVAL;
+ }
+ return store_instr(ins.channel, &ins);
+ }
+
+ case SNDCTL_SYNTH_INFO:
+ devc->fm_info.nr_voices =
+ (devc->nr_voice == 12) ? 6 : devc->nr_voice;
+ memcpy((&((char *) arg)[0]), (char *) &devc->fm_info, sizeof(devc->fm_info));
+ return 0;
+
+ case SNDCTL_SYNTH_MEMAVL:
+ return 0x7fffffff;
+
+ case SNDCTL_FM_4OP_ENABLE:
+ if (devc->model == 2)
+ enter_4op_mode();
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
}
-int
-opl3_detect (int ioaddr, int *osp)
+int opl3_detect(int ioaddr, int *osp)
{
- /*
- * This function returns 1 if the FM chip is present at the given I/O port
- * The detection algorithm plays with the timer built in the FM chip and
- * looks for a change in the status register.
- *
- * Note! The timers of the FM chip are not connected to AdLib (and compatible)
- * boards.
- *
- * Note2! The chip is initialized if detected.
- */
-
- unsigned char stat1, signature;
- int i;
-
- if (devc != NULL)
- return 0;
-
-
- devc = (struct opl_devinfo *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (*devc)));
- sound_mem_sizes[sound_nblocks] = sizeof (*devc);
- if (sound_nblocks < 1024)
- sound_nblocks++;;
-
- if (devc == NULL)
- {
- printk ("OPL3: Can't allocate memory for the device control structure\n");
- return 0;
- }
-
- devc->osp = osp;
- devc->base = ioaddr;
-
- /* Reset timers 1 and 2 */
- opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK);
-
- /* Reset the IRQ of the FM chip */
- opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET);
-
- signature = stat1 = inb (ioaddr); /* Status register */
-
- if (signature != 0x00 && signature != 0x06 && signature != 0x02 &&
- signature != 0x0f)
- {
- DDB (printk ("OPL3 not detected %x\n", signature));
- return 0;
- }
-
- if (signature == 0x06) /* OPL2 */
- {
- detected_model = 2;
- }
- else if (signature == 0x00 || signature == 0x0f) /* OPL3 or OPL4 */
- {
- unsigned char tmp;
-
- detected_model = 3;
-
- /*
- * Detect availability of OPL4 (_experimental_). Works probably
- * only after a cold boot. In addition the OPL4 port
- * of the chip may not be connected to the PC bus at all.
- */
-
- opl3_command (ioaddr + 2, OPL3_MODE_REGISTER, 0x00);
- opl3_command (ioaddr + 2, OPL3_MODE_REGISTER, OPL3_ENABLE | OPL4_ENABLE);
-
- if ((tmp = inb (ioaddr)) == 0x02) /* Have a OPL4 */
+ /*
+ * This function returns 1 if the FM chip is present at the given I/O port
+ * The detection algorithm plays with the timer built in the FM chip and
+ * looks for a change in the status register.
+ *
+ * Note! The timers of the FM chip are not connected to AdLib (and compatible)
+ * boards.
+ *
+ * Note2! The chip is initialized if detected.
+ */
+
+ unsigned char stat1, signature;
+ int i;
+
+ if (devc != NULL)
{
- detected_model = 4;
+ printk(KERN_ERR "opl3: Only one OPL3 supported.\n");
+ return 0;
}
- if (!check_region (ioaddr - 8, 2)) /* OPL4 port is free */
+ devc = (struct opl_devinfo *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(*devc)));
+ sound_mem_sizes[sound_nblocks] = sizeof(*devc);
+ if (sound_nblocks < 1024)
+ sound_nblocks++;;
+
+ if (devc == NULL)
{
- int tmp;
-
- outb ((0x02), ioaddr - 8); /* Select OPL4 ID register */
- tenmicrosec (devc->osp);
- tmp = inb (ioaddr - 7); /* Read it */
- tenmicrosec (devc->osp);
-
- if (tmp == 0x20) /* OPL4 should return 0x20 here */
- {
- detected_model = 4;
-
- outb ((0xF8), ioaddr - 8); /* Select OPL4 FM mixer control */
- tenmicrosec (devc->osp);
- outb ((0x1B), ioaddr - 7); /* Write value */
- tenmicrosec (devc->osp);
- }
- else
- detected_model = 3;
+ printk(KERN_ERR "OPL3: Can't allocate memory for the device control "
+ "structure \n ");
+ return 0;
}
+ devc->osp = osp;
+ devc->base = ioaddr;
- opl3_command (ioaddr + 2, OPL3_MODE_REGISTER, 0);
+ /* Reset timers 1 and 2 */
+ opl3_command(ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK);
- }
+ /* Reset the IRQ of the FM chip */
+ opl3_command(ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET);
- for (i = 0; i < 9; i++)
- opl3_command (ioaddr, KEYON_BLOCK + i, 0); /*
- * Note off
- */
+ signature = stat1 = inb(ioaddr); /* Status register */
- opl3_command (ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT);
- opl3_command (ioaddr, PERCOSSION_REGISTER, 0x00); /*
- * Melodic mode.
- */
+ if (signature != 0x00 && signature != 0x06 && signature != 0x02 &&
+ signature != 0x0f)
+ {
+ MDB(printk(KERN_INFO "OPL3 not detected %x\n", signature));
+ return 0;
+ }
+
+ if (signature == 0x06) /* OPL2 */
+ {
+ detected_model = 2;
+ }
+ else if (signature == 0x00 || signature == 0x0f) /* OPL3 or OPL4 */
+ {
+ unsigned char tmp;
+
+ detected_model = 3;
+
+ /*
+ * Detect availability of OPL4 (_experimental_). Works probably
+ * only after a cold boot. In addition the OPL4 port
+ * of the chip may not be connected to the PC bus at all.
+ */
+
+ opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0x00);
+ opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, OPL3_ENABLE | OPL4_ENABLE);
+
+ if ((tmp = inb(ioaddr)) == 0x02) /* Have a OPL4 */
+ {
+ detected_model = 4;
+ }
+
+ if (!check_region(ioaddr - 8, 2)) /* OPL4 port is free */
+ {
+ int tmp;
+
+ outb((0x02), ioaddr - 8); /* Select OPL4 ID register */
+ tenmicrosec(devc->osp);
+ tmp = inb(ioaddr - 7); /* Read it */
+ tenmicrosec(devc->osp);
+
+ if (tmp == 0x20) /* OPL4 should return 0x20 here */
+ {
+ detected_model = 4;
+ outb((0xF8), ioaddr - 8); /* Select OPL4 FM mixer control */
+ tenmicrosec(devc->osp);
+ outb((0x1B), ioaddr - 7); /* Write value */
+ tenmicrosec(devc->osp);
+ }
+ else
+ detected_model = 3;
+ }
+ opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0);
+ }
+ for (i = 0; i < 9; i++)
+ opl3_command(ioaddr, KEYON_BLOCK + i, 0); /*
+ * Note off
+ */
- return 1;
+ opl3_command(ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT);
+ opl3_command(ioaddr, PERCOSSION_REGISTER, 0x00); /*
+ * Melodic mode.
+ */
+ return 1;
}
-static int
-opl3_kill_note (int devno, int voice, int note, int velocity)
+static int opl3_kill_note (int devno, int voice, int note, int velocity)
{
- struct physical_voice_info *map;
-
- if (voice < 0 || voice >= devc->nr_voice)
- return 0;
+ struct physical_voice_info *map;
- devc->v_alloc->map[voice] = 0;
+ if (voice < 0 || voice >= devc->nr_voice)
+ return 0;
- map = &pv_map[devc->lv_map[voice]];
+ devc->v_alloc->map[voice] = 0;
- DEB (printk ("Kill note %d\n", voice));
+ map = &pv_map[devc->lv_map[voice]];
+ DEB(printk("Kill note %d\n", voice));
- if (map->voice_mode == 0)
- return 0;
+ if (map->voice_mode == 0)
+ return 0;
- opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, devc->voc[voice].keyon_byte & ~0x20);
-
- devc->voc[voice].keyon_byte = 0;
- devc->voc[voice].bender = 0;
- devc->voc[voice].volume = 64;
- devc->voc[voice].panning = 0xffff; /* Not set */
- devc->voc[voice].bender_range = 200;
- devc->voc[voice].orig_freq = 0;
- devc->voc[voice].current_freq = 0;
- devc->voc[voice].mode = 0;
-
- return 0;
+ opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, devc->voc[voice].keyon_byte & ~0x20);
+ devc->voc[voice].keyon_byte = 0;
+ devc->voc[voice].bender = 0;
+ devc->voc[voice].volume = 64;
+ devc->voc[voice].panning = 0xffff; /* Not set */
+ devc->voc[voice].bender_range = 200;
+ devc->voc[voice].orig_freq = 0;
+ devc->voc[voice].current_freq = 0;
+ devc->voc[voice].mode = 0;
+ return 0;
}
#define HIHAT 0
@@ -299,28 +283,23 @@ opl3_kill_note (int devno, int voice, int note, int velocity)
#define UNDEFINED TOMTOM
#define DEFAULT TOMTOM
-static int
-store_instr (int instr_no, struct sbi_instrument *instr)
+static int store_instr(int instr_no, struct sbi_instrument *instr)
{
-
- if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || devc->model != 2))
- printk ("FM warning: Invalid patch format field (key) 0x%x\n", instr->key);
- memcpy ((char *) &(devc->i_map[instr_no]), (char *) instr, sizeof (*instr));
-
- return 0;
+ if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || devc->model != 2))
+ printk(KERN_WARNING "FM warning: Invalid patch format field (key) 0x%x\n", instr->key);
+ memcpy((char *) &(devc->i_map[instr_no]), (char *) instr, sizeof(*instr));
+ return 0;
}
-static int
-opl3_set_instr (int dev, int voice, int instr_no)
+static int opl3_set_instr (int dev, int voice, int instr_no)
{
- if (voice < 0 || voice >= devc->nr_voice)
- return 0;
-
- if (instr_no < 0 || instr_no >= SBFM_MAXINSTR)
- instr_no = 0; /* Acoustic piano (usually) */
+ if (voice < 0 || voice >= devc->nr_voice)
+ return 0;
+ if (instr_no < 0 || instr_no >= SBFM_MAXINSTR)
+ instr_no = 0; /* Acoustic piano (usually) */
- devc->act_i[voice] = &devc->i_map[instr_no];
- return 0;
+ devc->act_i[voice] = &devc->i_map[instr_no];
+ return 0;
}
/*
@@ -332,882 +311,911 @@ opl3_set_instr (int dev, int voice, int instr_no)
* volume -8 it was implemented as a table because it is only 128 bytes and
* it saves a lot of log() calculations. (RH)
*/
-static char fm_volume_table[128] =
-{-64, -48, -40, -35, -32, -29, -27, -26,
- -24, -23, -21, -20, -19, -18, -18, -17,
- -16, -15, -15, -14, -13, -13, -12, -12,
- -11, -11, -10, -10, -10, -9, -9, -8,
- -8, -8, -7, -7, -7, -6, -6, -6,
- -5, -5, -5, -5, -4, -4, -4, -4,
- -3, -3, -3, -3, -2, -2, -2, -2,
- -2, -1, -1, -1, -1, 0, 0, 0,
- 0, 0, 0, 1, 1, 1, 1, 1,
- 1, 2, 2, 2, 2, 2, 2, 2,
- 3, 3, 3, 3, 3, 3, 3, 4,
- 4, 4, 4, 4, 4, 4, 4, 5,
- 5, 5, 5, 5, 5, 5, 5, 5,
- 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 8, 8, 8, 8, 8};
-
-static void
-calc_vol (unsigned char *regbyte, int volume, int main_vol)
+
+static char fm_volume_table[128] =
{
- int level = (~*regbyte & 0x3f);
+ -64, -48, -40, -35, -32, -29, -27, -26,
+ -24, -23, -21, -20, -19, -18, -18, -17,
+ -16, -15, -15, -14, -13, -13, -12, -12,
+ -11, -11, -10, -10, -10, -9, -9, -8,
+ -8, -8, -7, -7, -7, -6, -6, -6,
+ -5, -5, -5, -5, -4, -4, -4, -4,
+ -3, -3, -3, -3, -2, -2, -2, -2,
+ -2, -1, -1, -1, -1, 0, 0, 0,
+ 0, 0, 0, 1, 1, 1, 1, 1,
+ 1, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 4,
+ 4, 4, 4, 4, 4, 4, 4, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 8, 8, 8, 8, 8
+};
- if (main_vol > 127)
- main_vol = 127;
+static void calc_vol(unsigned char *regbyte, int volume, int main_vol)
+{
+ int level = (~*regbyte & 0x3f);
- volume = (volume * main_vol) / 127;
+ if (main_vol > 127)
+ main_vol = 127;
+ volume = (volume * main_vol) / 127;
- if (level)
- level += fm_volume_table[volume];
+ if (level)
+ level += fm_volume_table[volume];
- if (level > 0x3f)
- level = 0x3f;
- if (level < 0)
- level = 0;
+ if (level > 0x3f)
+ level = 0x3f;
+ if (level < 0)
+ level = 0;
- *regbyte = (*regbyte & 0xc0) | (~level & 0x3f);
+ *regbyte = (*regbyte & 0xc0) | (~level & 0x3f);
}
-static void
-set_voice_volume (int voice, int volume, int main_vol)
+static void set_voice_volume(int voice, int volume, int main_vol)
{
- unsigned char vol1, vol2, vol3, vol4;
- struct sbi_instrument *instr;
- struct physical_voice_info *map;
-
- if (voice < 0 || voice >= devc->nr_voice)
- return;
+ unsigned char vol1, vol2, vol3, vol4;
+ struct sbi_instrument *instr;
+ struct physical_voice_info *map;
- map = &pv_map[devc->lv_map[voice]];
+ if (voice < 0 || voice >= devc->nr_voice)
+ return;
- instr = devc->act_i[voice];
+ map = &pv_map[devc->lv_map[voice]];
+ instr = devc->act_i[voice];
- if (!instr)
- instr = &devc->i_map[0];
+ if (!instr)
+ instr = &devc->i_map[0];
- if (instr->channel < 0)
- return;
+ if (instr->channel < 0)
+ return;
- if (devc->voc[voice].mode == 0)
- return;
+ if (devc->voc[voice].mode == 0)
+ return;
- if (devc->voc[voice].mode == 2)
- {
-
- vol1 = instr->operators[2];
- vol2 = instr->operators[3];
-
- if ((instr->operators[10] & 0x01))
+ if (devc->voc[voice].mode == 2)
{
- calc_vol (&vol1, volume, main_vol);
- calc_vol (&vol2, volume, main_vol);
+ vol1 = instr->operators[2];
+ vol2 = instr->operators[3];
+ if ((instr->operators[10] & 0x01))
+ {
+ calc_vol(&vol1, volume, main_vol);
+ calc_vol(&vol2, volume, main_vol);
+ }
+ else
+ {
+ calc_vol(&vol2, volume, main_vol);
+ }
+ opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1);
+ opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2);
}
- else
- {
- calc_vol (&vol2, volume, main_vol);
- }
-
- opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1);
- opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2);
- }
- else
- { /*
- * 4 OP voice
- */
- int connection;
-
- vol1 = instr->operators[2];
- vol2 = instr->operators[3];
- vol3 = instr->operators[OFFS_4OP + 2];
- vol4 = instr->operators[OFFS_4OP + 3];
-
- /*
- * The connection method for 4 OP devc->voc is defined by the rightmost
- * bits at the offsets 10 and 10+OFFS_4OP
- */
-
- connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
-
- switch (connection)
- {
- case 0:
- calc_vol (&vol4, volume, main_vol);
- break;
-
- case 1:
- calc_vol (&vol2, volume, main_vol);
- calc_vol (&vol4, volume, main_vol);
- break;
-
- case 2:
- calc_vol (&vol1, volume, main_vol);
- calc_vol (&vol4, volume, main_vol);
- break;
-
- case 3:
- calc_vol (&vol1, volume, main_vol);
- calc_vol (&vol3, volume, main_vol);
- calc_vol (&vol4, volume, main_vol);
- break;
-
- default:;
+ else
+ { /*
+ * 4 OP voice
+ */
+ int connection;
+
+ vol1 = instr->operators[2];
+ vol2 = instr->operators[3];
+ vol3 = instr->operators[OFFS_4OP + 2];
+ vol4 = instr->operators[OFFS_4OP + 3];
+
+ /*
+ * The connection method for 4 OP devc->voc is defined by the rightmost
+ * bits at the offsets 10 and 10+OFFS_4OP
+ */
+
+ connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
+
+ switch (connection)
+ {
+ case 0:
+ calc_vol(&vol4, volume, main_vol);
+ break;
+
+ case 1:
+ calc_vol(&vol2, volume, main_vol);
+ calc_vol(&vol4, volume, main_vol);
+ break;
+
+ case 2:
+ calc_vol(&vol1, volume, main_vol);
+ calc_vol(&vol4, volume, main_vol);
+ break;
+
+ case 3:
+ calc_vol(&vol1, volume, main_vol);
+ calc_vol(&vol3, volume, main_vol);
+ calc_vol(&vol4, volume, main_vol);
+ break;
+
+ default:
+ ;
+ }
+ opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1);
+ opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2);
+ opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], vol3);
+ opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], vol4);
}
-
- opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1);
- opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2);
- opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], vol3);
- opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], vol4);
- }
}
-static int
-opl3_start_note (int dev, int voice, int note, int volume)
+static int opl3_start_note (int dev, int voice, int note, int volume)
{
- unsigned char data, fpc;
- int block, fnum, freq, voice_mode, pan;
- struct sbi_instrument *instr;
- struct physical_voice_info *map;
+ unsigned char data, fpc;
+ int block, fnum, freq, voice_mode, pan;
+ struct sbi_instrument *instr;
+ struct physical_voice_info *map;
- if (voice < 0 || voice >= devc->nr_voice)
- return 0;
+ if (voice < 0 || voice >= devc->nr_voice)
+ return 0;
- map = &pv_map[devc->lv_map[voice]];
- pan = devc->voc[voice].panning;
+ map = &pv_map[devc->lv_map[voice]];
+ pan = devc->voc[voice].panning;
- if (map->voice_mode == 0)
- return 0;
+ if (map->voice_mode == 0)
+ return 0;
- if (note == 255) /*
+ if (note == 255) /*
* Just change the volume
*/
- {
- set_voice_volume (voice, volume, devc->voc[voice].volume);
- return 0;
- }
-
- /*
- * Kill previous note before playing
- */
- opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], 0xff); /*
- * Carrier
- * volume to
- * min
- */
- opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], 0xff); /*
- * Modulator
- * volume to
- */
+ {
+ set_voice_volume(voice, volume, devc->voc[voice].volume);
+ return 0;
+ }
+
+ /*
+ * Kill previous note before playing
+ */
+
+ opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], 0xff); /*
+ * Carrier
+ * volume to
+ * min
+ */
+ opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], 0xff); /*
+ * Modulator
+ * volume to
+ */
- if (map->voice_mode == 4)
- {
- opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], 0xff);
- opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], 0xff);
- }
+ if (map->voice_mode == 4)
+ {
+ opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], 0xff);
+ opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], 0xff);
+ }
- opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00); /*
+ opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00); /*
* Note
* off
*/
- instr = devc->act_i[voice];
+ instr = devc->act_i[voice];
+
+ if (!instr)
+ instr = &devc->i_map[0];
- if (!instr)
- instr = &devc->i_map[0];
-
- if (instr->channel < 0)
- {
- printk (
- "OPL3: Initializing voice %d with undefined instrument\n",
- voice);
- return 0;
- }
+ if (instr->channel < 0)
+ {
+ printk(KERN_WARNING "OPL3: Initializing voice %d with undefined instrument\n", voice);
+ return 0;
+ }
- if (map->voice_mode == 2 && instr->key == OPL3_PATCH)
- return 0; /*
+ if (map->voice_mode == 2 && instr->key == OPL3_PATCH)
+ return 0; /*
* Cannot play
*/
- voice_mode = map->voice_mode;
+ voice_mode = map->voice_mode;
- if (voice_mode == 4)
- {
- int voice_shift;
+ if (voice_mode == 4)
+ {
+ int voice_shift;
- voice_shift = (map->ioaddr == devc->left_io) ? 0 : 3;
- voice_shift += map->voice_num;
+ voice_shift = (map->ioaddr == devc->left_io) ? 0 : 3;
+ voice_shift += map->voice_num;
- if (instr->key != OPL3_PATCH) /*
- * Just 2 OP patch
- */
- {
- voice_mode = 2;
- devc->cmask &= ~(1 << voice_shift);
+ if (instr->key != OPL3_PATCH) /*
+ * Just 2 OP patch
+ */
+ {
+ voice_mode = 2;
+ devc->cmask &= ~(1 << voice_shift);
+ }
+ else
+ {
+ devc->cmask |= (1 << voice_shift);
+ }
+
+ opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask);
}
- else
+
+ /*
+ * Set Sound Characteristics
+ */
+
+ opl3_command(map->ioaddr, AM_VIB + map->op[0], instr->operators[0]);
+ opl3_command(map->ioaddr, AM_VIB + map->op[1], instr->operators[1]);
+
+ /*
+ * Set Attack/Decay
+ */
+
+ opl3_command(map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]);
+ opl3_command(map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]);
+
+ /*
+ * Set Sustain/Release
+ */
+
+ opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]);
+ opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]);
+
+ /*
+ * Set Wave Select
+ */
+
+ opl3_command(map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]);
+ opl3_command(map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]);
+
+ /*
+ * Set Feedback/Connection
+ */
+
+ fpc = instr->operators[10];
+
+ if (pan != 0xffff)
{
- devc->cmask |= (1 << voice_shift);
+ fpc &= ~STEREO_BITS;
+ if (pan < -64)
+ fpc |= VOICE_TO_LEFT;
+ else
+ if (pan > 64)
+ fpc |= VOICE_TO_RIGHT;
+ else
+ fpc |= (VOICE_TO_LEFT | VOICE_TO_RIGHT);
}
- opl3_command (devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask);
- }
-
- /*
- * Set Sound Characteristics
- */
- opl3_command (map->ioaddr, AM_VIB + map->op[0], instr->operators[0]);
- opl3_command (map->ioaddr, AM_VIB + map->op[1], instr->operators[1]);
-
- /*
- * Set Attack/Decay
- */
- opl3_command (map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]);
- opl3_command (map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]);
-
- /*
- * Set Sustain/Release
- */
- opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]);
- opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]);
-
- /*
- * Set Wave Select
- */
- opl3_command (map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]);
- opl3_command (map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]);
-
- /*
- * Set Feedback/Connection
- */
- fpc = instr->operators[10];
-
- if (pan != 0xffff)
- {
- fpc &= ~STEREO_BITS;
-
- if (pan < -64)
- fpc |= VOICE_TO_LEFT;
- else if (pan > 64)
- fpc |= VOICE_TO_RIGHT;
- else
- fpc |= (VOICE_TO_LEFT | VOICE_TO_RIGHT);
- }
-
- if (!(fpc & 0x30))
- fpc |= 0x30; /*
- * Ensure that at least one chn is enabled
- */
- opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num,
- fpc);
-
- /*
- * If the voice is a 4 OP one, initialize the operators 3 and 4 also
- */
-
- if (voice_mode == 4)
- {
-
- /*
- * Set Sound Characteristics
- */
- opl3_command (map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]);
- opl3_command (map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]);
-
- /*
- * Set Attack/Decay
- */
- opl3_command (map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]);
- opl3_command (map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]);
-
- /*
- * Set Sustain/Release
- */
- opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]);
- opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]);
-
- /*
- * Set Wave Select
- */
- opl3_command (map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]);
- opl3_command (map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]);
-
- /*
- * Set Feedback/Connection
- */
- fpc = instr->operators[OFFS_4OP + 10];
- if (!(fpc & 0x30))
- fpc |= 0x30; /*
+ if (!(fpc & 0x30))
+ fpc |= 0x30; /*
* Ensure that at least one chn is enabled
*/
- opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc);
- }
+ opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num, fpc);
- devc->voc[voice].mode = voice_mode;
+ /*
+ * If the voice is a 4 OP one, initialize the operators 3 and 4 also
+ */
- set_voice_volume (voice, volume, devc->voc[voice].volume);
+ if (voice_mode == 4)
+ {
+ /*
+ * Set Sound Characteristics
+ */
+
+ opl3_command(map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]);
+ opl3_command(map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]);
+
+ /*
+ * Set Attack/Decay
+ */
+
+ opl3_command(map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]);
+ opl3_command(map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]);
+
+ /*
+ * Set Sustain/Release
+ */
+
+ opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]);
+ opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]);
+
+ /*
+ * Set Wave Select
+ */
+
+ opl3_command(map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]);
+ opl3_command(map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]);
+
+ /*
+ * Set Feedback/Connection
+ */
+
+ fpc = instr->operators[OFFS_4OP + 10];
+ if (!(fpc & 0x30))
+ fpc |= 0x30; /*
+ * Ensure that at least one chn is enabled
+ */
+ opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc);
+ }
+
+ devc->voc[voice].mode = voice_mode;
+ set_voice_volume(voice, volume, devc->voc[voice].volume);
- freq = devc->voc[voice].orig_freq = note_to_freq (note) / 1000;
+ freq = devc->voc[voice].orig_freq = note_to_freq(note) / 1000;
- /*
- * Since the pitch bender may have been set before playing the note, we
- * have to calculate the bending now.
- */
+ /*
+ * Since the pitch bender may have been set before playing the note, we
+ * have to calculate the bending now.
+ */
- freq = compute_finetune (devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0);
- devc->voc[voice].current_freq = freq;
+ freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0);
+ devc->voc[voice].current_freq = freq;
- freq_to_fnum (freq, &block, &fnum);
+ freq_to_fnum(freq, &block, &fnum);
- /*
- * Play note
- */
+ /*
+ * Play note
+ */
- data = fnum & 0xff; /*
+ data = fnum & 0xff; /*
* Least significant bits of fnumber
*/
- opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data);
+ opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data);
- data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
- devc->voc[voice].keyon_byte = data;
- opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data);
- if (voice_mode == 4)
- opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data);
+ data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
+ devc->voc[voice].keyon_byte = data;
+ opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data);
+ if (voice_mode == 4)
+ opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data);
- return 0;
+ return 0;
}
-static void
-freq_to_fnum (int freq, int *block, int *fnum)
+static void freq_to_fnum (int freq, int *block, int *fnum)
{
- int f, octave;
+ int f, octave;
- /*
- * Converts the note frequency to block and fnum values for the FM chip
- */
- /*
- * First try to compute the block -value (octave) where the note belongs
- */
+ /*
+ * Converts the note frequency to block and fnum values for the FM chip
+ */
+ /*
+ * First try to compute the block -value (octave) where the note belongs
+ */
- f = freq;
+ f = freq;
- octave = 5;
+ octave = 5;
- if (f == 0)
- octave = 0;
- else if (f < 261)
- {
- while (f < 261)
+ if (f == 0)
+ octave = 0;
+ else if (f < 261)
{
- octave--;
- f <<= 1;
+ while (f < 261)
+ {
+ octave--;
+ f <<= 1;
+ }
}
- }
- else if (f > 493)
- {
- while (f > 493)
+ else if (f > 493)
{
- octave++;
- f >>= 1;
+ while (f > 493)
+ {
+ octave++;
+ f >>= 1;
+ }
}
- }
- if (octave > 7)
- octave = 7;
+ if (octave > 7)
+ octave = 7;
- *fnum = freq * (1 << (20 - octave)) / 49716;
- *block = octave;
+ *fnum = freq * (1 << (20 - octave)) / 49716;
+ *block = octave;
}
-static void
-opl3_command (int io_addr, unsigned int addr, unsigned int val)
+static void opl3_command (int io_addr, unsigned int addr, unsigned int val)
{
- int i;
-
- /*
- * The original 2-OP synth requires a quite long delay after writing to a
- * register. The OPL-3 survives with just two INBs
- */
-
- outb (((unsigned char) (addr & 0xff)), io_addr);
-
- if (devc->model != 2)
- tenmicrosec (devc->osp);
- else
- for (i = 0; i < 2; i++)
- inb (io_addr);
-
- outb (((unsigned char) (val & 0xff)), io_addr + 1);
-
- if (devc->model != 2)
- {
- tenmicrosec (devc->osp);
- tenmicrosec (devc->osp);
- tenmicrosec (devc->osp);
- }
- else
- for (i = 0; i < 2; i++)
- inb (io_addr);
-}
+ int i;
-static void
-opl3_reset (int devno)
-{
- int i;
+ /*
+ * The original 2-OP synth requires a quite long delay after writing to a
+ * register. The OPL-3 survives with just two INBs
+ */
- for (i = 0; i < 18; i++)
- devc->lv_map[i] = i;
+ outb(((unsigned char) (addr & 0xff)), io_addr);
- for (i = 0; i < devc->nr_voice; i++)
- {
- opl3_command (pv_map[devc->lv_map[i]].ioaddr,
- KSL_LEVEL + pv_map[devc->lv_map[i]].op[0], 0xff);
+ if (devc->model != 2)
+ tenmicrosec(devc->osp);
+ else
+ for (i = 0; i < 2; i++)
+ inb(io_addr);
- opl3_command (pv_map[devc->lv_map[i]].ioaddr,
- KSL_LEVEL + pv_map[devc->lv_map[i]].op[1], 0xff);
+ outb(((unsigned char) (val & 0xff)), io_addr + 1);
- if (pv_map[devc->lv_map[i]].voice_mode == 4)
+ if (devc->model != 2)
{
- opl3_command (pv_map[devc->lv_map[i]].ioaddr,
- KSL_LEVEL + pv_map[devc->lv_map[i]].op[2], 0xff);
-
- opl3_command (pv_map[devc->lv_map[i]].ioaddr,
- KSL_LEVEL + pv_map[devc->lv_map[i]].op[3], 0xff);
+ tenmicrosec(devc->osp);
+ tenmicrosec(devc->osp);
+ tenmicrosec(devc->osp);
}
+ else
+ for (i = 0; i < 2; i++)
+ inb(io_addr);
+}
+
+static void opl3_reset(int devno)
+{
+ int i;
+
+ for (i = 0; i < 18; i++)
+ devc->lv_map[i] = i;
+
+ for (i = 0; i < devc->nr_voice; i++)
+ {
+ opl3_command(pv_map[devc->lv_map[i]].ioaddr,
+ KSL_LEVEL + pv_map[devc->lv_map[i]].op[0], 0xff);
+
+ opl3_command(pv_map[devc->lv_map[i]].ioaddr,
+ KSL_LEVEL + pv_map[devc->lv_map[i]].op[1], 0xff);
- opl3_kill_note (devno, i, 0, 64);
- }
+ if (pv_map[devc->lv_map[i]].voice_mode == 4)
+ {
+ opl3_command(pv_map[devc->lv_map[i]].ioaddr,
+ KSL_LEVEL + pv_map[devc->lv_map[i]].op[2], 0xff);
- if (devc->model == 2)
- {
- devc->v_alloc->max_voice = devc->nr_voice = 18;
+ opl3_command(pv_map[devc->lv_map[i]].ioaddr,
+ KSL_LEVEL + pv_map[devc->lv_map[i]].op[3], 0xff);
+ }
- for (i = 0; i < 18; i++)
- pv_map[i].voice_mode = 2;
+ opl3_kill_note(devno, i, 0, 64);
+ }
+
+ if (devc->model == 2)
+ {
+ devc->v_alloc->max_voice = devc->nr_voice = 18;
- }
+ for (i = 0; i < 18; i++)
+ pv_map[i].voice_mode = 2;
+ }
}
-static int
-opl3_open (int dev, int mode)
+static int opl3_open(int dev, int mode)
{
- int i;
+ int i;
- if (devc->busy)
- return -EBUSY;
- devc->busy = 1;
+ if (devc->busy)
+ return -EBUSY;
+ MOD_INC_USE_COUNT;
+ devc->busy = 1;
- devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9;
- devc->v_alloc->timestamp = 0;
+ devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9;
+ devc->v_alloc->timestamp = 0;
- for (i = 0; i < 18; i++)
- {
- devc->v_alloc->map[i] = 0;
- devc->v_alloc->alloc_times[i] = 0;
- }
+ for (i = 0; i < 18; i++)
+ {
+ devc->v_alloc->map[i] = 0;
+ devc->v_alloc->alloc_times[i] = 0;
+ }
- devc->cmask = 0x00; /*
+ devc->cmask = 0x00; /*
* Just 2 OP mode
*/
- if (devc->model == 2)
- opl3_command (devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask);
- return 0;
+ if (devc->model == 2)
+ opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask);
+ return 0;
}
-static void
-opl3_close (int dev)
+static void opl3_close(int dev)
{
- devc->busy = 0;
- devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9;
+ devc->busy = 0;
+ devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9;
- devc->fm_info.nr_drums = 0;
- devc->fm_info.perc_mode = 0;
+ devc->fm_info.nr_drums = 0;
+ devc->fm_info.perc_mode = 0;
- opl3_reset (dev);
+ opl3_reset(dev);
+ MOD_DEC_USE_COUNT;
}
-static void
-opl3_hw_control (int dev, unsigned char *event)
+static void opl3_hw_control(int dev, unsigned char *event)
{
}
-static int
-opl3_load_patch (int dev, int format, const char *addr,
- int offs, int count, int pmgr_flag)
+static int opl3_load_patch(int dev, int format, const char *addr,
+ int offs, int count, int pmgr_flag)
{
- struct sbi_instrument ins;
+ struct sbi_instrument ins;
- if (count < sizeof (ins))
- {
- printk ("FM Error: Patch record too short\n");
- return -EINVAL;
- }
+ if (count <sizeof(ins))
+ {
+ printk(KERN_WARNING "FM Error: Patch record too short\n");
+ return -EINVAL;
+ }
- copy_from_user (&((char *) &ins)[offs], &(addr)[offs], sizeof (ins) - offs);
+ copy_from_user(&((char *) &ins)[offs], &(addr)[offs], sizeof(ins) - offs);
- if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
- {
- printk ("FM Error: Invalid instrument number %d\n", ins.channel);
- return -EINVAL;
- }
- ins.key = format;
+ if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
+ {
+ printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel);
+ return -EINVAL;
+ }
+ ins.key = format;
- return store_instr (ins.channel, &ins);
+ return store_instr(ins.channel, &ins);
}
-static void
-opl3_panning (int dev, int voice, int value)
+static void opl3_panning(int dev, int voice, int value)
{
- devc->voc[voice].panning = value;
+ devc->voc[voice].panning = value;
}
-static void
-opl3_volume_method (int dev, int mode)
+static void opl3_volume_method(int dev, int mode)
{
}
#define SET_VIBRATO(cell) { \
- tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \
- if (pressure > 110) \
- tmp |= 0x40; /* Vibrato on */ \
- opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);}
+ tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \
+ if (pressure > 110) \
+ tmp |= 0x40; /* Vibrato on */ \
+ opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);}
-static void
-opl3_aftertouch (int dev, int voice, int pressure)
+static void opl3_aftertouch(int dev, int voice, int pressure)
{
- int tmp;
- struct sbi_instrument *instr;
- struct physical_voice_info *map;
-
- if (voice < 0 || voice >= devc->nr_voice)
- return;
+ int tmp;
+ struct sbi_instrument *instr;
+ struct physical_voice_info *map;
- map = &pv_map[devc->lv_map[voice]];
+ if (voice < 0 || voice >= devc->nr_voice)
+ return;
- DEB (printk ("Aftertouch %d\n", voice));
+ map = &pv_map[devc->lv_map[voice]];
- if (map->voice_mode == 0)
- return;
+ DEB(printk("Aftertouch %d\n", voice));
- /*
- * Adjust the amount of vibrato depending the pressure
- */
+ if (map->voice_mode == 0)
+ return;
- instr = devc->act_i[voice];
+ /*
+ * Adjust the amount of vibrato depending the pressure
+ */
- if (!instr)
- instr = &devc->i_map[0];
+ instr = devc->act_i[voice];
- if (devc->voc[voice].mode == 4)
- {
- int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
+ if (!instr)
+ instr = &devc->i_map[0];
- switch (connection)
+ if (devc->voc[voice].mode == 4)
+ {
+ int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
+
+ switch (connection)
+ {
+ case 0:
+ SET_VIBRATO(4);
+ break;
+
+ case 1:
+ SET_VIBRATO(2);
+ SET_VIBRATO(4);
+ break;
+
+ case 2:
+ SET_VIBRATO(1);
+ SET_VIBRATO(4);
+ break;
+
+ case 3:
+ SET_VIBRATO(1);
+ SET_VIBRATO(3);
+ SET_VIBRATO(4);
+ break;
+
+ }
+ /*
+ * Not implemented yet
+ */
+ }
+ else
{
- case 0:
- SET_VIBRATO (4);
- break;
-
- case 1:
- SET_VIBRATO (2);
- SET_VIBRATO (4);
- break;
-
- case 2:
- SET_VIBRATO (1);
- SET_VIBRATO (4);
- break;
-
- case 3:
- SET_VIBRATO (1);
- SET_VIBRATO (3);
- SET_VIBRATO (4);
- break;
+ SET_VIBRATO(1);
+ if ((instr->operators[10] & 0x01)) /*
+ * Additive synthesis
+ */
+ SET_VIBRATO(2);
}
- /*
- * Not implemented yet
- */
- }
- else
- {
- SET_VIBRATO (1);
-
- if ((instr->operators[10] & 0x01)) /*
- * Additive synthesis
- */
- SET_VIBRATO (2);
- }
}
#undef SET_VIBRATO
-static void
-bend_pitch (int dev, int voice, int value)
+static void bend_pitch(int dev, int voice, int value)
{
- unsigned char data;
- int block, fnum, freq;
- struct physical_voice_info *map;
+ unsigned char data;
+ int block, fnum, freq;
+ struct physical_voice_info *map;
- map = &pv_map[devc->lv_map[voice]];
+ map = &pv_map[devc->lv_map[voice]];
- if (map->voice_mode == 0)
- return;
+ if (map->voice_mode == 0)
+ return;
- devc->voc[voice].bender = value;
- if (!value)
- return;
- if (!(devc->voc[voice].keyon_byte & 0x20))
- return; /*
- * Not keyed on
- */
+ devc->voc[voice].bender = value;
+ if (!value)
+ return;
+ if (!(devc->voc[voice].keyon_byte & 0x20))
+ return; /*
+ * Not keyed on
+ */
- freq = compute_finetune (devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0);
- devc->voc[voice].current_freq = freq;
+ freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0);
+ devc->voc[voice].current_freq = freq;
- freq_to_fnum (freq, &block, &fnum);
+ freq_to_fnum(freq, &block, &fnum);
- data = fnum & 0xff; /*
+ data = fnum & 0xff; /*
* Least significant bits of fnumber
*/
- opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data);
+ opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data);
- data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
- devc->voc[voice].keyon_byte = data;
- opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data);
+ data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
+ devc->voc[voice].keyon_byte = data;
+ opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data);
}
-static void
-opl3_controller (int dev, int voice, int ctrl_num, int value)
+static void opl3_controller (int dev, int voice, int ctrl_num, int value)
{
- if (voice < 0 || voice >= devc->nr_voice)
- return;
-
- switch (ctrl_num)
- {
- case CTRL_PITCH_BENDER:
- bend_pitch (dev, voice, value);
- break;
-
- case CTRL_PITCH_BENDER_RANGE:
- devc->voc[voice].bender_range = value;
- break;
-
- case CTL_MAIN_VOLUME:
- devc->voc[voice].volume = value / 128;
- break;
-
- case CTL_PAN:
- devc->voc[voice].panning = (value * 2) - 128;
- break;
- }
+ if (voice < 0 || voice >= devc->nr_voice)
+ return;
+
+ switch (ctrl_num)
+ {
+ case CTRL_PITCH_BENDER:
+ bend_pitch(dev, voice, value);
+ break;
+
+ case CTRL_PITCH_BENDER_RANGE:
+ devc->voc[voice].bender_range = value;
+ break;
+
+ case CTL_MAIN_VOLUME:
+ devc->voc[voice].volume = value / 128;
+ break;
+
+ case CTL_PAN:
+ devc->voc[voice].panning = (value * 2) - 128;
+ break;
+ }
}
-static void
-opl3_bender (int dev, int voice, int value)
+static void opl3_bender(int dev, int voice, int value)
{
- if (voice < 0 || voice >= devc->nr_voice)
- return;
+ if (voice < 0 || voice >= devc->nr_voice)
+ return;
- bend_pitch (dev, voice, value - 8192);
+ bend_pitch(dev, voice, value - 8192);
}
-static int
-opl3_alloc_voice (int dev, int chn, int note, struct voice_alloc_info *alloc)
+static int opl3_alloc_voice(int dev, int chn, int note, struct voice_alloc_info *alloc)
{
- int i, p, best, first, avail, best_time = 0x7fffffff;
- struct sbi_instrument *instr;
- int is4op;
- int instr_no;
-
- if (chn < 0 || chn > 15)
- instr_no = 0;
- else
- instr_no = devc->chn_info[chn].pgm_num;
-
- instr = &devc->i_map[instr_no];
- if (instr->channel < 0 || /* Instrument not loaded */
- devc->nr_voice != 12) /* Not in 4 OP mode */
- is4op = 0;
- else if (devc->nr_voice == 12) /* 4 OP mode */
- is4op = (instr->key == OPL3_PATCH);
- else
- is4op = 0;
-
- if (is4op)
- {
- first = p = 0;
- avail = 6;
- }
- else
- {
- if (devc->nr_voice == 12) /* 4 OP mode. Use the '2 OP only' operators first */
- first = p = 6;
- else
- first = p = 0;
- avail = devc->nr_voice;
- }
-
- /*
- * Now try to find a free voice
- */
- best = first;
-
- for (i = 0; i < avail; i++)
- {
- if (alloc->map[p] == 0)
+ int i, p, best, first, avail, best_time = 0x7fffffff;
+ struct sbi_instrument *instr;
+ int is4op;
+ int instr_no;
+
+ if (chn < 0 || chn > 15)
+ instr_no = 0;
+ else
+ instr_no = devc->chn_info[chn].pgm_num;
+
+ instr = &devc->i_map[instr_no];
+ if (instr->channel < 0 || /* Instrument not loaded */
+ devc->nr_voice != 12) /* Not in 4 OP mode */
+ is4op = 0;
+ else if (devc->nr_voice == 12) /* 4 OP mode */
+ is4op = (instr->key == OPL3_PATCH);
+ else
+ is4op = 0;
+
+ if (is4op)
{
- return p;
+ first = p = 0;
+ avail = 6;
}
- if (alloc->alloc_times[p] < best_time) /* Find oldest playing note */
+ else
+ {
+ if (devc->nr_voice == 12) /* 4 OP mode. Use the '2 OP only' operators first */
+ first = p = 6;
+ else
+ first = p = 0;
+ avail = devc->nr_voice;
+ }
+
+ /*
+ * Now try to find a free voice
+ */
+ best = first;
+
+ for (i = 0; i < avail; i++)
{
- best_time = alloc->alloc_times[p];
- best = p;
+ if (alloc->map[p] == 0)
+ {
+ return p;
+ }
+ if (alloc->alloc_times[p] < best_time) /* Find oldest playing note */
+ {
+ best_time = alloc->alloc_times[p];
+ best = p;
+ }
+ p = (p + 1) % avail;
}
- p = (p + 1) % avail;
- }
- /*
- * Insert some kind of priority mechanism here.
- */
+ /*
+ * Insert some kind of priority mechanism here.
+ */
- if (best < 0)
- best = 0;
- if (best > devc->nr_voice)
- best -= devc->nr_voice;
+ if (best < 0)
+ best = 0;
+ if (best > devc->nr_voice)
+ best -= devc->nr_voice;
- return best; /* All devc->voc in use. Select the first one. */
+ return best; /* All devc->voc in use. Select the first one. */
}
-static void
-opl3_setup_voice (int dev, int voice, int chn)
+static void opl3_setup_voice(int dev, int voice, int chn)
{
- struct channel_info *info =
- &synth_devs[dev]->chn_info[chn];
+ struct channel_info *info =
+ &synth_devs[dev]->chn_info[chn];
- opl3_set_instr (dev, voice,
- info->pgm_num);
+ opl3_set_instr(dev, voice, info->pgm_num);
- devc->voc[voice].bender = 0;
- devc->voc[voice].bender_range = info->bender_range;
- devc->voc[voice].volume =
- info->controllers[CTL_MAIN_VOLUME];
- devc->voc[voice].panning = (info->controllers[CTL_PAN] * 2) - 128;
+ devc->voc[voice].bender = 0;
+ devc->voc[voice].bender_range = info->bender_range;
+ devc->voc[voice].volume = info->controllers[CTL_MAIN_VOLUME];
+ devc->voc[voice].panning = (info->controllers[CTL_PAN] * 2) - 128;
}
static struct synth_operations opl3_operations =
{
- "OPL",
- NULL,
- 0,
- SYNTH_TYPE_FM,
- FM_TYPE_ADLIB,
- opl3_open,
- opl3_close,
- opl3_ioctl,
- opl3_kill_note,
- opl3_start_note,
- opl3_set_instr,
- opl3_reset,
- opl3_hw_control,
- opl3_load_patch,
- opl3_aftertouch,
- opl3_controller,
- opl3_panning,
- opl3_volume_method,
- opl3_bender,
- opl3_alloc_voice,
- opl3_setup_voice
+ "OPL",
+ NULL,
+ 0,
+ SYNTH_TYPE_FM,
+ FM_TYPE_ADLIB,
+ opl3_open,
+ opl3_close,
+ opl3_ioctl,
+ opl3_kill_note,
+ opl3_start_note,
+ opl3_set_instr,
+ opl3_reset,
+ opl3_hw_control,
+ opl3_load_patch,
+ opl3_aftertouch,
+ opl3_controller,
+ opl3_panning,
+ opl3_volume_method,
+ opl3_bender,
+ opl3_alloc_voice,
+ opl3_setup_voice
};
-void
-opl3_init (int ioaddr, int *osp)
+int opl3_init(int ioaddr, int *osp)
{
- int i;
-
- if (num_synths >= MAX_SYNTH_DEV)
- {
- printk ("OPL3 Error: Too many synthesizers\n");
- return;
- }
-
- if (devc == NULL)
- {
- printk ("OPL3: Device control structure not initialized.\n");
- return;
- }
-
- memset ((char *) devc, 0x00, sizeof (*devc));
- devc->osp = osp;
- devc->base = ioaddr;
-
- devc->nr_voice = 9;
- strcpy (devc->fm_info.name, "OPL2");
-
- devc->fm_info.device = 0;
- devc->fm_info.synth_type = SYNTH_TYPE_FM;
- devc->fm_info.synth_subtype = FM_TYPE_ADLIB;
- devc->fm_info.perc_mode = 0;
- devc->fm_info.nr_voices = 9;
- devc->fm_info.nr_drums = 0;
- devc->fm_info.instr_bank_size = SBFM_MAXINSTR;
- devc->fm_info.capabilities = 0;
- devc->left_io = ioaddr;
- devc->right_io = ioaddr + 2;
-
- if (detected_model <= 2)
- devc->model = 1;
- else
- {
- devc->model = 2;
- if (detected_model == 4)
- devc->is_opl4 = 1;
- }
-
- opl3_operations.info = &devc->fm_info;
-
- synth_devs[num_synths++] = &opl3_operations;
- sequencer_init ();
- devc->v_alloc = &opl3_operations.alloc;
- devc->chn_info = &opl3_operations.chn_info[0];
-
- if (devc->model == 2)
- {
- if (devc->is_opl4)
- conf_printf2 ("Yamaha OPL4/OPL3 FM", ioaddr, 0, -1, -1);
- else
- conf_printf2 ("Yamaha OPL3 FM", ioaddr, 0, -1, -1);
-
- devc->v_alloc->max_voice = devc->nr_voice = 18;
- devc->fm_info.nr_drums = 0;
- devc->fm_info.synth_subtype = FM_TYPE_OPL3;
- devc->fm_info.capabilities |= SYNTH_CAP_OPL3;
- strcpy (devc->fm_info.name, "Yamaha OPL-3");
-
- for (i = 0; i < 18; i++)
- if (pv_map[i].ioaddr == USE_LEFT)
- pv_map[i].ioaddr = devc->left_io;
+ int i;
+ int me;
+
+ if (devc == NULL)
+ {
+ printk(KERN_ERR "opl3_init: Device control structure not initialized.\n");
+ return -1;
+ }
+
+ if ((me = sound_alloc_synthdev()) == -1)
+ {
+ printk(KERN_WARNING "opl3: Too many synthesizers\n");
+ return -1;
+ }
+
+ memset((char *) devc, 0x00, sizeof(*devc));
+ devc->osp = osp;
+ devc->base = ioaddr;
+
+ devc->nr_voice = 9;
+ strcpy(devc->fm_info.name, "OPL2");
+
+ devc->fm_info.device = 0;
+ devc->fm_info.synth_type = SYNTH_TYPE_FM;
+ devc->fm_info.synth_subtype = FM_TYPE_ADLIB;
+ devc->fm_info.perc_mode = 0;
+ devc->fm_info.nr_voices = 9;
+ devc->fm_info.nr_drums = 0;
+ devc->fm_info.instr_bank_size = SBFM_MAXINSTR;
+ devc->fm_info.capabilities = 0;
+ devc->left_io = ioaddr;
+ devc->right_io = ioaddr + 2;
+
+ if (detected_model <= 2)
+ devc->model = 1;
+ else
+ {
+ devc->model = 2;
+ if (detected_model == 4)
+ devc->is_opl4 = 1;
+ }
+
+ opl3_operations.info = &devc->fm_info;
+
+ synth_devs[me] = &opl3_operations;
+ sequencer_init();
+ devc->v_alloc = &opl3_operations.alloc;
+ devc->chn_info = &opl3_operations.chn_info[0];
+
+ if (devc->model == 2)
+ {
+ if (devc->is_opl4)
+ conf_printf2("Yamaha OPL4/OPL3 FM", ioaddr, 0, -1, -1);
+ else
+ conf_printf2("Yamaha OPL3 FM", ioaddr, 0, -1, -1);
+
+ devc->v_alloc->max_voice = devc->nr_voice = 18;
+ devc->fm_info.nr_drums = 0;
+ devc->fm_info.synth_subtype = FM_TYPE_OPL3;
+ devc->fm_info.capabilities |= SYNTH_CAP_OPL3;
+ strcpy(devc->fm_info.name, "Yamaha OPL-3");
+
+ for (i = 0; i < 18; i++)
+ {
+ if (pv_map[i].ioaddr == USE_LEFT)
+ pv_map[i].ioaddr = devc->left_io;
+ else
+ pv_map[i].ioaddr = devc->right_io;
+ }
+ opl3_command(devc->right_io, OPL3_MODE_REGISTER, OPL3_ENABLE);
+ opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x00);
+ }
else
- pv_map[i].ioaddr = devc->right_io;
+ {
+ conf_printf2("Yamaha OPL2 FM", ioaddr, 0, -1, -1);
+ devc->v_alloc->max_voice = devc->nr_voice = 9;
+ devc->fm_info.nr_drums = 0;
+
+ for (i = 0; i < 18; i++)
+ pv_map[i].ioaddr = devc->left_io;
+ };
+
+ for (i = 0; i < SBFM_MAXINSTR; i++)
+ devc->i_map[i].channel = -1;
+
+ return me;
+}
- opl3_command (devc->right_io, OPL3_MODE_REGISTER, OPL3_ENABLE);
- opl3_command (devc->right_io, CONNECTION_SELECT_REGISTER, 0x00);
- }
- else
- {
- conf_printf2 ("Yamaha OPL2 FM", ioaddr, 0, -1, -1);
- devc->v_alloc->max_voice = devc->nr_voice = 9;
- devc->fm_info.nr_drums = 0;
+#ifdef MODULE
- for (i = 0; i < 18; i++)
- pv_map[i].ioaddr = devc->left_io;
- };
+/*
+ * We provide OPL3 functions.
+ */
- for (i = 0; i < SBFM_MAXINSTR; i++)
- devc->i_map[i].channel = -1;
+int io = -1;
+int me;
+int init_module (void)
+{
+ printk("YM3812 and OPL-3 driver Copyright (C) by Hannu Savolainen, Rob Hooft 1993-1996\n");
+ if (io != -1) /* User loading pure OPL3 module */
+ {
+ if (!opl3_detect(io, NULL))
+ {
+ return -ENODEV;
+ }
+ me = opl3_init(io, NULL);
+ }
+ SOUND_LOCK;
+ return 0;
}
+void cleanup_module(void)
+{
+ if (devc)
+ {
+ vfree(devc);
+ devc = NULL;
+ sound_unload_synthdev(me);
+ }
+ SOUND_LOCK_END;
+}
+
+#else
+
#endif
+#endif
+
+EXPORT_SYMBOL(opl3_init);
+EXPORT_SYMBOL(opl3_detect);
+MODULE_PARM(io, "i");
diff --git a/drivers/sound/opl3sa.c b/drivers/sound/opl3sa.c
new file mode 100644
index 000000000..39adf4357
--- /dev/null
+++ b/drivers/sound/opl3sa.c
@@ -0,0 +1,277 @@
+/*
+ * sound/Xopl3sa.c
+ *
+ * Low level driver for Yamaha YMF701B aka OPL3-SA chip
+ *
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+#include <linux/config.h>
+
+#undef SB_OK
+
+#include "sound_config.h"
+#ifdef SB_OK
+#include "sb.h"
+static int sb_initialized = 0;
+
+#endif
+
+#ifdef CONFIG_OPL3SA1
+
+static int kilroy_was_here = 0; /* Don't detect twice */
+static int mpu_initialized = 0;
+
+static int *opl3sa_osp = NULL;
+
+static unsigned char
+opl3sa_read(int addr)
+{
+ unsigned long flags;
+ unsigned char tmp;
+
+ save_flags(flags);
+ cli();
+ outb((0x1d), 0xf86); /* password */
+ outb(((unsigned char) addr), 0xf86); /* address */
+ tmp = inb(0xf87); /* data */
+ restore_flags(flags);
+
+ return tmp;
+}
+
+static void
+opl3sa_write(int addr, int data)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb((0x1d), 0xf86); /* password */
+ outb(((unsigned char) addr), 0xf86); /* address */
+ outb(((unsigned char) data), 0xf87); /* data */
+ restore_flags(flags);
+}
+
+static int
+opl3sa_detect(void)
+{
+ int tmp;
+
+ if (((tmp = opl3sa_read(0x01)) & 0xc4) != 0x04)
+ {
+ DDB(printk("OPL3-SA detect error 1 (%x)\n", opl3sa_read(0x01)));
+ /* return 0; */
+ }
+/*
+ * Check that the password feature has any effect
+ */
+ if (inb(0xf87) == tmp)
+ {
+ DDB(printk("OPL3-SA detect failed 2 (%x/%x)\n", tmp, inb(0xf87)));
+ return 0;
+ }
+ tmp = (opl3sa_read(0x04) & 0xe0) >> 5;
+
+ if (tmp != 0 && tmp != 1)
+ {
+ DDB(printk("OPL3-SA detect failed 3 (%d)\n", tmp));
+ return 0;
+ }
+ DDB(printk("OPL3-SA mode %x detected\n", tmp));
+
+ opl3sa_write(0x01, 0x00); /* Disable MSS */
+ opl3sa_write(0x02, 0x00); /* Disable SB */
+ opl3sa_write(0x03, 0x00); /* Disable MPU */
+
+ return 1;
+}
+
+/*
+ * Probe and attach routines for the Windows Sound System mode of
+ * OPL3-SA
+ */
+
+int
+probe_opl3sa_wss(struct address_info *hw_config)
+{
+ int ret;
+ unsigned char tmp = 0x24; /* WSS enable */
+
+ if (check_region(0xf86, 2)) /* Control port is busy */
+ return 0;
+ /*
+ * Check if the IO port returns valid signature. The original MS Sound
+ * system returns 0x04 while some cards (OPL3-SA for example)
+ * return 0x00.
+ */
+ if (check_region(hw_config->io_base, 8))
+ {
+ printk("OPL3-SA: MSS I/O port conflict (%x)\n", hw_config->io_base);
+ return 0;
+ }
+ opl3sa_osp = hw_config->osp;
+
+ if (!opl3sa_detect())
+ {
+ printk("OSS: OPL3-SA chip not found\n");
+ return 0;
+ }
+ switch (hw_config->io_base)
+ {
+ case 0x530:
+ tmp |= 0x00;
+ break;
+ case 0xe80:
+ tmp |= 0x08;
+ break;
+ case 0xf40:
+ tmp |= 0x10;
+ break;
+ case 0x604:
+ tmp |= 0x18;
+ break;
+ default:
+ printk("OSS: Unsupported OPL3-SA/WSS base %x\n", hw_config->io_base);
+ return 0;
+ }
+
+ opl3sa_write(0x01, tmp); /* WSS setup register */
+ kilroy_was_here = 1;
+
+ ret = probe_ms_sound(hw_config);
+ if (ret)
+ request_region(0xf86, 2, "OPL3-SA");
+
+ return ret;
+}
+
+void
+attach_opl3sa_wss(struct address_info *hw_config)
+{
+ int nm = num_mixers;
+
+ attach_ms_sound(hw_config);
+ if (num_mixers > nm) /* A mixer was installed */
+ {
+ AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_CD);
+ AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH);
+ AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE);
+ }
+}
+
+
+void
+attach_opl3sa_mpu(struct address_info *hw_config)
+{
+#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
+ hw_config->name = "OPL3-SA (MPU401)";
+ attach_uart401(hw_config);
+#endif
+}
+
+int
+probe_opl3sa_mpu(struct address_info *hw_config)
+{
+#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
+ unsigned char conf;
+ static char irq_bits[] =
+ {-1, -1, -1, -1, -1, 1, -1, 2, -1, 3, 4};
+
+ if (!kilroy_was_here)
+ {
+ return 0; /* OPL3-SA has not been detected earlier */
+ }
+ if (mpu_initialized)
+ {
+ DDB(printk("OPL3-SA: MPU mode already initialized\n"));
+ return 0;
+ }
+ if (check_region(hw_config->io_base, 4))
+ {
+ printk("OPL3-SA: MPU I/O port conflict (%x)\n", hw_config->io_base);
+ return 0;
+ }
+ if (hw_config->irq > 10)
+ {
+ printk("OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq);
+ return 0;
+ }
+ if (irq_bits[hw_config->irq] == -1)
+ {
+ printk("OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq);
+ return 0;
+ }
+ switch (hw_config->io_base)
+ {
+ case 0x330:
+ conf = 0x00;
+ break;
+ case 0x332:
+ conf = 0x20;
+ break;
+ case 0x334:
+ conf = 0x40;
+ break;
+ case 0x300:
+ conf = 0x60;
+ break;
+ default:
+ return 0; /* Invalid port */
+ }
+
+ conf |= 0x83; /* MPU & OPL3 (synth) & game port enable */
+ conf |= irq_bits[hw_config->irq] << 2;
+
+ opl3sa_write(0x03, conf);
+
+ mpu_initialized = 1;
+
+ return probe_uart401(hw_config);
+#else
+ return 0;
+#endif
+}
+
+void
+unload_opl3sa_wss(struct address_info *hw_config)
+{
+ int dma2 = hw_config->dma2;
+
+ if (dma2 == -1)
+ dma2 = hw_config->dma;
+
+ release_region(0xf86, 2);
+ release_region(hw_config->io_base, 4);
+
+ ad1848_unload(hw_config->io_base + 4,
+ hw_config->irq,
+ hw_config->dma,
+ dma2,
+ 0);
+}
+
+void
+unload_opl3sa_mpu(struct address_info *hw_config)
+{
+#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
+ unload_uart401(hw_config);
+#endif
+}
+#ifdef SB_OK
+void
+unload_opl3sa_sb(struct address_info *hw_config)
+{
+#ifdef CONFIG_SBDSP
+ sb_dsp_unload(hw_config);
+#endif
+}
+#endif
+
+
+#endif
diff --git a/drivers/sound/os.h b/drivers/sound/os.h
index 4b1abd06e..1acffb768 100644
--- a/drivers/sound/os.h
+++ b/drivers/sound/os.h
@@ -1,21 +1,13 @@
-
-#ifdef __alpha__
-#else
-#endif
-
#define ALLOW_SELECT
#undef NO_INLINE_ASM
#define SHORT_BANNERS
#define MANUAL_PNP
-#undef DO_TIMINGS
+#undef DO_TIMINGS
#ifdef MODULE
#define __NO_VERSION__
#include <linux/module.h>
#include <linux/version.h>
-#ifdef MODVERSIONS
-#include <linux/modversions.h>
-#endif
#endif
#if LINUX_VERSION_CODE > 131328
#define LINUX21X
@@ -41,19 +33,14 @@
#include <linux/poll.h>
#include <linux/pci.h>
#include <linux/bios32.h>
-#else
#endif
#include <linux/wrapper.h>
-
#include <linux/soundcard.h>
-
#define FALSE 0
#define TRUE 1
-
-
struct snd_wait {
volatile int opts;
};
@@ -70,8 +57,6 @@ extern caddr_t sound_mem_blocks[1024];
extern int sound_mem_sizes[1024];
extern int sound_nblocks;
-
-
#undef PSEUDO_DMA_AUTOINIT
#define ALLOW_BUFFER_MAPPING
diff --git a/drivers/sound/pas2_card.c b/drivers/sound/pas2_card.c
index b109b22f1..7e3c23f59 100644
--- a/drivers/sound/pas2_card.c
+++ b/drivers/sound/pas2_card.c
@@ -5,9 +5,11 @@
*/
#include <linux/config.h>
+#include <linux/module.h>
#include "sound_config.h"
+#include "soundmodule.h"
-#if defined(CONFIG_PAS)
+#if defined(CONFIG_PAS) || defined(MODULE)
static unsigned char dma_bits[] =
{4, 1, 2, 3, 0, 5, 6, 7};
@@ -23,13 +25,13 @@ static unsigned char sb_dma_bits[] =
* be relative to the given base -register
*/
-int translate_code;
+int translate_code = 0;
static int pas_intr_mask = 0;
static int pas_irq = 0;
static int pas_sb_base = 0;
-char pas_model;
+char pas_model = 0;
static char *pas_model_names[] =
{"", "Pro AudioSpectrum+", "CDPC", "Pro AudioSpectrum 16", "Pro AudioSpectrum 16D"};
@@ -38,310 +40,359 @@ static char *pas_model_names[] =
* These routines perform the I/O address translation required
* to support other than the default base address
*/
-extern void mix_write (unsigned char data, int ioaddr);
+extern void mix_write(unsigned char data, int ioaddr);
unsigned char
-pas_read (int ioaddr)
+pas_read(int ioaddr)
{
- return inb (ioaddr ^ translate_code);
+ return inb(ioaddr ^ translate_code);
}
void
-pas_write (unsigned char data, int ioaddr)
+pas_write(unsigned char data, int ioaddr)
{
- outb ((data), ioaddr ^ translate_code);
+ outb((data), ioaddr ^ translate_code);
}
/******************* Begin of the Interrupt Handler ********************/
static void
-pasintr (int irq, void *dev_id, struct pt_regs *dummy)
+pasintr(int irq, void *dev_id, struct pt_regs *dummy)
{
- int status;
+ int status;
- status = pas_read (0x0B89);
- pas_write (status, 0x0B89); /* Clear interrupt */
+ status = pas_read(0x0B89);
+ pas_write(status, 0x0B89); /* Clear interrupt */
- if (status & 0x08)
- {
+ if (status & 0x08)
+ {
#ifdef CONFIG_AUDIO
- pas_pcm_interrupt (status, 1);
+ pas_pcm_interrupt(status, 1);
#endif
- status &= ~0x08;
- }
- if (status & 0x10)
- {
-#ifdef CONFIG_MIDI
- pas_midi_interrupt ();
+ status &= ~0x08;
+ }
+ if (status & 0x10)
+ {
+#if defined(CONFIG_MIDI)
+ pas_midi_interrupt();
#endif
- status &= ~0x10;
- }
+ status &= ~0x10;
+ }
}
int
-pas_set_intr (int mask)
+pas_set_intr(int mask)
{
- if (!mask)
- return 0;
+ if (!mask)
+ return 0;
- pas_intr_mask |= mask;
+ pas_intr_mask |= mask;
- pas_write (pas_intr_mask, 0x0B8B);
- return 0;
+ pas_write(pas_intr_mask, 0x0B8B);
+ return 0;
}
int
-pas_remove_intr (int mask)
+pas_remove_intr(int mask)
{
- if (!mask)
- return 0;
+ if (!mask)
+ return 0;
- pas_intr_mask &= ~mask;
- pas_write (pas_intr_mask, 0x0B8B);
+ pas_intr_mask &= ~mask;
+ pas_write(pas_intr_mask, 0x0B8B);
- return 0;
+ return 0;
}
/******************* End of the Interrupt handler **********************/
/******************* Begin of the Initialization Code ******************/
+extern struct address_info sbhw_config;
+
static int
-config_pas_hw (struct address_info *hw_config)
+config_pas_hw(struct address_info *hw_config)
{
- char ok = 1;
- unsigned int_ptrs; /* scsi/sound interrupt pointers */
-
- pas_irq = hw_config->irq;
-
- pas_write (0x00, 0x0B8B);
- pas_write (0x36, 0x138B);
- pas_write (0x36, 0x1388);
- pas_write (0, 0x1388);
- pas_write (0x74, 0x138B);
- pas_write (0x74, 0x1389);
- pas_write (0, 0x1389);
-
- pas_write (0x80 | 0x40 | 0x20 | 1, 0x0B8A);
- pas_write (0x80 | 0x20 | 0x10 | 0x08 | 0x01, 0xF8A);
- pas_write (0x01 | 0x02 | 0x04 | 0x10 /*
- * |
- * 0x80
- */ , 0xB88);
-
- pas_write (0x80
+ char ok = 1;
+ unsigned int_ptrs; /* scsi/sound interrupt pointers */
+
+ pas_irq = hw_config->irq;
+
+ pas_write(0x00, 0x0B8B);
+ pas_write(0x36, 0x138B);
+ pas_write(0x36, 0x1388);
+ pas_write(0, 0x1388);
+ pas_write(0x74, 0x138B);
+ pas_write(0x74, 0x1389);
+ pas_write(0, 0x1389);
+
+ pas_write(0x80 | 0x40 | 0x20 | 1, 0x0B8A);
+ pas_write(0x80 | 0x20 | 0x10 | 0x08 | 0x01, 0xF8A);
+ pas_write(0x01 | 0x02 | 0x04 | 0x10 /*
+ * |
+ * 0x80
+ */ , 0xB88);
+
+ pas_write(0x80
#ifdef PAS_JOYSTICK_ENABLE
- | 0x40
+ | 0x40
#endif
- ,0xF388);
-
- if (pas_irq < 0 || pas_irq > 15)
- {
- printk ("PAS16: Invalid IRQ %d", pas_irq);
- ok = 0;
- }
- else
- {
- int_ptrs = pas_read (0xF38A);
- int_ptrs |= irq_bits[pas_irq] & 0xf;
- pas_write (int_ptrs, 0xF38A);
- if (!irq_bits[pas_irq])
- {
- printk ("PAS16: Invalid IRQ %d", pas_irq);
- ok = 0;
- }
- else
- {
- if (snd_set_irq_handler (pas_irq, pasintr, "PAS16", hw_config->osp) < 0)
- ok = 0;
- }
- }
-
- if (hw_config->dma < 0 || hw_config->dma > 7)
- {
- printk ("PAS16: Invalid DMA selection %d", hw_config->dma);
- ok = 0;
- }
- else
- {
- pas_write (dma_bits[hw_config->dma], 0xF389);
- if (!dma_bits[hw_config->dma])
- {
- printk ("PAS16: Invalid DMA selection %d", hw_config->dma);
- ok = 0;
- }
- else
- {
- if (sound_alloc_dma (hw_config->dma, "PAS16"))
- {
- printk ("pas2_card.c: Can't allocate DMA channel\n");
- ok = 0;
- }
- }
- }
+ ,0xF388);
+
+ if (pas_irq < 0 || pas_irq > 15)
+ {
+ printk("PAS16: Invalid IRQ %d", pas_irq);
+ ok = 0;
+ } else
+ {
+ int_ptrs = pas_read(0xF38A);
+ int_ptrs |= irq_bits[pas_irq] & 0xf;
+ pas_write(int_ptrs, 0xF38A);
+ if (!irq_bits[pas_irq])
+ {
+ printk("PAS16: Invalid IRQ %d", pas_irq);
+ ok = 0;
+ } else
+ {
+ if (snd_set_irq_handler(pas_irq, pasintr, "PAS16", hw_config->osp) < 0)
+ ok = 0;
+ }
+ }
+
+ if (hw_config->dma < 0 || hw_config->dma > 7)
+ {
+ printk("PAS16: Invalid DMA selection %d", hw_config->dma);
+ ok = 0;
+ } else
+ {
+ pas_write(dma_bits[hw_config->dma], 0xF389);
+ if (!dma_bits[hw_config->dma])
+ {
+ printk("PAS16: Invalid DMA selection %d", hw_config->dma);
+ ok = 0;
+ } else
+ {
+ if (sound_alloc_dma(hw_config->dma, "PAS16"))
+ {
+ printk("pas2_card.c: Can't allocate DMA channel\n");
+ ok = 0;
+ }
+ }
+ }
- /*
- * This fixes the timing problems of the PAS due to the Symphony chipset
- * as per Media Vision. Only define this if your PAS doesn't work correctly.
- */
+ /*
+ * This fixes the timing problems of the PAS due to the Symphony chipset
+ * as per Media Vision. Only define this if your PAS doesn't work correctly.
+ */
#ifdef SYMPHONY_PAS
- outb ((0x05), 0xa8);
- outb ((0x60), 0xa9);
+ outb((0x05), 0xa8);
+ outb((0x60), 0xa9);
#endif
#ifdef BROKEN_BUS_CLOCK
- pas_write (0x01 | 0x10 | 0x20 | 0x04, 0x8388);
+ pas_write(0x01 | 0x10 | 0x20 | 0x04, 0x8388);
#else
- /*
- * pas_write(0x01, 0x8388);
- */
- pas_write (0x01 | 0x10 | 0x20, 0x8388);
-#endif
- pas_write (0x18, 0x838A); /* ??? */
- pas_write (0x20 | 0x01, 0x0B8A); /* Mute off, filter = 17.897 kHz */
- pas_write (8, 0xBF8A);
-
- mix_write (0x80 | 5, 0x078B);
- mix_write (5, 0x078B);
-
-#if !defined(DISABLE_SB_EMULATION) && defined(CONFIG_SB)
-
- {
- struct address_info *sb_config;
-
- if ((sb_config = sound_getconf (SNDCARD_SB)))
- {
- unsigned char irq_dma;
-
- /*
- * Turn on Sound Blaster compatibility
- * bit 1 = SB emulation
- * bit 0 = MPU401 emulation (CDPC only :-( )
- */
- pas_write (0x02, 0xF788);
-
/*
- * "Emulation address"
+ * pas_write(0x01, 0x8388);
*/
- pas_write ((sb_config->io_base >> 4) & 0x0f, 0xF789);
- pas_sb_base = sb_config->io_base;
+ pas_write(0x01 | 0x10 | 0x20, 0x8388);
+#endif
+ pas_write(0x18, 0x838A); /* ??? */
+ pas_write(0x20 | 0x01, 0x0B8A); /* Mute off, filter = 17.897 kHz */
+ pas_write(8, 0xBF8A);
- if (!sb_dma_bits[sb_config->dma])
- printk ("\n\nPAS16 Warning: Invalid SB DMA %d\n\n",
- sb_config->dma);
+ mix_write(0x80 | 5, 0x078B);
+ mix_write(5, 0x078B);
- if (!sb_irq_bits[sb_config->irq])
- printk ("\n\nPAS16 Warning: Invalid SB IRQ %d\n\n",
- sb_config->irq);
+#if !defined(DISABLE_SB_EMULATION) && (defined(CONFIG_SB) || defined(CONFIG_SB_MODULE))
- irq_dma = sb_dma_bits[sb_config->dma] |
- sb_irq_bits[sb_config->irq];
+ {
+ struct address_info *sb_config;
- pas_write (irq_dma, 0xFB8A);
- }
- else
- pas_write (0x00, 0xF788);
- }
+#ifndef MODULE
+ if ((sb_config = sound_getconf(SNDCARD_SB)))
+#else
+ sb_config = &sbhw_config;
+ if (sb_config->io_base)
+#endif
+ {
+ unsigned char irq_dma;
+
+ /*
+ * Turn on Sound Blaster compatibility
+ * bit 1 = SB emulation
+ * bit 0 = MPU401 emulation (CDPC only :-( )
+ */
+ pas_write(0x02, 0xF788);
+
+ /*
+ * "Emulation address"
+ */
+ pas_write((sb_config->io_base >> 4) & 0x0f, 0xF789);
+ pas_sb_base = sb_config->io_base;
+
+ if (!sb_dma_bits[sb_config->dma])
+ printk("\n\nPAS16 Warning: Invalid SB DMA %d\n\n", sb_config->dma);
+
+ if (!sb_irq_bits[sb_config->irq])
+ printk("\n\nPAS16 Warning: Invalid SB IRQ %d\n\n", sb_config->irq);
+
+ irq_dma = sb_dma_bits[sb_config->dma] |
+ sb_irq_bits[sb_config->irq];
+
+ pas_write(irq_dma, 0xFB8A);
+ } else
+ pas_write(0x00, 0xF788);
+ }
#else
- pas_write (0x00, 0xF788);
+ pas_write(0x00, 0xF788);
#endif
- if (!ok)
- printk ("PAS16: Driver not enabled\n");
+ if (!ok)
+ printk("PAS16: Driver not enabled\n");
- return ok;
+ return ok;
}
static int
-detect_pas_hw (struct address_info *hw_config)
+detect_pas_hw(struct address_info *hw_config)
{
- unsigned char board_id, foo;
+ unsigned char board_id, foo;
- /*
- * WARNING: Setting an option like W:1 or so that disables warm boot reset
- * of the card will screw up this detect code something fierce. Adding code
- * to handle this means possibly interfering with other cards on the bus if
- * you have something on base port 0x388. SO be forewarned.
- */
+ /*
+ * WARNING: Setting an option like W:1 or so that disables warm boot reset
+ * of the card will screw up this detect code something fierce. Adding code
+ * to handle this means possibly interfering with other cards on the bus if
+ * you have something on base port 0x388. SO be forewarned.
+ */
- outb ((0xBC), 0x9A01); /* Activate first board */
- outb ((hw_config->io_base >> 2), 0x9A01); /* Set base address */
- translate_code = 0x388 ^ hw_config->io_base;
- pas_write (1, 0xBF88); /* Select one wait states */
+ outb((0xBC), 0x9A01); /* Activate first board */
+ outb((hw_config->io_base >> 2), 0x9A01); /* Set base address */
+ translate_code = 0x388 ^ hw_config->io_base;
+ pas_write(1, 0xBF88); /* Select one wait states */
- board_id = pas_read (0x0B8B);
+ board_id = pas_read(0x0B8B);
- if (board_id == 0xff)
- return 0;
+ if (board_id == 0xff)
+ return 0;
- /*
- * We probably have a PAS-series board, now check for a PAS16-series board
- * by trying to change the board revision bits. PAS16-series hardware won't
- * let you do this - the bits are read-only.
- */
+ /*
+ * We probably have a PAS-series board, now check for a PAS16-series board
+ * by trying to change the board revision bits. PAS16-series hardware won't
+ * let you do this - the bits are read-only.
+ */
- foo = board_id ^ 0xe0;
+ foo = board_id ^ 0xe0;
- pas_write (foo, 0x0B8B);
- foo = pas_read (0x0B8B);
- pas_write (board_id, 0x0B8B);
+ pas_write(foo, 0x0B8B);
+ foo = pas_read(0x0B8B);
+ pas_write(board_id, 0x0B8B);
- if (board_id != foo)
- return 0;
+ if (board_id != foo)
+ return 0;
- pas_model = pas_read (0xFF88);
+ pas_model = pas_read(0xFF88);
- return pas_model;
+ return pas_model;
}
void
-attach_pas_card (struct address_info *hw_config)
+attach_pas_card(struct address_info *hw_config)
{
- pas_irq = hw_config->irq;
-
- if (detect_pas_hw (hw_config))
- {
-
- if ((pas_model = pas_read (0xFF88)))
- {
- char temp[100];
-
- sprintf (temp,
- "%s rev %d", pas_model_names[(int) pas_model],
- pas_read (0x2789));
- conf_printf (temp, hw_config);
- }
-
- if (config_pas_hw (hw_config))
- {
+ pas_irq = hw_config->irq;
+
+ if (detect_pas_hw(hw_config))
+ {
+
+ if ((pas_model = pas_read(0xFF88)))
+ {
+ char temp[100];
+
+ sprintf(temp,
+ "%s rev %d", pas_model_names[(int) pas_model],
+ pas_read(0x2789));
+ conf_printf(temp, hw_config);
+ }
+ if (config_pas_hw(hw_config))
+ {
#ifdef CONFIG_AUDIO
- pas_pcm_init (hw_config);
+ pas_pcm_init(hw_config);
#endif
-#if !defined(DISABLE_SB_EMULATION) && defined(CONFIG_SB)
+#if !defined(DISABLE_SB_EMULATION) && (defined(CONFIG_SB) || defined(CONFIG_SB_MODULE))
- sb_dsp_disable_midi (pas_sb_base); /* No MIDI capability */
+ sb_dsp_disable_midi(pas_sb_base); /* No MIDI capability */
#endif
-#ifdef CONFIG_MIDI
- pas_midi_init ();
+#if defined(CONFIG_MIDI)
+ pas_midi_init();
#endif
- pas_init_mixer ();
- }
- }
+ pas_init_mixer();
+ }
+ }
}
int
-probe_pas (struct address_info *hw_config)
+probe_pas(struct address_info *hw_config)
{
- return detect_pas_hw (hw_config);
+ return detect_pas_hw(hw_config);
}
void
-unload_pas (struct address_info *hw_config)
+unload_pas(struct address_info *hw_config)
{
- sound_free_dma (hw_config->dma);
- snd_release_irq (hw_config->irq);
+ sound_free_dma(hw_config->dma);
+ snd_release_irq(hw_config->irq);
}
+#ifdef MODULE
+
+int io = -1;
+int irq = -1;
+int dma = -1;
+int dma16 = -1; /* Set this for modules that need it */
+
+int sb_io = 0;
+int sb_irq = -1;
+int sb_dma = -1;
+int sb_dma16 = -1;
+
+struct address_info config;
+struct address_info sbhw_config;
+
+int init_module(void)
+{
+ printk("MediaTrix audio driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+
+ if (io == -1 || dma == -1 || irq == -1)
+ {
+ printk("I/O, IRQ, DMA and type are mandatory\n");
+ return -EINVAL;
+ }
+ config.io_base = io;
+ config.irq = irq;
+ config.dma = dma;
+ config.dma2 = dma16;
+
+ sbhw_config.io_base = sb_io;
+ sbhw_config.irq = sb_irq;
+ sbhw_config.dma = sb_dma;
+ sbhw_config.dma2 = sb_dma16;
+
+ if (!probe_pas(&config))
+ return -ENODEV;
+ attach_pas_card(&config);
+ SOUND_LOCK;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unload_pas(&config);
+ SOUND_LOCK_END;
+}
+
+
+#endif
#endif
diff --git a/drivers/sound/pas2_midi.c b/drivers/sound/pas2_midi.c
index 05a509f91..45b60fefd 100644
--- a/drivers/sound/pas2_midi.c
+++ b/drivers/sound/pas2_midi.c
@@ -15,7 +15,7 @@
#include "sound_config.h"
-#if defined(CONFIG_PAS) && defined(CONFIG_MIDI)
+#if ( defined(MODULE) || defined(CONFIG_PAS) ) && defined(CONFIG_MIDI)
static int midi_busy = 0, input_opened = 0;
static int my_dev;
@@ -27,87 +27,86 @@ static volatile unsigned char qhead, qtail;
static void (*midi_input_intr) (int dev, unsigned char data);
static int
-pas_midi_open (int dev, int mode,
- void (*input) (int dev, unsigned char data),
- void (*output) (int dev)
+pas_midi_open(int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
)
{
- int err;
- unsigned long flags;
- unsigned char ctrl;
+ int err;
+ unsigned long flags;
+ unsigned char ctrl;
- if (midi_busy)
- {
- printk ("PAS16: Midi busy\n");
- return -EBUSY;
- }
-
- /*
- * Reset input and output FIFO pointers
- */
- pas_write (0x20 | 0x40,
- 0x178b);
-
- save_flags (flags);
- cli ();
-
- if ((err = pas_set_intr (0x10)) < 0)
- return err;
-
- /*
- * Enable input available and output FIFO empty interrupts
- */
+ if (midi_busy)
+ {
+ printk("PAS16: Midi busy\n");
+ return -EBUSY;
+ }
+ /*
+ * Reset input and output FIFO pointers
+ */
+ pas_write(0x20 | 0x40,
+ 0x178b);
- ctrl = 0;
- input_opened = 0;
- midi_input_intr = input;
+ save_flags(flags);
+ cli();
- if (mode == OPEN_READ || mode == OPEN_READWRITE)
- {
- ctrl |= 0x04; /* Enable input */
- input_opened = 1;
- }
+ if ((err = pas_set_intr(0x10)) < 0)
+ {
+ restore_flags(flags);
+ return err;
+ }
+ /*
+ * Enable input available and output FIFO empty interrupts
+ */
- if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
- {
- ctrl |= 0x08 | 0x10; /* Enable output */
- }
+ ctrl = 0;
+ input_opened = 0;
+ midi_input_intr = input;
- pas_write (ctrl, 0x178b);
+ if (mode == OPEN_READ || mode == OPEN_READWRITE)
+ {
+ ctrl |= 0x04; /* Enable input */
+ input_opened = 1;
+ }
+ if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+ {
+ ctrl |= 0x08 | 0x10; /* Enable output */
+ }
+ pas_write(ctrl, 0x178b);
- /*
- * Acknowledge any pending interrupts
- */
+ /*
+ * Acknowledge any pending interrupts
+ */
- pas_write (0xff, 0x1B88);
+ pas_write(0xff, 0x1B88);
- restore_flags (flags);
+ restore_flags(flags);
- midi_busy = 1;
- qlen = qhead = qtail = 0;
- return 0;
+ midi_busy = 1;
+ qlen = qhead = qtail = 0;
+ return 0;
}
static void
-pas_midi_close (int dev)
+pas_midi_close(int dev)
{
- /*
- * Reset FIFO pointers, disable intrs
- */
- pas_write (0x20 | 0x40, 0x178b);
+ /*
+ * Reset FIFO pointers, disable intrs
+ */
+ pas_write(0x20 | 0x40, 0x178b);
- pas_remove_intr (0x10);
- midi_busy = 0;
+ pas_remove_intr(0x10);
+ midi_busy = 0;
}
static int
-dump_to_midi (unsigned char midi_byte)
+dump_to_midi(unsigned char midi_byte)
{
- int fifo_space, x;
+ int fifo_space, x;
- fifo_space = ((x = pas_read (0x1B89)) >> 4) & 0x0f;
+ fifo_space = ((x = pas_read(0x1B89)) >> 4) & 0x0f;
/*
* The MIDI FIFO space register and it's documentation is nonunderstandable.
@@ -117,91 +116,90 @@ dump_to_midi (unsigned char midi_byte)
* means that the buffer is empty.
*/
- if (fifo_space < 2 && fifo_space != 0) /* Full (almost) */
- {
- return 0; /* Ask upper layers to retry after some time */
- }
-
- pas_write (midi_byte, 0x178A);
+ if (fifo_space < 2 && fifo_space != 0) /* Full (almost) */
+ {
+ return 0; /* Ask upper layers to retry after some time */
+ }
+ pas_write(midi_byte, 0x178A);
- return 1;
+ return 1;
}
static int
-pas_midi_out (int dev, unsigned char midi_byte)
+pas_midi_out(int dev, unsigned char midi_byte)
{
- unsigned long flags;
+ unsigned long flags;
- /*
- * Drain the local queue first
- */
+ /*
+ * Drain the local queue first
+ */
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- while (qlen && dump_to_midi (tmp_queue[qhead]))
- {
- qlen--;
- qhead++;
- }
+ while (qlen && dump_to_midi(tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
- restore_flags (flags);
+ restore_flags(flags);
- /*
- * Output the byte if the local queue is empty.
- */
+ /*
+ * Output the byte if the local queue is empty.
+ */
- if (!qlen)
- if (dump_to_midi (midi_byte))
- return 1;
+ if (!qlen)
+ if (dump_to_midi(midi_byte))
+ return 1;
- /*
- * Put to the local queue
- */
+ /*
+ * Put to the local queue
+ */
- if (qlen >= 256)
- return 0; /* Local queue full */
+ if (qlen >= 256)
+ return 0; /* Local queue full */
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- tmp_queue[qtail] = midi_byte;
- qlen++;
- qtail++;
+ tmp_queue[qtail] = midi_byte;
+ qlen++;
+ qtail++;
- restore_flags (flags);
+ restore_flags(flags);
- return 1;
+ return 1;
}
static int
-pas_midi_start_read (int dev)
+pas_midi_start_read(int dev)
{
- return 0;
+ return 0;
}
static int
-pas_midi_end_read (int dev)
+pas_midi_end_read(int dev)
{
- return 0;
+ return 0;
}
static int
-pas_midi_ioctl (int dev, unsigned cmd, caddr_t arg)
+pas_midi_ioctl(int dev, unsigned cmd, caddr_t arg)
{
- return -EINVAL;
+ return -EINVAL;
}
static void
-pas_midi_kick (int dev)
+pas_midi_kick(int dev)
{
}
static int
-pas_buffer_status (int dev)
+pas_buffer_status(int dev)
{
- return qlen;
+ return qlen;
}
#define MIDI_SYNTH_NAME "Pro Audio Spectrum Midi"
@@ -210,80 +208,76 @@ pas_buffer_status (int dev)
static struct midi_operations pas_midi_operations =
{
- {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS},
- &std_midi_synth,
- {0},
- pas_midi_open,
- pas_midi_close,
- pas_midi_ioctl,
- pas_midi_out,
- pas_midi_start_read,
- pas_midi_end_read,
- pas_midi_kick,
- NULL,
- pas_buffer_status,
- NULL
+ {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS},
+ &std_midi_synth,
+ {0},
+ pas_midi_open,
+ pas_midi_close,
+ pas_midi_ioctl,
+ pas_midi_out,
+ pas_midi_start_read,
+ pas_midi_end_read,
+ pas_midi_kick,
+ NULL,
+ pas_buffer_status,
+ NULL
};
void
-pas_midi_init (void)
+pas_midi_init(void)
{
- if (num_midis >= MAX_MIDI_DEV)
- {
- printk ("Sound: Too many midi devices detected\n");
- return;
- }
-
- std_midi_synth.midi_dev = my_dev = num_midis;
- midi_devs[num_midis++] = &pas_midi_operations;
- sequencer_init ();
+ int dev = sound_alloc_mididev();
+
+ if (dev == -1)
+ {
+ printk(KERN_WARNING "pas_midi_init: Too many midi devices detected\n");
+ return;
+ }
+ std_midi_synth.midi_dev = my_dev = dev;
+ midi_devs[dev] = &pas_midi_operations;
+ sequencer_init();
}
void
-pas_midi_interrupt (void)
+pas_midi_interrupt(void)
{
- unsigned char stat;
- int i, incount;
- unsigned long flags;
-
- stat = pas_read (0x1B88);
+ unsigned char stat;
+ int i, incount;
+ unsigned long flags;
- if (stat & 0x04) /* Input data available */
- {
- incount = pas_read (0x1B89) & 0x0f; /* Input FIFO size */
- if (!incount)
- incount = 16;
+ stat = pas_read(0x1B88);
- for (i = 0; i < incount; i++)
- if (input_opened)
+ if (stat & 0x04) /* Input data available */
{
- midi_input_intr (my_dev, pas_read (0x178A));
+ incount = pas_read(0x1B89) & 0x0f; /* Input FIFO size */
+ if (!incount)
+ incount = 16;
+
+ for (i = 0; i < incount; i++)
+ if (input_opened)
+ {
+ midi_input_intr(my_dev, pas_read(0x178A));
+ } else
+ pas_read(0x178A); /* Flush */
}
- else
- pas_read (0x178A); /* Flush */
- }
-
- if (stat & (0x08 | 0x10))
- {
- save_flags (flags);
- cli ();
-
- while (qlen && dump_to_midi (tmp_queue[qhead]))
- {
- qlen--;
- qhead++;
- }
-
- restore_flags (flags);
- }
-
+ if (stat & (0x08 | 0x10))
+ {
+ save_flags(flags);
+ cli();
- if (stat & 0x40)
- {
- printk ("MIDI output overrun %x,%x\n", pas_read (0x1B89), stat);
- }
+ while (qlen && dump_to_midi(tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
- pas_write (stat, 0x1B88); /* Acknowledge interrupts */
+ restore_flags(flags);
+ }
+ if (stat & 0x40)
+ {
+ printk("MIDI output overrun %x,%x\n", pas_read(0x1B89), stat);
+ }
+ pas_write(stat, 0x1B88); /* Acknowledge interrupts */
}
#endif
diff --git a/drivers/sound/pas2_mixer.c b/drivers/sound/pas2_mixer.c
index 8a240e28b..335e7dd39 100644
--- a/drivers/sound/pas2_mixer.c
+++ b/drivers/sound/pas2_mixer.c
@@ -1,3 +1,4 @@
+
/*
* sound/pas2_mixer.c
*
@@ -15,7 +16,7 @@
#include "sound_config.h"
-#if defined(CONFIG_PAS)
+#if defined(CONFIG_PAS) || defined(MODULE)
#ifndef DEB
#define DEB(what) /* (what) */
@@ -40,325 +41,317 @@ static int *levels;
static int default_levels[32] =
{
- 0x3232, /* Master Volume */
- 0x3232, /* Bass */
- 0x3232, /* Treble */
- 0x5050, /* FM */
- 0x4b4b, /* PCM */
- 0x3232, /* PC Speaker */
- 0x4b4b, /* Ext Line */
- 0x4b4b, /* Mic */
- 0x4b4b, /* CD */
- 0x6464, /* Recording monitor */
- 0x4b4b, /* SB PCM */
- 0x6464 /* Recording level */
+ 0x3232, /* Master Volume */
+ 0x3232, /* Bass */
+ 0x3232, /* Treble */
+ 0x5050, /* FM */
+ 0x4b4b, /* PCM */
+ 0x3232, /* PC Speaker */
+ 0x4b4b, /* Ext Line */
+ 0x4b4b, /* Mic */
+ 0x4b4b, /* CD */
+ 0x6464, /* Recording monitor */
+ 0x4b4b, /* SB PCM */
+ 0x6464 /* Recording level */
};
void
-mix_write (unsigned char data, int ioaddr)
+mix_write(unsigned char data, int ioaddr)
{
- /*
- * The Revision D cards have a problem with their MVA508 interface. The
- * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and
- * MSBs out of the output byte and to do a 16-bit out to the mixer port -
- * 1. We need to do this because it isn't timing problem but chip access
- * sequence problem.
- */
-
- if (pas_model == 4)
- {
- outw (data | (data << 8), (ioaddr ^ translate_code) - 1);
- outb ((0x80), 0);
- }
- else
- pas_write (data, ioaddr);
+ /*
+ * The Revision D cards have a problem with their MVA508 interface. The
+ * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and
+ * MSBs out of the output byte and to do a 16-bit out to the mixer port -
+ * 1. We need to do this because it isn't timing problem but chip access
+ * sequence problem.
+ */
+
+ if (pas_model == 4)
+ {
+ outw(data | (data << 8), (ioaddr ^ translate_code) - 1);
+ outb((0x80), 0);
+ } else
+ pas_write(data, ioaddr);
}
static int
-mixer_output (int right_vol, int left_vol, int div, int bits,
- int mixer) /* Input or output mixer */
+mixer_output(int right_vol, int left_vol, int div, int bits,
+ int mixer) /* Input or output mixer */
{
- int left = left_vol * div / 100;
- int right = right_vol * div / 100;
-
-
- if (bits & 0x10)
- {
- left |= mixer;
- right |= mixer;
- }
-
- if (bits == 0x03 || bits == 0x04)
- {
- mix_write (0x80 | bits, 0x078B);
- mix_write (left, 0x078B);
- right_vol = left_vol;
- }
- else
- {
- mix_write (0x80 | 0x20 | bits, 0x078B);
- mix_write (left, 0x078B);
- mix_write (0x80 | 0x40 | bits, 0x078B);
- mix_write (right, 0x078B);
- }
-
- return (left_vol | (right_vol << 8));
+ int left = left_vol * div / 100;
+ int right = right_vol * div / 100;
+
+
+ if (bits & 0x10)
+ {
+ left |= mixer;
+ right |= mixer;
+ }
+ if (bits == 0x03 || bits == 0x04)
+ {
+ mix_write(0x80 | bits, 0x078B);
+ mix_write(left, 0x078B);
+ right_vol = left_vol;
+ } else
+ {
+ mix_write(0x80 | 0x20 | bits, 0x078B);
+ mix_write(left, 0x078B);
+ mix_write(0x80 | 0x40 | bits, 0x078B);
+ mix_write(right, 0x078B);
+ }
+
+ return (left_vol | (right_vol << 8));
}
static void
-set_mode (int new_mode)
+set_mode(int new_mode)
{
- mix_write (0x80 | 0x05, 0x078B);
- mix_write (new_mode, 0x078B);
+ mix_write(0x80 | 0x05, 0x078B);
+ mix_write(new_mode, 0x078B);
- mode_control = new_mode;
+ mode_control = new_mode;
}
static int
-pas_mixer_set (int whichDev, unsigned int level)
+pas_mixer_set(int whichDev, unsigned int level)
{
- int left, right, devmask, changed, i, mixer = 0;
-
- DEB (printk ("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level));
-
- left = level & 0x7f;
- right = (level & 0x7f00) >> 8;
-
- if (whichDev < SOUND_MIXER_NRDEVICES)
- if ((1 << whichDev) & rec_devices)
- mixer = 0x20;
- else
- mixer = 0x00;
-
- switch (whichDev)
- {
- case SOUND_MIXER_VOLUME: /* Master volume (0-63) */
- levels[whichDev] = mixer_output (right, left, 63, 0x01, 0);
- break;
-
- /*
- * Note! Bass and Treble are mono devices. Will use just the left
- * channel.
- */
- case SOUND_MIXER_BASS: /* Bass (0-12) */
- levels[whichDev] = mixer_output (right, left, 12, 0x03, 0);
- break;
- case SOUND_MIXER_TREBLE: /* Treble (0-12) */
- levels[whichDev] = mixer_output (right, left, 12, 0x04, 0);
- break;
-
- case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-31) */
- levels[whichDev] = mixer_output (right, left, 31, 0x10 | 0x00, mixer);
- break;
- case SOUND_MIXER_PCM: /* PAS PCM (0-31) */
- levels[whichDev] = mixer_output (right, left, 31, 0x10 | 0x05, mixer);
- break;
- case SOUND_MIXER_ALTPCM: /* SB PCM (0-31) */
- levels[whichDev] = mixer_output (right, left, 31, 0x10 | 0x07, mixer);
- break;
- case SOUND_MIXER_SPEAKER: /* PC speaker (0-31) */
- levels[whichDev] = mixer_output (right, left, 31, 0x10 | 0x06, mixer);
- break;
- case SOUND_MIXER_LINE: /* External line (0-31) */
- levels[whichDev] = mixer_output (right, left, 31, 0x10 | 0x02, mixer);
- break;
- case SOUND_MIXER_CD: /* CD (0-31) */
- levels[whichDev] = mixer_output (right, left, 31, 0x10 | 0x03, mixer);
- break;
- case SOUND_MIXER_MIC: /* External microphone (0-31) */
- levels[whichDev] = mixer_output (right, left, 31, 0x10 | 0x04, mixer);
- break;
- case SOUND_MIXER_IMIX: /* Recording monitor (0-31) (Output mixer only) */
- levels[whichDev] = mixer_output (right, left, 31, 0x10 | 0x01,
- 0x00);
- break;
- case SOUND_MIXER_RECLEV: /* Recording level (0-15) */
- levels[whichDev] = mixer_output (right, left, 15, 0x02, 0);
- break;
-
-
- case SOUND_MIXER_RECSRC:
- devmask = level & POSSIBLE_RECORDING_DEVICES;
-
- changed = devmask ^ rec_devices;
- rec_devices = devmask;
-
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- if (changed & (1 << i))
+ int left, right, devmask, changed, i, mixer = 0;
+
+ DEB(printk("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level));
+
+ left = level & 0x7f;
+ right = (level & 0x7f00) >> 8;
+
+ if (whichDev < SOUND_MIXER_NRDEVICES)
+ if ((1 << whichDev) & rec_devices)
+ mixer = 0x20;
+ else
+ mixer = 0x00;
+
+ switch (whichDev)
{
- pas_mixer_set (i, levels[i]);
+ case SOUND_MIXER_VOLUME: /* Master volume (0-63) */
+ levels[whichDev] = mixer_output(right, left, 63, 0x01, 0);
+ break;
+
+ /*
+ * Note! Bass and Treble are mono devices. Will use just the left
+ * channel.
+ */
+ case SOUND_MIXER_BASS: /* Bass (0-12) */
+ levels[whichDev] = mixer_output(right, left, 12, 0x03, 0);
+ break;
+ case SOUND_MIXER_TREBLE: /* Treble (0-12) */
+ levels[whichDev] = mixer_output(right, left, 12, 0x04, 0);
+ break;
+
+ case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-31) */
+ levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x00, mixer);
+ break;
+ case SOUND_MIXER_PCM: /* PAS PCM (0-31) */
+ levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x05, mixer);
+ break;
+ case SOUND_MIXER_ALTPCM: /* SB PCM (0-31) */
+ levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x07, mixer);
+ break;
+ case SOUND_MIXER_SPEAKER: /* PC speaker (0-31) */
+ levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x06, mixer);
+ break;
+ case SOUND_MIXER_LINE: /* External line (0-31) */
+ levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x02, mixer);
+ break;
+ case SOUND_MIXER_CD: /* CD (0-31) */
+ levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x03, mixer);
+ break;
+ case SOUND_MIXER_MIC: /* External microphone (0-31) */
+ levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x04, mixer);
+ break;
+ case SOUND_MIXER_IMIX: /* Recording monitor (0-31) (Output mixer only) */
+ levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x01,
+ 0x00);
+ break;
+ case SOUND_MIXER_RECLEV: /* Recording level (0-15) */
+ levels[whichDev] = mixer_output(right, left, 15, 0x02, 0);
+ break;
+
+
+ case SOUND_MIXER_RECSRC:
+ devmask = level & POSSIBLE_RECORDING_DEVICES;
+
+ changed = devmask ^ rec_devices;
+ rec_devices = devmask;
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (changed & (1 << i))
+ {
+ pas_mixer_set(i, levels[i]);
+ }
+ return rec_devices;
+ break;
+
+ default:
+ return -EINVAL;
}
- return rec_devices;
- break;
- default:
- return -EINVAL;
- }
-
- return (levels[whichDev]);
+ return (levels[whichDev]);
}
/*****/
static void
-pas_mixer_reset (void)
+pas_mixer_reset(void)
{
- int foo;
+ int foo;
- DEB (printk ("pas2_mixer.c: void pas_mixer_reset(void)\n"));
+ DEB(printk("pas2_mixer.c: void pas_mixer_reset(void)\n"));
- for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
- pas_mixer_set (foo, levels[foo]);
+ for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
+ pas_mixer_set(foo, levels[foo]);
- set_mode (0x04 | 0x01);
+ set_mode(0x04 | 0x01);
}
static int
-pas_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
+pas_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
{
- DEB (printk ("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
-
- if (cmd == SOUND_MIXER_PRIVATE1) /* Set loudness bit */
- {
- int level;
-
- level = *(int *) arg;
-
- if (level == -1) /* Return current settings */
- {
- if (mode_control & 0x04)
- return (*(int *) arg = 1);
- else
- return (*(int *) arg = 0);
- }
- else
- {
- mode_control &= ~0x04;
- if (level)
- mode_control |= 0x04;
- set_mode (mode_control);
- return (*(int *) arg = !!level); /* 0 or 1 */
- }
- }
-
-
- if (cmd == SOUND_MIXER_PRIVATE2) /* Set enhance bit */
- {
- int level;
-
- level = *(int *) arg;
-
- if (level == -1) /* Return current settings */
- {
- if (!(mode_control & 0x03))
- return (*(int *) arg = 0);
- return (*(int *) arg = ((mode_control & 0x03) + 1) * 20);
- }
- else
- {
- int i = 0;
-
- level &= 0x7f;
- if (level)
- i = (level / 20) - 1;
-
- mode_control &= ~0x03;
- mode_control |= i & 0x03;
- set_mode (mode_control);
-
- if (i)
- i = (i + 1) * 20;
-
- return i;
- }
- }
-
- if (cmd == SOUND_MIXER_PRIVATE3) /* Set mute bit */
- {
- int level;
-
- level = *(int *) arg;
-
- if (level == -1) /* Return current settings */
- {
- return (*(int *) arg = !(pas_read (0x0B8A) & 0x20));
- }
- else
- {
- if (level)
- pas_write (pas_read (0x0B8A) & (~0x20),
- 0x0B8A);
- else
- pas_write (pas_read (0x0B8A) | 0x20,
- 0x0B8A);
-
- return !(pas_read (0x0B8A) & 0x20);
- }
- }
-
- if (((cmd >> 8) & 0xff) == 'M')
- {
- int v;
-
- v = *(int *) arg;
-
- if (_SIOC_DIR (cmd) & _SIOC_WRITE)
- return (*(int *) arg = pas_mixer_set (cmd & 0xff, v));
- else
- {
-
- switch (cmd & 0xff)
- {
-
- case SOUND_MIXER_RECSRC:
- return (*(int *) arg = rec_devices);
- break;
-
- case SOUND_MIXER_STEREODEVS:
- return (*(int *) arg = SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE));
- break;
-
- case SOUND_MIXER_DEVMASK:
- return (*(int *) arg = SUPPORTED_MIXER_DEVICES);
- break;
-
- case SOUND_MIXER_RECMASK:
- return (*(int *) arg = POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES);
- break;
-
- case SOUND_MIXER_CAPS:
- return (*(int *) arg = 0); /* No special capabilities */
- break;
-
-
- default:
- return (*(int *) arg = levels[cmd & 0xff]);
- }
- }
- }
- return -EINVAL;
+ DEB(printk("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
+
+ if (cmd == SOUND_MIXER_PRIVATE1) /* Set loudness bit */
+ {
+ int level;
+
+ level = *(int *) arg;
+
+ if (level == -1) /* Return current settings */
+ {
+ if (mode_control & 0x04)
+ return (*(int *) arg = 1);
+ else
+ return (*(int *) arg = 0);
+ } else
+ {
+ mode_control &= ~0x04;
+ if (level)
+ mode_control |= 0x04;
+ set_mode(mode_control);
+ return (*(int *) arg = !!level); /* 0 or 1 */
+ }
+ }
+ if (cmd == SOUND_MIXER_PRIVATE2) /* Set enhance bit */
+ {
+ int level;
+
+ level = *(int *) arg;
+
+ if (level == -1) /* Return current settings */
+ {
+ if (!(mode_control & 0x03))
+ return (*(int *) arg = 0);
+ return (*(int *) arg = ((mode_control & 0x03) + 1) * 20);
+ } else
+ {
+ int i = 0;
+
+ level &= 0x7f;
+ if (level)
+ i = (level / 20) - 1;
+
+ mode_control &= ~0x03;
+ mode_control |= i & 0x03;
+ set_mode(mode_control);
+
+ if (i)
+ i = (i + 1) * 20;
+
+ return i;
+ }
+ }
+ if (cmd == SOUND_MIXER_PRIVATE3) /* Set mute bit */
+ {
+ int level;
+
+ level = *(int *) arg;
+
+ if (level == -1) /* Return current settings */
+ {
+ return (*(int *) arg = !(pas_read(0x0B8A) & 0x20));
+ } else
+ {
+ if (level)
+ pas_write(pas_read(0x0B8A) & (~0x20),
+ 0x0B8A);
+ else
+ pas_write(pas_read(0x0B8A) | 0x20,
+ 0x0B8A);
+
+ return !(pas_read(0x0B8A) & 0x20);
+ }
+ }
+ if (((cmd >> 8) & 0xff) == 'M')
+ {
+ int v;
+
+ v = *(int *) arg;
+
+ if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+ return (*(int *) arg = pas_mixer_set(cmd & 0xff, v));
+ else
+ {
+
+ switch (cmd & 0xff)
+ {
+
+ case SOUND_MIXER_RECSRC:
+ return (*(int *) arg = rec_devices);
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ return (*(int *) arg = SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE));
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ return (*(int *) arg = SUPPORTED_MIXER_DEVICES);
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ return (*(int *) arg = POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES);
+ break;
+
+ case SOUND_MIXER_CAPS:
+ return (*(int *) arg = 0); /* No special capabilities */
+ break;
+
+
+ default:
+ return (*(int *) arg = levels[cmd & 0xff]);
+ }
+ }
+ }
+ return -EINVAL;
}
static struct mixer_operations pas_mixer_operations =
{
- "PAS16",
- "Pro Audio Spectrum 16",
- pas_mixer_ioctl
+ "PAS16",
+ "Pro Audio Spectrum 16",
+ pas_mixer_ioctl
};
int
-pas_init_mixer (void)
+pas_init_mixer(void)
{
- levels = load_mixer_volumes ("PAS16_1", default_levels, 1);
+ int d;
+
+ levels = load_mixer_volumes("PAS16_1", default_levels, 1);
- pas_mixer_reset ();
+ pas_mixer_reset();
- if (num_mixers < MAX_MIXER_DEV)
- {
- audio_devs[pas_audiodev]->mixer_dev = num_mixers;
- mixer_devs[num_mixers++] = &pas_mixer_operations;
- }
- return 1;
+ if ((d = sound_alloc_mixerdev()) != -1)
+ {
+ audio_devs[pas_audiodev]->mixer_dev = d;
+ mixer_devs[d] = &pas_mixer_operations;
+ }
+ return 1;
}
#endif
diff --git a/drivers/sound/pas2_pcm.c b/drivers/sound/pas2_pcm.c
index e082fc9cc..99e897161 100644
--- a/drivers/sound/pas2_pcm.c
+++ b/drivers/sound/pas2_pcm.c
@@ -12,7 +12,7 @@
#include "sound_config.h"
-#if defined(CONFIG_PAS) && defined(CONFIG_AUDIO)
+#if defined(MODULE) || ( defined(CONFIG_PAS) && defined(CONFIG_AUDIO) )
#ifndef DEB
#define DEB(WHAT)
@@ -39,424 +39,418 @@ int pas_audiodev = 0;
static int open_mode = 0;
static int
-pcm_set_speed (int arg)
+pcm_set_speed(int arg)
{
- int foo, tmp;
- unsigned long flags;
-
- if (arg == 0)
- return pcm_speed;
-
- if (arg > 44100)
- arg = 44100;
- if (arg < 5000)
- arg = 5000;
-
- if (pcm_channels & 2)
- {
- foo = (596590 + (arg / 2)) / arg;
- arg = (596590 + (foo / 2)) / foo;
- }
- else
- {
- foo = (1193180 + (arg / 2)) / arg;
- arg = (1193180 + (foo / 2)) / foo;
- }
-
- pcm_speed = arg;
-
- tmp = pas_read (0x0B8A);
-
- /*
- * Set anti-aliasing filters according to sample rate. You really *NEED*
- * to enable this feature for all normal recording unless you want to
- * experiment with aliasing effects.
- * These filters apply to the selected "recording" source.
- * I (pfw) don't know the encoding of these 5 bits. The values shown
- * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/.
- *
- * I cleared bit 5 of these values, since that bit controls the master
- * mute flag. (Olav Wölfelschneider)
- *
- */
+ int foo, tmp;
+ unsigned long flags;
+
+ if (arg == 0)
+ return pcm_speed;
+
+ if (arg > 44100)
+ arg = 44100;
+ if (arg < 5000)
+ arg = 5000;
+
+ if (pcm_channels & 2)
+ {
+ foo = (596590 + (arg / 2)) / arg;
+ arg = (596590 + (foo / 2)) / foo;
+ } else
+ {
+ foo = (1193180 + (arg / 2)) / arg;
+ arg = (1193180 + (foo / 2)) / foo;
+ }
+
+ pcm_speed = arg;
+
+ tmp = pas_read(0x0B8A);
+
+ /*
+ * Set anti-aliasing filters according to sample rate. You really *NEED*
+ * to enable this feature for all normal recording unless you want to
+ * experiment with aliasing effects.
+ * These filters apply to the selected "recording" source.
+ * I (pfw) don't know the encoding of these 5 bits. The values shown
+ * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/.
+ *
+ * I cleared bit 5 of these values, since that bit controls the master
+ * mute flag. (Olav Wölfelschneider)
+ *
+ */
#if !defined NO_AUTO_FILTER_SET
- tmp &= 0xe0;
- if (pcm_speed >= 2 * 17897)
- tmp |= 0x01;
- else if (pcm_speed >= 2 * 15909)
- tmp |= 0x02;
- else if (pcm_speed >= 2 * 11931)
- tmp |= 0x09;
- else if (pcm_speed >= 2 * 8948)
- tmp |= 0x11;
- else if (pcm_speed >= 2 * 5965)
- tmp |= 0x19;
- else if (pcm_speed >= 2 * 2982)
- tmp |= 0x04;
- pcm_filter = tmp;
+ tmp &= 0xe0;
+ if (pcm_speed >= 2 * 17897)
+ tmp |= 0x01;
+ else if (pcm_speed >= 2 * 15909)
+ tmp |= 0x02;
+ else if (pcm_speed >= 2 * 11931)
+ tmp |= 0x09;
+ else if (pcm_speed >= 2 * 8948)
+ tmp |= 0x11;
+ else if (pcm_speed >= 2 * 5965)
+ tmp |= 0x19;
+ else if (pcm_speed >= 2 * 2982)
+ tmp |= 0x04;
+ pcm_filter = tmp;
#endif
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- pas_write (tmp & ~(0x40 | 0x80), 0x0B8A);
- pas_write (0x00 | 0x30 | 0x04, 0x138B);
- pas_write (foo & 0xff, 0x1388);
- pas_write ((foo >> 8) & 0xff, 0x1388);
- pas_write (tmp, 0x0B8A);
+ pas_write(tmp & ~(0x40 | 0x80), 0x0B8A);
+ pas_write(0x00 | 0x30 | 0x04, 0x138B);
+ pas_write(foo & 0xff, 0x1388);
+ pas_write((foo >> 8) & 0xff, 0x1388);
+ pas_write(tmp, 0x0B8A);
- restore_flags (flags);
+ restore_flags(flags);
- return pcm_speed;
+ return pcm_speed;
}
static int
-pcm_set_channels (int arg)
+pcm_set_channels(int arg)
{
- if ((arg != 1) && (arg != 2))
- return pcm_channels;
+ if ((arg != 1) && (arg != 2))
+ return pcm_channels;
- if (arg != pcm_channels)
- {
- pas_write (pas_read (0xF8A) ^ 0x20, 0xF8A);
+ if (arg != pcm_channels)
+ {
+ pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A);
- pcm_channels = arg;
- pcm_set_speed (pcm_speed); /* The speed must be reinitialized */
- }
-
- return pcm_channels;
+ pcm_channels = arg;
+ pcm_set_speed(pcm_speed); /* The speed must be reinitialized */
+ }
+ return pcm_channels;
}
static int
-pcm_set_bits (int arg)
+pcm_set_bits(int arg)
{
- if (arg == 0)
- return pcm_bits;
-
- if ((arg & pcm_bitsok) != arg)
- return pcm_bits;
+ if (arg == 0)
+ return pcm_bits;
- if (arg != pcm_bits)
- {
- pas_write (pas_read (0x8389) ^ 0x04, 0x8389);
+ if ((arg & pcm_bitsok) != arg)
+ return pcm_bits;
- pcm_bits = arg;
- }
+ if (arg != pcm_bits)
+ {
+ pas_write(pas_read(0x8389) ^ 0x04, 0x8389);
- return pcm_bits;
+ pcm_bits = arg;
+ }
+ return pcm_bits;
}
static int
-pas_audio_ioctl (int dev, unsigned int cmd, caddr_t arg)
+pas_audio_ioctl(int dev, unsigned int cmd, caddr_t arg)
{
- int val;
+ int val;
- DEB (printk ("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
+ DEB(printk("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
- switch (cmd)
- {
- case SOUND_PCM_WRITE_RATE:
- val = *(int *) arg;
- return (*(int *) arg = pcm_set_speed (val));
- break;
+ switch (cmd)
+ {
+ case SOUND_PCM_WRITE_RATE:
+ val = *(int *) arg;
+ return (*(int *) arg = pcm_set_speed(val));
+ break;
- case SOUND_PCM_READ_RATE:
- return (*(int *) arg = pcm_speed);
- break;
+ case SOUND_PCM_READ_RATE:
+ return (*(int *) arg = pcm_speed);
+ break;
- case SNDCTL_DSP_STEREO:
- val = *(int *) arg;
- return (*(int *) arg = pcm_set_channels (val + 1) - 1);
- break;
+ case SNDCTL_DSP_STEREO:
+ val = *(int *) arg;
+ return (*(int *) arg = pcm_set_channels(val + 1) - 1);
+ break;
- case SOUND_PCM_WRITE_CHANNELS:
- val = *(int *) arg;
- return (*(int *) arg = pcm_set_channels (val));
- break;
+ case SOUND_PCM_WRITE_CHANNELS:
+ val = *(int *) arg;
+ return (*(int *) arg = pcm_set_channels(val));
+ break;
- case SOUND_PCM_READ_CHANNELS:
- return (*(int *) arg = pcm_channels);
- break;
+ case SOUND_PCM_READ_CHANNELS:
+ return (*(int *) arg = pcm_channels);
+ break;
- case SNDCTL_DSP_SETFMT:
- val = *(int *) arg;
- return (*(int *) arg = pcm_set_bits (val));
- break;
+ case SNDCTL_DSP_SETFMT:
+ val = *(int *) arg;
+ return (*(int *) arg = pcm_set_bits(val));
+ break;
- case SOUND_PCM_READ_BITS:
- return (*(int *) arg = pcm_bits);
+ case SOUND_PCM_READ_BITS:
+ return (*(int *) arg = pcm_bits);
- default:
- return -EINVAL;
- }
+ default:
+ return -EINVAL;
+ }
- return -EINVAL;
+ return -EINVAL;
}
static void
-pas_audio_reset (int dev)
+pas_audio_reset(int dev)
{
- DEB (printk ("pas2_pcm.c: static void pas_audio_reset(void)\n"));
+ DEB(printk("pas2_pcm.c: static void pas_audio_reset(void)\n"));
- pas_write (pas_read (0xF8A) & ~0x40, 0xF8A); /* Disable PCM */
+ pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); /* Disable PCM */
}
static int
-pas_audio_open (int dev, int mode)
+pas_audio_open(int dev, int mode)
{
- int err;
- unsigned long flags;
-
- DEB (printk ("pas2_pcm.c: static int pas_audio_open(int mode = %X)\n", mode));
+ int err;
+ unsigned long flags;
- save_flags (flags);
- cli ();
- if (pcm_busy)
- {
- restore_flags (flags);
- return -EBUSY;
- }
+ DEB(printk("pas2_pcm.c: static int pas_audio_open(int mode = %X)\n", mode));
- pcm_busy = 1;
- restore_flags (flags);
+ save_flags(flags);
+ cli();
+ if (pcm_busy)
+ {
+ restore_flags(flags);
+ return -EBUSY;
+ }
+ pcm_busy = 1;
+ restore_flags(flags);
- if ((err = pas_set_intr (PAS_PCM_INTRBITS)) < 0)
- return err;
+ if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0)
+ return err;
- pcm_count = 0;
- open_mode = mode;
+ pcm_count = 0;
+ open_mode = mode;
- return 0;
+ return 0;
}
static void
-pas_audio_close (int dev)
+pas_audio_close(int dev)
{
- unsigned long flags;
+ unsigned long flags;
- DEB (printk ("pas2_pcm.c: static void pas_audio_close(void)\n"));
+ DEB(printk("pas2_pcm.c: static void pas_audio_close(void)\n"));
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- pas_audio_reset (dev);
- pas_remove_intr (PAS_PCM_INTRBITS);
- pcm_mode = PCM_NON;
+ pas_audio_reset(dev);
+ pas_remove_intr(PAS_PCM_INTRBITS);
+ pcm_mode = PCM_NON;
- pcm_busy = 0;
- restore_flags (flags);
+ pcm_busy = 0;
+ restore_flags(flags);
}
static void
-pas_audio_output_block (int dev, unsigned long buf, int count,
- int intrflag)
+pas_audio_output_block(int dev, unsigned long buf, int count,
+ int intrflag)
{
- unsigned long flags, cnt;
+ unsigned long flags, cnt;
- DEB (printk ("pas2_pcm.c: static void pas_audio_output_block(char *buf = %P, int count = %X)\n", buf, count));
+ DEB(printk("pas2_pcm.c: static void pas_audio_output_block(char *buf = %P, int count = %X)\n", buf, count));
- cnt = count;
- if (audio_devs[dev]->dmap_out->dma > 3)
- cnt >>= 1;
+ cnt = count;
+ if (audio_devs[dev]->dmap_out->dma > 3)
+ cnt >>= 1;
- if (audio_devs[dev]->flags & DMA_AUTOMODE &&
- intrflag &&
- cnt == pcm_count)
- return;
+ if (audio_devs[dev]->flags & DMA_AUTOMODE &&
+ intrflag &&
+ cnt == pcm_count)
+ return;
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- pas_write (pas_read (0xF8A) & ~0x40,
- 0xF8A);
+ pas_write(pas_read(0xF8A) & ~0x40,
+ 0xF8A);
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
+ /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
+ if (audio_devs[dev]->dmap_out->dma > 3)
+ count >>= 1;
- if (count != pcm_count)
- {
- pas_write (pas_read (0x0B8A) & ~0x80, 0x0B8A);
- pas_write (0x40 | 0x30 | 0x04, 0x138B);
- pas_write (count & 0xff, 0x1389);
- pas_write ((count >> 8) & 0xff, 0x1389);
- pas_write (pas_read (0x0B8A) | 0x80, 0x0B8A);
+ if (count != pcm_count)
+ {
+ pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
+ pas_write(0x40 | 0x30 | 0x04, 0x138B);
+ pas_write(count & 0xff, 0x1389);
+ pas_write((count >> 8) & 0xff, 0x1389);
+ pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
- pcm_count = count;
- }
- pas_write (pas_read (0x0B8A) | 0x80 | 0x40, 0x0B8A);
+ pcm_count = count;
+ }
+ pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
#ifdef NO_TRIGGER
- pas_write (pas_read (0xF8A) | 0x40 | 0x10, 0xF8A);
+ pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
#endif
- pcm_mode = PCM_DAC;
+ pcm_mode = PCM_DAC;
- restore_flags (flags);
+ restore_flags(flags);
}
static void
-pas_audio_start_input (int dev, unsigned long buf, int count,
- int intrflag)
+pas_audio_start_input(int dev, unsigned long buf, int count,
+ int intrflag)
{
- unsigned long flags;
- int cnt;
+ unsigned long flags;
+ int cnt;
- DEB (printk ("pas2_pcm.c: static void pas_audio_start_input(char *buf = %P, int count = %X)\n", buf, count));
+ DEB(printk("pas2_pcm.c: static void pas_audio_start_input(char *buf = %P, int count = %X)\n", buf, count));
- cnt = count;
- if (audio_devs[dev]->dmap_out->dma > 3)
- cnt >>= 1;
+ cnt = count;
+ if (audio_devs[dev]->dmap_out->dma > 3)
+ cnt >>= 1;
- if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE &&
- intrflag &&
- cnt == pcm_count)
- return;
+ if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE &&
+ intrflag &&
+ cnt == pcm_count)
+ return;
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
+ /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
+ if (audio_devs[dev]->dmap_out->dma > 3)
+ count >>= 1;
- if (count != pcm_count)
- {
- pas_write (pas_read (0x0B8A) & ~0x80, 0x0B8A);
- pas_write (0x40 | 0x30 | 0x04, 0x138B);
- pas_write (count & 0xff, 0x1389);
- pas_write ((count >> 8) & 0xff, 0x1389);
- pas_write (pas_read (0x0B8A) | 0x80, 0x0B8A);
+ if (count != pcm_count)
+ {
+ pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
+ pas_write(0x40 | 0x30 | 0x04, 0x138B);
+ pas_write(count & 0xff, 0x1389);
+ pas_write((count >> 8) & 0xff, 0x1389);
+ pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
- pcm_count = count;
- }
- pas_write (pas_read (0x0B8A) | 0x80 | 0x40, 0x0B8A);
+ pcm_count = count;
+ }
+ pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
#ifdef NO_TRIGGER
- pas_write ((pas_read (0xF8A) | 0x40) & ~0x10, 0xF8A);
+ pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
#endif
- pcm_mode = PCM_ADC;
+ pcm_mode = PCM_ADC;
- restore_flags (flags);
+ restore_flags(flags);
}
#ifndef NO_TRIGGER
static void
-pas_audio_trigger (int dev, int state)
+pas_audio_trigger(int dev, int state)
{
- unsigned long flags;
+ unsigned long flags;
- save_flags (flags);
- cli ();
- state &= open_mode;
+ save_flags(flags);
+ cli();
+ state &= open_mode;
- if (state & PCM_ENABLE_OUTPUT)
- pas_write (pas_read (0xF8A) | 0x40 | 0x10, 0xF8A);
- else if (state & PCM_ENABLE_INPUT)
- pas_write ((pas_read (0xF8A) | 0x40) & ~0x10, 0xF8A);
- else
- pas_write (pas_read (0xF8A) & ~0x40, 0xF8A);
+ if (state & PCM_ENABLE_OUTPUT)
+ pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
+ else if (state & PCM_ENABLE_INPUT)
+ pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
+ else
+ pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
- restore_flags (flags);
+ restore_flags(flags);
}
#endif
static int
-pas_audio_prepare_for_input (int dev, int bsize, int bcount)
+pas_audio_prepare_for_input(int dev, int bsize, int bcount)
{
- pas_audio_reset (dev);
- return 0;
+ pas_audio_reset(dev);
+ return 0;
}
static int
-pas_audio_prepare_for_output (int dev, int bsize, int bcount)
+pas_audio_prepare_for_output(int dev, int bsize, int bcount)
{
- pas_audio_reset (dev);
- return 0;
+ pas_audio_reset(dev);
+ return 0;
}
static struct audio_driver pas_audio_driver =
{
- pas_audio_open,
- pas_audio_close,
- pas_audio_output_block,
- pas_audio_start_input,
- pas_audio_ioctl,
- pas_audio_prepare_for_input,
- pas_audio_prepare_for_output,
- pas_audio_reset,
- NULL,
- NULL,
- NULL,
- NULL,
- pas_audio_trigger
+ pas_audio_open,
+ pas_audio_close,
+ pas_audio_output_block,
+ pas_audio_start_input,
+ pas_audio_ioctl,
+ pas_audio_prepare_for_input,
+ pas_audio_prepare_for_output,
+ pas_audio_reset,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ pas_audio_trigger
};
void
-pas_pcm_init (struct address_info *hw_config)
+pas_pcm_init(struct address_info *hw_config)
{
- DEB (printk ("pas2_pcm.c: long pas_pcm_init()\n"));
-
- pcm_bitsok = 8;
- if (pas_read (0xEF8B) & 0x08)
- pcm_bitsok |= 16;
-
- pcm_set_speed (DSP_DEFAULT_SPEED);
-
- if (num_audiodevs < MAX_AUDIO_DEV)
- {
-
- if ((pas_audiodev = sound_install_audiodrv (AUDIO_DRIVER_VERSION,
- "Pro Audio Spectrum",
- &pas_audio_driver,
- sizeof (struct audio_driver),
- DMA_AUTOMODE,
- AFMT_U8 | AFMT_S16_LE,
- NULL,
- hw_config->dma,
- hw_config->dma)) < 0)
- {
- return;
- }
- }
- else
- printk ("PAS16: Too many PCM devices available\n");
+ DEB(printk("pas2_pcm.c: long pas_pcm_init()\n"));
+
+ pcm_bitsok = 8;
+ if (pas_read(0xEF8B) & 0x08)
+ pcm_bitsok |= 16;
+
+ pcm_set_speed(DSP_DEFAULT_SPEED);
+
+ if ((pas_audiodev = sound_alloc_audiodev()) != -1)
+ {
+
+ if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
+ "Pro Audio Spectrum",
+ &pas_audio_driver,
+ sizeof(struct audio_driver),
+ DMA_AUTOMODE,
+ AFMT_U8 | AFMT_S16_LE,
+ NULL,
+ hw_config->dma,
+ hw_config->dma)) < 0)
+ {
+ return;
+ }
+ } else
+ printk(KERN_WARNING "PAS16: Too many PCM devices available\n");
}
void
-pas_pcm_interrupt (unsigned char status, int cause)
+pas_pcm_interrupt(unsigned char status, int cause)
{
- if (cause == 1)
- {
- /*
- * Halt the PCM first. Otherwise we don't have time to start a new
- * block before the PCM chip proceeds to the next sample
- */
-
- if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE))
- {
- pas_write (pas_read (0xF8A) & ~0x40,
- 0xF8A);
- }
-
- switch (pcm_mode)
- {
-
- case PCM_DAC:
- DMAbuf_outputintr (pas_audiodev, 1);
- break;
-
- case PCM_ADC:
- DMAbuf_inputintr (pas_audiodev);
- break;
-
- default:
- printk ("PAS: Unexpected PCM interrupt\n");
- }
- }
+ if (cause == 1)
+ {
+ /*
+ * Halt the PCM first. Otherwise we don't have time to start a new
+ * block before the PCM chip proceeds to the next sample
+ */
+
+ if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE))
+ {
+ pas_write(pas_read(0xF8A) & ~0x40,
+ 0xF8A);
+ }
+ switch (pcm_mode)
+ {
+
+ case PCM_DAC:
+ DMAbuf_outputintr(pas_audiodev, 1);
+ break;
+
+ case PCM_ADC:
+ DMAbuf_inputintr(pas_audiodev);
+ break;
+
+ default:
+ printk("PAS: Unexpected PCM interrupt\n");
+ }
+ }
}
#endif
diff --git a/drivers/sound/pnp.c b/drivers/sound/pnp.c
deleted file mode 100644
index 9e07fa3ab..000000000
--- a/drivers/sound/pnp.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * sound/pnp.c - Temporary kludge for PnP soundcards.
- *
- * Copyright by Hannu Savolainen 1997.
- *
- * This file is just a temporary solution to be used with
- * PnP soundcards until final kernel PnP support gets ready.
- * The code contained in this file is largely untested and
- * may cause failures in some systems. In particular it will
- * cause troubles with other PnP ISA cards such as network cards.
- * This file is also incompatible with (future) PnP support in kernel.
- *
- * For the above reasons I don't want this file to be widely distributed.
- * You have permission to use this file with this particular sound driver
- * and only for your own evaluation purposes. Any other use of this file
- * or parts of it requires written permission by the author.
- */
-extern int pnp_trace;
-int pnp_trace_io = 0;
-
-#define UDELAY(x) udelay(x)
-
-#ifdef TEST_PNP
-#include "pnp.h"
-#include <malloc.h>
-#include <stdio.h>
-#define printk printf
-
-#define MALLOC(sz) malloc(sz)
-
-unsigned char res[10000];
-int rp;
-
-#else
-#include "sound_config.h"
-#endif
diff --git a/drivers/sound/pnp.h b/drivers/sound/pnp.h
deleted file mode 100644
index eb18c13eb..000000000
--- a/drivers/sound/pnp.h
+++ /dev/null
@@ -1,112 +0,0 @@
-#ifndef _PNP_H_
-#define _PNP_H_
-
-#define MAX_PNP_CARDS 16
-
-#define PNP_DEVID(a, b, c, id) \
- ((a-'@')<<26) | ((b-'@')<<21) | ((c-'@') << 16) | (id&0xffff)
-
-#define NO_PORT 0
-#define NO_IRQ 0
-#define NO_DMA 4
-
-struct pnp_port_resource
-{
- short range_min, range_max;
- unsigned char align, len;
-};
-
-struct pnp_func
- {
- struct pnp_dev *dev;
- unsigned long flags;
- struct pnp_func *next;
- int nports;
- struct pnp_port_resource ports[8];
- int nirq;
- unsigned short irq[2];
- int ndma;
- unsigned char dma[2];
- };
-
-struct pnp_dev
- {
- int key; /* A PnP device id identifying this device */
- char *name; /* ANSI identifier string */
- int devno; /* Logical device number within a card */
- int ncompat; /* Number of compatible device idents */
- int compat_keys[8]; /* List of PnP compatible device idents */
- struct pnp_card *card; /* Link to the card which holds this device */
- struct pnp_dev *next; /* Pointer to next logical device or NULL */
-
- int nports, nirq, ndma;
-
- int nfunc; /* Number of dependent function records */
- struct pnp_func *functions; /* List of dependent functions */
- int driver; /* Driver signature or 0 */
- int preconfig; /* 1 if config has been set manully */
-
- };
-
-struct pnp_card
- {
- int key; /* Unique PnP device identifier */
- char *name; /* ANSI identifier string of the card */
- int csn; /* Card select number */
-
- char pnp_version;
- char vendor_version;
-
- int driven; /* 0=No driver assigned, */
- /* 1=Drivers assigned to some of logical devices */
- /* 2=Card and all of it's devices have a driver */
- int relocated; /* 0=Card is inactive, 1=card is up and running */
-
- int ndevs; /* Number of logical devices on the card */
- struct pnp_dev *devices; /* Pointer to first function entry */
- };
-
-typedef struct pnp_card_info {
- struct pnp_card card;
- char name[64];
-} pnp_card_info;
-
-extern int pnp_card_count;
-extern struct pnp_card *pnp_cards[MAX_PNP_CARDS];
-extern struct pnp_dev *pnp_device_list;
-
-extern int pnp_trace;
-
-/*
- * Callable functions
- */
-
-extern void pnp_init(void); /* Called by kernel during boot */
-extern void terminate_pnp(void);
-
-extern int pnp_connect(char *driver_name);
-extern void pnp_disconnect(int driver_signature);
-
-/*
- * pnp_get_descr() returns an ASCII desctription string for a device.
- * The parameter is an compressed EISA identifier of the device/card.
- */
-extern char *pnp_get_descr (int id);
-
-extern void pnp_enable_device(struct pnp_dev *dev, int state);
-extern void pnp_set_port(struct pnp_dev *dev, int selec, unsigned short base);
-extern void pnp_set_irq(struct pnp_dev *dev, int selec, unsigned short val);
-extern void pnp_set_dma(struct pnp_dev *dev, int selec, unsigned short val);
-extern unsigned short pnp_get_port(struct pnp_dev *dev, int selec);
-extern unsigned short pnp_get_irq(struct pnp_dev *dev, int selec);
-extern unsigned short pnp_get_dma(struct pnp_dev *dev, int selec);
-extern int pnp_allocate_device(int driver_sig, struct pnp_dev *dev, int basemask, int irqmask,
- int dmamask, int memmask);
-extern void pnp_release_device(int driver_sig, struct pnp_dev *dev);
-extern int pnp_asc2devid(char *name);
-extern char *pnp_devid2asc(int id);
-extern void pnp_dump_resources(void);
-extern int pnp_device_status (struct pnp_dev *dev);
-struct pnp_dev *pnp_get_next_device(int driver_sig, struct pnp_dev *prev);
-unsigned char pnp_readreg (struct pnp_dev *dev, int reg);
-#endif
diff --git a/drivers/sound/pss.c b/drivers/sound/pss.c
index 52374790f..2a9080a8f 100644
--- a/drivers/sound/pss.c
+++ b/drivers/sound/pss.c
@@ -11,11 +11,13 @@
* for more info.
*/
#include <linux/config.h>
-
+#include <linux/module.h>
#include "sound_config.h"
+#include "sound_firmware.h"
+#include "soundmodule.h"
-#if defined(CONFIG_PSS) && defined(CONFIG_AUDIO)
+#if (defined(CONFIG_PSS) && defined(CONFIG_AUDIO))||defined(MODULE)
/*
* PSS registers.
@@ -60,10 +62,10 @@ NULL;
typedef struct pss_confdata
{
- int base;
- int irq;
- int dma;
- int *osp;
+ int base;
+ int irq;
+ int dma;
+ int *osp;
}
pss_confdata;
@@ -75,819 +77,844 @@ static int pss_initialized = 0;
static int nonstandard_microcode = 0;
static void
-pss_write (int data)
+pss_write(int data)
{
- int i, limit;
-
- limit = jiffies + 10; /* The timeout is 0.1 seconds */
- /*
- * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes
- * called while interrupts are disabled. This means that the timer is
- * disabled also. However the timeout situation is a abnormal condition.
- * Normally the DSP should be ready to accept commands after just couple of
- * loops.
- */
-
- for (i = 0; i < 5000000 && jiffies < limit; i++)
- {
- if (inw (devc->base + PSS_STATUS) & PSS_WRITE_EMPTY)
- {
- outw (devc->base + PSS_DATA, data);
- return;
- }
- }
- printk ("PSS: DSP Command (%04x) Timeout.\n", data);
+ int i, limit;
+
+ limit = jiffies + 10; /* The timeout is 0.1 seconds */
+ /*
+ * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes
+ * called while interrupts are disabled. This means that the timer is
+ * disabled also. However the timeout situation is a abnormal condition.
+ * Normally the DSP should be ready to accept commands after just couple of
+ * loops.
+ */
+
+ for (i = 0; i < 5000000 && jiffies < limit; i++)
+ {
+ if (inw(devc->base + PSS_STATUS) & PSS_WRITE_EMPTY)
+ {
+ outw(devc->base + PSS_DATA, data);
+ return;
+ }
+ }
+ printk("PSS: DSP Command (%04x) Timeout.\n", data);
}
int
-probe_pss (struct address_info *hw_config)
+probe_pss(struct address_info *hw_config)
{
- unsigned short id;
- int irq, dma;
-
- devc->base = hw_config->io_base;
- irq = devc->irq = hw_config->irq;
- dma = devc->dma = hw_config->dma;
- devc->osp = hw_config->osp;
-
- if (devc->base != 0x220 && devc->base != 0x240)
- if (devc->base != 0x230 && devc->base != 0x250) /* Some cards use these */
- return 0;
-
- if (check_region (devc->base, 16))
- {
- printk ("PSS: I/O port conflict\n");
- return 0;
- }
-
- id = inw (REG (PSS_ID));
- if ((id >> 8) != 'E')
- {
- /* printk ("No PSS signature detected at 0x%x (0x%x)\n", devc->base, id); */
- return 0;
- }
-
- return 1;
+ unsigned short id;
+ int irq, dma;
+
+ devc->base = hw_config->io_base;
+ irq = devc->irq = hw_config->irq;
+ dma = devc->dma = hw_config->dma;
+ devc->osp = hw_config->osp;
+
+ if (devc->base != 0x220 && devc->base != 0x240)
+ if (devc->base != 0x230 && devc->base != 0x250) /* Some cards use these */
+ return 0;
+
+ if (check_region(devc->base, 16))
+ {
+ printk("PSS: I/O port conflict\n");
+ return 0;
+ }
+ id = inw(REG(PSS_ID));
+ if ((id >> 8) != 'E')
+ {
+ /* printk( "No PSS signature detected at 0x%x (0x%x)\n", devc->base, id); */
+ return 0;
+ }
+ return 1;
}
static int
-set_irq (pss_confdata * devc, int dev, int irq)
+set_irq(pss_confdata * devc, int dev, int irq)
{
- static unsigned short irq_bits[16] =
- {
- 0x0000, 0x0000, 0x0000, 0x0008,
- 0x0000, 0x0010, 0x0000, 0x0018,
- 0x0000, 0x0020, 0x0028, 0x0030,
- 0x0038, 0x0000, 0x0000, 0x0000
- };
-
- unsigned short tmp, bits;
+ static unsigned short irq_bits[16] =
+ {
+ 0x0000, 0x0000, 0x0000, 0x0008,
+ 0x0000, 0x0010, 0x0000, 0x0018,
+ 0x0000, 0x0020, 0x0028, 0x0030,
+ 0x0038, 0x0000, 0x0000, 0x0000
+ };
- if (irq < 0 || irq > 15)
- return 0;
+ unsigned short tmp, bits;
- tmp = inw (REG (dev)) & ~0x38; /* Load confreg, mask IRQ bits out */
+ if (irq < 0 || irq > 15)
+ return 0;
- if ((bits = irq_bits[irq]) == 0 && irq != 0)
- {
- printk ("PSS: Invalid IRQ %d\n", irq);
- return 0;
- }
+ tmp = inw(REG(dev)) & ~0x38; /* Load confreg, mask IRQ bits out */
- outw (tmp | bits, REG (dev));
- return 1;
+ if ((bits = irq_bits[irq]) == 0 && irq != 0)
+ {
+ printk("PSS: Invalid IRQ %d\n", irq);
+ return 0;
+ }
+ outw(tmp | bits, REG(dev));
+ return 1;
}
static int
-set_io_base (pss_confdata * devc, int dev, int base)
+set_io_base(pss_confdata * devc, int dev, int base)
{
- unsigned short tmp = inw (REG (dev)) & 0x003f;
- unsigned short bits = (base & 0x0ffc) << 4;
+ unsigned short tmp = inw(REG(dev)) & 0x003f;
+ unsigned short bits = (base & 0x0ffc) << 4;
- outw (bits | tmp, REG (dev));
+ outw(bits | tmp, REG(dev));
- return 1;
+ return 1;
}
static int
-set_dma (pss_confdata * devc, int dev, int dma)
+set_dma(pss_confdata * devc, int dev, int dma)
{
- static unsigned short dma_bits[8] =
- {
- 0x0001, 0x0002, 0x0000, 0x0003,
- 0x0000, 0x0005, 0x0006, 0x0007
- };
-
- unsigned short tmp, bits;
+ static unsigned short dma_bits[8] =
+ {
+ 0x0001, 0x0002, 0x0000, 0x0003,
+ 0x0000, 0x0005, 0x0006, 0x0007
+ };
- if (dma < 0 || dma > 7)
- return 0;
+ unsigned short tmp, bits;
- tmp = inw (REG (dev)) & ~0x07; /* Load confreg, mask DMA bits out */
+ if (dma < 0 || dma > 7)
+ return 0;
- if ((bits = dma_bits[dma]) == 0 && dma != 4)
- {
- printk ("PSS: Invalid DMA %d\n", dma);
- return 0;
- }
+ tmp = inw(REG(dev)) & ~0x07; /* Load confreg, mask DMA bits out */
- outw (tmp | bits, REG (dev));
- return 1;
+ if ((bits = dma_bits[dma]) == 0 && dma != 4)
+ {
+ printk("PSS: Invalid DMA %d\n", dma);
+ return 0;
+ }
+ outw(tmp | bits, REG(dev));
+ return 1;
}
static int
-pss_reset_dsp (pss_confdata * devc)
+pss_reset_dsp(pss_confdata * devc)
{
- unsigned long i, limit = jiffies + 10;
+ unsigned long i, limit = jiffies + 10;
- outw (0x2000, REG (PSS_CONTROL));
+ outw(0x2000, REG(PSS_CONTROL));
- for (i = 0; i < 32768 && jiffies < limit; i++)
- inw (REG (PSS_CONTROL));
+ for (i = 0; i < 32768 && jiffies < limit; i++)
+ inw(REG(PSS_CONTROL));
- outw (0x0000, REG (PSS_CONTROL));
+ outw(0x0000, REG(PSS_CONTROL));
- return 1;
+ return 1;
}
static int
-pss_put_dspword (pss_confdata * devc, unsigned short word)
+pss_put_dspword(pss_confdata * devc, unsigned short word)
{
- int i, val;
+ int i, val;
- for (i = 0; i < 327680; i++)
- {
- val = inw (REG (PSS_STATUS));
- if (val & PSS_WRITE_EMPTY)
- {
- outw (word, REG (PSS_DATA));
- return 1;
- }
- }
- return 0;
+ for (i = 0; i < 327680; i++)
+ {
+ val = inw(REG(PSS_STATUS));
+ if (val & PSS_WRITE_EMPTY)
+ {
+ outw(word, REG(PSS_DATA));
+ return 1;
+ }
+ }
+ return 0;
}
static int
-pss_get_dspword (pss_confdata * devc, unsigned short *word)
+pss_get_dspword(pss_confdata * devc, unsigned short *word)
{
- int i, val;
+ int i, val;
- for (i = 0; i < 327680; i++)
- {
- val = inw (REG (PSS_STATUS));
- if (val & PSS_READ_FULL)
- {
- *word = inw (REG (PSS_DATA));
- return 1;
- }
- }
+ for (i = 0; i < 327680; i++)
+ {
+ val = inw(REG(PSS_STATUS));
+ if (val & PSS_READ_FULL)
+ {
+ *word = inw(REG(PSS_DATA));
+ return 1;
+ }
+ }
- return 0;
+ return 0;
}
static int
-pss_download_boot (pss_confdata * devc, unsigned char *block, int size, int flags)
+pss_download_boot(pss_confdata * devc, unsigned char *block, int size, int flags)
{
- int i, limit, val, count;
+ int i, limit, val, count;
- if (flags & CPF_FIRST)
- {
+ if (flags & CPF_FIRST)
+ {
/*_____ Warn DSP software that a boot is coming */
- outw (0x00fe, REG (PSS_DATA));
+ outw(0x00fe, REG(PSS_DATA));
- limit = jiffies + 10;
+ limit = jiffies + 10;
- for (i = 0; i < 32768 && jiffies < limit; i++)
- if (inw (REG (PSS_DATA)) == 0x5500)
- break;
+ for (i = 0; i < 32768 && jiffies < limit; i++)
+ if (inw(REG(PSS_DATA)) == 0x5500)
+ break;
- outw (*block++, REG (PSS_DATA));
+ outw(*block++, REG(PSS_DATA));
- pss_reset_dsp (devc);
- }
-
- count = 1;
- while (1)
- {
- int j;
+ pss_reset_dsp(devc);
+ }
+ count = 1;
+ while (1)
+ {
+ int j;
- for (j = 0; j < 327670; j++)
- {
+ for (j = 0; j < 327670; j++)
+ {
/*_____ Wait for BG to appear */
- if (inw (REG (PSS_STATUS)) & PSS_FLAG3)
- break;
- }
-
- if (j == 327670)
- {
- /* It's ok we timed out when the file was empty */
- if (count >= size && flags & CPF_LAST)
- break;
- else
- {
- printk ("\nPSS: Download timeout problems, byte %d=%d\n",
- count, size);
- return 0;
- }
- }
+ if (inw(REG(PSS_STATUS)) & PSS_FLAG3)
+ break;
+ }
+
+ if (j == 327670)
+ {
+ /* It's ok we timed out when the file was empty */
+ if (count >= size && flags & CPF_LAST)
+ break;
+ else
+ {
+ printk("\nPSS: Download timeout problems, byte %d=%d\n", count, size);
+ return 0;
+ }
+ }
/*_____ Send the next byte */
- outw (*block++, REG (PSS_DATA));
- count++;
- }
+ outw(*block++, REG(PSS_DATA));
+ count++;
+ }
- if (flags & CPF_LAST)
- {
+ if (flags & CPF_LAST)
+ {
/*_____ Why */
- outw (0, REG (PSS_DATA));
-
- limit = jiffies + 10;
- for (i = 0; i < 32768 && jiffies < limit; i++)
- val = inw (REG (PSS_STATUS));
-
- limit = jiffies + 10;
- for (i = 0; i < 32768 && jiffies < limit; i++)
- {
- val = inw (REG (PSS_STATUS));
- if (val & 0x4000)
- break;
- }
-
- /* now read the version */
- for (i = 0; i < 32000; i++)
- {
- val = inw (REG (PSS_STATUS));
- if (val & PSS_READ_FULL)
- break;
- }
- if (i == 32000)
- return 0;
-
- val = inw (REG (PSS_DATA));
- /* printk("<PSS: microcode version %d.%d loaded>", val/16, val % 16); */
- }
-
- return 1;
+ outw(0, REG(PSS_DATA));
+
+ limit = jiffies + 10;
+ for (i = 0; i < 32768 && jiffies < limit; i++)
+ val = inw(REG(PSS_STATUS));
+
+ limit = jiffies + 10;
+ for (i = 0; i < 32768 && jiffies < limit; i++)
+ {
+ val = inw(REG(PSS_STATUS));
+ if (val & 0x4000)
+ break;
+ }
+
+ /* now read the version */
+ for (i = 0; i < 32000; i++)
+ {
+ val = inw(REG(PSS_STATUS));
+ if (val & PSS_READ_FULL)
+ break;
+ }
+ if (i == 32000)
+ return 0;
+
+ val = inw(REG(PSS_DATA));
+ /* printk( "<PSS: microcode version %d.%d loaded>", val/16, val % 16); */
+ }
+ return 1;
}
void
-attach_pss (struct address_info *hw_config)
+attach_pss(struct address_info *hw_config)
{
- unsigned short id;
- char tmp[100];
+ unsigned short id;
+ char tmp[100];
- devc->base = hw_config->io_base;
- devc->irq = hw_config->irq;
- devc->dma = hw_config->dma;
- devc->osp = hw_config->osp;
+ devc->base = hw_config->io_base;
+ devc->irq = hw_config->irq;
+ devc->dma = hw_config->dma;
+ devc->osp = hw_config->osp;
- if (!probe_pss (hw_config))
- return;
+ if (!probe_pss(hw_config))
+ return;
- id = inw (REG (PSS_ID)) & 0x00ff;
+ id = inw(REG(PSS_ID)) & 0x00ff;
- /*
- * Disable all emulations. Will be enabled later (if required).
- */
- outw (0x0000, REG (CONF_PSS)); /* 0x0400 enables joystick */
- outw (0x0000, REG (CONF_WSS));
- outw (0x0000, REG (CONF_SB));
- outw (0x0000, REG (CONF_MIDI));
- outw (0x0000, REG (CONF_CDROM));
+ /*
+ * Disable all emulations. Will be enabled later (if required).
+ */
+ outw(0x0000, REG(CONF_PSS)); /* 0x0400 enables joystick */
+ outw(0x0000, REG(CONF_WSS));
+ outw(0x0000, REG(CONF_SB));
+ outw(0x0000, REG(CONF_MIDI));
+ outw(0x0000, REG(CONF_CDROM));
#if YOU_REALLY_WANT_TO_ALLOCATE_THESE_RESOURCES
- if (sound_alloc_dma (hw_config->dma, "PSS"))
- {
- printk ("pss.c: Can't allocate DMA channel\n");
- return;
- }
-
- if (!set_irq (devc, CONF_PSS, devc->irq))
- {
- printk ("PSS: IRQ error\n");
- return;
- }
-
- if (!set_dma (devc, CONF_PSS, devc->dma))
- {
- printk ("PSS: DRQ error\n");
- return;
- }
+ if (sound_alloc_dma(hw_config->dma, "PSS"))
+ {
+ printk("pss.c: Can't allocate DMA channel\n");
+ return;
+ }
+ if (!set_irq(devc, CONF_PSS, devc->irq))
+ {
+ printk("PSS: IRQ error\n");
+ return;
+ }
+ if (!set_dma(devc, CONF_PSS, devc->dma))
+ {
+ printk("PSS: DRQ error\n");
+ return;
+ }
#endif
- pss_initialized = 1;
- sprintf (tmp, "ECHO-PSS Rev. %d", id);
- conf_printf (tmp, hw_config);
+ pss_initialized = 1;
+ sprintf(tmp, "ECHO-PSS Rev. %d", id);
+ conf_printf(tmp, hw_config);
}
static void
-pss_init_speaker (void)
+pss_init_speaker(void)
{
/* Don't ask what are these commands. I really don't know */
- pss_write (0x0010);
- pss_write (0x0000 | 252); /* Left master volume */
- pss_write (0x0010);
- pss_write (0x0100 | 252); /* Right master volume */
- pss_write (0x0010);
- pss_write (0x0200 | 246); /* Bass */
- pss_write (0x0010);
- pss_write (0x0300 | 246); /* Treble */
- pss_write (0x0010);
- pss_write (0x0800 | 0x00ce); /* Stereo switch? */
+ pss_write(0x0010);
+ pss_write(0x0000 | 252); /* Left master volume */
+ pss_write(0x0010);
+ pss_write(0x0100 | 252); /* Right master volume */
+ pss_write(0x0010);
+ pss_write(0x0200 | 246); /* Bass */
+ pss_write(0x0010);
+ pss_write(0x0300 | 246); /* Treble */
+ pss_write(0x0010);
+ pss_write(0x0800 | 0x00ce); /* Stereo switch? */
}
int
-probe_pss_mpu (struct address_info *hw_config)
+probe_pss_mpu(struct address_info *hw_config)
{
- int timeout;
-
- if (!pss_initialized)
- return 0;
-
- if (check_region (hw_config->io_base, 2))
- {
- printk ("PSS: MPU I/O port conflict\n");
- return 0;
- }
-
- if (!set_io_base (devc, CONF_MIDI, hw_config->io_base))
- {
- printk ("PSS: MIDI base error.\n");
- return 0;
- }
-
- if (!set_irq (devc, CONF_MIDI, hw_config->irq))
- {
- printk ("PSS: MIDI IRQ error.\n");
- return 0;
- }
-
- if (!pss_synthLen)
- {
- printk ("PSS: Can't enable MPU. MIDI synth microcode not available.\n");
- return 0;
- }
-
- if (!pss_download_boot (devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
- {
- printk ("PSS: Unable to load MIDI synth microcode to DSP.\n");
- return 0;
- }
-
- pss_init_speaker ();
+ int timeout;
+
+ if (!pss_initialized)
+ return 0;
+
+ if (check_region(hw_config->io_base, 2))
+ {
+ printk("PSS: MPU I/O port conflict\n");
+ return 0;
+ }
+ if (!set_io_base(devc, CONF_MIDI, hw_config->io_base))
+ {
+ printk("PSS: MIDI base error.\n");
+ return 0;
+ }
+ if (!set_irq(devc, CONF_MIDI, hw_config->irq))
+ {
+ printk("PSS: MIDI IRQ error.\n");
+ return 0;
+ }
+ if (!pss_synthLen)
+ {
+ printk("PSS: Can't enable MPU. MIDI synth microcode not available.\n");
+ return 0;
+ }
+ if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
+ {
+ printk("PSS: Unable to load MIDI synth microcode to DSP.\n");
+ return 0;
+ }
+ pss_init_speaker();
/*
* Finally wait until the DSP algorithm has initialized itself and
* deactivates receive interrupt.
*/
- for (timeout = 900000; timeout > 0; timeout--)
- {
- if ((inb (hw_config->io_base + 1) & 0x80) == 0) /* Input data avail */
- inb (hw_config->io_base); /* Discard it */
- else
- break; /* No more input */
- }
+ for (timeout = 900000; timeout > 0; timeout--)
+ {
+ if ((inb(hw_config->io_base + 1) & 0x80) == 0) /* Input data avail */
+ inb(hw_config->io_base); /* Discard it */
+ else
+ break; /* No more input */
+ }
#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI)
- return probe_mpu401 (hw_config);
+ return probe_mpu401(hw_config);
#else
- return 0;
+ return 0;
#endif
}
static int
-pss_coproc_open (void *dev_info, int sub_device)
+pss_coproc_open(void *dev_info, int sub_device)
{
- switch (sub_device)
- {
- case COPR_MIDI:
-
- if (pss_synthLen == 0)
- {
- printk ("PSS: MIDI synth microcode not available.\n");
- return -EIO;
- }
-
- if (nonstandard_microcode)
- if (!pss_download_boot (devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
+ switch (sub_device)
{
- printk ("PSS: Unable to load MIDI synth microcode to DSP.\n");
- return -EIO;
+ case COPR_MIDI:
+
+ if (pss_synthLen == 0)
+ {
+ printk("PSS: MIDI synth microcode not available.\n");
+ return -EIO;
+ }
+ if (nonstandard_microcode)
+ if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
+ {
+ printk("PSS: Unable to load MIDI synth microcode to DSP.\n");
+ return -EIO;
+ }
+ nonstandard_microcode = 0;
+ break;
+
+ default:;
}
- nonstandard_microcode = 0;
- break;
-
- default:;
- }
- return 0;
+ return 0;
}
static void
-pss_coproc_close (void *dev_info, int sub_device)
+pss_coproc_close(void *dev_info, int sub_device)
{
- return;
+ return;
}
static void
-pss_coproc_reset (void *dev_info)
+pss_coproc_reset(void *dev_info)
{
- if (pss_synthLen)
- if (!pss_download_boot (devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
- {
- printk ("PSS: Unable to load MIDI synth microcode to DSP.\n");
- }
- nonstandard_microcode = 0;
+ if (pss_synthLen)
+ if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
+ {
+ printk("PSS: Unable to load MIDI synth microcode to DSP.\n");
+ }
+ nonstandard_microcode = 0;
}
static int
-download_boot_block (void *dev_info, copr_buffer * buf)
+download_boot_block(void *dev_info, copr_buffer * buf)
{
- if (buf->len <= 0 || buf->len > sizeof (buf->data))
- return -EINVAL;
-
- if (!pss_download_boot (devc, buf->data, buf->len, buf->flags))
- {
- printk ("PSS: Unable to load microcode block to DSP.\n");
- return -EIO;
- }
- nonstandard_microcode = 1; /* The MIDI microcode has been overwritten */
-
- return 0;
-}
+ if (buf->len <= 0 || buf->len > sizeof(buf->data))
+ return -EINVAL;
-static int
-pss_coproc_ioctl (void *dev_info, unsigned int cmd, caddr_t arg, int local)
-{
- /* printk("PSS coproc ioctl %x %x %d\n", cmd, arg, local); */
-
- switch (cmd)
- {
- case SNDCTL_COPR_RESET:
- pss_coproc_reset (dev_info);
- return 0;
- break;
-
- case SNDCTL_COPR_LOAD:
- {
- copr_buffer *buf;
- int err;
-
- buf = (copr_buffer *) vmalloc (sizeof (copr_buffer));
- if (buf == NULL)
- return -ENOSPC;
-
- memcpy ((char *) buf, (&((char *) arg)[0]), sizeof (*buf));
- err = download_boot_block (dev_info, buf);
- vfree (buf);
- return err;
- }
- break;
-
- case SNDCTL_COPR_SENDMSG:
- {
- copr_msg *buf;
- unsigned long flags;
- unsigned short *data;
- int i;
-
- buf = (copr_msg *) vmalloc (sizeof (copr_msg));
- if (buf == NULL)
- return -ENOSPC;
-
- memcpy ((char *) buf, (&((char *) arg)[0]), sizeof (*buf));
-
- data = (unsigned short *) (buf->data);
-
- save_flags (flags);
- cli ();
-
- for (i = 0; i < buf->len; i++)
+ if (!pss_download_boot(devc, buf->data, buf->len, buf->flags))
{
- if (!pss_put_dspword (devc, *data++))
- {
- restore_flags (flags);
- buf->len = i; /* feed back number of WORDs sent */
- memcpy ((&((char *) arg)[0]), (char *) buf, sizeof (*buf));
- vfree (buf);
- return -EIO;
- }
+ printk("PSS: Unable to load microcode block to DSP.\n");
+ return -EIO;
}
-
- restore_flags (flags);
- vfree (buf);
+ nonstandard_microcode = 1; /* The MIDI microcode has been overwritten */
return 0;
- }
- break;
-
-
- case SNDCTL_COPR_RCVMSG:
- {
- copr_msg *buf;
- unsigned long flags;
- unsigned short *data;
- unsigned int i;
- int err = 0;
-
- buf = (copr_msg *) vmalloc (sizeof (copr_msg));
- if (buf == NULL)
- return -ENOSPC;
-
-
- data = (unsigned short *) buf->data;
+}
- save_flags (flags);
- cli ();
+static int
+pss_coproc_ioctl(void *dev_info, unsigned int cmd, caddr_t arg, int local)
+{
+ /* printk( "PSS coproc ioctl %x %x %d\n", cmd, arg, local); */
- for (i = 0; i < buf->len; i++)
+ switch (cmd)
{
- buf->len = i; /* feed back number of WORDs read */
- if (!pss_get_dspword (devc, data++))
- {
- if (i == 0)
- err = -EIO;
- break;
- }
+ case SNDCTL_COPR_RESET:
+ pss_coproc_reset(dev_info);
+ return 0;
+ break;
+
+ case SNDCTL_COPR_LOAD:
+ {
+ copr_buffer *buf;
+ int err;
+
+ buf = (copr_buffer *) vmalloc(sizeof(copr_buffer));
+ if (buf == NULL)
+ return -ENOSPC;
+
+ memcpy((char *) buf, (&((char *) arg)[0]), sizeof(*buf));
+ err = download_boot_block(dev_info, buf);
+ vfree(buf);
+ return err;
+ }
+ break;
+
+ case SNDCTL_COPR_SENDMSG:
+ {
+ copr_msg *buf;
+ unsigned long flags;
+ unsigned short *data;
+ int i;
+
+ buf = (copr_msg *) vmalloc(sizeof(copr_msg));
+ if (buf == NULL)
+ return -ENOSPC;
+
+ memcpy((char *) buf, (&((char *) arg)[0]), sizeof(*buf));
+
+ data = (unsigned short *) (buf->data);
+
+ save_flags(flags);
+ cli();
+
+ for (i = 0; i < buf->len; i++)
+ {
+ if (!pss_put_dspword(devc, *data++))
+ {
+ restore_flags(flags);
+ buf->len = i; /* feed back number of WORDs sent */
+ memcpy((&((char *) arg)[0]), (char *) buf, sizeof(*buf));
+ vfree(buf);
+ return -EIO;
+ }
+ }
+
+ restore_flags(flags);
+ vfree(buf);
+
+ return 0;
+ }
+ break;
+
+
+ case SNDCTL_COPR_RCVMSG:
+ {
+ copr_msg *buf;
+ unsigned long flags;
+ unsigned short *data;
+ unsigned int i;
+ int err = 0;
+
+ buf = (copr_msg *) vmalloc(sizeof(copr_msg));
+ if (buf == NULL)
+ return -ENOSPC;
+
+
+ data = (unsigned short *) buf->data;
+
+ save_flags(flags);
+ cli();
+
+ for (i = 0; i < buf->len; i++)
+ {
+ buf->len = i; /* feed back number of WORDs read */
+ if (!pss_get_dspword(devc, data++))
+ {
+ if (i == 0)
+ err = -EIO;
+ break;
+ }
+ }
+
+ restore_flags(flags);
+
+ memcpy((&((char *) arg)[0]), (char *) buf, sizeof(*buf));
+ vfree(buf);
+
+ return err;
+ }
+ break;
+
+
+ case SNDCTL_COPR_RDATA:
+ {
+ copr_debug_buf buf;
+ unsigned long flags;
+ unsigned short tmp;
+
+ memcpy((char *) &buf, (&((char *) arg)[0]), sizeof(buf));
+
+ save_flags(flags);
+ cli();
+ if (!pss_put_dspword(devc, 0x00d0))
+ {
+ restore_flags(flags);
+ return -EIO;
+ }
+ if (!pss_put_dspword(devc, (unsigned short) (buf.parm1 & 0xffff)))
+ {
+ restore_flags(flags);
+ return -EIO;
+ }
+ if (!pss_get_dspword(devc, &tmp))
+ {
+ restore_flags(flags);
+ return -EIO;
+ }
+ buf.parm1 = tmp;
+ restore_flags(flags);
+
+ memcpy((&((char *) arg)[0]), (char *) &buf, sizeof(buf));
+ return 0;
+ }
+ break;
+
+ case SNDCTL_COPR_WDATA:
+ {
+ copr_debug_buf buf;
+ unsigned long flags;
+ unsigned short tmp;
+
+ memcpy((char *) &buf, (&((char *) arg)[0]), sizeof(buf));
+
+ save_flags(flags);
+ cli();
+ if (!pss_put_dspword(devc, 0x00d1))
+ {
+ restore_flags(flags);
+ return -EIO;
+ }
+ if (!pss_put_dspword(devc, (unsigned short) (buf.parm1 & 0xffff)))
+ {
+ restore_flags(flags);
+ return -EIO;
+ }
+ tmp = (unsigned int) buf.parm2 & 0xffff;
+ if (!pss_put_dspword(devc, tmp))
+ {
+ restore_flags(flags);
+ return -EIO;
+ }
+ restore_flags(flags);
+ return 0;
+ }
+ break;
+
+ case SNDCTL_COPR_WCODE:
+ {
+ copr_debug_buf buf;
+ unsigned long flags;
+ unsigned short tmp;
+
+ memcpy((char *) &buf, (&((char *) arg)[0]), sizeof(buf));
+
+ save_flags(flags);
+ cli();
+ if (!pss_put_dspword(devc, 0x00d3))
+ {
+ restore_flags(flags);
+ return -EIO;
+ }
+ if (!pss_put_dspword(devc, (unsigned short) (buf.parm1 & 0xffff)))
+ {
+ restore_flags(flags);
+ return -EIO;
+ }
+ tmp = (unsigned int) buf.parm2 & 0x00ff;
+ if (!pss_put_dspword(devc, tmp))
+ {
+ restore_flags(flags);
+ return -EIO;
+ }
+ tmp = ((unsigned int) buf.parm2 >> 8) & 0xffff;
+ if (!pss_put_dspword(devc, tmp))
+ {
+ restore_flags(flags);
+ return -EIO;
+ }
+ restore_flags(flags);
+ return 0;
+ }
+ break;
+
+ case SNDCTL_COPR_RCODE:
+ {
+ copr_debug_buf buf;
+ unsigned long flags;
+ unsigned short tmp;
+
+ memcpy((char *) &buf, (&((char *) arg)[0]), sizeof(buf));
+
+ save_flags(flags);
+ cli();
+ if (!pss_put_dspword(devc, 0x00d2))
+ {
+ restore_flags(flags);
+ return -EIO;
+ }
+ if (!pss_put_dspword(devc, (unsigned short) (buf.parm1 & 0xffff)))
+ {
+ restore_flags(flags);
+ return -EIO;
+ }
+ if (!pss_get_dspword(devc, &tmp)) /* Read MSB */
+ {
+ restore_flags(flags);
+ return -EIO;
+ }
+ buf.parm1 = tmp << 8;
+
+ if (!pss_get_dspword(devc, &tmp)) /* Read LSB */
+ {
+ restore_flags(flags);
+ return -EIO;
+ }
+ buf.parm1 |= tmp & 0x00ff;
+
+ restore_flags(flags);
+
+ memcpy((&((char *) arg)[0]), (char *) &buf, sizeof(buf));
+ return 0;
+ }
+ break;
+
+ default:
+ return -EINVAL;
}
- restore_flags (flags);
-
- memcpy ((&((char *) arg)[0]), (char *) buf, sizeof (*buf));
- vfree (buf);
-
- return err;
- }
- break;
+ return -EINVAL;
+}
+static coproc_operations pss_coproc_operations =
+{
+ "ADSP-2115",
+ pss_coproc_open,
+ pss_coproc_close,
+ pss_coproc_ioctl,
+ pss_coproc_reset,
+ &pss_data
+};
- case SNDCTL_COPR_RDATA:
- {
- copr_debug_buf buf;
- unsigned long flags;
- unsigned short tmp;
+void
+attach_pss_mpu(struct address_info *hw_config)
+{
+#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI)
+ {
+ attach_mpu401(hw_config); /* Slot 1 */
- memcpy ((char *) &buf, (&((char *) arg)[0]), sizeof (buf));
+ if (hw_config->slots[1] != -1) /* The MPU driver installed itself */
+ midi_devs[hw_config->slots[1]]->coproc = &pss_coproc_operations;
+ }
+#endif
+}
- save_flags (flags);
- cli ();
- if (!pss_put_dspword (devc, 0x00d0))
- {
- restore_flags (flags);
- return -EIO;
- }
+int
+probe_pss_mss(struct address_info *hw_config)
+{
+ volatile int timeout;
- if (!pss_put_dspword (devc, (unsigned short) (buf.parm1 & 0xffff)))
- {
- restore_flags (flags);
- return -EIO;
- }
+ if (!pss_initialized)
+ return 0;
- if (!pss_get_dspword (devc, &tmp))
+ if (check_region(hw_config->io_base, 8))
{
- restore_flags (flags);
- return -EIO;
+ printk("PSS: WSS I/O port conflict\n");
+ return 0;
}
-
- buf.parm1 = tmp;
- restore_flags (flags);
-
- memcpy ((&((char *) arg)[0]), (char *) &buf, sizeof (buf));
- return 0;
- }
- break;
-
- case SNDCTL_COPR_WDATA:
- {
- copr_debug_buf buf;
- unsigned long flags;
- unsigned short tmp;
-
- memcpy ((char *) &buf, (&((char *) arg)[0]), sizeof (buf));
-
- save_flags (flags);
- cli ();
- if (!pss_put_dspword (devc, 0x00d1))
+ if (!set_io_base(devc, CONF_WSS, hw_config->io_base))
{
- restore_flags (flags);
- return -EIO;
+ printk("PSS: WSS base error.\n");
+ return 0;
}
-
- if (!pss_put_dspword (devc, (unsigned short) (buf.parm1 & 0xffff)))
+ if (!set_irq(devc, CONF_WSS, hw_config->irq))
{
- restore_flags (flags);
- return -EIO;
+ printk("PSS: WSS IRQ error.\n");
+ return 0;
}
-
- tmp = (unsigned int) buf.parm2 & 0xffff;
- if (!pss_put_dspword (devc, tmp))
+ if (!set_dma(devc, CONF_WSS, hw_config->dma))
{
- restore_flags (flags);
- return -EIO;
+ printk("PSS: WSS DRQ error\n");
+ return 0;
}
+ /*
+ * For some reason the card returns 0xff in the WSS status register
+ * immediately after boot. Probably MIDI+SB emulation algorithm
+ * downloaded to the ADSP2115 spends some time initializing the card.
+ * Let's try to wait until it finishes this task.
+ */
+ for (timeout = 0;
+ timeout < 100000 && (inb(hw_config->io_base + 3) & 0x3f) != 0x04;
+ timeout++);
+
+ outb((0x0b), hw_config->io_base + 4); /* Required by some cards */
+
+ for (timeout = 0;
+ timeout < 100000;
+ timeout++);
+ return probe_ms_sound(hw_config);
+}
- restore_flags (flags);
- return 0;
- }
- break;
+void
+attach_pss_mss(struct address_info *hw_config)
+{
+ attach_ms_sound(hw_config); /* Slot 0 */
- case SNDCTL_COPR_WCODE:
- {
- copr_debug_buf buf;
- unsigned long flags;
- unsigned short tmp;
+ if (hw_config->slots[0] != -1) /* The MSS driver installed itself */
+ audio_devs[hw_config->slots[0]]->coproc = &pss_coproc_operations;
+}
- memcpy ((char *) &buf, (&((char *) arg)[0]), sizeof (buf));
+void
+unload_pss(struct address_info *hw_config)
+{
+}
- save_flags (flags);
- cli ();
- if (!pss_put_dspword (devc, 0x00d3))
- {
- restore_flags (flags);
- return -EIO;
- }
+void
+unload_pss_mpu(struct address_info *hw_config)
+{
+#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI)
+ unload_mpu401(hw_config);
+#endif
+}
- if (!pss_put_dspword (devc, (unsigned short) (buf.parm1 & 0xffff)))
- {
- restore_flags (flags);
- return -EIO;
- }
+void
+unload_pss_mss(struct address_info *hw_config)
+{
+ unload_ms_sound(hw_config);
+}
- tmp = (unsigned int) buf.parm2 & 0x00ff;
- if (!pss_put_dspword (devc, tmp))
- {
- restore_flags (flags);
- return -EIO;
- }
+#ifdef MODULE
- tmp = ((unsigned int) buf.parm2 >> 8) & 0xffff;
- if (!pss_put_dspword (devc, tmp))
- {
- restore_flags (flags);
- return -EIO;
- }
+int io = -1;
+int irq = -1;
+int dma = -1;
- restore_flags (flags);
- return 0;
- }
- break;
+int pssmpu, pssmss;
+struct address_info cfg;
- case SNDCTL_COPR_RCODE:
- {
- copr_debug_buf buf;
- unsigned long flags;
- unsigned short tmp;
+static int fw_load = 0;
- memcpy ((char *) &buf, (&((char *) arg)[0]), sizeof (buf));
+/*
+ * Load a PSS sound card module
+ */
- save_flags (flags);
- cli ();
- if (!pss_put_dspword (devc, 0x00d2))
+int
+init_module(void)
+{
+ if (io == -1 || irq == -1 || dma == -1)
{
- restore_flags (flags);
- return -EIO;
+ printk("pss: dma, irq and io must be set.\n");
+ return -EINVAL;
}
+ cfg.io_base = io;
+ cfg.irq = irq;
- if (!pss_put_dspword (devc, (unsigned short) (buf.parm1 & 0xffff)))
+ if (!pss_synth)
{
- restore_flags (flags);
- return -EIO;
+ fw_load = 1;
+ pss_synthLen = mod_firmware_load("/etc/sound/pss_synth", (void *) &pss_synth);
}
-
- if (!pss_get_dspword (devc, &tmp)) /* Read MSB */
+ if (probe_pss(&cfg))
+ return -ENODEV;
+ /*
+ * Attach stuff
+ */
+ if (probe_pss_mpu(&cfg))
{
- restore_flags (flags);
- return -EIO;
+ pssmpu = 1;
+ attach_pss_mpu(&cfg);
}
-
- buf.parm1 = tmp << 8;
-
- if (!pss_get_dspword (devc, &tmp)) /* Read LSB */
+ if (probe_pss_mss(&cfg))
{
- restore_flags (flags);
- return -EIO;
+ pssmss = 1;
+ attach_pss_mss(&cfg);
}
-
- buf.parm1 |= tmp & 0x00ff;
-
- restore_flags (flags);
-
- memcpy ((&((char *) arg)[0]), (char *) &buf, sizeof (buf));
+ SOUND_LOCK;
return 0;
- }
- break;
-
- default:
- return -EINVAL;
- }
-
- return -EINVAL;
-}
-
-static coproc_operations pss_coproc_operations =
-{
- "ADSP-2115",
- pss_coproc_open,
- pss_coproc_close,
- pss_coproc_ioctl,
- pss_coproc_reset,
- &pss_data
-};
-
-void
-attach_pss_mpu (struct address_info *hw_config)
-{
-#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI)
- {
- int prev_devs;
-
- prev_devs = num_midis;
- attach_mpu401 (hw_config);
-
- if (num_midis == (prev_devs + 1)) /* The MPU driver installed itself */
- midi_devs[prev_devs]->coproc = &pss_coproc_operations;
- }
-#endif
-}
-
-int
-probe_pss_mss (struct address_info *hw_config)
-{
- volatile int timeout;
-
- if (!pss_initialized)
- return 0;
-
- if (check_region (hw_config->io_base, 8))
- {
- printk ("PSS: WSS I/O port conflict\n");
- return 0;
- }
-
- if (!set_io_base (devc, CONF_WSS, hw_config->io_base))
- {
- printk ("PSS: WSS base error.\n");
- return 0;
- }
-
- if (!set_irq (devc, CONF_WSS, hw_config->irq))
- {
- printk ("PSS: WSS IRQ error.\n");
- return 0;
- }
-
- if (!set_dma (devc, CONF_WSS, hw_config->dma))
- {
- printk ("PSS: WSS DRQ error\n");
- return 0;
- }
-
- /*
- * For some reason the card returns 0xff in the WSS status register
- * immediately after boot. Probably MIDI+SB emulation algorithm
- * downloaded to the ADSP2115 spends some time initializing the card.
- * Let's try to wait until it finishes this task.
- */
- for (timeout = 0;
- timeout < 100000 && (inb (hw_config->io_base + 3) & 0x3f) != 0x04;
- timeout++);
-
- outb ((0x0b), hw_config->io_base + 4); /* Required by some cards */
-
- for (timeout = 0;
- timeout < 100000;
- timeout++);
- return probe_ms_sound (hw_config);
-}
-
-void
-attach_pss_mss (struct address_info *hw_config)
-{
- int prev_devs;
-
- prev_devs = num_audiodevs;
- attach_ms_sound (hw_config);
-
- if (num_audiodevs == (prev_devs + 1)) /* The MSS driver installed itself */
- audio_devs[prev_devs]->coproc = &pss_coproc_operations;
}
-void
-unload_pss (struct address_info *hw_config)
+void
+cleanup_module(void)
{
+ if (fw_load && pss_synth)
+ kfree(pss_synth);
+ if (pssmss)
+ unload_pss_mss(&cfg);
+ if (pssmpu)
+ unload_pss_mpu(&cfg);
+ unload_pss(&cfg);
+ SOUND_LOCK_END;
}
-
-void
-unload_pss_mpu (struct address_info *hw_config)
-{
-#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI)
- unload_mpu401 (hw_config);
#endif
-}
-
-void
-unload_pss_mss (struct address_info *hw_config)
-{
- unload_ms_sound (hw_config);
-}
#endif
diff --git a/drivers/sound/sb.h b/drivers/sound/sb.h
index ba2f3be18..861f13c3b 100644
--- a/drivers/sound/sb.h
+++ b/drivers/sound/sb.h
@@ -1,3 +1,4 @@
+#ifdef CONFIG_SBDSP
#define DSP_RESET (devc->base + 0x6)
#define DSP_READ (devc->base + 0xA)
#define DSP_WRITE (devc->base + 0xC)
@@ -124,3 +125,6 @@ void sb_audio_init (sb_devc *devc, char *name);
void sb_midi_interrupt (sb_devc *devc);
int ess_write (sb_devc *devc, unsigned char reg, unsigned char data);
int ess_read (sb_devc *devc, unsigned char reg);
+
+extern int acer;
+#endif
diff --git a/drivers/sound/sb_audio.c b/drivers/sound/sb_audio.c
index a4e29ba8c..13751da3d 100644
--- a/drivers/sound/sb_audio.c
+++ b/drivers/sound/sb_audio.c
@@ -15,89 +15,88 @@
#include "sound_config.h"
-#if defined(CONFIG_SBDSP)
+#if defined(CONFIG_SBDSP) || defined(MODULE)
#include "sb_mixer.h"
#include "sb.h"
static int
-sb_audio_open (int dev, int mode)
+sb_audio_open(int dev, int mode)
{
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned long flags;
-
- if (devc == NULL)
- {
- printk ("SB: Incomplete initialization\n");
- return -ENXIO;
- }
-
- if (devc->caps & SB_NO_RECORDING && mode & OPEN_READ)
- {
- printk ("SB: Recording is not possible with this device\n");
- return -EPERM;
- }
-
- save_flags (flags);
- cli ();
- if (devc->opened)
- {
- restore_flags (flags);
- return -EBUSY;
- }
-
- if (devc->dma16 != -1 && devc->dma16 != devc->dma8)
- {
- if (sound_open_dma (devc->dma16, "Sound Blaster 16 bit"))
- {
- return -EBUSY;
- }
- }
- devc->opened = mode;
- restore_flags (flags);
-
- devc->irq_mode = IMODE_NONE;
- sb_dsp_reset (devc);
-
- return 0;
+ sb_devc *devc = audio_devs[dev]->devc;
+ unsigned long flags;
+
+ if (devc == NULL)
+ {
+ printk("SB: Incomplete initialization\n");
+ return -ENXIO;
+ }
+ if (devc->caps & SB_NO_RECORDING && mode & OPEN_READ)
+ {
+ printk("Notice: Recording is not possible with /dev/dsp%d\n", dev);
+ if (mode == OPEN_READ)
+ return -EPERM;
+ }
+ save_flags(flags);
+ cli();
+ if (devc->opened)
+ {
+ restore_flags(flags);
+ return -EBUSY;
+ }
+ if (devc->dma16 != -1 && devc->dma16 != devc->dma8)
+ {
+ if (sound_open_dma(devc->dma16, "Sound Blaster 16 bit"))
+ {
+ restore_flags(flags);
+ return -EBUSY;
+ }
+ }
+ devc->opened = mode;
+ restore_flags(flags);
+
+ devc->irq_mode = IMODE_NONE;
+ sb_dsp_reset(devc);
+
+ return 0;
}
static void
-sb_audio_close (int dev)
+sb_audio_close(int dev)
{
- sb_devc *devc = audio_devs[dev]->devc;
+ sb_devc *devc = audio_devs[dev]->devc;
- audio_devs[dev]->dmap_in->dma =
- audio_devs[dev]->dmap_out->dma =
- devc->dma8;
+ audio_devs[dev]->dmap_in->dma =
+ audio_devs[dev]->dmap_out->dma =
+ devc->dma8;
- if (devc->dma16 != -1 && devc->dma16 != devc->dma8)
- sound_close_dma (devc->dma16);
+ if (devc->dma16 != -1 && devc->dma16 != devc->dma8)
+ sound_close_dma(devc->dma16);
- devc->opened = 0;
+ devc->opened = 0;
}
static void
-sb_set_output_parms (int dev, unsigned long buf, int nr_bytes,
- int intrflag)
+sb_set_output_parms(int dev, unsigned long buf, int nr_bytes,
+ int intrflag)
{
- sb_devc *devc = audio_devs[dev]->devc;
+ sb_devc *devc = audio_devs[dev]->devc;
- devc->trg_buf = buf;
- devc->trg_bytes = nr_bytes;
- devc->trg_intrflag = intrflag;
- devc->irq_mode = IMODE_OUTPUT;
+ devc->trg_buf = buf;
+ devc->trg_bytes = nr_bytes;
+ devc->trg_intrflag = intrflag;
+ devc->irq_mode = IMODE_OUTPUT;
}
static void
-sb_set_input_parms (int dev, unsigned long buf, int count, int intrflag)
+sb_set_input_parms(int dev, unsigned long buf, int count, int intrflag)
{
- sb_devc *devc = audio_devs[dev]->devc;
+ sb_devc *devc = audio_devs[dev]->devc;
- devc->trg_buf = buf;
- devc->trg_bytes = count;
- devc->trg_intrflag = intrflag;
- devc->irq_mode = IMODE_INPUT;
+ devc->trg_buf = buf;
+ devc->trg_bytes = count;
+ devc->trg_intrflag = intrflag;
+ devc->irq_mode = IMODE_INPUT;
}
/*
@@ -105,183 +104,180 @@ sb_set_input_parms (int dev, unsigned long buf, int count, int intrflag)
*/
static void
-sb1_audio_output_block (int dev, unsigned long buf, int nr_bytes,
- int intrflag)
+sb1_audio_output_block(int dev, unsigned long buf, int nr_bytes,
+ int intrflag)
{
- unsigned long flags;
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
- count--;
-
- devc->irq_mode = IMODE_OUTPUT;
-
- save_flags (flags);
- cli ();
- if (sb_dsp_command (devc, 0x14)) /* 8 bit DAC using DMA */
- {
- sb_dsp_command (devc, (unsigned char) (count & 0xff));
- sb_dsp_command (devc, (unsigned char) ((count >> 8) & 0xff));
- }
- else
- printk ("SB: Unable to start DAC\n");
- restore_flags (flags);
- devc->intr_active = 1;
+ unsigned long flags;
+ int count = nr_bytes;
+ sb_devc *devc = audio_devs[dev]->devc;
+
+ /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
+
+ if (audio_devs[dev]->dmap_out->dma > 3)
+ count >>= 1;
+ count--;
+
+ devc->irq_mode = IMODE_OUTPUT;
+
+ save_flags(flags);
+ cli();
+ if (sb_dsp_command(devc, 0x14)) /* 8 bit DAC using DMA */
+ {
+ sb_dsp_command(devc, (unsigned char) (count & 0xff));
+ sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
+ } else
+ printk("SB: Unable to start DAC\n");
+ restore_flags(flags);
+ devc->intr_active = 1;
}
static void
-sb1_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag)
+sb1_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag)
{
- unsigned long flags;
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
-
- /*
- * Start a DMA input to the buffer pointed by dmaqtail
- */
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
- count--;
-
- devc->irq_mode = IMODE_INPUT;
-
- save_flags (flags);
- cli ();
- if (sb_dsp_command (devc, 0x24)) /* 8 bit ADC using DMA */
- {
- sb_dsp_command (devc, (unsigned char) (count & 0xff));
- sb_dsp_command (devc, (unsigned char) ((count >> 8) & 0xff));
- }
- else
- printk ("SB Error: Unable to start ADC\n");
- restore_flags (flags);
-
- devc->intr_active = 1;
-}
+ unsigned long flags;
+ int count = nr_bytes;
+ sb_devc *devc = audio_devs[dev]->devc;
-static void
-sb1_audio_trigger (int dev, int bits)
-{
- sb_devc *devc = audio_devs[dev]->devc;
+ /*
+ * Start a DMA input to the buffer pointed by dmaqtail
+ */
- bits &= devc->irq_mode;
+ /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
- if (!bits)
- sb_dsp_command (devc, 0xd0); /* Halt DMA */
- else
- {
- switch (devc->irq_mode)
- {
- case IMODE_INPUT:
- sb1_audio_start_input (dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
-
- case IMODE_OUTPUT:
- sb1_audio_output_block (dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
- }
- }
+ if (audio_devs[dev]->dmap_out->dma > 3)
+ count >>= 1;
+ count--;
- devc->trigger_bits = bits;
+ devc->irq_mode = IMODE_INPUT;
+
+ save_flags(flags);
+ cli();
+ if (sb_dsp_command(devc, 0x24)) /* 8 bit ADC using DMA */
+ {
+ sb_dsp_command(devc, (unsigned char) (count & 0xff));
+ sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
+ } else
+ printk("SB Error: Unable to start ADC\n");
+ restore_flags(flags);
+
+ devc->intr_active = 1;
}
-static int
-sb1_audio_prepare_for_input (int dev, int bsize, int bcount)
+static void
+sb1_audio_trigger(int dev, int bits)
{
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned long flags;
-
- save_flags (flags);
- cli ();
- if (sb_dsp_command (devc, 0x40))
- sb_dsp_command (devc, devc->tconst);
- sb_dsp_command (devc, DSP_CMD_SPKOFF);
- restore_flags (flags);
-
- devc->trigger_bits = 0;
- return 0;
+ sb_devc *devc = audio_devs[dev]->devc;
+
+ bits &= devc->irq_mode;
+
+ if (!bits)
+ sb_dsp_command(devc, 0xd0); /* Halt DMA */
+ else
+ {
+ switch (devc->irq_mode)
+ {
+ case IMODE_INPUT:
+ sb1_audio_start_input(dev, devc->trg_buf, devc->trg_bytes,
+ devc->trg_intrflag);
+ break;
+
+ case IMODE_OUTPUT:
+ sb1_audio_output_block(dev, devc->trg_buf, devc->trg_bytes,
+ devc->trg_intrflag);
+ break;
+ }
+ }
+
+ devc->trigger_bits = bits;
}
static int
-sb1_audio_prepare_for_output (int dev, int bsize, int bcount)
+sb1_audio_prepare_for_input(int dev, int bsize, int bcount)
{
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned long flags;
-
- save_flags (flags);
- cli ();
- if (sb_dsp_command (devc, 0x40))
- sb_dsp_command (devc, devc->tconst);
- sb_dsp_command (devc, DSP_CMD_SPKON);
- restore_flags (flags);
- devc->trigger_bits = 0;
- return 0;
+ sb_devc *devc = audio_devs[dev]->devc;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (sb_dsp_command(devc, 0x40))
+ sb_dsp_command(devc, devc->tconst);
+ sb_dsp_command(devc, DSP_CMD_SPKOFF);
+ restore_flags(flags);
+
+ devc->trigger_bits = 0;
+ return 0;
}
static int
-sb1_audio_set_speed (int dev, int speed)
+sb1_audio_prepare_for_output(int dev, int bsize, int bcount)
{
- int max_speed = 23000;
- sb_devc *devc = audio_devs[dev]->devc;
- int tmp;
+ sb_devc *devc = audio_devs[dev]->devc;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (sb_dsp_command(devc, 0x40))
+ sb_dsp_command(devc, devc->tconst);
+ sb_dsp_command(devc, DSP_CMD_SPKON);
+ restore_flags(flags);
+ devc->trigger_bits = 0;
+ return 0;
+}
- if (devc->opened & OPEN_READ)
- max_speed = 13000;
+static int
+sb1_audio_set_speed(int dev, int speed)
+{
+ int max_speed = 23000;
+ sb_devc *devc = audio_devs[dev]->devc;
+ int tmp;
- if (speed > 0)
- {
- if (speed < 4000)
- speed = 4000;
+ if (devc->opened & OPEN_READ)
+ max_speed = 13000;
- if (speed > max_speed)
- speed = max_speed;
+ if (speed > 0)
+ {
+ if (speed < 4000)
+ speed = 4000;
- devc->tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff;
+ if (speed > max_speed)
+ speed = max_speed;
- tmp = 256 - devc->tconst;
- speed = (1000000 + tmp / 2) / tmp;
+ devc->tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff;
- devc->speed = speed;
- }
+ tmp = 256 - devc->tconst;
+ speed = (1000000 + tmp / 2) / tmp;
- return devc->speed;
+ devc->speed = speed;
+ }
+ return devc->speed;
}
static short
-sb1_audio_set_channels (int dev, short channels)
+sb1_audio_set_channels(int dev, short channels)
{
- sb_devc *devc = audio_devs[dev]->devc;
+ sb_devc *devc = audio_devs[dev]->devc;
- return devc->channels = 1;
+ return devc->channels = 1;
}
static unsigned int
-sb1_audio_set_bits (int dev, unsigned int bits)
+sb1_audio_set_bits(int dev, unsigned int bits)
{
- sb_devc *devc = audio_devs[dev]->devc;
+ sb_devc *devc = audio_devs[dev]->devc;
- return devc->bits = 8;
+ return devc->bits = 8;
}
static void
-sb1_audio_halt_xfer (int dev)
+sb1_audio_halt_xfer(int dev)
{
- unsigned long flags;
- sb_devc *devc = audio_devs[dev]->devc;
+ unsigned long flags;
+ sb_devc *devc = audio_devs[dev]->devc;
- save_flags (flags);
- cli ();
- sb_dsp_reset (devc);
- restore_flags (flags);
+ save_flags(flags);
+ cli();
+ sb_dsp_reset(devc);
+ restore_flags(flags);
}
/*
@@ -289,112 +285,110 @@ sb1_audio_halt_xfer (int dev)
*/
static void
-sb20_audio_output_block (int dev, unsigned long buf, int nr_bytes,
- int intrflag)
+sb20_audio_output_block(int dev, unsigned long buf, int nr_bytes,
+ int intrflag)
{
- unsigned long flags;
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned char cmd;
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
- count--;
-
- devc->irq_mode = IMODE_OUTPUT;
-
- save_flags (flags);
- cli ();
- if (sb_dsp_command (devc, 0x48)) /* DSP Block size */
- {
- sb_dsp_command (devc, (unsigned char) (count & 0xff));
- sb_dsp_command (devc, (unsigned char) ((count >> 8) & 0xff));
-
- if (devc->speed * devc->channels <= 23000)
- cmd = 0x1c; /* 8 bit PCM output */
- else
- cmd = 0x90; /* 8 bit high speed PCM output (SB2.01/Pro) */
-
- if (!sb_dsp_command (devc, cmd))
- printk ("SB: Unable to start DAC\n");
-
- }
- else
- printk ("SB: Unable to start DAC\n");
- restore_flags (flags);
- devc->intr_active = 1;
+ unsigned long flags;
+ int count = nr_bytes;
+ sb_devc *devc = audio_devs[dev]->devc;
+ unsigned char cmd;
+
+ /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
+
+ if (audio_devs[dev]->dmap_out->dma > 3)
+ count >>= 1;
+ count--;
+
+ devc->irq_mode = IMODE_OUTPUT;
+
+ save_flags(flags);
+ cli();
+ if (sb_dsp_command(devc, 0x48)) /* DSP Block size */
+ {
+ sb_dsp_command(devc, (unsigned char) (count & 0xff));
+ sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
+
+ if (devc->speed * devc->channels <= 23000)
+ cmd = 0x1c; /* 8 bit PCM output */
+ else
+ cmd = 0x90; /* 8 bit high speed PCM output (SB2.01/Pro) */
+
+ if (!sb_dsp_command(devc, cmd))
+ printk("SB: Unable to start DAC\n");
+
+ } else
+ printk("SB: Unable to start DAC\n");
+ restore_flags(flags);
+ devc->intr_active = 1;
}
static void
-sb20_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag)
+sb20_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag)
{
- unsigned long flags;
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned char cmd;
-
- /*
- * Start a DMA input to the buffer pointed by dmaqtail
- */
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
- count--;
-
- devc->irq_mode = IMODE_INPUT;
-
- save_flags (flags);
- cli ();
- if (sb_dsp_command (devc, 0x48)) /* DSP Block size */
- {
- sb_dsp_command (devc, (unsigned char) (count & 0xff));
- sb_dsp_command (devc, (unsigned char) ((count >> 8) & 0xff));
-
- if (devc->speed * devc->channels <= (devc->major == 3 ? 23000 : 13000))
- cmd = 0x2c; /* 8 bit PCM input */
- else
- cmd = 0x98; /* 8 bit high speed PCM input (SB2.01/Pro) */
-
- if (!sb_dsp_command (devc, cmd))
- printk ("SB: Unable to start ADC\n");
- }
- else
- printk ("SB Error: Unable to start ADC\n");
- restore_flags (flags);
-
- devc->intr_active = 1;
+ unsigned long flags;
+ int count = nr_bytes;
+ sb_devc *devc = audio_devs[dev]->devc;
+ unsigned char cmd;
+
+ /*
+ * Start a DMA input to the buffer pointed by dmaqtail
+ */
+
+ /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
+
+ if (audio_devs[dev]->dmap_out->dma > 3)
+ count >>= 1;
+ count--;
+
+ devc->irq_mode = IMODE_INPUT;
+
+ save_flags(flags);
+ cli();
+ if (sb_dsp_command(devc, 0x48)) /* DSP Block size */
+ {
+ sb_dsp_command(devc, (unsigned char) (count & 0xff));
+ sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
+
+ if (devc->speed * devc->channels <= (devc->major == 3 ? 23000 : 13000))
+ cmd = 0x2c; /* 8 bit PCM input */
+ else
+ cmd = 0x98; /* 8 bit high speed PCM input (SB2.01/Pro) */
+
+ if (!sb_dsp_command(devc, cmd))
+ printk("SB: Unable to start ADC\n");
+ } else
+ printk("SB Error: Unable to start ADC\n");
+ restore_flags(flags);
+
+ devc->intr_active = 1;
}
static void
-sb20_audio_trigger (int dev, int bits)
+sb20_audio_trigger(int dev, int bits)
{
- sb_devc *devc = audio_devs[dev]->devc;
-
- bits &= devc->irq_mode;
-
- if (!bits)
- sb_dsp_command (devc, 0xd0); /* Halt DMA */
- else
- {
- switch (devc->irq_mode)
- {
- case IMODE_INPUT:
- sb20_audio_start_input (dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
-
- case IMODE_OUTPUT:
- sb20_audio_output_block (dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
- }
- }
-
- devc->trigger_bits = bits;
+ sb_devc *devc = audio_devs[dev]->devc;
+
+ bits &= devc->irq_mode;
+
+ if (!bits)
+ sb_dsp_command(devc, 0xd0); /* Halt DMA */
+ else
+ {
+ switch (devc->irq_mode)
+ {
+ case IMODE_INPUT:
+ sb20_audio_start_input(dev, devc->trg_buf, devc->trg_bytes,
+ devc->trg_intrflag);
+ break;
+
+ case IMODE_OUTPUT:
+ sb20_audio_output_block(dev, devc->trg_buf, devc->trg_bytes,
+ devc->trg_intrflag);
+ break;
+ }
+ }
+
+ devc->trigger_bits = bits;
}
/*
@@ -402,33 +396,32 @@ sb20_audio_trigger (int dev, int bits)
*/
static int
-sb201_audio_set_speed (int dev, int speed)
+sb201_audio_set_speed(int dev, int speed)
{
- sb_devc *devc = audio_devs[dev]->devc;
- int tmp;
- int s = speed * devc->channels;
-
- if (speed > 0)
- {
- if (speed < 4000)
- speed = 4000;
+ sb_devc *devc = audio_devs[dev]->devc;
+ int tmp;
+ int s = speed * devc->channels;
- if (speed > 44100)
- speed = 44100;
+ if (speed > 0)
+ {
+ if (speed < 4000)
+ speed = 4000;
- if (devc->opened & OPEN_READ && speed > 15000)
- speed = 15000;
+ if (speed > 44100)
+ speed = 44100;
- devc->tconst = ((65536 - ((256000000 + s / 2) /
- s)) >> 8) & 0xff;
+ if (devc->opened & OPEN_READ && speed > 15000)
+ speed = 15000;
- tmp = 256 - devc->tconst;
- speed = ((1000000 + tmp / 2) / tmp) / devc->channels;
+ devc->tconst = ((65536 - ((256000000 + s / 2) /
+ s)) >> 8) & 0xff;
- devc->speed = speed;
- }
+ tmp = 256 - devc->tconst;
+ speed = ((1000000 + tmp / 2) / tmp) / devc->channels;
- return devc->speed;
+ devc->speed = speed;
+ }
+ return devc->speed;
}
/*
@@ -436,144 +429,145 @@ sb201_audio_set_speed (int dev, int speed)
*/
static int
-sbpro_audio_prepare_for_input (int dev, int bsize, int bcount)
+sbpro_audio_prepare_for_input(int dev, int bsize, int bcount)
{ /* For SB Pro and Jazz16 */
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned long flags;
- unsigned char bits = 0;
-
- if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
- audio_devs[dev]->dmap_out->dma =
- audio_devs[dev]->dmap_in->dma =
- devc->bits == 16 ? devc->dma16 : devc->dma8;
-
- if (devc->model == MDL_JAZZ || devc->model == MDL_SMW)
- if (devc->bits == AFMT_S16_LE)
- bits = 0x04; /* 16 bit mode */
-
- save_flags (flags);
- cli ();
- if (sb_dsp_command (devc, 0x40))
- sb_dsp_command (devc, devc->tconst);
- sb_dsp_command (devc, DSP_CMD_SPKOFF);
- if (devc->channels == 1)
- sb_dsp_command (devc, 0xa0 | bits); /* Mono input */
- else
- sb_dsp_command (devc, 0xa8 | bits); /* Stereo input */
- restore_flags (flags);
-
- devc->trigger_bits = 0;
- return 0;
+ sb_devc *devc = audio_devs[dev]->devc;
+ unsigned long flags;
+ unsigned char bits = 0;
+
+ if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
+ audio_devs[dev]->dmap_out->dma =
+ audio_devs[dev]->dmap_in->dma =
+ devc->bits == 16 ? devc->dma16 : devc->dma8;
+
+ if (devc->model == MDL_JAZZ || devc->model == MDL_SMW)
+ if (devc->bits == AFMT_S16_LE)
+ bits = 0x04; /* 16 bit mode */
+
+ save_flags(flags);
+ cli();
+ if (sb_dsp_command(devc, 0x40))
+ sb_dsp_command(devc, devc->tconst);
+ sb_dsp_command(devc, DSP_CMD_SPKOFF);
+ if (devc->channels == 1)
+ sb_dsp_command(devc, 0xa0 | bits); /* Mono input */
+ else
+ sb_dsp_command(devc, 0xa8 | bits); /* Stereo input */
+ restore_flags(flags);
+
+ devc->trigger_bits = 0;
+ return 0;
}
static int
-sbpro_audio_prepare_for_output (int dev, int bsize, int bcount)
+sbpro_audio_prepare_for_output(int dev, int bsize, int bcount)
{ /* For SB Pro and Jazz16 */
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned long flags;
- unsigned char tmp;
- unsigned char bits = 0;
-
- if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
- audio_devs[dev]->dmap_out->dma =
- audio_devs[dev]->dmap_in->dma =
- devc->bits == 16 ? devc->dma16 : devc->dma8;
- if (devc->model == MDL_SBPRO)
- sb_mixer_set_stereo (devc, devc->channels == 2);
-
- save_flags (flags);
- cli ();
- if (sb_dsp_command (devc, 0x40))
- sb_dsp_command (devc, devc->tconst);
- sb_dsp_command (devc, DSP_CMD_SPKON);
-
- if (devc->model == MDL_JAZZ || devc->model == MDL_SMW)
- {
- if (devc->bits == AFMT_S16_LE)
- bits = 0x04; /* 16 bit mode */
-
- if (devc->channels == 1)
- sb_dsp_command (devc, 0xa0 | bits); /* Mono output */
- else
- sb_dsp_command (devc, 0xa8 | bits); /* Stereo output */
- }
- else
- {
- tmp = sb_getmixer (devc, 0x0e);
- if (devc->channels == 1)
- tmp &= ~0x02;
- else
- tmp |= 0x02;
- sb_setmixer (devc, 0x0e, tmp);
- }
- restore_flags (flags);
- devc->trigger_bits = 0;
- return 0;
+ sb_devc *devc = audio_devs[dev]->devc;
+ unsigned long flags;
+ unsigned char tmp;
+ unsigned char bits = 0;
+
+ if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
+ audio_devs[dev]->dmap_out->dma =
+ audio_devs[dev]->dmap_in->dma =
+ devc->bits == 16 ? devc->dma16 : devc->dma8;
+ if (devc->model == MDL_SBPRO)
+ sb_mixer_set_stereo(devc, devc->channels == 2);
+
+ save_flags(flags);
+ cli();
+ if (sb_dsp_command(devc, 0x40))
+ sb_dsp_command(devc, devc->tconst);
+ sb_dsp_command(devc, DSP_CMD_SPKON);
+
+ if (devc->model == MDL_JAZZ || devc->model == MDL_SMW)
+ {
+ if (devc->bits == AFMT_S16_LE)
+ bits = 0x04; /* 16 bit mode */
+
+ if (devc->channels == 1)
+ sb_dsp_command(devc, 0xa0 | bits); /* Mono output */
+ else
+ sb_dsp_command(devc, 0xa8 | bits); /* Stereo output */
+ } else
+ {
+ tmp = sb_getmixer(devc, 0x0e);
+ if (devc->channels == 1)
+ tmp &= ~0x02;
+ else
+ tmp |= 0x02;
+ sb_setmixer(devc, 0x0e, tmp);
+ }
+ restore_flags(flags);
+ devc->trigger_bits = 0;
+ return 0;
}
static int
-sbpro_audio_set_speed (int dev, int speed)
+sbpro_audio_set_speed(int dev, int speed)
{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (speed > 0)
- {
- if (speed < 4000)
- speed = 4000;
+ sb_devc *devc = audio_devs[dev]->devc;
- if (speed > 44100)
- speed = 44100;
+ if (speed > 0)
+ {
+ if (speed < 4000)
+ speed = 4000;
- if (devc->channels > 1 && speed > 22050)
- speed = 22050;
+ if (speed > 44100)
+ speed = 44100;
- sb201_audio_set_speed (dev, speed);
- }
+ if (devc->channels > 1 && speed > 22050)
+ speed = 22050;
- return devc->speed;
+ sb201_audio_set_speed(dev, speed);
+ }
+ return devc->speed;
}
static short
-sbpro_audio_set_channels (int dev, short channels)
+sbpro_audio_set_channels(int dev, short channels)
{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (channels == 1 || channels == 2)
- if (channels != devc->channels)
- {
- devc->channels = channels;
- if (devc->model == MDL_SBPRO)
- sbpro_audio_set_speed (dev, devc->speed);
- }
- return devc->channels;
+ sb_devc *devc = audio_devs[dev]->devc;
+
+ if (channels == 1 || channels == 2)
+ if (channels != devc->channels)
+ {
+ devc->channels = channels;
+ if (devc->model == MDL_SBPRO && devc->channels == 2)
+ {
+ if (devc->speed > 22050)
+ printk("OSS: Application error. Wrong ioctl call order.\n");
+ sbpro_audio_set_speed(dev, devc->speed);
+ }
+ }
+ return devc->channels;
}
static int
-jazz16_audio_set_speed (int dev, int speed)
+jazz16_audio_set_speed(int dev, int speed)
{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (speed > 0)
- {
- int tmp;
- int s = speed * devc->channels;
+ sb_devc *devc = audio_devs[dev]->devc;
- if (speed < 5000)
- speed = 4000;
+ if (speed > 0)
+ {
+ int tmp;
+ int s = speed * devc->channels;
- if (speed > 44100)
- speed = 44100;
+ if (speed < 5000)
+ speed = 4000;
- devc->tconst = ((65536 - ((256000000 + s / 2) /
- s)) >> 8) & 0xff;
+ if (speed > 44100)
+ speed = 44100;
- tmp = 256 - devc->tconst;
- speed = ((1000000 + tmp / 2) / tmp) / devc->channels;
+ devc->tconst = ((65536 - ((256000000 + s / 2) /
+ s)) >> 8) & 0xff;
- devc->speed = speed;
- }
+ tmp = 256 - devc->tconst;
+ speed = ((1000000 + tmp / 2) / tmp) / devc->channels;
- return devc->speed;
+ devc->speed = speed;
+ }
+ return devc->speed;
}
/*
@@ -581,620 +575,601 @@ jazz16_audio_set_speed (int dev, int speed)
*/
static int
-ess_audio_set_speed (int dev, int speed)
+ess_audio_set_speed(int dev, int speed)
{
- sb_devc *devc = audio_devs[dev]->devc;
- int divider;
-
- if (speed > 0)
- {
- if (speed < 5000)
- speed = 4000;
-
- if (speed > 48000)
- speed = 48000;
-
- if (speed > 22000)
- {
- divider = (795500 + speed / 2) / speed;
- speed = (795500 + divider / 2) / divider;
- }
- else
- {
- divider = (397700 + speed / 2) / speed;
- speed = (397700 + divider / 2) / divider;
- }
-
- devc->speed = speed;
- }
-
- return devc->speed;
+ sb_devc *devc = audio_devs[dev]->devc;
+ int divider;
+
+ if (speed > 0)
+ {
+ if (speed < 5000)
+ speed = 4000;
+
+ if (speed > 48000)
+ speed = 48000;
+
+ if (speed > 22000)
+ {
+ divider = (795500 + speed / 2) / speed;
+ speed = (795500 + divider / 2) / divider;
+ } else
+ {
+ divider = (397700 + speed / 2) / speed;
+ speed = (397700 + divider / 2) / divider;
+ }
+
+ devc->speed = speed;
+ }
+ return devc->speed;
}
static void
-ess_speed (sb_devc * devc)
+ess_speed(sb_devc * devc)
{
- int divider;
- unsigned char bits = 0;
- int speed = devc->speed;
-
- if (speed < 4000)
- speed = 4000;
- else if (speed > 48000)
- speed = 48000;
-
- if (speed > 22000)
- {
- bits = 0x80;
- divider = 256 - (795500 + speed / 2) / speed;
- }
- else
- {
- divider = 128 - (397700 + speed / 2) / speed;
- }
-
- bits |= (unsigned char) divider;
- ess_write (devc, 0xa1, bits);
+ int divider;
+ unsigned char bits = 0;
+ int speed = devc->speed;
+
+ if (speed < 4000)
+ speed = 4000;
+ else if (speed > 48000)
+ speed = 48000;
+
+ if (speed > 22000)
+ {
+ bits = 0x80;
+ divider = 256 - (795500 + speed / 2) / speed;
+ } else
+ {
+ divider = 128 - (397700 + speed / 2) / speed;
+ }
+
+ bits |= (unsigned char) divider;
+ ess_write(devc, 0xa1, bits);
/*
* Set filter divider register
*/
- speed = (speed * 9) / 20; /* Set filter roll-off to 90% of speed/2 */
- divider = 256 - 7160000 / (speed * 82);
- ess_write (devc, 0xa2, divider);
+ speed = (speed * 9) / 20; /* Set filter roll-off to 90% of speed/2 */
+ divider = 256 - 7160000 / (speed * 82);
+ ess_write(devc, 0xa2, divider);
- return;
+ return;
}
static int
-ess_audio_prepare_for_input (int dev, int bsize, int bcount)
+ess_audio_prepare_for_input(int dev, int bsize, int bcount)
{
- sb_devc *devc = audio_devs[dev]->devc;
-
- ess_speed (devc);
- sb_dsp_command (devc, DSP_CMD_SPKOFF);
-
- ess_write (devc, 0xb8, 0x0e); /* Auto init DMA mode */
- ess_write (devc, 0xa8, (ess_read (devc, 0xa8) & ~0x03) |
- (3 - devc->channels)); /* Mono/stereo */
- ess_write (devc, 0xb9, 2); /* Demand mode (4 bytes/DMA request) */
-
- if (devc->channels == 1)
- {
- if (devc->bits == AFMT_U8)
- { /* 8 bit mono */
- ess_write (devc, 0xb7, 0x51);
- ess_write (devc, 0xb7, 0xd0);
- }
- else
- { /* 16 bit mono */
- ess_write (devc, 0xb7, 0x71);
- ess_write (devc, 0xb7, 0xf4);
- }
- }
- else
- { /* Stereo */
- if (devc->bits == AFMT_U8)
- { /* 8 bit stereo */
- ess_write (devc, 0xb7, 0x51);
- ess_write (devc, 0xb7, 0x98);
- }
- else
- { /* 16 bit stereo */
- ess_write (devc, 0xb7, 0x71);
- ess_write (devc, 0xb7, 0xbc);
- }
- }
-
- ess_write (devc, 0xb1, (ess_read (devc, 0xb1) & 0x0f) | 0x50);
- ess_write (devc, 0xb2, (ess_read (devc, 0xb2) & 0x0f) | 0x50);
-
- devc->trigger_bits = 0;
- return 0;
+ sb_devc *devc = audio_devs[dev]->devc;
+
+ ess_speed(devc);
+ sb_dsp_command(devc, DSP_CMD_SPKOFF);
+
+ ess_write(devc, 0xb8, 0x0e); /* Auto init DMA mode */
+ ess_write(devc, 0xa8, (ess_read(devc, 0xa8) & ~0x03) |
+ (3 - devc->channels)); /* Mono/stereo */
+ ess_write(devc, 0xb9, 2); /* Demand mode (4 bytes/DMA request) */
+
+ if (devc->channels == 1)
+ {
+ if (devc->bits == AFMT_U8)
+ { /* 8 bit mono */
+ ess_write(devc, 0xb7, 0x51);
+ ess_write(devc, 0xb7, 0xd0);
+ } else
+ { /* 16 bit mono */
+ ess_write(devc, 0xb7, 0x71);
+ ess_write(devc, 0xb7, 0xf4);
+ }
+ } else
+ { /* Stereo */
+ if (devc->bits == AFMT_U8)
+ { /* 8 bit stereo */
+ ess_write(devc, 0xb7, 0x51);
+ ess_write(devc, 0xb7, 0x98);
+ } else
+ { /* 16 bit stereo */
+ ess_write(devc, 0xb7, 0x71);
+ ess_write(devc, 0xb7, 0xbc);
+ }
+ }
+
+ ess_write(devc, 0xb1, (ess_read(devc, 0xb1) & 0x0f) | 0x50);
+ ess_write(devc, 0xb2, (ess_read(devc, 0xb2) & 0x0f) | 0x50);
+
+ devc->trigger_bits = 0;
+ return 0;
}
-static int
-ess_audio_prepare_for_output (int dev, int bsize, int bcount)
+static int ess_audio_prepare_for_output(int dev, int bsize, int bcount)
{
- sb_devc *devc = audio_devs[dev]->devc;
-
- sb_dsp_reset (devc);
- ess_speed (devc);
-
- ess_write (devc, 0xb8, 4); /* Auto init DMA mode */
- ess_write (devc, 0xa8, (ess_read (devc, 0xa8) & ~0x03) |
- (3 - devc->channels)); /* Mono/stereo */
- ess_write (devc, 0xb9, 2); /* Demand mode (4 bytes/request) */
-
- if (devc->channels == 1)
- {
- if (devc->bits == AFMT_U8)
- { /* 8 bit mono */
- ess_write (devc, 0xb6, 0x80);
- ess_write (devc, 0xb7, 0x51);
- ess_write (devc, 0xb7, 0xd0);
- }
- else
- { /* 16 bit mono */
- ess_write (devc, 0xb6, 0x00);
- ess_write (devc, 0xb7, 0x71);
- ess_write (devc, 0xb7, 0xf4);
- }
- }
- else
- { /* Stereo */
- if (devc->bits == AFMT_U8)
- { /* 8 bit stereo */
- ess_write (devc, 0xb6, 0x80);
- ess_write (devc, 0xb7, 0x51);
- ess_write (devc, 0xb7, 0x98);
+ sb_devc *devc = audio_devs[dev]->devc;
+
+ sb_dsp_reset(devc);
+ ess_speed(devc);
+
+ ess_write(devc, 0xb8, 4); /* Auto init DMA mode */
+ ess_write(devc, 0xa8, (ess_read(devc, 0xa8) & ~0x03) |
+ (3 - devc->channels)); /* Mono/stereo */
+ ess_write(devc, 0xb9, 2); /* Demand mode (4 bytes/request) */
+
+ if (devc->channels == 1)
+ {
+ if (devc->bits == AFMT_U8)
+ { /* 8 bit mono */
+ ess_write(devc, 0xb6, 0x80);
+ ess_write(devc, 0xb7, 0x51);
+ ess_write(devc, 0xb7, 0xd0);
+ } else
+ { /* 16 bit mono */
+ ess_write(devc, 0xb6, 0x00);
+ ess_write(devc, 0xb7, 0x71);
+ ess_write(devc, 0xb7, 0xf4);
+ }
}
- else
- { /* 16 bit stereo */
- ess_write (devc, 0xb6, 0x00);
- ess_write (devc, 0xb7, 0x71);
- ess_write (devc, 0xb7, 0xbc);
+ else
+ { /* Stereo */
+ if (devc->bits == AFMT_U8)
+ { /* 8 bit stereo */
+ ess_write(devc, 0xb6, 0x80);
+ ess_write(devc, 0xb7, 0x51);
+ ess_write(devc, 0xb7, 0x98);
+ }
+ else
+ { /* 16 bit stereo */
+ ess_write(devc, 0xb6, 0x00);
+ ess_write(devc, 0xb7, 0x71);
+ ess_write(devc, 0xb7, 0xbc);
+ }
}
- }
- ess_write (devc, 0xb1, (ess_read (devc, 0xb1) & 0x0f) | 0x50);
- ess_write (devc, 0xb2, (ess_read (devc, 0xb2) & 0x0f) | 0x50);
- sb_dsp_command (devc, DSP_CMD_SPKON);
+ ess_write(devc, 0xb1, (ess_read(devc, 0xb1) & 0x0f) | 0x50);
+ ess_write(devc, 0xb2, (ess_read(devc, 0xb2) & 0x0f) | 0x50);
+ sb_dsp_command(devc, DSP_CMD_SPKON);
- devc->trigger_bits = 0;
- return 0;
+ devc->trigger_bits = 0;
+ return 0;
}
-static void
-ess_audio_output_block (int dev, unsigned long buf, int nr_bytes,
- int intrflag)
+static void ess_audio_output_block(int dev, unsigned long buf, int nr_bytes,
+ int intrflag)
{
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
- short c = -nr_bytes;
+ int count = nr_bytes;
+ sb_devc *devc = audio_devs[dev]->devc;
+ short c = -nr_bytes;
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
+ /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
- count--;
+ if (audio_devs[dev]->dmap_out->dma > 3)
+ count >>= 1;
+ count--;
- devc->irq_mode = IMODE_OUTPUT;
+ devc->irq_mode = IMODE_OUTPUT;
- ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff));
- ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
+ ess_write(devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff));
+ ess_write(devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
- ess_write (devc, 0xb8, ess_read (devc, 0xb8) | 0x05); /* Go */
- devc->intr_active = 1;
+ ess_write(devc, 0xb8, ess_read(devc, 0xb8) | 0x05); /* Go */
+ devc->intr_active = 1;
}
-static void
-ess_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag)
+static void ess_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag)
{
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
- short c = -nr_bytes;
+ int count = nr_bytes;
+ sb_devc *devc = audio_devs[dev]->devc;
+ short c = -nr_bytes;
- /*
- * Start a DMA input to the buffer pointed by dmaqtail
- */
+ /*
+ * Start a DMA input to the buffer pointed by dmaqtail
+ */
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
+ /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
- count--;
+ if (audio_devs[dev]->dmap_out->dma > 3)
+ count >>= 1;
+ count--;
- devc->irq_mode = IMODE_INPUT;
+ devc->irq_mode = IMODE_INPUT;
- ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff));
- ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
+ ess_write(devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff));
+ ess_write(devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
- ess_write (devc, 0xb8, ess_read (devc, 0xb8) | 0x0f); /* Go */
- devc->intr_active = 1;
+ ess_write(devc, 0xb8, ess_read(devc, 0xb8) | 0x0f); /* Go */
+ devc->intr_active = 1;
}
-static void
-ess_audio_trigger (int dev, int bits)
+static void ess_audio_trigger(int dev, int bits)
{
- sb_devc *devc = audio_devs[dev]->devc;
+ sb_devc *devc = audio_devs[dev]->devc;
- bits &= devc->irq_mode;
+ bits &= devc->irq_mode;
- if (!bits)
- sb_dsp_command (devc, 0xd0); /* Halt DMA */
- else
- {
- switch (devc->irq_mode)
+ if (!bits)
+ sb_dsp_command(devc, 0xd0); /* Halt DMA */
+ else
{
- case IMODE_INPUT:
- ess_audio_start_input (dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
-
- case IMODE_OUTPUT:
- ess_audio_output_block (dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
+ switch (devc->irq_mode)
+ {
+ case IMODE_INPUT:
+ ess_audio_start_input(dev, devc->trg_buf, devc->trg_bytes,
+ devc->trg_intrflag);
+ break;
+
+ case IMODE_OUTPUT:
+ ess_audio_output_block(dev, devc->trg_buf, devc->trg_bytes,
+ devc->trg_intrflag);
+ break;
+ }
}
- }
- devc->trigger_bits = bits;
+ devc->trigger_bits = bits;
}
/*
* SB16 specific routines
*/
-static int
-sb16_audio_set_speed (int dev, int speed)
+static int sb16_audio_set_speed(int dev, int speed)
{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (speed > 0)
- {
- if (speed < 5000)
- speed = 4000;
+ sb_devc *devc = audio_devs[dev]->devc;
- if (speed > 44100)
- speed = 44100;
+ if (speed > 0)
+ {
+ if (speed < 5000)
+ speed = 4000;
- devc->speed = speed;
- }
+ if (speed > 44100)
+ speed = 44100;
- return devc->speed;
+ devc->speed = speed;
+ }
+ return devc->speed;
}
-static unsigned int
-sb16_audio_set_bits (int dev, unsigned int bits)
+static unsigned int sb16_audio_set_bits(int dev, unsigned int bits)
{
- sb_devc *devc = audio_devs[dev]->devc;
+ sb_devc *devc = audio_devs[dev]->devc;
- if (bits != 0)
- if (devc->bits == AFMT_U8 || bits == AFMT_S16_LE)
- devc->bits = bits;
- else
- devc->bits = AFMT_U8;
+ if (bits != 0)
+ {
+ if (devc->bits == AFMT_U8 || bits == AFMT_S16_LE)
+ devc->bits = bits;
+ else
+ devc->bits = AFMT_U8;
+ }
- return devc->bits;
+ return devc->bits;
}
-static int
-sb16_audio_prepare_for_input (int dev, int bsize, int bcount)
+static int sb16_audio_prepare_for_input(int dev, int bsize, int bcount)
{
- sb_devc *devc = audio_devs[dev]->devc;
+ sb_devc *devc = audio_devs[dev]->devc;
- audio_devs[dev]->dmap_out->dma =
- audio_devs[dev]->dmap_in->dma =
- devc->bits == AFMT_S16_LE ? devc->dma16 : devc->dma8;
+ audio_devs[dev]->dmap_out->dma =
+ audio_devs[dev]->dmap_in->dma =
+ devc->bits == AFMT_S16_LE ? devc->dma16 : devc->dma8;
- devc->trigger_bits = 0;
- return 0;
+ devc->trigger_bits = 0;
+ return 0;
}
-static int
-sb16_audio_prepare_for_output (int dev, int bsize, int bcount)
+static int sb16_audio_prepare_for_output(int dev, int bsize, int bcount)
{
- sb_devc *devc = audio_devs[dev]->devc;
+ sb_devc *devc = audio_devs[dev]->devc;
- audio_devs[dev]->dmap_out->dma =
- audio_devs[dev]->dmap_in->dma =
- devc->bits == AFMT_S16_LE ? devc->dma16 : devc->dma8;
+ audio_devs[dev]->dmap_out->dma =
+ audio_devs[dev]->dmap_in->dma =
+ devc->bits == AFMT_S16_LE ? devc->dma16 : devc->dma8;
- devc->trigger_bits = 0;
- return 0;
+ devc->trigger_bits = 0;
+ return 0;
}
-static void
-sb16_audio_output_block (int dev, unsigned long buf, int count,
- int intrflag)
+static void sb16_audio_output_block(int dev, unsigned long buf, int count,
+ int intrflag)
{
- unsigned long flags, cnt;
- sb_devc *devc = audio_devs[dev]->devc;
+ unsigned long flags, cnt;
+ sb_devc *devc = audio_devs[dev]->devc;
- devc->irq_mode = IMODE_OUTPUT;
- devc->intr_active = 1;
+ devc->irq_mode = IMODE_OUTPUT;
+ devc->intr_active = 1;
- cnt = count;
- if (devc->bits == AFMT_S16_LE)
- cnt >>= 1;
- cnt--;
+ cnt = count;
+ if (devc->bits == AFMT_S16_LE)
+ cnt >>= 1;
+ cnt--;
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
+ /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
- sb_dsp_command (devc, 0x41);
- sb_dsp_command (devc, (unsigned char) ((devc->speed >> 8) & 0xff));
- sb_dsp_command (devc, (unsigned char) (devc->speed & 0xff));
+ sb_dsp_command(devc, 0x41);
+ sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff));
+ sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff));
- sb_dsp_command (devc, (devc->bits == AFMT_S16_LE ? 0xb6 : 0xc6));
- sb_dsp_command (devc, ((devc->channels == 2 ? 0x20 : 0) +
- (devc->bits == AFMT_S16_LE ? 0x10 : 0)));
- sb_dsp_command (devc, (unsigned char) (cnt & 0xff));
- sb_dsp_command (devc, (unsigned char) (cnt >> 8));
+ sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xb6 : 0xc6));
+ sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) +
+ (devc->bits == AFMT_S16_LE ? 0x10 : 0)));
+ sb_dsp_command(devc, (unsigned char) (cnt & 0xff));
+ sb_dsp_command(devc, (unsigned char) (cnt >> 8));
- restore_flags (flags);
+ restore_flags(flags);
}
-static void
-sb16_audio_start_input (int dev, unsigned long buf, int count, int intrflag)
+static void sb16_audio_start_input(int dev, unsigned long buf, int count, int intrflag)
{
- unsigned long flags, cnt;
- sb_devc *devc = audio_devs[dev]->devc;
+ unsigned long flags, cnt;
+ sb_devc *devc = audio_devs[dev]->devc;
- devc->irq_mode = IMODE_INPUT;
- devc->intr_active = 1;
+ devc->irq_mode = IMODE_INPUT;
+ devc->intr_active = 1;
- cnt = count;
- if (devc->bits == AFMT_S16_LE)
- cnt >>= 1;
- cnt--;
+ cnt = count;
+ if (devc->bits == AFMT_S16_LE)
+ cnt >>= 1;
+ cnt--;
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
+ /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
- sb_dsp_command (devc, 0x42);
- sb_dsp_command (devc, (unsigned char) ((devc->speed >> 8) & 0xff));
- sb_dsp_command (devc, (unsigned char) (devc->speed & 0xff));
+ sb_dsp_command(devc, 0x42);
+ sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff));
+ sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff));
- sb_dsp_command (devc, (devc->bits == AFMT_S16_LE ? 0xbe : 0xce));
- sb_dsp_command (devc, ((devc->channels == 2 ? 0x20 : 0) +
- (devc->bits == AFMT_S16_LE ? 0x10 : 0)));
- sb_dsp_command (devc, (unsigned char) (cnt & 0xff));
- sb_dsp_command (devc, (unsigned char) (cnt >> 8));
+ sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xbe : 0xce));
+ sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) +
+ (devc->bits == AFMT_S16_LE ? 0x10 : 0)));
+ sb_dsp_command(devc, (unsigned char) (cnt & 0xff));
+ sb_dsp_command(devc, (unsigned char) (cnt >> 8));
- restore_flags (flags);
+ restore_flags(flags);
}
-static void
-sb16_audio_trigger (int dev, int bits)
+static void sb16_audio_trigger(int dev, int bits)
{
- sb_devc *devc = audio_devs[dev]->devc;
+ sb_devc *devc = audio_devs[dev]->devc;
- bits &= devc->irq_mode;
+ bits &= devc->irq_mode;
- if (!bits)
- sb_dsp_command (devc, 0xd0); /* Halt DMA */
- else
- {
- switch (devc->irq_mode)
+ if (!bits)
+ sb_dsp_command(devc, 0xd0); /* Halt DMA */
+ else
{
- case IMODE_INPUT:
- sb16_audio_start_input (dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
-
- case IMODE_OUTPUT:
- sb16_audio_output_block (dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
+ switch (devc->irq_mode)
+ {
+ case IMODE_INPUT:
+ sb16_audio_start_input(dev, devc->trg_buf, devc->trg_bytes,
+ devc->trg_intrflag);
+ break;
+
+ case IMODE_OUTPUT:
+ sb16_audio_output_block(dev, devc->trg_buf, devc->trg_bytes,
+ devc->trg_intrflag);
+ break;
+ }
}
- }
- devc->trigger_bits = bits;
+ devc->trigger_bits = bits;
}
-static int
-sb_audio_ioctl (int dev, unsigned int cmd, caddr_t arg)
+static int sb_audio_ioctl(int dev, unsigned int cmd, caddr_t arg)
{
- return -EINVAL;
+ return -EINVAL;
}
static struct audio_driver sb1_audio_driver = /* SB1.x */
{
- sb_audio_open,
- sb_audio_close,
- sb_set_output_parms,
- sb_set_input_parms,
- sb_audio_ioctl,
- sb1_audio_prepare_for_input,
- sb1_audio_prepare_for_output,
- sb1_audio_halt_xfer,
- NULL, /* local_qlen */
- NULL, /* copy_from_user */
- NULL,
- NULL,
- sb1_audio_trigger,
- sb1_audio_set_speed,
- sb1_audio_set_bits,
- sb1_audio_set_channels
+ sb_audio_open,
+ sb_audio_close,
+ sb_set_output_parms,
+ sb_set_input_parms,
+ sb_audio_ioctl,
+ sb1_audio_prepare_for_input,
+ sb1_audio_prepare_for_output,
+ sb1_audio_halt_xfer,
+ NULL, /* local_qlen */
+ NULL, /* copy_from_user */
+ NULL,
+ NULL,
+ sb1_audio_trigger,
+ sb1_audio_set_speed,
+ sb1_audio_set_bits,
+ sb1_audio_set_channels
};
static struct audio_driver sb20_audio_driver = /* SB2.0 */
{
- sb_audio_open,
- sb_audio_close,
- sb_set_output_parms,
- sb_set_input_parms,
- sb_audio_ioctl,
- sb1_audio_prepare_for_input,
- sb1_audio_prepare_for_output,
- sb1_audio_halt_xfer,
- NULL, /* local_qlen */
- NULL, /* copy_from_user */
- NULL,
- NULL,
- sb20_audio_trigger,
- sb1_audio_set_speed,
- sb1_audio_set_bits,
- sb1_audio_set_channels
+ sb_audio_open,
+ sb_audio_close,
+ sb_set_output_parms,
+ sb_set_input_parms,
+ sb_audio_ioctl,
+ sb1_audio_prepare_for_input,
+ sb1_audio_prepare_for_output,
+ sb1_audio_halt_xfer,
+ NULL, /* local_qlen */
+ NULL, /* copy_from_user */
+ NULL,
+ NULL,
+ sb20_audio_trigger,
+ sb1_audio_set_speed,
+ sb1_audio_set_bits,
+ sb1_audio_set_channels
};
static struct audio_driver sb201_audio_driver = /* SB2.01 */
{
- sb_audio_open,
- sb_audio_close,
- sb_set_output_parms,
- sb_set_input_parms,
- sb_audio_ioctl,
- sb1_audio_prepare_for_input,
- sb1_audio_prepare_for_output,
- sb1_audio_halt_xfer,
- NULL, /* local_qlen */
- NULL, /* copy_from_user */
- NULL,
- NULL,
- sb20_audio_trigger,
- sb201_audio_set_speed,
- sb1_audio_set_bits,
- sb1_audio_set_channels
+ sb_audio_open,
+ sb_audio_close,
+ sb_set_output_parms,
+ sb_set_input_parms,
+ sb_audio_ioctl,
+ sb1_audio_prepare_for_input,
+ sb1_audio_prepare_for_output,
+ sb1_audio_halt_xfer,
+ NULL, /* local_qlen */
+ NULL, /* copy_from_user */
+ NULL,
+ NULL,
+ sb20_audio_trigger,
+ sb201_audio_set_speed,
+ sb1_audio_set_bits,
+ sb1_audio_set_channels
};
static struct audio_driver sbpro_audio_driver = /* SB Pro */
{
- sb_audio_open,
- sb_audio_close,
- sb_set_output_parms,
- sb_set_input_parms,
- sb_audio_ioctl,
- sbpro_audio_prepare_for_input,
- sbpro_audio_prepare_for_output,
- sb1_audio_halt_xfer,
- NULL, /* local_qlen */
- NULL, /* copy_from_user */
- NULL,
- NULL,
- sb20_audio_trigger,
- sbpro_audio_set_speed,
- sb1_audio_set_bits,
- sbpro_audio_set_channels
+ sb_audio_open,
+ sb_audio_close,
+ sb_set_output_parms,
+ sb_set_input_parms,
+ sb_audio_ioctl,
+ sbpro_audio_prepare_for_input,
+ sbpro_audio_prepare_for_output,
+ sb1_audio_halt_xfer,
+ NULL, /* local_qlen */
+ NULL, /* copy_from_user */
+ NULL,
+ NULL,
+ sb20_audio_trigger,
+ sbpro_audio_set_speed,
+ sb1_audio_set_bits,
+ sbpro_audio_set_channels
};
static struct audio_driver jazz16_audio_driver = /* Jazz16 and SM Wave */
{
- sb_audio_open,
- sb_audio_close,
- sb_set_output_parms,
- sb_set_input_parms,
- sb_audio_ioctl,
- sbpro_audio_prepare_for_input,
- sbpro_audio_prepare_for_output,
- sb1_audio_halt_xfer,
- NULL, /* local_qlen */
- NULL, /* copy_from_user */
- NULL,
- NULL,
- sb20_audio_trigger,
- jazz16_audio_set_speed,
- sb16_audio_set_bits,
- sbpro_audio_set_channels
+ sb_audio_open,
+ sb_audio_close,
+ sb_set_output_parms,
+ sb_set_input_parms,
+ sb_audio_ioctl,
+ sbpro_audio_prepare_for_input,
+ sbpro_audio_prepare_for_output,
+ sb1_audio_halt_xfer,
+ NULL, /* local_qlen */
+ NULL, /* copy_from_user */
+ NULL,
+ NULL,
+ sb20_audio_trigger,
+ jazz16_audio_set_speed,
+ sb16_audio_set_bits,
+ sbpro_audio_set_channels
};
static struct audio_driver sb16_audio_driver = /* SB16 */
{
- sb_audio_open,
- sb_audio_close,
- sb_set_output_parms,
- sb_set_input_parms,
- sb_audio_ioctl,
- sb16_audio_prepare_for_input,
- sb16_audio_prepare_for_output,
- sb1_audio_halt_xfer,
- NULL, /* local_qlen */
- NULL, /* copy_from_user */
- NULL,
- NULL,
- sb16_audio_trigger,
- sb16_audio_set_speed,
- sb16_audio_set_bits,
- sbpro_audio_set_channels
+ sb_audio_open,
+ sb_audio_close,
+ sb_set_output_parms,
+ sb_set_input_parms,
+ sb_audio_ioctl,
+ sb16_audio_prepare_for_input,
+ sb16_audio_prepare_for_output,
+ sb1_audio_halt_xfer,
+ NULL, /* local_qlen */
+ NULL, /* copy_from_user */
+ NULL,
+ NULL,
+ sb16_audio_trigger,
+ sb16_audio_set_speed,
+ sb16_audio_set_bits,
+ sbpro_audio_set_channels
};
static struct audio_driver ess_audio_driver = /* ESS ES688/1688 */
{
- sb_audio_open,
- sb_audio_close,
- sb_set_output_parms,
- sb_set_input_parms,
- sb_audio_ioctl,
- ess_audio_prepare_for_input,
- ess_audio_prepare_for_output,
- sb1_audio_halt_xfer,
- NULL, /* local_qlen */
- NULL, /* copy_from_user */
- NULL,
- NULL,
- ess_audio_trigger,
- ess_audio_set_speed,
- sb16_audio_set_bits,
- sbpro_audio_set_channels
+ sb_audio_open,
+ sb_audio_close,
+ sb_set_output_parms,
+ sb_set_input_parms,
+ sb_audio_ioctl,
+ ess_audio_prepare_for_input,
+ ess_audio_prepare_for_output,
+ sb1_audio_halt_xfer,
+ NULL, /* local_qlen */
+ NULL, /* copy_from_user */
+ NULL,
+ NULL,
+ ess_audio_trigger,
+ ess_audio_set_speed,
+ sb16_audio_set_bits,
+ sbpro_audio_set_channels
};
-void
-sb_audio_init (sb_devc * devc, char *name)
+void sb_audio_init(sb_devc * devc, char *name)
{
- int audio_flags = 0;
- int format_mask = AFMT_U8;
-
- struct audio_driver *driver = &sb1_audio_driver;
-
- switch (devc->model)
- {
- case MDL_SB1: /* SB1.0 or SB 1.5 */
- DDB (printk ("Will use standard SB1.x driver\n"));
- audio_flags = DMA_HARDSTOP;
- break;
-
- case MDL_SB2:
- DDB (printk ("Will use SB2.0 driver\n"));
- audio_flags = DMA_AUTOMODE;
- driver = &sb20_audio_driver;
- break;
-
- case MDL_SB201:
- DDB (printk ("Will use SB2.01 (high speed) driver\n"));
- audio_flags = DMA_AUTOMODE;
- driver = &sb201_audio_driver;
- break;
-
- case MDL_JAZZ:
- case MDL_SMW:
- DDB (printk ("Will use Jazz16 driver\n"));
- audio_flags = DMA_AUTOMODE;
- format_mask |= AFMT_S16_LE;
- driver = &jazz16_audio_driver;
- break;
-
- case MDL_ESS:
- DDB (printk ("Will use ESS ES688/1688 driver\n"));
- audio_flags = DMA_AUTOMODE;
- format_mask |= AFMT_S16_LE;
- driver = &ess_audio_driver;
- break;
-
- case MDL_SB16:
- DDB (printk ("Will use SB16 driver\n"));
- audio_flags = DMA_AUTOMODE;
- format_mask |= AFMT_S16_LE;
- driver = &sb16_audio_driver;
- break;
-
- default:
- DDB (printk ("Will use SB Pro driver\n"));
- audio_flags = DMA_AUTOMODE;
- driver = &sbpro_audio_driver;
- }
-
- if ((devc->my_dev = sound_install_audiodrv (AUDIO_DRIVER_VERSION,
- name,
- driver,
- sizeof (struct audio_driver),
- audio_flags,
- format_mask,
- devc,
- devc->dma8,
- devc->dma8)) < 0)
- {
- return;
- }
-
- audio_devs[devc->my_dev]->mixer_dev = devc->my_mixerdev;
- audio_devs[devc->my_dev]->min_fragment = 5;
+ int audio_flags = 0;
+ int format_mask = AFMT_U8;
+
+ struct audio_driver *driver = &sb1_audio_driver;
+
+ switch (devc->model)
+ {
+ case MDL_SB1: /* SB1.0 or SB 1.5 */
+ DDB(printk("Will use standard SB1.x driver\n"));
+ audio_flags = DMA_HARDSTOP;
+ break;
+
+ case MDL_SB2:
+ DDB(printk("Will use SB2.0 driver\n"));
+ audio_flags = DMA_AUTOMODE;
+ driver = &sb20_audio_driver;
+ break;
+
+ case MDL_SB201:
+ DDB(printk("Will use SB2.01 (high speed) driver\n"));
+ audio_flags = DMA_AUTOMODE;
+ driver = &sb201_audio_driver;
+ break;
+
+ case MDL_JAZZ:
+ case MDL_SMW:
+ DDB(printk("Will use Jazz16 driver\n"));
+ audio_flags = DMA_AUTOMODE;
+ format_mask |= AFMT_S16_LE;
+ driver = &jazz16_audio_driver;
+ break;
+
+ case MDL_ESS:
+ DDB(printk("Will use ESS ES688/1688 driver\n"));
+ audio_flags = DMA_AUTOMODE;
+ format_mask |= AFMT_S16_LE;
+ driver = &ess_audio_driver;
+ break;
+
+ case MDL_SB16:
+ DDB(printk("Will use SB16 driver\n"));
+ audio_flags = DMA_AUTOMODE;
+ format_mask |= AFMT_S16_LE;
+ driver = &sb16_audio_driver;
+ break;
+
+ default:
+ DDB(printk("Will use SB Pro driver\n"));
+ audio_flags = DMA_AUTOMODE;
+ driver = &sbpro_audio_driver;
+ }
+
+ if ((devc->my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
+ name,
+ driver,
+ sizeof(struct audio_driver),
+ audio_flags,
+ format_mask,
+ devc,
+ devc->dma8,
+ devc->dma8)) < 0)
+ {
+ printk(KERN_ERR "sb: unable to install audio.\n");
+ return;
+ }
+ audio_devs[devc->my_dev]->mixer_dev = devc->my_mixerdev;
+ audio_devs[devc->my_dev]->min_fragment = 5;
}
#endif
diff --git a/drivers/sound/sb_card.c b/drivers/sound/sb_card.c
index d6e4bc67a..57cdc59e3 100644
--- a/drivers/sound/sb_card.c
+++ b/drivers/sound/sb_card.c
@@ -11,40 +11,150 @@
* for more info.
*/
#include <linux/config.h>
+#include <linux/module.h>
#include "sound_config.h"
+#include "soundmodule.h"
-#if defined(CONFIG_SBDSP)
+#if defined(CONFIG_SBDSP) || defined (MODULE)
#include "sb_mixer.h"
#include "sb.h"
void
-attach_sb_card (struct address_info *hw_config)
+attach_sb_card(struct address_info *hw_config)
{
#if defined(CONFIG_AUDIO) || defined(CONFIG_MIDI)
- sb_dsp_init (hw_config);
+ sb_dsp_init(hw_config);
#endif
}
int
-probe_sb (struct address_info *hw_config)
+probe_sb(struct address_info *hw_config)
{
- if (check_region (hw_config->io_base, 16))
- {
- printk ("\n\nsb_dsp.c: I/O port %x already in use\n\n",
- hw_config->io_base);
- return 0;
- }
-
- return sb_dsp_detect (hw_config);
+ if (check_region(hw_config->io_base, 16))
+ {
+ printk("\n\nsb_dsp.c: I/O port %x already in use\n\n", hw_config->io_base);
+ return 0;
+ }
+ return sb_dsp_detect(hw_config);
}
void
-unload_sb (struct address_info *hw_config)
+unload_sb(struct address_info *hw_config)
{
- sb_dsp_unload (hw_config);
+ sb_dsp_unload(hw_config);
}
+#ifdef MODULE
+
+static struct address_info config;
+static struct address_info config_mpu;
+
+/*
+ * Note DMA2 of -1 has the right meaning in the SB16 driver as well
+ * as here. It will cause either an error if it is needed or a fallback
+ * to the 8bit channel.
+ */
+
+int mpu_io = 0;
+int io = -1;
+int irq = -1;
+int dma = -1;
+int dma16 = -1; /* Set this for modules that need it */
+int type = 0; /* Can set this to a specific card type */
+int mad16 = 0; /* Set mad16=1 to load this as support for mad16 */
+int trix = 0; /* Set trix=1 to load this as support for trix */
+int pas2 = 0; /* Set pas2=1 to load this as support for pas2 */
+int sm_games = 0; /* Mixer - see sb_mixer.c */
+int acer = 0; /* Do acer notebook init */
+
+MODULE_PARM(io, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(dma, "i");
+MODULE_PARM(dma16, "i");
+MODULE_PARM(mpu_io, "i");
+MODULE_PARM(type, "i");
+MODULE_PARM(mad16, "i");
+MODULE_PARM(trix, "i");
+MODULE_PARM(pas2, "i");
+MODULE_PARM(sm_games, "i");
+
+static int sbmpu = 0;
+
+void *smw_free = NULL;
+
+int
+init_module(void)
+{
+ printk("Soundblaster audio driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+
+ if (mad16 == 0 && trix == 0 && pas2 == 0)
+ {
+ if (io == -1 || dma == -1 || irq == -1)
+ {
+ printk("I/O, IRQ, DMA and type are mandatory\n");
+ return -EINVAL;
+ }
+ config.io_base = io;
+ config.irq = irq;
+ config.dma = dma;
+ config.dma2 = dma16;
+ config.card_subtype = type;
+
+ if (!probe_sb(&config))
+ return -ENODEV;
+ attach_sb_card(&config);
+#ifdef CONFIG_MIDI
+ config_mpu.io_base = mpu_io;
+ if (mpu_io && probe_sbmpu(&config_mpu))
+ sbmpu = 1;
+#endif
+#ifdef CONFIG_MIDI
+ if (sbmpu)
+ attach_sbmpu(&config_mpu);
+#endif
+ }
+ SOUND_LOCK;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ if (smw_free)
+ kfree(smw_free);
+ if (!mad16 && !trix && !pas2)
+ unload_sb(&config);
+ if (sbmpu)
+ unload_sbmpu(&config_mpu);
+ SOUND_LOCK_END;
+}
+
+#else
+
+#ifdef SM_GAMES
+int sm_games = 1;
+
+#else
+int sm_games = 0;
+
+#endif
+#ifdef SB_ACER
+int acer = 1;
+
+#else
+int acer = 0;
+
#endif
+#endif
+#endif
+
+EXPORT_SYMBOL(sb_dsp_init);
+EXPORT_SYMBOL(sb_dsp_detect);
+EXPORT_SYMBOL(sb_dsp_unload);
+EXPORT_SYMBOL(sb_dsp_disable_midi);
+EXPORT_SYMBOL(attach_sb_card);
+EXPORT_SYMBOL(probe_sb);
+EXPORT_SYMBOL(unload_sb);
diff --git a/drivers/sound/sb_common.c b/drivers/sound/sb_common.c
index bd9e05704..70f67d75f 100644
--- a/drivers/sound/sb_common.c
+++ b/drivers/sound/sb_common.c
@@ -14,8 +14,9 @@
#include "sound_config.h"
+#include "sound_firmware.h"
-#if defined(CONFIG_SBDSP)
+#if defined(CONFIG_SBDSP) || defined(MODULE)
#ifndef CONFIG_AUDIO
#error You will need to configure the sound driver with CONFIG_AUDIO option.
@@ -52,767 +53,840 @@ static int smw_ucodeLen = 0;
#endif
+
int
-sb_dsp_command (sb_devc * devc, unsigned char val)
+sb_dsp_command(sb_devc * devc, unsigned char val)
{
- int i;
- unsigned long limit;
-
- limit = jiffies + HZ / 10; /* Timeout */
- /*
- * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes
- * called while interrupts are disabled. This means that the timer is
- * disabled also. However the timeout situation is a abnormal condition.
- * Normally the DSP should be ready to accept commands after just couple of
- * loops.
- */
-
- for (i = 0; i < 500000 && jiffies < limit; i++)
- {
- if ((inb (DSP_STATUS) & 0x80) == 0)
- {
- outb ((val), DSP_COMMAND);
- return 1;
- }
- }
+ int i;
+ unsigned long limit;
+
+ limit = jiffies + HZ / 10; /* Timeout */
+ /*
+ * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes
+ * called while interrupts are disabled. This means that the timer is
+ * disabled also. However the timeout situation is a abnormal condition.
+ * Normally the DSP should be ready to accept commands after just couple of
+ * loops.
+ */
+
+ for (i = 0; i < 500000 && jiffies < limit; i++)
+ {
+ if ((inb(DSP_STATUS) & 0x80) == 0)
+ {
+ outb((val), DSP_COMMAND);
+ return 1;
+ }
+ }
- printk ("Sound Blaster: DSP Command(%x) Timeout.\n", val);
- return 0;
+ printk("Sound Blaster: DSP Command(%x) Timeout.\n", val);
+ return 0;
}
static int
-sb_dsp_get_byte (sb_devc * devc)
+sb_dsp_get_byte(sb_devc * devc)
{
- int i;
-
- for (i = 1000; i; i--)
- if (inb (DSP_DATA_AVAIL) & 0x80)
- {
- return inb (DSP_READ);
- }
-
- return 0xffff;
+ int i;
+
+ for (i = 1000; i; i--)
+ if (inb(DSP_DATA_AVAIL) & 0x80)
+ {
+ return inb(DSP_READ);
+ }
+ return 0xffff;
}
int
-ess_write (sb_devc * devc, unsigned char reg, unsigned char data)
+ess_write(sb_devc * devc, unsigned char reg, unsigned char data)
{
- /* Write a byte to an extended mode register of ES1688 */
+ /* Write a byte to an extended mode register of ES1688 */
- if (!sb_dsp_command (devc, reg))
- return 0;
+ if (!sb_dsp_command(devc, reg))
+ return 0;
- return sb_dsp_command (devc, data);
+ return sb_dsp_command(devc, data);
}
int
-ess_read (sb_devc * devc, unsigned char reg)
+ess_read(sb_devc * devc, unsigned char reg)
{
/* Read a byte from an extended mode register of ES1688 */
- if (!sb_dsp_command (devc, 0xc0)) /* Read register command */
- return -1;
+ if (!sb_dsp_command(devc, 0xc0)) /* Read register command */
+ return -1;
- if (!sb_dsp_command (devc, reg))
- return -1;
+ if (!sb_dsp_command(devc, reg))
+ return -1;
- return sb_dsp_get_byte (devc);
+ return sb_dsp_get_byte(devc);
}
static void
-sbintr (int irq, void *dev_id, struct pt_regs *dummy)
+sbintr(int irq, void *dev_id, struct pt_regs *dummy)
{
- int status;
- unsigned char src = 0xff;
-
- sb_devc *devc = irq2devc[irq];
+ int status;
+ unsigned char src = 0xff;
- if (devc == NULL || devc->irq != irq)
- {
- DEB (printk ("sbintr: Bogus interrupt IRQ%d\n", irq));
- return;
- }
+ sb_devc *devc = irq2devc[irq];
- devc->irq_ok = 1;
+ if (devc == NULL || devc->irq != irq)
+ {
+ DEB(printk("sbintr: Bogus interrupt IRQ%d\n", irq));
+ return;
+ }
+ devc->irq_ok = 1;
- if (devc->model == MDL_SB16)
- {
+ if (devc->model == MDL_SB16)
+ {
- src = sb_getmixer (devc, IRQ_STAT); /* Interrupt source register */
+ src = sb_getmixer(devc, IRQ_STAT); /* Interrupt source register */
-#if defined(CONFIG_MIDI) && defined(CONFIG_UART401)
- if (src & 4)
- uart401intr (devc->irq, NULL, NULL); /* MPU401 interrupt */
+#if defined(CONFIG_MIDI)&& (defined(CONFIG_UART401)||defined(CONFIG_UART401_MODULE))
+ if (src & 4)
+ uart401intr(devc->irq, NULL, NULL); /* MPU401 interrupt */
#endif
- if (!(src & 3))
- return; /* Not a DSP interrupt */
- }
-
- if (devc->intr_active)
- switch (devc->irq_mode)
- {
- case IMODE_OUTPUT:
- DMAbuf_outputintr (devc->dev, 1);
- break;
-
- case IMODE_INPUT:
- DMAbuf_inputintr (devc->dev);
- break;
-
- case IMODE_INIT:
- break;
-
- case IMODE_MIDI:
-#ifdef CONFIG_MIDI
- sb_midi_interrupt (devc);
+ if (!(src & 3))
+ return; /* Not a DSP interrupt */
+ }
+ if (devc->intr_active)
+ switch (devc->irq_mode)
+ {
+ case IMODE_OUTPUT:
+ DMAbuf_outputintr(devc->dev, 1);
+ break;
+
+ case IMODE_INPUT:
+ DMAbuf_inputintr(devc->dev);
+ break;
+
+ case IMODE_INIT:
+ break;
+
+ case IMODE_MIDI:
+#if defined(CONFIG_MIDI)
+ sb_midi_interrupt(devc);
#endif
- break;
+ break;
- default:
- /* printk ("Sound Blaster: Unexpected interrupt\n"); */
- ;
- }
+ default:
+ /* printk( "Sound Blaster: Unexpected interrupt\n"); */
+ ;
+ }
/*
* Acknowledge interrupts
*/
- if (src & 0x01)
- status = inb (DSP_DATA_AVAIL);
+ if (src & 0x01)
+ status = inb(DSP_DATA_AVAIL);
- if (devc->model == MDL_SB16 && src & 0x02)
- status = inb (DSP_DATA_AVL16);
+ if (devc->model == MDL_SB16 && src & 0x02)
+ status = inb(DSP_DATA_AVL16);
}
+
int
-sb_dsp_reset (sb_devc * devc)
+sb_dsp_reset(sb_devc * devc)
{
- int loopc;
+ int loopc;
- DDB (printk ("Entered sb_dsp_reset()\n"));
+ DEB(printk("Entered sb_dsp_reset()\n"));
- if (devc->model == MDL_ESS)
- outb ((3), DSP_RESET); /* Reset FIFO too */
- else
- outb ((1), DSP_RESET);
+ if (devc->model == MDL_ESS)
+ outb((3), DSP_RESET); /* Reset FIFO too */
+ else
+ outb((1), DSP_RESET);
- tenmicrosec (devc->osp);
- outb ((0), DSP_RESET);
- tenmicrosec (devc->osp);
- tenmicrosec (devc->osp);
- tenmicrosec (devc->osp);
+ tenmicrosec(devc->osp);
+ outb((0), DSP_RESET);
+ tenmicrosec(devc->osp);
+ tenmicrosec(devc->osp);
+ tenmicrosec(devc->osp);
- for (loopc = 0; loopc < 1000 && !(inb (DSP_DATA_AVAIL) & 0x80); loopc++);
+ for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++);
- if (inb (DSP_READ) != 0xAA)
- {
- DDB (printk ("sb: No response to RESET\n"));
- return 0; /* Sorry */
- }
-
- if (devc->model == MDL_ESS)
- sb_dsp_command (devc, 0xc6); /* Enable extended mode */
+ if (inb(DSP_READ) != 0xAA)
+ {
+ DDB(printk("sb: No response to RESET\n"));
+ return 0; /* Sorry */
+ }
+ if (devc->model == MDL_ESS)
+ sb_dsp_command(devc, 0xc6); /* Enable extended mode */
- DDB (printk ("sb_dsp_reset() OK\n"));
- return 1;
+ DEB(printk("sb_dsp_reset() OK\n"));
+ return 1;
}
static void
-dsp_get_vers (sb_devc * devc)
+dsp_get_vers(sb_devc * devc)
{
- int i;
+ int i;
- unsigned long flags;
+ unsigned long flags;
- DDB (printk ("Entered dsp_get_vers()\n"));
- save_flags (flags);
- cli ();
- devc->major = devc->minor = 0;
- sb_dsp_command (devc, 0xe1); /* Get version */
+ DDB(printk("Entered dsp_get_vers()\n"));
+ save_flags(flags);
+ cli();
+ devc->major = devc->minor = 0;
+ sb_dsp_command(devc, 0xe1); /* Get version */
- for (i = 100000; i; i--)
- {
- if (inb (DSP_DATA_AVAIL) & 0x80)
- {
- if (devc->major == 0)
- devc->major = inb (DSP_READ);
- else
- {
- devc->minor = inb (DSP_READ);
- break;
- }
- }
- }
- DDB (printk ("DSP version %d.%d\n", devc->major, devc->minor));
- restore_flags (flags);
+ for (i = 100000; i; i--)
+ {
+ if (inb(DSP_DATA_AVAIL) & 0x80)
+ {
+ if (devc->major == 0)
+ devc->major = inb(DSP_READ);
+ else
+ {
+ devc->minor = inb(DSP_READ);
+ break;
+ }
+ }
+ }
+ DDB(printk("DSP version %d.%d\n", devc->major, devc->minor));
+ restore_flags(flags);
}
static int
-sb16_set_dma_hw (sb_devc * devc)
+sb16_set_dma_hw(sb_devc * devc)
{
- int bits;
-
- if (devc->dma8 != 0 && devc->dma8 != 1 && devc->dma8 != 3)
- {
- printk ("SB16: Invalid 8 bit DMA (%d)\n", devc->dma8);
- return 0;
- }
+ int bits;
- bits = (1 << devc->dma8);
+ if (devc->dma8 != 0 && devc->dma8 != 1 && devc->dma8 != 3)
+ {
+ printk("SB16: Invalid 8 bit DMA (%d)\n", devc->dma8);
+ return 0;
+ }
+ bits = (1 << devc->dma8);
- if (devc->dma16 >= 5 && devc->dma16 <= 7)
- bits |= (1 << devc->dma16);
+ if (devc->dma16 >= 5 && devc->dma16 <= 7)
+ bits |= (1 << devc->dma16);
- sb_setmixer (devc, DMA_NR, bits);
- return 1;
+ sb_setmixer(devc, DMA_NR, bits);
+ return 1;
}
#if defined(CONFIG_MIDI) && defined(CONFIG_UART401)
static void
-sb16_set_mpu_port (sb_devc * devc, struct address_info *hw_config)
+sb16_set_mpu_port(sb_devc * devc, struct address_info *hw_config)
{
/*
* This routine initializes new MIDI port setup register of SB Vibra (CT2502).
*/
- unsigned char bits = sb_getmixer (devc, 0x84) & ~0x06;
-
- switch (hw_config->io_base)
- {
- case 0x300:
- sb_setmixer (devc, 0x84, bits | 0x04);
- break;
-
- case 0x330:
- sb_setmixer (devc, 0x84, bits | 0x00);
- break;
-
- default:
- sb_setmixer (devc, 0x84, bits | 0x02); /* Disable MPU */
- printk ("SB16: Invalid MIDI I/O port %x\n", hw_config->io_base);
- }
+ unsigned char bits = sb_getmixer(devc, 0x84) & ~0x06;
+
+ switch (hw_config->io_base)
+ {
+ case 0x300:
+ sb_setmixer(devc, 0x84, bits | 0x04);
+ break;
+
+ case 0x330:
+ sb_setmixer(devc, 0x84, bits | 0x00);
+ break;
+
+ default:
+ sb_setmixer(devc, 0x84, bits | 0x02); /* Disable MPU */
+ printk("SB16: Invalid MIDI I/O port %x\n", hw_config->io_base);
+ }
}
#endif
static int
-sb16_set_irq_hw (sb_devc * devc, int level)
+sb16_set_irq_hw(sb_devc * devc, int level)
{
- int ival;
-
- switch (level)
- {
- case 5:
- ival = 2;
- break;
- case 7:
- ival = 4;
- break;
- case 9:
- ival = 1;
- break;
- case 10:
- ival = 8;
- break;
- default:
- printk ("SB16 IRQ%d is not possible\n", level);
- return 0;
- }
- sb_setmixer (devc, IRQ_NR, ival);
- return 1;
+ int ival;
+
+ switch (level)
+ {
+ case 5:
+ ival = 2;
+ break;
+ case 7:
+ ival = 4;
+ break;
+ case 9:
+ ival = 1;
+ break;
+ case 10:
+ ival = 8;
+ break;
+ default:
+ printk("SB16 IRQ%d is not possible\n", level);
+ return 0;
+ }
+ sb_setmixer(devc, IRQ_NR, ival);
+ return 1;
}
static void
-relocate_Jazz16 (sb_devc * devc, struct address_info *hw_config)
+relocate_Jazz16(sb_devc * devc, struct address_info *hw_config)
{
- unsigned char bits = 0;
- unsigned long flags;
-
- if (jazz16_base != 0 && jazz16_base != hw_config->io_base)
- return;
+ unsigned char bits = 0;
+ unsigned long flags;
- switch (hw_config->io_base)
- {
- case 0x220:
- bits = 1;
- break;
- case 0x240:
- bits = 2;
- break;
- case 0x260:
- bits = 3;
- break;
+ if (jazz16_base != 0 && jazz16_base != hw_config->io_base)
+ return;
- default:
- return;
- }
+ switch (hw_config->io_base)
+ {
+ case 0x220:
+ bits = 1;
+ break;
+ case 0x240:
+ bits = 2;
+ break;
+ case 0x260:
+ bits = 3;
+ break;
+
+ default:
+ return;
+ }
- bits = jazz16_bits = bits << 5;
+ bits = jazz16_bits = bits << 5;
- jazz16_base = hw_config->io_base;
+ jazz16_base = hw_config->io_base;
/*
* Magic wake up sequence by writing to 0x201 (aka Joystick port)
*/
- save_flags (flags);
- cli ();
- outb ((0xAF), 0x201);
- outb ((0x50), 0x201);
- outb ((bits), 0x201);
- restore_flags (flags);
+ save_flags(flags);
+ cli();
+ outb((0xAF), 0x201);
+ outb((0x50), 0x201);
+ outb((bits), 0x201);
+ restore_flags(flags);
}
static int
-init_Jazz16 (sb_devc * devc, struct address_info *hw_config)
+init_Jazz16(sb_devc * devc, struct address_info *hw_config)
{
- char name[100];
+ char name[100];
/*
* First try to check that the card has Jazz16 chip. It identifies itself
* by returning 0x12 as response to DSP command 0xfa.
*/
- if (!sb_dsp_command (devc, 0xfa))
- return 0;
+ if (!sb_dsp_command(devc, 0xfa))
+ return 0;
- if (sb_dsp_get_byte (devc) != 0x12)
- return 0;
+ if (sb_dsp_get_byte(devc) != 0x12)
+ return 0;
/*
* OK so far. Now configure the IRQ and DMA channel used by the card.
*/
- if (hw_config->irq < 1 || hw_config->irq > 15 ||
- jazz_irq_bits[hw_config->irq] == 0)
- {
- printk ("Jazz16: Invalid interrupt (IRQ%d)\n", hw_config->irq);
- return 0;
- }
-
- if (hw_config->dma < 0 || hw_config->dma > 3 ||
- jazz_dma_bits[hw_config->dma] == 0)
- {
- printk ("Jazz16: Invalid 8 bit DMA (DMA%d)\n", hw_config->dma);
- return 0;
- }
-
- if (hw_config->dma2 < 0)
- {
- printk ("Jazz16: No 16 bit DMA channel defined\n");
- return 0;
- }
-
- if (hw_config->dma2 < 5 || hw_config->dma2 > 7 ||
- jazz_dma_bits[hw_config->dma2] == 0)
- {
- printk ("Jazz16: Invalid 16 bit DMA (DMA%d)\n", hw_config->dma2);
- return 0;
- }
-
- devc->dma16 = hw_config->dma2;
-
- if (!sb_dsp_command (devc, 0xfb))
- return 0;
-
- if (!sb_dsp_command (devc, jazz_dma_bits[hw_config->dma] |
- (jazz_dma_bits[hw_config->dma2] << 4)))
- return 0;
-
- if (!sb_dsp_command (devc, jazz_irq_bits[hw_config->irq]))
- return 0;
+ if (hw_config->irq < 1 || hw_config->irq > 15 ||
+ jazz_irq_bits[hw_config->irq] == 0)
+ {
+ printk("Jazz16: Invalid interrupt (IRQ%d)\n", hw_config->irq);
+ return 0;
+ }
+ if (hw_config->dma < 0 || hw_config->dma > 3 ||
+ jazz_dma_bits[hw_config->dma] == 0)
+ {
+ printk("Jazz16: Invalid 8 bit DMA (DMA%d)\n", hw_config->dma);
+ return 0;
+ }
+ if (hw_config->dma2 < 0)
+ {
+ printk("Jazz16: No 16 bit DMA channel defined\n");
+ return 0;
+ }
+ if (hw_config->dma2 < 5 || hw_config->dma2 > 7 ||
+ jazz_dma_bits[hw_config->dma2] == 0)
+ {
+ printk("Jazz16: Invalid 16 bit DMA (DMA%d)\n", hw_config->dma2);
+ return 0;
+ }
+ devc->dma16 = hw_config->dma2;
+
+ if (!sb_dsp_command(devc, 0xfb))
+ return 0;
+
+ if (!sb_dsp_command(devc, jazz_dma_bits[hw_config->dma] |
+ (jazz_dma_bits[hw_config->dma2] << 4)))
+ return 0;
+
+ if (!sb_dsp_command(devc, jazz_irq_bits[hw_config->irq]))
+ return 0;
/*
* Now we have configured a standard Jazz16 device.
*/
- devc->model = MDL_JAZZ;
- strcpy (name, "Jazz16");
-
-
- hw_config->name = (char *) (sound_mem_blocks[sound_nblocks] = vmalloc (strlen (name + 1)));
- sound_mem_sizes[sound_nblocks] = strlen (name + 1);
- if (sound_nblocks < 1024)
- sound_nblocks++;;
- if (hw_config->name != NULL)
- strcpy (hw_config->name, name);
- devc->caps |= SB_NO_MIDI;
- return 1;
+ devc->model = MDL_JAZZ;
+ strcpy(name, "Jazz16");
+
+
+ hw_config->name = (char *) (sound_mem_blocks[sound_nblocks] = vmalloc(strlen(name + 1)));
+ sound_mem_sizes[sound_nblocks] = strlen(name + 1);
+ if (sound_nblocks < 1024)
+ sound_nblocks++;;
+ if (hw_config->name != NULL)
+ strcpy(hw_config->name, name);
+ devc->caps |= SB_NO_MIDI;
+ return 1;
}
+static void
+relocate_ess1688(sb_devc * devc)
+{
+ unsigned char bits;
+
+ switch (devc->base)
+ {
+ case 0x220:
+ bits = 0x04;
+ break;
+ case 0x230:
+ bits = 0x05;
+ break;
+ case 0x240:
+ bits = 0x06;
+ break;
+ case 0x250:
+ bits = 0x07;
+ break;
+ default:
+ return; /* Wrong port */
+ }
+
+ DDB(printk("Doing ESS1688 address selection\n"));
+
+/*
+ * ES1688 supports two alternative ways for software address config.
+ * First try the so called Read-Sequence-Key method.
+ */
+
+ /* Reset the sequence logic */
+ inb(0x229);
+ inb(0x229);
+ inb(0x229);
+
+ /* Perform the read sequence */
+ inb(0x22b);
+ inb(0x229);
+ inb(0x22b);
+ inb(0x229);
+ inb(0x229);
+ inb(0x22b);
+ inb(0x229);
+
+ /* Select the base address by reading from it. Then probe using the port. */
+ inb(devc->base);
+ if (sb_dsp_reset(devc)) /* Bingo */
+ return;
+
+#if 0 /* This causes system lockups (Nokia 386/25 at least) */
+/*
+ * The last resort is the system control register method.
+ */
+
+ outb((0x00), 0xfb); /* 0xFB is the unlock register */
+ outb((0x00), 0xe0); /* Select index 0 */
+ outb((bits), 0xe1); /* Write the config bits */
+ outb((0x00), 0xf9); /* 0xFB is the lock register */
+#endif
+}
static int
-ess_init (sb_devc * devc, struct address_info *hw_config)
+ess_init(sb_devc * devc, struct address_info *hw_config)
{
- unsigned char cfg, irq_bits = 0, dma_bits = 0;
- int ess_major = 0, ess_minor = 0;
- int i;
- char name[100];
+ unsigned char cfg, irq_bits = 0, dma_bits = 0;
+ int ess_major = 0, ess_minor = 0;
+ int i;
+ char name[100];
/*
* Try to detect ESS chips.
*/
- sb_dsp_command (devc, 0xe7); /* Return identification */
+ sb_dsp_command(devc, 0xe7); /* Return identification */
- for (i = 1000; i; i--)
- {
- if (inb (DSP_DATA_AVAIL) & 0x80)
- {
- if (ess_major == 0)
- ess_major = inb (DSP_READ);
- else
- {
- ess_minor = inb (DSP_READ);
- break;
- }
- }
- }
-
- if (ess_major == 0)
- return 0;
-
- if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80)
- {
- sprintf (name, "ESS ES488 AudioDrive (rev %d)",
- ess_minor & 0x0f);
- hw_config->name = name;
- devc->model = MDL_SBPRO;
- return 1;
- }
- else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80)
- {
- char *chip = "ES688";
-
- if ((ess_minor & 0x0f) >= 8)
- chip = "ES1688";
-
- sprintf (name,
- "ESS %s AudioDrive (rev %d)",
- chip, ess_minor & 0x0f);
- }
- else
- strcpy (name, "Jazz16");
-
- devc->model = MDL_ESS;
- devc->submodel = ess_minor & 0x0f;
-
- hw_config->name = (char *) (sound_mem_blocks[sound_nblocks] = vmalloc (strlen (name + 1)));
- sound_mem_sizes[sound_nblocks] = strlen (name + 1);
- if (sound_nblocks < 1024)
- sound_nblocks++;;
- if (hw_config->name != NULL)
- strcpy (hw_config->name, name);
-
-
- sb_dsp_reset (devc); /* Turn on extended mode */
+ for (i = 1000; i; i--)
+ {
+ if (inb(DSP_DATA_AVAIL) & 0x80)
+ {
+ if (ess_major == 0)
+ ess_major = inb(DSP_READ);
+ else
+ {
+ ess_minor = inb(DSP_READ);
+ break;
+ }
+ }
+ }
-/*
- * Set IRQ configuration register
- */
+ if (ess_major == 0)
+ return 0;
- cfg = 0x50; /* Enable only DMA counter interrupt */
+ if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80)
+ {
+ sprintf(name, "ESS ES488 AudioDrive (rev %d)",
+ ess_minor & 0x0f);
+ hw_config->name = name;
+ devc->model = MDL_SBPRO;
+ return 1;
+ } else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80)
+ {
+ char *chip = "ES688";
- switch (devc->irq)
- {
- case 2:
- case 9:
- irq_bits = 0;
- break;
+ if ((ess_minor & 0x0f) >= 8)
+ chip = "ES1688";
- case 5:
- irq_bits = 1;
- break;
+ sprintf(name,
+ "ESS %s AudioDrive (rev %d)",
+ chip, ess_minor & 0x0f);
+ } else
+ strcpy(name, "Jazz16");
- case 7:
- irq_bits = 2;
- break;
+ devc->model = MDL_ESS;
+ devc->submodel = ess_minor & 0x0f;
- case 10:
- irq_bits = 3;
- break;
+ hw_config->name = (char *) (sound_mem_blocks[sound_nblocks] = vmalloc(strlen(name + 1)));
+ sound_mem_sizes[sound_nblocks] = strlen(name + 1);
+ if (sound_nblocks < 1024)
+ sound_nblocks++;;
+ if (hw_config->name != NULL)
+ strcpy(hw_config->name, name);
- default:
- irq_bits = 0;
- cfg = 0x10; /* Disable all interrupts */
- printk ("\nESS1688: Invalid IRQ %d\n", devc->irq);
- return 0;
- }
- if (!ess_write (devc, 0xb1, cfg | (irq_bits << 2)))
- printk ("\nESS1688: Failed to write to IRQ config register\n");
+ sb_dsp_reset(devc); /* Turn on extended mode */
+
+/*
+ * Set IRQ configuration register
+ */
+
+ cfg = 0x50; /* Enable only DMA counter interrupt */
+
+ switch (devc->irq)
+ {
+ case 2:
+ case 9:
+ irq_bits = 0;
+ break;
+
+ case 5:
+ irq_bits = 1;
+ break;
+
+ case 7:
+ irq_bits = 2;
+ break;
+
+ case 10:
+ irq_bits = 3;
+ break;
+
+ default:
+ irq_bits = 0;
+ cfg = 0x10; /* Disable all interrupts */
+ printk("\nESS1688: Invalid IRQ %d\n", devc->irq);
+ return 0;
+ }
+
+ if (!ess_write(devc, 0xb1, cfg | (irq_bits << 2)))
+ printk("\nESS1688: Failed to write to IRQ config register\n");
/*
* Set DMA configuration register
*/
- cfg = 0x50; /* Extended mode DMA enable */
-
- if (devc->dma8 > 3 || devc->dma8 < 0 || devc->dma8 == 2)
- {
- dma_bits = 0;
- cfg = 0x00; /* Disable all DMA */
- printk ("\nESS1688: Invalid DMA %d\n", devc->dma8);
- }
- else
- {
- if (devc->dma8 == 3)
- dma_bits = 3;
- else
- dma_bits = devc->dma8 + 1;
- }
-
- if (!ess_write (devc, 0xb2, cfg | (dma_bits << 2)))
- printk ("\nESS1688: Failed to write to DMA config register\n");
+ cfg = 0x50; /* Extended mode DMA enable */
+
+ if (devc->dma8 > 3 || devc->dma8 < 0 || devc->dma8 == 2)
+ {
+ dma_bits = 0;
+ cfg = 0x00; /* Disable all DMA */
+ printk("\nESS1688: Invalid DMA %d\n", devc->dma8);
+ } else
+ {
+ if (devc->dma8 == 3)
+ dma_bits = 3;
+ else
+ dma_bits = devc->dma8 + 1;
+ }
+
+ if (!ess_write(devc, 0xb2, cfg | (dma_bits << 2)))
+ printk("\nESS1688: Failed to write to DMA config register\n");
/*
* Enable joystick and OPL3
*/
- cfg = sb_getmixer (devc, 0x40);
- sb_setmixer (devc, 0x40, cfg | 0x03);
- if (devc->submodel >= 8) /* ES1688 */
- devc->caps |= SB_NO_MIDI; /* ES1688 uses MPU401 MIDI mode */
- sb_dsp_reset (devc);
- return 1;
+ cfg = sb_getmixer(devc, 0x40);
+ sb_setmixer(devc, 0x40, cfg | 0x03);
+ if (devc->submodel >= 8) /* ES1688 */
+ devc->caps |= SB_NO_MIDI; /* ES1688 uses MPU401 MIDI mode */
+ sb_dsp_reset(devc);
+ return 1;
}
int
-sb_dsp_detect (struct address_info *hw_config)
+sb_dsp_detect(struct address_info *hw_config)
{
- sb_devc sb_info;
- sb_devc *devc = &sb_info;
+ sb_devc sb_info;
+ sb_devc *devc = &sb_info;
+ sb_info.my_mididev = -1;
+ sb_info.my_mixerdev = -1;
+ sb_info.my_dev = -1;
/*
* Initialize variables
*/
- DDB (printk ("sb_dsp_detect(%x) entered\n", hw_config->io_base));
- if (check_region (hw_config->io_base, 16))
- return 0;
-
- memset ((char *) &sb_info, 0, sizeof (sb_info)); /* Zero everything */
+ DDB(printk("sb_dsp_detect(%x) entered\n", hw_config->io_base));
+ if (check_region(hw_config->io_base, 16))
+ {
+#ifdef MODULE
+ printk("sb: I/O region in use.\n");
+#endif
+ return 0;
+ }
+ memset((char *) &sb_info, 0, sizeof(sb_info)); /* Zero everything */
- devc->type = hw_config->card_subtype;
+ devc->type = hw_config->card_subtype;
- devc->base = hw_config->io_base;
- devc->irq = hw_config->irq;
- devc->dma8 = hw_config->dma;
+ devc->base = hw_config->io_base;
+ devc->irq = hw_config->irq;
+ devc->dma8 = hw_config->dma;
- devc->dma16 = -1;
+ devc->dma16 = -1;
+ if (acer)
+ {
+ cli();
+ inb(devc->base + 0x09);
+ inb(devc->base + 0x09);
+ inb(devc->base + 0x09);
+ inb(devc->base + 0x0b);
+ inb(devc->base + 0x09);
+ inb(devc->base + 0x0b);
+ inb(devc->base + 0x09);
+ inb(devc->base + 0x09);
+ inb(devc->base + 0x0b);
+ inb(devc->base + 0x09);
+ inb(devc->base + 0x00);
+ sti();
+ }
/*
* Detect the device
*/
- if (sb_dsp_reset (devc))
- dsp_get_vers (devc);
- else
- devc->major = 0;
+ if (sb_dsp_reset(devc))
+ dsp_get_vers(devc);
+ else
+ devc->major = 0;
- if (devc->type == 0 || devc->type == MDL_JAZZ || devc->type == MDL_SMW)
- if (devc->major == 0 || (devc->major == 3 && devc->minor == 1))
- relocate_Jazz16 (devc, hw_config);
+ if (devc->type == 0 || devc->type == MDL_JAZZ || devc->type == MDL_SMW)
+ if (devc->major == 0 || (devc->major == 3 && devc->minor == 1))
+ relocate_Jazz16(devc, hw_config);
+ if (devc->major == 0 && (devc->type == MDL_ESS || devc->type == 0))
+ relocate_ess1688(devc);
- if (!sb_dsp_reset (devc))
- {
- DDB (printk ("SB reset failed\n"));
- return 0;
- }
-
- if (devc->major == 0)
- dsp_get_vers (devc);
-
- if (devc->major == 3 && devc->minor == 1)
- {
- if (devc->type == MDL_AZTECH) /* SG Washington? */
- {
- if (sb_dsp_command (devc, 0x09))
- if (sb_dsp_command (devc, 0x00)) /* Enter WSS mode */
- {
- int i;
-
- /* Have some delay */
- for (i = 0; i < 10000; i++)
- inb (DSP_DATA_AVAIL);
- devc->caps = SB_NO_AUDIO | SB_NO_MIDI; /* Mixer only */
- devc->model = MDL_AZTECH;
- }
- }
- }
+ if (!sb_dsp_reset(devc))
+ {
+ DDB(printk("SB reset failed\n"));
+#ifdef MODULE
+ printk("sb: dsp reset failed.\n");
+#endif
+ return 0;
+ }
+ if (devc->major == 0)
+ dsp_get_vers(devc);
+ if (devc->major == 3 && devc->minor == 1)
+ {
+ if (devc->type == MDL_AZTECH) /* SG Washington? */
+ {
+ if (sb_dsp_command(devc, 0x09))
+ if (sb_dsp_command(devc, 0x00)) /* Enter WSS mode */
+ {
+ int i;
+
+ /* Have some delay */
+ for (i = 0; i < 10000; i++)
+ inb(DSP_DATA_AVAIL);
+ devc->caps = SB_NO_AUDIO | SB_NO_MIDI; /* Mixer only */
+ devc->model = MDL_AZTECH;
+ }
+ }
+ }
/*
* Save device information for sb_dsp_init()
*/
- detected_devc = (sb_devc *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (sb_devc)));
- sound_mem_sizes[sound_nblocks] = sizeof (sb_devc);
- if (sound_nblocks < 1024)
- sound_nblocks++;;
-
- if (detected_devc == NULL)
- {
- printk ("sb: Can't allocate memory for device information\n");
- return 0;
- }
+ detected_devc = (sb_devc *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(sb_devc)));
+ sound_mem_sizes[sound_nblocks] = sizeof(sb_devc);
+ if (sound_nblocks < 1024)
+ sound_nblocks++;;
- memcpy ((char *) detected_devc, (char *) devc, sizeof (sb_devc));
+ if (detected_devc == NULL)
+ {
+ printk(KERN_ERR "sb: Can't allocate memory for device information\n");
+ return 0;
+ }
+ memcpy((char *) detected_devc, (char *) devc, sizeof(sb_devc));
- DDB (printk ("SB %d.%d detected OK (%x)\n", devc->major, devc->minor,
- hw_config->io_base));
- return 1;
+ MDB(printk("SB %d.%d detected OK (%x)\n", devc->major, devc->minor, hw_config->io_base));
+ return 1;
}
void
-sb_dsp_init (struct address_info *hw_config)
+sb_dsp_init(struct address_info *hw_config)
{
- sb_devc *devc;
- int n;
- char name[100];
- extern int sb_be_quiet;
+ sb_devc *devc;
+ char name[100];
+ extern int sb_be_quiet;
/*
* Check if we had detected a SB device earlier
*/
- DDB (printk ("sb_dsp_init(%x) entered\n", hw_config->io_base));
- name[0] = 0;
-
- if (detected_devc == NULL)
- {
- DDB (printk ("No detected device\n"));
- return;
- }
+ DDB(printk("sb_dsp_init(%x) entered\n", hw_config->io_base));
+ name[0] = 0;
- devc = detected_devc;
- detected_devc = NULL;
-
- if (devc->base != hw_config->io_base)
- {
- DDB (printk ("I/O port mismatch\n"));
- return;
- }
+ if (detected_devc == NULL)
+ {
+ MDB(printk("No detected device\n"));
+ return;
+ }
+ devc = detected_devc;
+ detected_devc = NULL;
+ if (devc->base != hw_config->io_base)
+ {
+ DDB(printk("I/O port mismatch\n"));
+ return;
+ }
/*
* Now continue initialization of the device
*/
- devc->dev = num_audiodevs;
- devc->caps = hw_config->driver_use_1;
-
- if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI) &&
- hw_config->irq > 0)
- { /* IRQ setup */
- if (snd_set_irq_handler (hw_config->irq,
- sbintr, "soundblaster", devc->osp) < 0)
- {
- printk ("SB: Can't allocate IRQ%d\n", hw_config->irq);
- return;
- }
-
- irq2devc[hw_config->irq] = devc;
- devc->irq_ok = 0;
-
- if (devc->major == 4)
- if (!sb16_set_irq_hw (devc, devc->irq)) /* Unsupported IRQ */
+ devc->dev = sound_alloc_audiodev();
+ if (devc->dev == -1)
{
- snd_release_irq (devc->irq);
- irq2devc[hw_config->irq] = NULL;
- return;
+ printk(KERN_WARNING "sb: too many audio devices.\n");
+ return;
}
-
- if ((devc->type == 0 || devc->type == MDL_ESS) &&
- devc->major == 3 && devc->minor == 1)
- { /* Handle various chipsets which claim they are SB Pro compatible */
- if ((devc->type != 0 && devc->type != MDL_ESS) ||
- !ess_init (devc, hw_config))
- if ((devc->type != 0 && devc->type != MDL_JAZZ &&
- devc->type != MDL_SMW) || !init_Jazz16 (devc, hw_config))
- {
- DDB (printk ("This is a genuine SB Pro\n"));
- }
- }
-
-#ifdef __SMP__
- /* Skip IRQ detection if SMP (doesn't work) */
- devc->irq_ok = 1;
+ devc->caps = hw_config->driver_use_1;
+
+ if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI) &&
+ hw_config->irq > 0)
+ { /* IRQ setup */
+ if (snd_set_irq_handler(hw_config->irq,
+ sbintr, "soundblaster", devc->osp) < 0)
+ {
+ printk(KERN_ERR "SB: Can't allocate IRQ%d\n", hw_config->irq);
+ sound_unload_audiodev(devc->dev);
+ return;
+ }
+ irq2devc[hw_config->irq] = devc;
+ devc->irq_ok = 0;
+
+ if (devc->major == 4)
+ if (!sb16_set_irq_hw(devc, devc->irq)) /* Unsupported IRQ */
+ {
+ snd_release_irq(devc->irq);
+ sound_unload_audiodev(devc->dev);
+ irq2devc[hw_config->irq] = NULL;
+ return;
+ }
+ if ((devc->type == 0 || devc->type == MDL_ESS) &&
+ devc->major == 3 && devc->minor == 1)
+ { /* Handle various chipsets which claim they are SB Pro compatible */
+ if ((devc->type != 0 && devc->type != MDL_ESS) ||
+ !ess_init(devc, hw_config))
+ if ((devc->type != 0 && devc->type != MDL_JAZZ &&
+ devc->type != MDL_SMW) || !init_Jazz16(devc, hw_config))
+ {
+ DDB(printk("This is a genuine SB Pro\n"));
+ }
+ }
+#if defined(__SMP__) || defined(__FreeBSD__)
+ /* Skip IRQ detection if SMP (doesn't work) */
+ devc->irq_ok = 1;
#else
- if (devc->major == 4 && devc->minor <= 11) /* Won't work */
- devc->irq_ok = 1;
- else
- {
- for (n = 0; n < 3 && devc->irq_ok == 0; n++)
- if (sb_dsp_command (devc, 0xf2)) /* Cause interrupt immediately */
- {
- int i;
-
- for (i = 0; !devc->irq_ok && i < 10000; i++);
- }
-
- if (!devc->irq_ok)
- {
- printk ("sb: Interrupt test on IRQ%d failed - Probable IRQ conflict\n", devc->irq);
- }
- else
- {
- DDB (printk ("IRQ test OK (IRQ%d)\n", devc->irq));
- }
-
- }
-#endif /* __SMP__ */
- } /* IRQ setup */
-
- request_region (hw_config->io_base, 16, "soundblaster");
-
- switch (devc->major)
- {
- case 1: /* SB 1.0 or 1.5 */
- devc->model = hw_config->card_subtype = MDL_SB1;
- break;
-
- case 2: /* SB 2.x */
- if (devc->minor == 0)
- devc->model = hw_config->card_subtype = MDL_SB2;
- else
- devc->model = hw_config->card_subtype = MDL_SB201;
- break;
-
- case 3: /* SB Pro and most clones */
- if (devc->model == 0)
- {
- devc->model = hw_config->card_subtype = MDL_SBPRO;
- if (hw_config->name == NULL)
- hw_config->name = "Sound Blaster Pro (8 BIT ONLY)";
- }
- break;
-
- case 4:
- devc->model = hw_config->card_subtype = MDL_SB16;
- if (hw_config->name == NULL)
- hw_config->name = "Sound Blaster 16";
-
- if (hw_config->dma2 == -1)
- devc->dma16 = devc->dma8;
- else if (hw_config->dma2 < 5 || hw_config->dma2 > 7)
- {
- printk ("SB16: Bad or missing 16 bit DMA channel\n");
- devc->dma16 = devc->dma8;
- }
- else
- devc->dma16 = hw_config->dma2;
-
- sb16_set_dma_hw (devc);
- devc->caps |= SB_NO_MIDI;
- }
+ if (devc->major == 4 && devc->minor <= 11) /* Won't work */
+ devc->irq_ok = 1;
+ else
+ {
+ int n;
+
+ for (n = 0; n < 3 && devc->irq_ok == 0; n++)
+ if (sb_dsp_command(devc, 0xf2)) /* Cause interrupt immediately */
+ {
+ int i;
+
+ for (i = 0; !devc->irq_ok && i < 10000; i++);
+ }
+ if (!devc->irq_ok)
+ {
+ printk("sb: Interrupt test on IRQ%d failed - Probable IRQ conflict\n", devc->irq);
+ } else
+ {
+ DDB(printk("IRQ test OK (IRQ%d)\n", devc->irq));
+ }
+
+ }
+#endif /* __SMP__ */
+ } /* IRQ setup */
+ request_region(hw_config->io_base, 16, "soundblaster");
+
+ switch (devc->major)
+ {
+ case 1: /* SB 1.0 or 1.5 */
+ devc->model = hw_config->card_subtype = MDL_SB1;
+ break;
+
+ case 2: /* SB 2.x */
+ if (devc->minor == 0)
+ devc->model = hw_config->card_subtype = MDL_SB2;
+ else
+ devc->model = hw_config->card_subtype = MDL_SB201;
+ break;
+
+ case 3: /* SB Pro and most clones */
+ if (devc->model == 0)
+ {
+ devc->model = hw_config->card_subtype = MDL_SBPRO;
+ if (hw_config->name == NULL)
+ hw_config->name = "Sound Blaster Pro (8 BIT ONLY)";
+ }
+ break;
+
+ case 4:
+ devc->model = hw_config->card_subtype = MDL_SB16;
+ if (hw_config->name == NULL)
+ hw_config->name = "Sound Blaster 16";
+
+ if (hw_config->dma2 == -1)
+ devc->dma16 = devc->dma8;
+ else if (hw_config->dma2 < 5 || hw_config->dma2 > 7)
+ {
+ printk("SB16: Bad or missing 16 bit DMA channel\n");
+ devc->dma16 = devc->dma8;
+ } else
+ devc->dma16 = hw_config->dma2;
+
+ sb16_set_dma_hw(devc);
+ devc->caps |= SB_NO_MIDI;
+ }
- if (!(devc->caps & SB_NO_MIXER))
- if (devc->major == 3 || devc->major == 4)
- sb_mixer_init (devc);
+ if (!(devc->caps & SB_NO_MIXER))
+ if (devc->major == 3 || devc->major == 4)
+ sb_mixer_init(devc);
-#ifdef CONFIG_MIDI
- if (!(devc->caps & SB_NO_MIDI))
- sb_dsp_midi_init (devc);
+#if defined(CONFIG_MIDI)
+ if (!(devc->caps & SB_NO_MIDI))
+ sb_dsp_midi_init(devc);
#endif
- if (hw_config->name == NULL)
- hw_config->name = "Sound Blaster (8 BIT/MONO ONLY)";
+ if (hw_config->name == NULL)
+ hw_config->name = "Sound Blaster (8 BIT/MONO ONLY)";
- sprintf (name, "%s (%d.%d)", hw_config->name, devc->major, devc->minor);
- conf_printf (name, hw_config);
+ sprintf(name, "%s (%d.%d)", hw_config->name, devc->major, devc->minor);
+ conf_printf(name, hw_config);
/*
* Assuming that a soundcard is Sound Blaster (compatible) is the most common
@@ -821,84 +895,87 @@ sb_dsp_init (struct address_info *hw_config)
* used in Unix. See Readme.cards for more information about configuring OSS/Free
* properly.
*/
- if (devc->model <= MDL_SBPRO)
- if (devc->major == 3 && devc->minor != 1) /* "True" SB Pro should have v3.1. */
- {
- printk ("This soundcard doesn't seem to be fully Sound Blaster Pro compatible.\n");
- printk ("Almost certainly there is another way to configure OSS so that\n");
- printk ("it works properly with OSS (for example in 16 bit mode).\n");
- }
- else if (!sb_be_quiet && devc->model == MDL_SBPRO)
- {
- printk ("SB DSP version is just %d.%d which means that your card is\n",
- devc->major, devc->minor);
- printk ("several years old (8 bit only device)\n");
- printk ("or alternatively the sound driver is incorrectly configured.\n");
- }
-
- hw_config->card_subtype = devc->model;
- last_devc = devc; /* For SB MPU detection */
-
- if (!(devc->caps & SB_NO_AUDIO) && devc->dma8 >= 0)
- {
- if (sound_alloc_dma (devc->dma8, "SoundBlaster8"))
- {
- printk ("SB: Can't allocate 8 bit DMA channel %d\n", devc->dma8);
- }
- if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
- if (sound_alloc_dma (devc->dma16, "SoundBlaster16"))
+ if (devc->model <= MDL_SBPRO)
+ if (devc->major == 3 && devc->minor != 1) /* "True" SB Pro should have v3.1 (rare ones may have 3.2). */
+ {
+ printk("This soundcard may not be fully Sound Blaster Pro compatible.\n");
+ printk("In many cases there is another way to configure OSS so that\n");
+ printk("it works properly with OSS (for example in 16 bit mode).\n");
+ printk("Please ignore this message if you _really_ have a SB Pro.\n");
+ } else if (!sb_be_quiet && devc->model == MDL_SBPRO)
+ {
+ printk("SB DSP version is just %d.%d which means that your card is\n", devc->major, devc->minor);
+ printk("several years old (8 bit only device)\n");
+ printk("or alternatively the sound driver is incorrectly configured.\n");
+ }
+ hw_config->card_subtype = devc->model;
+ last_devc = devc; /* For SB MPU detection */
+
+ if (!(devc->caps & SB_NO_AUDIO) && devc->dma8 >= 0)
+ {
+ if (sound_alloc_dma(devc->dma8, "SoundBlaster8"))
+ {
+ printk("SB: Can't allocate 8 bit DMA channel %d\n", devc->dma8);
+ }
+ if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
+ if (sound_alloc_dma(devc->dma16, "SoundBlaster16"))
+ {
+ printk("SB: Can't allocate 16 bit DMA channel %d\n", devc->dma16);
+ }
+ sb_audio_init(devc, name);
+ } else
{
- printk ("SB: Can't allocate 16 bit DMA channel %d\n", devc->dma16);
+ MDB(printk("sb: No audio devices found.\n"));
}
- sb_audio_init (devc, name);
- }
}
void
-sb_dsp_disable_midi (int io_base)
+sb_dsp_disable_midi(int io_base)
{
}
void
-sb_dsp_disable_recording (int io_base)
+sb_dsp_disable_recording(int io_base)
{
}
void
-sb_dsp_unload (struct address_info *hw_config)
+sb_dsp_unload(struct address_info *hw_config)
{
- sb_devc *devc;
- int irq = hw_config->irq;
+ sb_devc *devc;
+ int irq = hw_config->irq;
- if (irq < 0)
- irq *= -1;
+ if (irq < 0)
+ irq *= -1;
- if (irq > 2 && irq < 16)
- devc = irq2devc[irq];
- else
- devc = NULL;
-
- if (devc && devc->base == hw_config->io_base)
- {
- release_region (devc->base, 16);
-
- if (!(devc->caps & SB_NO_AUDIO))
- {
- sound_free_dma (devc->dma8);
+ if (irq > 2 && irq < 16)
+ devc = irq2devc[irq];
+ else
+ devc = NULL;
- if (devc->dma16 >= 0)
- sound_free_dma (devc->dma16);
- }
+ if (devc && devc->base == hw_config->io_base)
+ {
+ release_region(devc->base, 16);
+
+ if (!(devc->caps & SB_NO_AUDIO))
+ {
+ sound_free_dma(devc->dma8);
+
+ if (devc->dma16 >= 0)
+ sound_free_dma(devc->dma16);
+ }
+ if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI) &&
+ devc->irq > 0)
+ {
+ snd_release_irq(devc->irq);
+ irq2devc[devc->irq] = NULL;
+ sound_unload_mixerdev(devc->my_mixerdev);
+ sound_unload_mididev(devc->my_mididev);
+ sound_unload_audiodev(devc->my_dev);
+ }
+ } else
+ release_region(hw_config->io_base, 16);
- if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI) &&
- devc->irq > 0)
- {
- snd_release_irq (devc->irq);
- irq2devc[devc->irq] = NULL;
- }
- }
- else
- release_region (hw_config->io_base, 16);
}
/*
@@ -906,401 +983,404 @@ sb_dsp_unload (struct address_info *hw_config)
*/
void
-sb_setmixer (sb_devc * devc, unsigned int port, unsigned int value)
+sb_setmixer(sb_devc * devc, unsigned int port, unsigned int value)
{
- unsigned long flags;
-
- save_flags (flags);
- cli ();
- outb (((unsigned char) (port & 0xff)), MIXER_ADDR);
-
- tenmicrosec (devc->osp);
- tenmicrosec (devc->osp);
- outb (((unsigned char) (value & 0xff)), MIXER_DATA);
- tenmicrosec (devc->osp);
- tenmicrosec (devc->osp);
- restore_flags (flags);
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
+
+ tenmicrosec(devc->osp);
+ tenmicrosec(devc->osp);
+ outb(((unsigned char) (value & 0xff)), MIXER_DATA);
+ tenmicrosec(devc->osp);
+ tenmicrosec(devc->osp);
+ restore_flags(flags);
}
unsigned int
-sb_getmixer (sb_devc * devc, unsigned int port)
+sb_getmixer(sb_devc * devc, unsigned int port)
{
- unsigned int val;
- unsigned long flags;
+ unsigned int val;
+ unsigned long flags;
- save_flags (flags);
- cli ();
- outb (((unsigned char) (port & 0xff)), MIXER_ADDR);
+ save_flags(flags);
+ cli();
+ outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
- tenmicrosec (devc->osp);
- tenmicrosec (devc->osp);
- val = inb (MIXER_DATA);
- tenmicrosec (devc->osp);
- tenmicrosec (devc->osp);
- restore_flags (flags);
+ tenmicrosec(devc->osp);
+ tenmicrosec(devc->osp);
+ val = inb(MIXER_DATA);
+ tenmicrosec(devc->osp);
+ tenmicrosec(devc->osp);
+ restore_flags(flags);
- return val;
+ return val;
}
-#ifdef CONFIG_MIDI
+#if defined(CONFIG_MIDI)
/*
* MPU401 MIDI initialization.
*/
static void
-smw_putmem (sb_devc * devc, int base, int addr, unsigned char val)
+smw_putmem(sb_devc * devc, int base, int addr, unsigned char val)
{
- unsigned long flags;
+ unsigned long flags;
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- outb ((addr & 0xff), base + 1); /* Low address bits */
- outb ((addr >> 8), base + 2); /* High address bits */
- outb ((val), base); /* Data */
+ outb((addr & 0xff), base + 1); /* Low address bits */
+ outb((addr >> 8), base + 2); /* High address bits */
+ outb((val), base); /* Data */
- restore_flags (flags);
+ restore_flags(flags);
}
static unsigned char
-smw_getmem (sb_devc * devc, int base, int addr)
+smw_getmem(sb_devc * devc, int base, int addr)
{
- unsigned long flags;
- unsigned char val;
+ unsigned long flags;
+ unsigned char val;
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- outb ((addr & 0xff), base + 1); /* Low address bits */
- outb ((addr >> 8), base + 2); /* High address bits */
- val = inb (base); /* Data */
+ outb((addr & 0xff), base + 1); /* Low address bits */
+ outb((addr >> 8), base + 2); /* High address bits */
+ val = inb(base); /* Data */
- restore_flags (flags);
- return val;
+ restore_flags(flags);
+ return val;
}
static int
-smw_midi_init (sb_devc * devc, struct address_info *hw_config)
+smw_midi_init(sb_devc * devc, struct address_info *hw_config)
{
- int mpu_base = hw_config->io_base;
- int mp_base = mpu_base + 4; /* Microcontroller base */
- int i;
- unsigned char control;
-
-
- /*
- * Reset the microcontroller so that the RAM can be accessed
- */
+ int mpu_base = hw_config->io_base;
+ int mp_base = mpu_base + 4; /* Microcontroller base */
+ int i;
+ unsigned char control;
- control = inb (mpu_base + 7);
- outb ((control | 3), mpu_base + 7); /* Set last two bits to 1 (?) */
- outb (((control & 0xfe) | 2), mpu_base + 7); /* xxxxxxx0 resets the mc */
- for (i = 0; i < 300; i++) /* Wait at least 1ms */
- tenmicrosec (devc->osp);
+ /*
+ * Reset the microcontroller so that the RAM can be accessed
+ */
- outb ((control & 0xfc), mpu_base + 7); /* xxxxxx00 enables RAM */
+ control = inb(mpu_base + 7);
+ outb((control | 3), mpu_base + 7); /* Set last two bits to 1 (?) */
+ outb(((control & 0xfe) | 2), mpu_base + 7); /* xxxxxxx0 resets the mc */
- /*
- * Detect microcontroller by probing the 8k RAM area
- */
- smw_putmem (devc, mp_base, 0, 0x00);
- smw_putmem (devc, mp_base, 1, 0xff);
- tenmicrosec (devc->osp);
+ for (i = 0; i < 300; i++) /* Wait at least 1ms */
+ tenmicrosec(devc->osp);
- if (smw_getmem (devc, mp_base, 0) != 0x00 || smw_getmem (devc, mp_base, 1) != 0xff)
- {
- DDB (printk ("\nSM Wave: No microcontroller RAM detected (%02x, %02x)\n",
- smw_getmem (devc, mp_base, 0), smw_getmem (devc, mp_base, 1)));
- return 0; /* No RAM */
- }
+ outb((control & 0xfc), mpu_base + 7); /* xxxxxx00 enables RAM */
- /*
- * There is RAM so assume it's really a SM Wave
- */
+ /*
+ * Detect microcontroller by probing the 8k RAM area
+ */
+ smw_putmem(devc, mp_base, 0, 0x00);
+ smw_putmem(devc, mp_base, 1, 0xff);
+ tenmicrosec(devc->osp);
- devc->model = MDL_SMW;
- smw_mixer_init (devc);
-
- if (smw_ucodeLen > 0)
- {
- if (smw_ucodeLen != 8192)
- {
- printk ("\nSM Wave: Invalid microcode (MIDI0001.BIN) length\n");
- return 1;
- }
-
- /*
- * Download microcode
- */
+ if (smw_getmem(devc, mp_base, 0) != 0x00 || smw_getmem(devc, mp_base, 1) != 0xff)
+ {
+ DDB(printk("\nSM Wave: No microcontroller RAM detected (%02x, %02x)\n", smw_getmem(devc, mp_base, 0), smw_getmem(devc, mp_base, 1)));
+ return 0; /* No RAM */
+ }
+ /*
+ * There is RAM so assume it's really a SM Wave
+ */
- for (i = 0; i < 8192; i++)
- smw_putmem (devc, mp_base, i, smw_ucode[i]);
+ devc->model = MDL_SMW;
+ smw_mixer_init(devc);
- /*
- * Verify microcode
- */
+#ifdef MODULE
+ if (!smw_ucode)
+ {
+ extern void *smw_free;
- for (i = 0; i < 8192; i++)
- if (smw_getmem (devc, mp_base, i) != smw_ucode[i])
+ smw_ucodeLen = mod_firmware_load("/etc/sound/midi0001.bin", (void *) &smw_ucode);
+ smw_free = smw_ucode;
+ }
+#endif
+ if (smw_ucodeLen > 0)
{
- printk ("SM Wave: Microcode verification failed\n");
- return 0;
+ if (smw_ucodeLen != 8192)
+ {
+ printk("\nSM Wave: Invalid microcode (MIDI0001.BIN) length\n");
+ return 1;
+ }
+ /*
+ * Download microcode
+ */
+
+ for (i = 0; i < 8192; i++)
+ smw_putmem(devc, mp_base, i, smw_ucode[i]);
+
+ /*
+ * Verify microcode
+ */
+
+ for (i = 0; i < 8192; i++)
+ if (smw_getmem(devc, mp_base, i) != smw_ucode[i])
+ {
+ printk("SM Wave: Microcode verification failed\n");
+ return 0;
+ }
}
- }
-
- control = 0;
+ control = 0;
#ifdef SMW_SCSI_IRQ
- /*
- * Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt
- * is disabled by default.
- *
- * BTW the Zilog 5380 SCSI controller is located at MPU base + 0x10.
- */
- {
- static unsigned char scsi_irq_bits[] =
- {0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0};
-
- control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6;
- }
+ /*
+ * Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt
+ * is disabled by default.
+ *
+ * BTW the Zilog 5380 SCSI controller is located at MPU base + 0x10.
+ */
+ {
+ static unsigned char scsi_irq_bits[] =
+ {0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0};
+
+ control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6;
+ }
#endif
#ifdef SMW_OPL4_ENABLE
- /*
- * Make the OPL4 chip visible on the PC bus at 0x380.
- *
- * There is no need to enable this feature since this driver
- * doesn't support OPL4 yet. Also there is no RAM in SM Wave so
- * enabling OPL4 is pretty useless.
- */
- control |= 0x10; /* Uses IRQ12 if bit 0x20 == 0 */
- /* control |= 0x20; Uncomment this if you want to use IRQ7 */
+ /*
+ * Make the OPL4 chip visible on the PC bus at 0x380.
+ *
+ * There is no need to enable this feature since this driver
+ * doesn't support OPL4 yet. Also there is no RAM in SM Wave so
+ * enabling OPL4 is pretty useless.
+ */
+ control |= 0x10; /* Uses IRQ12 if bit 0x20 == 0 */
+ /* control |= 0x20; Uncomment this if you want to use IRQ7 */
#endif
- outb ((control | 0x03), mpu_base + 7); /* xxxxxx11 restarts */
- hw_config->name = "SoundMan Wave";
- return 1;
+ outb((control | 0x03), mpu_base + 7); /* xxxxxx11 restarts */
+ hw_config->name = "SoundMan Wave";
+ return 1;
}
static int
-ess_midi_init (sb_devc * devc, struct address_info *hw_config)
+ess_midi_init(sb_devc * devc, struct address_info *hw_config)
{
- unsigned char cfg, tmp;
-
- cfg = sb_getmixer (devc, 0x40) & 0x03;
-
- if (devc->submodel < 8)
- {
- sb_setmixer (devc, 0x40, cfg | 0x03); /* Enable OPL3 & joystick */
- return 0; /* ES688 doesn't support MPU401 mode */
- }
-
- tmp = (hw_config->io_base & 0x0f0) >> 4;
-
- if (tmp > 3)
- {
- sb_setmixer (devc, 0x40, cfg);
- return 0;
- }
-
- cfg |= tmp << 3;
-
- tmp = 1; /* MPU enabled without interrupts */
-
- switch (hw_config->irq)
- {
- case 9:
- tmp = 0x4;
- break;
- case 5:
- tmp = 0x5;
- break;
- case 7:
- tmp = 0x6;
- break;
- case 10:
- tmp = 0x7;
- break;
- default:
- return 0;
- }
-
- cfg |= tmp << 5;
-
- sb_setmixer (devc, 0x40, cfg | 0x03);
- return 1;
+ unsigned char cfg, tmp;
+
+ cfg = sb_getmixer(devc, 0x40) & 0x03;
+
+ if (devc->submodel < 8)
+ {
+ sb_setmixer(devc, 0x40, cfg | 0x03); /* Enable OPL3 & joystick */
+ return 0; /* ES688 doesn't support MPU401 mode */
+ }
+ tmp = (hw_config->io_base & 0x0f0) >> 4;
+
+ if (tmp > 3)
+ {
+ sb_setmixer(devc, 0x40, cfg);
+ return 0;
+ }
+ cfg |= tmp << 3;
+
+ tmp = 1; /* MPU enabled without interrupts */
+
+ switch (hw_config->irq)
+ {
+ case 9:
+ tmp = 0x4;
+ break;
+ case 5:
+ tmp = 0x5;
+ break;
+ case 7:
+ tmp = 0x6;
+ break;
+ case 10:
+ tmp = 0x7;
+ break;
+ default:
+ return 0;
+ }
+
+ cfg |= tmp << 5;
+
+ sb_setmixer(devc, 0x40, cfg | 0x03);
+ return 1;
}
static int
-init_Jazz16_midi (sb_devc * devc, struct address_info *hw_config)
+init_Jazz16_midi(sb_devc * devc, struct address_info *hw_config)
{
- int mpu_base = hw_config->io_base;
- int sb_base = devc->base;
- int irq = hw_config->irq;
-
- unsigned char bits = 0;
- unsigned long flags;
-
- if (irq < 0)
- irq *= -1;
-
- if (irq < 1 || irq > 15 ||
- jazz_irq_bits[irq] == 0)
- {
- printk ("Jazz16: Invalid MIDI interrupt (IRQ%d)\n", irq);
- return 0;
- }
-
- switch (sb_base)
- {
- case 0x220:
- bits = 1;
- break;
- case 0x240:
- bits = 2;
- break;
- case 0x260:
- bits = 3;
- break;
-
- default:
- return 0;
- }
-
- bits = jazz16_bits = bits << 5;
-
- switch (mpu_base)
- {
- case 0x310:
- bits |= 1;
- break;
- case 0x320:
- bits |= 2;
- break;
- case 0x330:
- bits |= 3;
- break;
-
- default:
- printk ("Jazz16: Invalid MIDI I/O port %x\n", mpu_base);
- return 0;
- }
+ int mpu_base = hw_config->io_base;
+ int sb_base = devc->base;
+ int irq = hw_config->irq;
+
+ unsigned char bits = 0;
+ unsigned long flags;
+
+ if (irq < 0)
+ irq *= -1;
+
+ if (irq < 1 || irq > 15 ||
+ jazz_irq_bits[irq] == 0)
+ {
+ printk("Jazz16: Invalid MIDI interrupt (IRQ%d)\n", irq);
+ return 0;
+ }
+ switch (sb_base)
+ {
+ case 0x220:
+ bits = 1;
+ break;
+ case 0x240:
+ bits = 2;
+ break;
+ case 0x260:
+ bits = 3;
+ break;
+
+ default:
+ return 0;
+ }
+
+ bits = jazz16_bits = bits << 5;
+
+ switch (mpu_base)
+ {
+ case 0x310:
+ bits |= 1;
+ break;
+ case 0x320:
+ bits |= 2;
+ break;
+ case 0x330:
+ bits |= 3;
+ break;
+
+ default:
+ printk("Jazz16: Invalid MIDI I/O port %x\n", mpu_base);
+ return 0;
+ }
/*
* Magic wake up sequence by writing to 0x201 (aka Joystick port)
*/
- save_flags (flags);
- cli ();
- outb ((0xAF), 0x201);
- outb ((0x50), 0x201);
- outb ((bits), 0x201);
- restore_flags (flags);
+ save_flags(flags);
+ cli();
+ outb((0xAF), 0x201);
+ outb((0x50), 0x201);
+ outb((bits), 0x201);
+ restore_flags(flags);
- hw_config->name = "Jazz16";
- smw_midi_init (devc, hw_config);
+ hw_config->name = "Jazz16";
+ smw_midi_init(devc, hw_config);
- if (!sb_dsp_command (devc, 0xfb))
- return 0;
+ if (!sb_dsp_command(devc, 0xfb))
+ return 0;
- if (!sb_dsp_command (devc, jazz_dma_bits[devc->dma8] |
- (jazz_dma_bits[devc->dma16] << 4)))
- return 0;
+ if (!sb_dsp_command(devc, jazz_dma_bits[devc->dma8] |
+ (jazz_dma_bits[devc->dma16] << 4)))
+ return 0;
- if (!sb_dsp_command (devc, jazz_irq_bits[devc->irq] |
- (jazz_irq_bits[irq] << 4)))
- return 0;
+ if (!sb_dsp_command(devc, jazz_irq_bits[devc->irq] |
+ (jazz_irq_bits[irq] << 4)))
+ return 0;
- return 1;
+ return 1;
}
void
-attach_sbmpu (struct address_info *hw_config)
+attach_sbmpu(struct address_info *hw_config)
{
-#if defined(CONFIG_MIDI) && defined(CONFIG_UART401)
- attach_uart401 (hw_config);
+#if defined(CONFIG_MIDI) && (defined(CONFIG_UART401)||defined(CONFIG_UART401_MODULE))
+ attach_uart401(hw_config);
#endif
}
int
-probe_sbmpu (struct address_info *hw_config)
+probe_sbmpu(struct address_info *hw_config)
{
-#if defined(CONFIG_MIDI) && defined(CONFIG_UART401)
- sb_devc *devc = last_devc;
+#if defined(CONFIG_MIDI) && (defined(CONFIG_UART401)||defined(CONFIG_UART401_MODULE))
+ sb_devc *devc = last_devc;
- if (last_devc == NULL)
- return 0;
+ if (last_devc == NULL)
+ return 0;
- last_devc = 0;
+ last_devc = 0;
- if (hw_config->io_base <= 0)
- return 0;
+ if (hw_config->io_base <= 0)
+ return 0;
- if (check_region (hw_config->io_base, 4))
- {
- printk ("sbmpu: I/O port conflict (%x)\n", hw_config->io_base);
- return 0;
- }
-
- switch (devc->model)
- {
- case MDL_SB16:
- if (hw_config->io_base != 0x300 && hw_config->io_base != 0x330)
- {
- printk ("SB16: Invalid MIDI port %x\n", hw_config->irq);
- return 0;
- }
- hw_config->name = "Sound Blaster 16";
- hw_config->irq = -devc->irq;
- if (devc->minor > 12) /* What is Vibra's version??? */
- sb16_set_mpu_port (devc, hw_config);
- break;
-
- case MDL_ESS:
- if (hw_config->irq < 3 || hw_config->irq == devc->irq)
- hw_config->irq = -devc->irq;
- if (!ess_midi_init (devc, hw_config))
- return 0;
- hw_config->name = "ESS ES1688";
- break;
-
- case MDL_JAZZ:
- if (hw_config->irq < 3 || hw_config->irq == devc->irq)
- hw_config->irq = -devc->irq;
- if (!init_Jazz16_midi (devc, hw_config))
- return 0;
- break;
-
- default:
- return 0;
- }
+ if (check_region(hw_config->io_base, 4))
+ {
+ printk("sbmpu: I/O port conflict (%x)\n", hw_config->io_base);
+ return 0;
+ }
+ switch (devc->model)
+ {
+ case MDL_SB16:
+ if (hw_config->io_base != 0x300 && hw_config->io_base != 0x330)
+ {
+ printk("SB16: Invalid MIDI port %x\n", hw_config->irq);
+ return 0;
+ }
+ hw_config->name = "Sound Blaster 16";
+ hw_config->irq = -devc->irq;
+#if defined(CONFIG_MIDI) && defined(CONFIG_UART401)
+ if (devc->minor > 12) /* What is Vibra's version??? */
+ sb16_set_mpu_port(devc, hw_config);
+#endif
+ break;
+
+ case MDL_ESS:
+ if (hw_config->irq < 3 || hw_config->irq == devc->irq)
+ hw_config->irq = -devc->irq;
+ if (!ess_midi_init(devc, hw_config))
+ return 0;
+ hw_config->name = "ESS ES1688";
+ break;
+
+ case MDL_JAZZ:
+ if (hw_config->irq < 3 || hw_config->irq == devc->irq)
+ hw_config->irq = -devc->irq;
+ if (!init_Jazz16_midi(devc, hw_config))
+ return 0;
+ break;
+
+ default:
+ return 0;
+ }
- return probe_uart401 (hw_config);
+ return probe_uart401(hw_config);
#else
- return 0;
+ return 0;
#endif
}
-void
-unload_sbmpu (struct address_info *hw_config)
+void
+unload_sbmpu(struct address_info *hw_config)
{
-#if defined(CONFIG_MIDI) && defined(CONFIG_UART401)
- unload_uart401 (hw_config);
+#if defined(CONFIG_MIDI) && (defined(CONFIG_UART401)||defined(CONFIG_UART401_MODULE))
+ unload_uart401(hw_config);
#endif
}
-#else /* !CONFIG_MIDI */
+#else /* !CONFIG_MIDI */
-void
-unload_sbmpu (struct address_info *hw_config)
+void
+unload_sbmpu(struct address_info *hw_config)
{
}
int
-probe_sbmpu (struct address_info *hw_config)
+probe_sbmpu(struct address_info *hw_config)
{
- return 0;
+ return 0;
}
void
-attach_sbmpu (struct address_info *hw_config)
+attach_sbmpu(struct address_info *hw_config)
{
}
#endif
diff --git a/drivers/sound/sb_midi.c b/drivers/sound/sb_midi.c
index cffaafc7b..06acc19ef 100644
--- a/drivers/sound/sb_midi.c
+++ b/drivers/sound/sb_midi.c
@@ -15,7 +15,8 @@
#include "sound_config.h"
-#if defined(CONFIG_SBDSP) && defined(CONFIG_MIDI)
+#ifdef CONFIG_SBDSP
+#ifdef CONFIG_MIDI
#include "sb.h"
#undef SB_TEST_IRQ
@@ -30,129 +31,126 @@
static int
-sb_midi_open (int dev, int mode,
- void (*input) (int dev, unsigned char data),
- void (*output) (int dev)
+sb_midi_open(int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
)
{
- sb_devc *devc = midi_devs[dev]->devc;
- unsigned long flags;
-
- if (devc == NULL)
- return -ENXIO;
-
- save_flags (flags);
- cli ();
- if (devc->opened)
- {
- restore_flags (flags);
- return -EBUSY;
- }
- devc->opened = 1;
- restore_flags (flags);
-
- devc->irq_mode = IMODE_MIDI;
- devc->midi_broken = 0;
-
- sb_dsp_reset (devc);
-
- if (!sb_dsp_command (devc, 0x35)) /* Start MIDI UART mode */
- {
- devc->opened = 0;
- return -EIO;
- }
-
- devc->intr_active = 1;
-
- if (mode & OPEN_READ)
- {
- devc->input_opened = 1;
- devc->midi_input_intr = input;
- }
-
- return 0;
+ sb_devc *devc = midi_devs[dev]->devc;
+ unsigned long flags;
+
+ if (devc == NULL)
+ return -ENXIO;
+
+ save_flags(flags);
+ cli();
+ if (devc->opened)
+ {
+ restore_flags(flags);
+ return -EBUSY;
+ }
+ devc->opened = 1;
+ restore_flags(flags);
+
+ devc->irq_mode = IMODE_MIDI;
+ devc->midi_broken = 0;
+
+ sb_dsp_reset(devc);
+
+ if (!sb_dsp_command(devc, 0x35)) /* Start MIDI UART mode */
+ {
+ devc->opened = 0;
+ return -EIO;
+ }
+ devc->intr_active = 1;
+
+ if (mode & OPEN_READ)
+ {
+ devc->input_opened = 1;
+ devc->midi_input_intr = input;
+ }
+ return 0;
}
static void
-sb_midi_close (int dev)
+sb_midi_close(int dev)
{
- sb_devc *devc = midi_devs[dev]->devc;
- unsigned long flags;
-
- if (devc == NULL)
- return;
-
- save_flags (flags);
- cli ();
- sb_dsp_reset (devc);
- devc->intr_active = 0;
- devc->input_opened = 0;
- devc->opened = 0;
- restore_flags (flags);
+ sb_devc *devc = midi_devs[dev]->devc;
+ unsigned long flags;
+
+ if (devc == NULL)
+ return;
+
+ save_flags(flags);
+ cli();
+ sb_dsp_reset(devc);
+ devc->intr_active = 0;
+ devc->input_opened = 0;
+ devc->opened = 0;
+ restore_flags(flags);
}
static int
-sb_midi_out (int dev, unsigned char midi_byte)
+sb_midi_out(int dev, unsigned char midi_byte)
{
- sb_devc *devc = midi_devs[dev]->devc;
+ sb_devc *devc = midi_devs[dev]->devc;
- if (devc == NULL)
- return 1;
+ if (devc == NULL)
+ return 1;
- if (devc->midi_broken)
- return 1;
+ if (devc->midi_broken)
+ return 1;
- if (!sb_dsp_command (devc, midi_byte))
- {
- devc->midi_broken = 1;
- return 1;
- }
-
- return 1;
+ if (!sb_dsp_command(devc, midi_byte))
+ {
+ devc->midi_broken = 1;
+ return 1;
+ }
+ return 1;
}
static int
-sb_midi_start_read (int dev)
+sb_midi_start_read(int dev)
{
- return 0;
+ return 0;
}
static int
-sb_midi_end_read (int dev)
+sb_midi_end_read(int dev)
{
- sb_devc *devc = midi_devs[dev]->devc;
+ sb_devc *devc = midi_devs[dev]->devc;
- if (devc == NULL)
- return -ENXIO;
+ if (devc == NULL)
+ return -ENXIO;
- sb_dsp_reset (devc);
- devc->intr_active = 0;
- return 0;
+ sb_dsp_reset(devc);
+ devc->intr_active = 0;
+ return 0;
}
static int
-sb_midi_ioctl (int dev, unsigned cmd, caddr_t arg)
+sb_midi_ioctl(int dev, unsigned cmd, caddr_t arg)
{
- return -EPERM;
+ return -EPERM;
}
void
-sb_midi_interrupt (sb_devc * devc)
+sb_midi_interrupt(sb_devc * devc)
{
- unsigned long flags;
- unsigned char data;
+ unsigned long flags;
+ unsigned char data;
- if (devc == NULL)
- return;
+ if (devc == NULL)
+ return;
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- data = inb (DSP_READ);
- if (devc->input_opened)
- devc->midi_input_intr (devc->my_mididev, data);
+ data = inb(DSP_READ);
+ if (devc->input_opened)
+ devc->midi_input_intr(devc->my_mididev, data);
- restore_flags (flags);
+ restore_flags(flags);
}
#define MIDI_SYNTH_NAME "Sound Blaster Midi"
@@ -161,74 +159,77 @@ sb_midi_interrupt (sb_devc * devc)
static struct midi_operations sb_midi_operations =
{
- {"Sound Blaster", 0, 0, SNDCARD_SB},
- &std_midi_synth,
- {0},
- sb_midi_open,
- sb_midi_close,
- sb_midi_ioctl,
- sb_midi_out,
- sb_midi_start_read,
- sb_midi_end_read,
- NULL,
- NULL,
- NULL,
- NULL
+ {"Sound Blaster", 0, 0, SNDCARD_SB},
+ &std_midi_synth,
+ {0},
+ sb_midi_open,
+ sb_midi_close,
+ sb_midi_ioctl,
+ sb_midi_out,
+ sb_midi_start_read,
+ sb_midi_end_read,
+ NULL,
+ NULL,
+ NULL,
+ NULL
};
void
-sb_dsp_midi_init (sb_devc * devc)
+sb_dsp_midi_init(sb_devc * devc)
{
- if (devc->model < 2) /* No MIDI support for SB 1.x */
- return;
-
- if (num_midis >= MAX_MIDI_DEV)
- {
- printk ("Sound: Too many midi devices detected\n");
- return;
- }
+ int dev;
- std_midi_synth.midi_dev = num_midis;
- devc->my_mididev = num_midis;
+ if (devc->model < 2) /* No MIDI support for SB 1.x */
+ return;
- std_midi_synth.midi_dev = devc->my_mididev = num_midis;
+ dev = sound_alloc_mididev();
+ if (dev == -1)
+ {
+ printk("Sound: Too many midi devices detected\n");
+ return;
+ }
+ std_midi_synth.midi_dev = dev;
+ devc->my_mididev = dev;
- midi_devs[num_midis] = (struct midi_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct midi_operations)));
- sound_mem_sizes[sound_nblocks] = sizeof (struct midi_operations);
+ std_midi_synth.midi_dev = devc->my_mididev = dev;
- if (sound_nblocks < 1024)
- sound_nblocks++;;
- if (midi_devs[num_midis] == NULL)
- {
- printk ("sb MIDI: Failed to allocate memory\n");
- return;
- }
- memcpy ((char *) midi_devs[num_midis], (char *) &sb_midi_operations,
- sizeof (struct midi_operations));
+ midi_devs[dev] = (struct midi_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct midi_operations)));
+ sound_mem_sizes[sound_nblocks] = sizeof(struct midi_operations);
- midi_devs[num_midis]->devc = devc;
+ if (sound_nblocks < 1024)
+ sound_nblocks++;;
+ if (midi_devs[dev] == NULL)
+ {
+ printk(KERN_WARNING "sb MIDI: Failed to allocate memory\n");
+ sound_unload_mididev(dev);
+ return;
+ }
+ memcpy((char *) midi_devs[dev], (char *) &sb_midi_operations,
+ sizeof(struct midi_operations));
+ midi_devs[dev]->devc = devc;
- midi_devs[num_midis]->converter = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct synth_operations)));
- sound_mem_sizes[sound_nblocks] = sizeof (struct synth_operations);
- if (sound_nblocks < 1024)
- sound_nblocks++;;
+ midi_devs[dev]->converter = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct synth_operations)));
+ sound_mem_sizes[sound_nblocks] = sizeof(struct synth_operations);
- if (midi_devs[num_midis]->converter == NULL)
- {
- printk ("sb MIDI: Failed to allocate memory\n");
- return;
- }
+ if (sound_nblocks < 1024)
+ sound_nblocks++;;
- memcpy ((char *) midi_devs[num_midis]->converter, (char *) &std_midi_synth,
- sizeof (struct synth_operations));
+ if (midi_devs[dev]->converter == NULL)
+ {
+ printk(KERN_WARNING "sb MIDI: Failed to allocate memory\n");
+ sound_unload_mididev(dev);
+ return;
+ }
+ memcpy((char *) midi_devs[dev]->converter, (char *) &std_midi_synth,
+ sizeof(struct synth_operations));
- midi_devs[num_midis]->converter->id = "SBMIDI";
- num_midis++;
- sequencer_init ();
+ midi_devs[dev]->converter->id = "SBMIDI";
+ sequencer_init();
}
#endif
+#endif
diff --git a/drivers/sound/sb_mixer.c b/drivers/sound/sb_mixer.c
index 5e6c57899..6a7070e28 100644
--- a/drivers/sound/sb_mixer.c
+++ b/drivers/sound/sb_mixer.c
@@ -16,7 +16,7 @@
#include "sound_config.h"
-#if defined(CONFIG_SBDSP)
+#if defined(CONFIG_SBDSP) || defined(MODULE)
#define __SB_MIXER_C__
#include "sb.h"
@@ -24,445 +24,432 @@
static int sbmixnum = 1;
-static void sb_mixer_reset (sb_devc * devc);
+static void sb_mixer_reset(sb_devc * devc);
void
-sb_mixer_set_stereo (sb_devc * devc, int mode)
+sb_mixer_set_stereo(sb_devc * devc, int mode)
{
- sb_setmixer (devc, OUT_FILTER, ((sb_getmixer (devc, OUT_FILTER) & ~STEREO_DAC)
- | (mode ? STEREO_DAC : MONO_DAC)));
+ sb_setmixer(devc, OUT_FILTER, ((sb_getmixer(devc, OUT_FILTER) & ~STEREO_DAC)
+ | (mode ? STEREO_DAC : MONO_DAC)));
}
static int
-detect_mixer (sb_devc * devc)
+detect_mixer(sb_devc * devc)
{
- /*
- * Detect the mixer by changing parameters of two volume channels. If the
- * values read back match with the values written, the mixer is there (is
- * it?)
- */
- sb_setmixer (devc, FM_VOL, 0xff);
- sb_setmixer (devc, VOC_VOL, 0x33);
-
- if (sb_getmixer (devc, FM_VOL) != 0xff)
- return 0;
- if (sb_getmixer (devc, VOC_VOL) != 0x33)
- return 0;
-
- return 1;
+ /* Just trust the mixer is there */
+ return 1;
}
static void
-change_bits (sb_devc * devc, unsigned char *regval, int dev, int chn, int newval)
+change_bits(sb_devc * devc, unsigned char *regval, int dev, int chn, int newval)
{
- unsigned char mask;
- int shift;
+ unsigned char mask;
+ int shift;
- mask = (1 << (*devc->iomap)[dev][chn].nbits) - 1;
- newval = (int) ((newval * mask) + 50) / 100; /* Scale */
+ mask = (1 << (*devc->iomap)[dev][chn].nbits) - 1;
+ newval = (int) ((newval * mask) + 50) / 100; /* Scale */
- shift = (*devc->iomap)[dev][chn].bitoffs - (*devc->iomap)[dev][LEFT_CHN].nbits + 1;
+ shift = (*devc->iomap)[dev][chn].bitoffs - (*devc->iomap)[dev][LEFT_CHN].nbits + 1;
- *regval &= ~(mask << shift); /* Mask out previous value */
- *regval |= (newval & mask) << shift; /* Set the new value */
+ *regval &= ~(mask << shift); /* Mask out previous value */
+ *regval |= (newval & mask) << shift; /* Set the new value */
}
static int
-sb_mixer_get (sb_devc * devc, int dev)
+sb_mixer_get(sb_devc * devc, int dev)
{
- if (!((1 << dev) & devc->supported_devices))
- return -EINVAL;
+ if (!((1 << dev) & devc->supported_devices))
+ return -EINVAL;
- return devc->levels[dev];
+ return devc->levels[dev];
}
void
-smw_mixer_init (sb_devc * devc)
+smw_mixer_init(sb_devc * devc)
{
- int i;
+ int i;
- sb_setmixer (devc, 0x00, 0x18); /* Mute unused (Telephone) line */
- sb_setmixer (devc, 0x10, 0x38); /* Config register 2 */
+ sb_setmixer(devc, 0x00, 0x18); /* Mute unused (Telephone) line */
+ sb_setmixer(devc, 0x10, 0x38); /* Config register 2 */
- devc->supported_devices = 0;
- for (i = 0; i < sizeof (smw_mix_regs); i++)
- if (smw_mix_regs[i] != 0)
- devc->supported_devices |= (1 << i);
+ devc->supported_devices = 0;
+ for (i = 0; i < sizeof(smw_mix_regs); i++)
+ if (smw_mix_regs[i] != 0)
+ devc->supported_devices |= (1 << i);
- devc->supported_rec_devices = devc->supported_devices &
- ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_PCM |
- SOUND_MASK_VOLUME);
+ devc->supported_rec_devices = devc->supported_devices &
+ ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_PCM |
+ SOUND_MASK_VOLUME);
- sb_mixer_reset (devc);
+ sb_mixer_reset(devc);
}
static int
-smw_mixer_set (sb_devc * devc, int dev, int value)
+smw_mixer_set(sb_devc * devc, int dev, int value)
{
- int left = value & 0x000000ff;
- int right = (value & 0x0000ff00) >> 8;
- int reg, val;
-
- if (left > 100)
- left = 100;
- if (right > 100)
- right = 100;
-
- if (dev > 31)
- return -EINVAL;
-
- if (!(devc->supported_devices & (1 << dev))) /* Not supported */
- return -EINVAL;
-
- switch (dev)
- {
- case SOUND_MIXER_VOLUME:
- sb_setmixer (devc, 0x0b, 96 - (96 * left / 100)); /* 96=mute, 0=max */
- sb_setmixer (devc, 0x0c, 96 - (96 * right / 100));
- break;
-
- case SOUND_MIXER_BASS:
- case SOUND_MIXER_TREBLE:
- devc->levels[dev] = left | (right << 8);
-
- /* Set left bass and treble values */
- val = ((devc->levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / (unsigned) 100) << 4;
- val |= ((devc->levels[SOUND_MIXER_BASS] & 0xff) * 16 / (unsigned) 100) & 0x0f;
- sb_setmixer (devc, 0x0d, val);
-
- /* Set right bass and treble values */
- val = (((devc->levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / (unsigned) 100) << 4;
- val |= (((devc->levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / (unsigned) 100) & 0x0f;
- sb_setmixer (devc, 0x0e, val);
- break;
-
- default:
- reg = smw_mix_regs[dev];
- if (reg == 0)
- return -EINVAL;
- sb_setmixer (devc, reg, (24 - (24 * left / 100)) | 0x20); /* 24=mute, 0=max */
- sb_setmixer (devc, reg + 1, (24 - (24 * right / 100)) | 0x40);
- }
-
- devc->levels[dev] = left | (right << 8);
- return left | (right << 8);
+ int left = value & 0x000000ff;
+ int right = (value & 0x0000ff00) >> 8;
+ int reg, val;
+
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
+
+ if (dev > 31)
+ return -EINVAL;
+
+ if (!(devc->supported_devices & (1 << dev))) /* Not supported */
+ return -EINVAL;
+
+ switch (dev)
+ {
+ case SOUND_MIXER_VOLUME:
+ sb_setmixer(devc, 0x0b, 96 - (96 * left / 100)); /* 96=mute, 0=max */
+ sb_setmixer(devc, 0x0c, 96 - (96 * right / 100));
+ break;
+
+ case SOUND_MIXER_BASS:
+ case SOUND_MIXER_TREBLE:
+ devc->levels[dev] = left | (right << 8);
+
+ /* Set left bass and treble values */
+ val = ((devc->levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / (unsigned) 100) << 4;
+ val |= ((devc->levels[SOUND_MIXER_BASS] & 0xff) * 16 / (unsigned) 100) & 0x0f;
+ sb_setmixer(devc, 0x0d, val);
+
+ /* Set right bass and treble values */
+ val = (((devc->levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / (unsigned) 100) << 4;
+ val |= (((devc->levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / (unsigned) 100) & 0x0f;
+ sb_setmixer(devc, 0x0e, val);
+ break;
+
+ default:
+ reg = smw_mix_regs[dev];
+ if (reg == 0)
+ return -EINVAL;
+ sb_setmixer(devc, reg, (24 - (24 * left / 100)) | 0x20); /* 24=mute, 0=max */
+ sb_setmixer(devc, reg + 1, (24 - (24 * right / 100)) | 0x40);
+ }
+
+ devc->levels[dev] = left | (right << 8);
+ return left | (right << 8);
}
static int
-sb_mixer_set (sb_devc * devc, int dev, int value)
+sb_mixer_set(sb_devc * devc, int dev, int value)
{
- int left = value & 0x000000ff;
- int right = (value & 0x0000ff00) >> 8;
+ int left = value & 0x000000ff;
+ int right = (value & 0x0000ff00) >> 8;
- int regoffs;
- unsigned char val;
+ int regoffs;
+ unsigned char val;
- if (devc->model == MDL_SMW)
- return smw_mixer_set (devc, dev, value);
+ if (devc->model == MDL_SMW)
+ return smw_mixer_set(devc, dev, value);
- if (left > 100)
- left = 100;
- if (right > 100)
- right = 100;
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
- if (dev > 31)
- return -EINVAL;
+ if (dev > 31)
+ return -EINVAL;
- if (!(devc->supported_devices & (1 << dev))) /*
- * Not supported
- */
- return -EINVAL;
+ if (!(devc->supported_devices & (1 << dev))) /*
+ * Not supported
+ */
+ return -EINVAL;
- regoffs = (*devc->iomap)[dev][LEFT_CHN].regno;
+ regoffs = (*devc->iomap)[dev][LEFT_CHN].regno;
- if (regoffs == 0)
- return -EINVAL;
+ if (regoffs == 0)
+ return -EINVAL;
- val = sb_getmixer (devc, regoffs);
- change_bits (devc, &val, dev, LEFT_CHN, left);
+ val = sb_getmixer(devc, regoffs);
+ change_bits(devc, &val, dev, LEFT_CHN, left);
- devc->levels[dev] = left | (left << 8);
+ devc->levels[dev] = left | (left << 8);
- if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs) /*
- * Change register
+ if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs) /*
+ * Change register
+ */
+ {
+ sb_setmixer(devc, regoffs, val); /*
+ * Save the old one
*/
- {
- sb_setmixer (devc, regoffs, val); /*
- * Save the old one
- */
- regoffs = (*devc->iomap)[dev][RIGHT_CHN].regno;
-
- if (regoffs == 0)
- return left | (left << 8); /*
- * Just left channel present
- */
+ regoffs = (*devc->iomap)[dev][RIGHT_CHN].regno;
- val = sb_getmixer (devc, regoffs); /*
- * Read the new one
- */
- }
+ if (regoffs == 0)
+ return left | (left << 8); /*
+ * Just left channel present
+ */
- change_bits (devc, &val, dev, RIGHT_CHN, right);
+ val = sb_getmixer(devc, regoffs); /*
+ * Read the new one
+ */
+ }
+ change_bits(devc, &val, dev, RIGHT_CHN, right);
- sb_setmixer (devc, regoffs, val);
+ sb_setmixer(devc, regoffs, val);
- devc->levels[dev] = left | (right << 8);
- return left | (right << 8);
+ devc->levels[dev] = left | (right << 8);
+ return left | (right << 8);
}
static void
-set_recsrc (sb_devc * devc, int src)
+set_recsrc(sb_devc * devc, int src)
{
- sb_setmixer (devc, RECORD_SRC, (sb_getmixer (devc, RECORD_SRC) & ~7) | (src & 0x7));
+ sb_setmixer(devc, RECORD_SRC, (sb_getmixer(devc, RECORD_SRC) & ~7) | (src & 0x7));
}
static int
-set_recmask (sb_devc * devc, int mask)
+set_recmask(sb_devc * devc, int mask)
{
- int devmask, i;
- unsigned char regimageL, regimageR;
-
- devmask = mask & devc->supported_rec_devices;
-
- switch (devc->model)
- {
- case MDL_SBPRO:
- case MDL_ESS:
- case MDL_JAZZ:
- case MDL_SMW:
-
- if (devmask != SOUND_MASK_MIC &&
- devmask != SOUND_MASK_LINE &&
- devmask != SOUND_MASK_CD)
- { /*
+ int devmask, i;
+ unsigned char regimageL, regimageR;
+
+ devmask = mask & devc->supported_rec_devices;
+
+ switch (devc->model)
+ {
+ case MDL_SBPRO:
+ case MDL_ESS:
+ case MDL_JAZZ:
+ case MDL_SMW:
+
+ if (devmask != SOUND_MASK_MIC &&
+ devmask != SOUND_MASK_LINE &&
+ devmask != SOUND_MASK_CD)
+ { /*
* More than one devices selected. Drop the *
* previous selection
*/
- devmask &= ~devc->recmask;
- }
-
- if (devmask != SOUND_MASK_MIC &&
- devmask != SOUND_MASK_LINE &&
- devmask != SOUND_MASK_CD)
- { /*
+ devmask &= ~devc->recmask;
+ }
+ if (devmask != SOUND_MASK_MIC &&
+ devmask != SOUND_MASK_LINE &&
+ devmask != SOUND_MASK_CD)
+ { /*
* More than one devices selected. Default to
* * mic
*/
- devmask = SOUND_MASK_MIC;
- }
-
-
- if (devmask ^ devc->recmask) /*
- * Input source changed
- */
- {
- switch (devmask)
- {
-
- case SOUND_MASK_MIC:
- set_recsrc (devc, SRC__MIC);
- break;
-
- case SOUND_MASK_LINE:
- set_recsrc (devc, SRC__LINE);
- break;
-
- case SOUND_MASK_CD:
- set_recsrc (devc, SRC__CD);
- break;
-
- default:
- set_recsrc (devc, SRC__MIC);
- }
- }
-
- break;
-
- case MDL_SB16:
- if (!devmask)
- devmask = SOUND_MASK_MIC;
-
- regimageL = regimageR = 0;
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- if ((1 << i) & devmask)
- {
- regimageL |= sb16_recmasks_L[i];
- regimageR |= sb16_recmasks_R[i];
+ devmask = SOUND_MASK_MIC;
+ }
+ if (devmask ^ devc->recmask) /*
+ * Input source changed
+ */
+ {
+ switch (devmask)
+ {
+
+ case SOUND_MASK_MIC:
+ set_recsrc(devc, SRC__MIC);
+ break;
+
+ case SOUND_MASK_LINE:
+ set_recsrc(devc, SRC__LINE);
+ break;
+
+ case SOUND_MASK_CD:
+ set_recsrc(devc, SRC__CD);
+ break;
+
+ default:
+ set_recsrc(devc, SRC__MIC);
+ }
+ }
+ break;
+
+ case MDL_SB16:
+ if (!devmask)
+ devmask = SOUND_MASK_MIC;
+
+ regimageL = regimageR = 0;
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((1 << i) & devmask)
+ {
+ regimageL |= sb16_recmasks_L[i];
+ regimageR |= sb16_recmasks_R[i];
+ }
+ sb_setmixer(devc, SB16_IMASK_L, regimageL);
+ sb_setmixer(devc, SB16_IMASK_R, regimageR);
+ break;
}
- sb_setmixer (devc, SB16_IMASK_L, regimageL);
- sb_setmixer (devc, SB16_IMASK_R, regimageR);
- break;
- }
- devc->recmask = devmask;
- return devc->recmask;
+ devc->recmask = devmask;
+ return devc->recmask;
}
static int
-sb_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
+sb_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
{
- sb_devc *devc = mixer_devs[dev]->devc;
- int val;
+ sb_devc *devc = mixer_devs[dev]->devc;
+ int val;
/*
* Use ioctl(fd, SOUND_MIXER_PRIVATE1, &mode) to turn AGC off (0) or on (1).
*/
- if (cmd == SOUND_MIXER_PRIVATE1 && devc->model == MDL_SB16)
- {
- int tmp;
-
- tmp = *(int *) arg;
-
- sb_setmixer (devc, 0x43, (~tmp) & 0x01);
- return 0;
- }
-
- if (((cmd >> 8) & 0xff) == 'M')
- {
- if (_SIOC_DIR (cmd) & _SIOC_WRITE)
- switch (cmd & 0xff)
+ if (cmd == SOUND_MIXER_PRIVATE1 && devc->model == MDL_SB16)
{
- case SOUND_MIXER_RECSRC:
- val = *(int *) arg;
- return (*(int *) arg = set_recmask (devc, val));
- break;
+ int tmp;
- default:
+ tmp = *(int *) arg;
- val = *(int *) arg;
- return (*(int *) arg = sb_mixer_set (devc, cmd & 0xff, val));
+ sb_setmixer(devc, 0x43, (~tmp) & 0x01);
+ return 0;
}
- else
- switch (cmd & 0xff)
+ if (((cmd >> 8) & 0xff) == 'M')
{
-
- case SOUND_MIXER_RECSRC:
- return (*(int *) arg = devc->recmask);
- break;
-
- case SOUND_MIXER_DEVMASK:
- return (*(int *) arg = devc->supported_devices);
- break;
-
- case SOUND_MIXER_STEREODEVS:
- if (devc->model == MDL_JAZZ || devc->model == MDL_SMW)
- return (*(int *) arg = devc->supported_devices);
- else
- return (*(int *) arg = devc->supported_devices & ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX));
- break;
-
- case SOUND_MIXER_RECMASK:
- return (*(int *) arg = devc->supported_rec_devices);
- break;
-
- case SOUND_MIXER_CAPS:
- return (*(int *) arg = devc->mixer_caps);
- break;
-
- default:
- return (*(int *) arg = sb_mixer_get (devc, cmd & 0xff));
- }
- }
- else
- return -EINVAL;
+ if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+ switch (cmd & 0xff)
+ {
+ case SOUND_MIXER_RECSRC:
+ val = *(int *) arg;
+ return (*(int *) arg = set_recmask(devc, val));
+ break;
+
+ default:
+
+ val = *(int *) arg;
+ return (*(int *) arg = sb_mixer_set(devc, cmd & 0xff, val));
+ } else
+ switch (cmd & 0xff)
+ {
+
+ case SOUND_MIXER_RECSRC:
+ return (*(int *) arg = devc->recmask);
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ return (*(int *) arg = devc->supported_devices);
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ if (devc->model == MDL_JAZZ || devc->model == MDL_SMW)
+ return (*(int *) arg = devc->supported_devices);
+ else
+ return (*(int *) arg = devc->supported_devices & ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX));
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ return (*(int *) arg = devc->supported_rec_devices);
+ break;
+
+ case SOUND_MIXER_CAPS:
+ return (*(int *) arg = devc->mixer_caps);
+ break;
+
+ default:
+ return (*(int *) arg = sb_mixer_get(devc, cmd & 0xff));
+ }
+ } else
+ return -EINVAL;
}
static struct mixer_operations sb_mixer_operations =
{
- "SB",
- "Sound Blaster",
- sb_mixer_ioctl
+ "SB",
+ "Sound Blaster",
+ sb_mixer_ioctl
};
static void
-sb_mixer_reset (sb_devc * devc)
+sb_mixer_reset(sb_devc * devc)
{
- char name[32];
- int i;
+ char name[32];
+ int i;
+ extern int sm_games;
- sprintf (name, "SB_%d", devc->sbmixnum);
+ sprintf(name, "SB_%d", devc->sbmixnum);
- devc->levels = load_mixer_volumes (name, default_levels, 1);
+ if (sm_games)
+ devc->levels = load_mixer_volumes(name, smg_default_levels, 1);
+ else
+ devc->levels = load_mixer_volumes(name, sb_default_levels, 1);
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- sb_mixer_set (devc, i, devc->levels[i]);
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ sb_mixer_set(devc, i, devc->levels[i]);
- set_recmask (devc, SOUND_MASK_MIC);
+ set_recmask(devc, SOUND_MASK_MIC);
}
int
-sb_mixer_init (sb_devc * devc)
+sb_mixer_init(sb_devc * devc)
{
- int mixer_type = 0;
-
- devc->sbmixnum = sbmixnum++;
- devc->levels = NULL;
-
- sb_setmixer (devc, 0x00, 0); /* Reset mixer */
-
- if (!(mixer_type = detect_mixer (devc)))
- return 0; /* No mixer. Why? */
-
- switch (devc->model)
- {
- case MDL_SBPRO:
- case MDL_JAZZ:
- devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
- devc->supported_devices = SBPRO_MIXER_DEVICES;
- devc->supported_rec_devices = SBPRO_RECORDING_DEVICES;
- devc->iomap = &sbpro_mix;
- break;
-
- case MDL_ESS:
- devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
- devc->supported_devices = ES688_MIXER_DEVICES;
- devc->supported_rec_devices = ES688_RECORDING_DEVICES;
- devc->iomap = &es688_mix;
- break;
-
- case MDL_SMW:
- devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
- devc->supported_devices = 0;
- devc->supported_rec_devices = 0;
- devc->iomap = &sbpro_mix;
- smw_mixer_init (devc);
- break;
-
- case MDL_SB16:
- devc->mixer_caps = 0;
- devc->supported_devices = SB16_MIXER_DEVICES;
- devc->supported_rec_devices = SB16_RECORDING_DEVICES;
- devc->iomap = &sb16_mix;
- break;
-
- default:
- printk ("SB Warning: Unsupported mixer type %d\n", devc->model);
- return 0;
- }
-
- if (num_mixers >= MAX_MIXER_DEV)
- return 0;
-
-
- mixer_devs[num_mixers] = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct mixer_operations)));
- sound_mem_sizes[sound_nblocks] = sizeof (struct mixer_operations);
-
- if (sound_nblocks < 1024)
- sound_nblocks++;;
- if (mixer_devs[num_mixers] == NULL)
- {
- printk ("sb_mixer: Can't allocate memory\n");
- return 0;
- }
-
- memcpy ((char *) mixer_devs[num_mixers], (char *) &sb_mixer_operations,
- sizeof (struct mixer_operations));
-
- mixer_devs[num_mixers]->devc = devc;
-
- sb_mixer_reset (devc);
- devc->my_mixerdev = num_mixers++;
- return 1;
+ int mixer_type = 0;
+ int m;
+
+ devc->sbmixnum = sbmixnum++;
+ devc->levels = NULL;
+
+ sb_setmixer(devc, 0x00, 0); /* Reset mixer */
+
+ if (!(mixer_type = detect_mixer(devc)))
+ return 0; /* No mixer. Why? */
+
+ switch (devc->model)
+ {
+ case MDL_SBPRO:
+ case MDL_AZTECH:
+ case MDL_JAZZ:
+ devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
+ devc->supported_devices = SBPRO_MIXER_DEVICES;
+ devc->supported_rec_devices = SBPRO_RECORDING_DEVICES;
+ devc->iomap = &sbpro_mix;
+ break;
+
+ case MDL_ESS:
+ devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
+ devc->supported_devices = ES688_MIXER_DEVICES;
+ devc->supported_rec_devices = ES688_RECORDING_DEVICES;
+ devc->iomap = &es688_mix;
+ break;
+
+ case MDL_SMW:
+ devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
+ devc->supported_devices = 0;
+ devc->supported_rec_devices = 0;
+ devc->iomap = &sbpro_mix;
+ smw_mixer_init(devc);
+ break;
+
+ case MDL_SB16:
+ devc->mixer_caps = 0;
+ devc->supported_devices = SB16_MIXER_DEVICES;
+ devc->supported_rec_devices = SB16_RECORDING_DEVICES;
+ devc->iomap = &sb16_mix;
+ break;
+
+ default:
+ printk("SB Warning: Unsupported mixer type %d\n", devc->model);
+ return 0;
+ }
+
+ m = sound_alloc_mixerdev();
+ if (m == -1)
+ return 0;
+
+
+ mixer_devs[m] = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct mixer_operations)));
+ sound_mem_sizes[sound_nblocks] = sizeof(struct mixer_operations);
+
+ if (sound_nblocks < 1024)
+ sound_nblocks++;;
+ if (mixer_devs[m] == NULL)
+ {
+ printk(KERN_ERR "sb_mixer: Can't allocate memory\n");
+ sound_unload_mixerdev(m);
+ return 0;
+ }
+ memcpy((char *) mixer_devs[m], (char *) &sb_mixer_operations,
+ sizeof(struct mixer_operations));
+
+ mixer_devs[m]->devc = devc;
+
+ devc->my_mixerdev = m;
+ sb_mixer_reset(devc);
+ return 1;
}
#endif
diff --git a/drivers/sound/sb_mixer.h b/drivers/sound/sb_mixer.h
index 5711b9105..d83c2dbad 100644
--- a/drivers/sound/sb_mixer.h
+++ b/drivers/sound/sb_mixer.h
@@ -17,6 +17,7 @@
* Added defines for the Sound Galaxy NX Pro mixer.
*
*/
+#ifdef CONFIG_SBDSP
#define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
@@ -168,11 +169,11 @@ MIX_ENT(SOUND_MIXER_IGAIN, 0x3f, 7, 2, 0x40, 7, 2),
MIX_ENT(SOUND_MIXER_OGAIN, 0x41, 7, 2, 0x42, 7, 2)
};
-#ifdef SM_GAMES /* Master volume is lower and PCM & FM volumes
+/* SM_GAMES Master volume is lower and PCM & FM volumes
higher than with SB Pro. This improves the
sound quality */
-static int default_levels[32] =
+static int smg_default_levels[32] =
{
0x2020, /* Master Volume */
0x4b4b, /* Bass */
@@ -193,9 +194,7 @@ static int default_levels[32] =
0x1515 /* Line3 */
};
-#else /* If the user selected just plain SB Pro */
-
-static int default_levels[32] =
+static int sb_default_levels[32] =
{
0x5a5a, /* Master Volume */
0x4b4b, /* Bass */
@@ -215,7 +214,6 @@ static int default_levels[32] =
0x4040, /* Line2 */
0x1515 /* Line3 */
};
-#endif /* SM_GAMES */
static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] =
{
@@ -283,3 +281,4 @@ static char smw_mix_regs[] = /* Left mixer registers */
#define SRC__LINE 7 /* Use Line-in for recording source */
#endif
+#endif
diff --git a/drivers/sound/sequencer.c b/drivers/sound/sequencer.c
index 14d2f24de..8995e0072 100644
--- a/drivers/sound/sequencer.c
+++ b/drivers/sound/sequencer.c
@@ -16,7 +16,9 @@
#define SEQUENCER_C
#include "sound_config.h"
-#ifdef CONFIG_SEQUENCER
+#if defined(CONFIG_SEQUENCER) || defined(MODULE)
+#include "softoss.h"
+int (*softsynthp) (int cmd, int parm1, int parm2, unsigned long parm3) = NULL;
#include "midi_ctrl.h"
@@ -57,8 +59,8 @@ static int midi_opened[MAX_MIDI_DEV] =
static int midi_written[MAX_MIDI_DEV] =
{0};
-unsigned long prev_input_time = 0;
-int prev_event_time;
+static unsigned long prev_input_time = 0;
+static int prev_event_time;
#include "tuning.h"
@@ -75,433 +77,423 @@ static int output_threshold;
static int pre_event_timeout;
static unsigned synth_open_mask;
-static int seq_queue (unsigned char *note, char nonblock);
-static void seq_startplay (void);
-static int seq_sync (void);
-static void seq_reset (void);
+static int seq_queue(unsigned char *note, char nonblock);
+static void seq_startplay(void);
+static int seq_sync(void);
+static void seq_reset(void);
#if MAX_SYNTH_DEV > 15
#error Too many synthesizer devices enabled.
#endif
int
-sequencer_read (int dev, struct fileinfo *file, char *buf, int count)
+sequencer_read(int dev, struct fileinfo *file, char *buf, int count)
{
- int c = count, p = 0;
- int ev_len;
- unsigned long flags;
-
- dev = dev >> 4;
-
- ev_len = seq_mode == SEQ_1 ? 4 : 8;
-
- save_flags (flags);
- cli ();
- if (!iqlen)
- {
- if ((file->flags & (O_NONBLOCK) ?
- 1 : 0))
- {
- restore_flags (flags);
- return -EAGAIN;
- }
+ int c = count, p = 0;
+ int ev_len;
+ unsigned long flags;
+ dev = dev >> 4;
- {
- unsigned long tlimit;
+ ev_len = seq_mode == SEQ_1 ? 4 : 8;
- if (pre_event_timeout)
- current->timeout = tlimit = jiffies + (pre_event_timeout);
- else
- tlimit = (unsigned long) -1;
- midi_sleep_flag.opts = WK_SLEEP;
- interruptible_sleep_on (&midi_sleeper);
- if (!(midi_sleep_flag.opts & WK_WAKEUP))
+ save_flags(flags);
+ cli();
+ if (!iqlen)
{
- if (jiffies >= tlimit)
- midi_sleep_flag.opts |= WK_TIMEOUT;
- }
- midi_sleep_flag.opts &= ~WK_SLEEP;
- };
-
- if (!iqlen)
- {
- restore_flags (flags);
- return 0;
- }
- }
+ if ((file->flags & (O_NONBLOCK) ?
+ 1 : 0))
+ {
+ restore_flags(flags);
+ return -EAGAIN;
+ }
+ {
+ unsigned long tlimit;
+
+ if (pre_event_timeout)
+ current->timeout = tlimit = jiffies + (pre_event_timeout);
+ else
+ tlimit = (unsigned long) -1;
+ midi_sleep_flag.opts = WK_SLEEP;
+ interruptible_sleep_on(&midi_sleeper);
+ if (!(midi_sleep_flag.opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ midi_sleep_flag.opts |= WK_TIMEOUT;
+ }
+ midi_sleep_flag.opts &= ~WK_SLEEP;
+ };
- while (iqlen && c >= ev_len)
- {
+ if (!iqlen)
+ {
+ restore_flags(flags);
+ return 0;
+ }
+ }
+ while (iqlen && c >= ev_len)
+ {
- {
- char *fixit = (char *) &iqueue[iqhead * IEV_SZ];
+ {
+ char *fixit = (char *) &iqueue[iqhead * IEV_SZ];
- copy_to_user (&(buf)[p], fixit, ev_len);
- };
- p += ev_len;
- c -= ev_len;
+ copy_to_user(&(buf)[p], fixit, ev_len);
+ };
+ p += ev_len;
+ c -= ev_len;
- iqhead = (iqhead + 1) % SEQ_MAX_QUEUE;
- iqlen--;
- }
- restore_flags (flags);
+ iqhead = (iqhead + 1) % SEQ_MAX_QUEUE;
+ iqlen--;
+ }
+ restore_flags(flags);
- return count - c;
+ return count - c;
}
static void
-sequencer_midi_output (int dev)
+sequencer_midi_output(int dev)
{
- /*
- * Currently NOP
- */
+ /*
+ * Currently NOP
+ */
}
void
-seq_copy_to_input (unsigned char *event_rec, int len)
-{
- unsigned long flags;
-
- /*
- * Verify that the len is valid for the current mode.
- */
-
- if (len != 4 && len != 8)
- return;
- if ((seq_mode == SEQ_1) != (len == 4))
- return;
-
- if (iqlen >= (SEQ_MAX_QUEUE - 1))
- return; /* Overflow */
-
- save_flags (flags);
- cli ();
- memcpy (&iqueue[iqtail * IEV_SZ], event_rec, len);
- iqlen++;
- iqtail = (iqtail + 1) % SEQ_MAX_QUEUE;
-
- if ((midi_sleep_flag.opts & WK_SLEEP))
- {
- {
- midi_sleep_flag.opts = WK_WAKEUP;
- wake_up (&midi_sleeper);
- };
- }
- restore_flags (flags);
-}
-
-static void
-sequencer_midi_input (int dev, unsigned char data)
+seq_copy_to_input(unsigned char *event_rec, int len)
{
- unsigned int tstamp;
- unsigned char event_rec[4];
-
- if (data == 0xfe) /* Ignore active sensing */
- return;
+ unsigned long flags;
- tstamp = jiffies - seq_time;
+ /*
+ * Verify that the len is valid for the current mode.
+ */
- if (tstamp != prev_input_time)
- {
- tstamp = (tstamp << 8) | SEQ_WAIT;
+ if (len != 4 && len != 8)
+ return;
+ if ((seq_mode == SEQ_1) != (len == 4))
+ return;
- seq_copy_to_input ((unsigned char *) &tstamp, 4);
- prev_input_time = tstamp;
- }
+ if (iqlen >= (SEQ_MAX_QUEUE - 1))
+ return; /* Overflow */
- event_rec[0] = SEQ_MIDIPUTC;
- event_rec[1] = data;
- event_rec[2] = dev;
- event_rec[3] = 0;
+ save_flags(flags);
+ cli();
+ memcpy(&iqueue[iqtail * IEV_SZ], event_rec, len);
+ iqlen++;
+ iqtail = (iqtail + 1) % SEQ_MAX_QUEUE;
- seq_copy_to_input (event_rec, 4);
+ if ((midi_sleep_flag.opts & WK_SLEEP))
+ {
+ {
+ midi_sleep_flag.opts = WK_WAKEUP;
+ wake_up(&midi_sleeper);
+ };
+ }
+ restore_flags(flags);
}
-void
-seq_input_event (unsigned char *event_rec, int len)
+static void
+sequencer_midi_input(int dev, unsigned char data)
{
- unsigned long this_time;
+ unsigned int tstamp;
+ unsigned char event_rec[4];
- if (seq_mode == SEQ_2)
- this_time = tmr->get_time (tmr_no);
- else
- this_time = jiffies - seq_time;
+ if (data == 0xfe) /* Ignore active sensing */
+ return;
- if (this_time != prev_input_time)
- {
- unsigned char tmp_event[8];
+ if (softsynthp != NULL)
+ tstamp = softsynthp(SSYN_GETTIME, 0, 0, 0);
+ else
+ tstamp = jiffies - seq_time;
- tmp_event[0] = EV_TIMING;
- tmp_event[1] = TMR_WAIT_ABS;
- tmp_event[2] = 0;
- tmp_event[3] = 0;
- *(unsigned int *) &tmp_event[4] = this_time;
+ if (tstamp != prev_input_time)
+ {
+ tstamp = (tstamp << 8) | SEQ_WAIT;
- seq_copy_to_input (tmp_event, 8);
- prev_input_time = this_time;
- }
+ seq_copy_to_input((unsigned char *) &tstamp, 4);
+ prev_input_time = tstamp;
+ }
+ event_rec[0] = SEQ_MIDIPUTC;
+ event_rec[1] = data;
+ event_rec[2] = dev;
+ event_rec[3] = 0;
- seq_copy_to_input (event_rec, len);
+ seq_copy_to_input(event_rec, 4);
}
-int
-sequencer_write (int dev, struct fileinfo *file, const char *buf, int count)
+void
+seq_input_event(unsigned char *event_rec, int len)
{
- unsigned char event_rec[EV_SZ], ev_code;
- int p = 0, c, ev_size;
- int err;
- int mode = file->mode & O_ACCMODE;
-
- dev = dev >> 4;
-
- DEB (printk ("sequencer_write(dev=%d, count=%d)\n", dev, count));
-
- if (mode == OPEN_READ)
- return -EIO;
-
- c = count;
-
- while (c >= 4)
- {
- copy_from_user ((char *) event_rec, &(buf)[p], 4);
- ev_code = event_rec[0];
-
- if (ev_code == SEQ_FULLSIZE)
- {
- int err, fmt;
+ unsigned long this_time;
- dev = *(unsigned short *) &event_rec[2];
- if (dev < 0 || dev >= max_synthdev)
- return -ENXIO;
-
- if (!(synth_open_mask & (1 << dev)))
- return -ENXIO;
-
- fmt = (*(short *) &event_rec[0]) & 0xffff;
- err = synth_devs[dev]->load_patch (dev, fmt, buf, p + 4, c, 0);
- if (err < 0)
- return err;
-
- return err;
- }
-
- if (ev_code >= 128)
- {
- if (seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED)
- {
- printk ("Sequencer: Invalid level 2 event %x\n", ev_code);
- return -EINVAL;
- }
-
- ev_size = 8;
-
- if (c < ev_size)
- {
- if (!seq_playing)
- seq_startplay ();
- return count - c;
- }
-
- copy_from_user ((char *) &event_rec[4], &(buf)[p + 4], 4);
-
- }
- else
- {
- if (seq_mode == SEQ_2)
- {
- printk ("Sequencer: 4 byte event in level 2 mode\n");
- return -EINVAL;
- }
- ev_size = 4;
-
- if (event_rec[0] != SEQ_MIDIPUTC)
- obsolete_api_used = 1;
- }
-
- if (event_rec[0] == SEQ_MIDIPUTC)
- {
+ if (seq_mode == SEQ_2)
+ this_time = tmr->get_time(tmr_no);
+ else if (softsynthp != NULL)
+ this_time = softsynthp(SSYN_GETTIME, 0, 0, 0);
+ else
+ this_time = jiffies - seq_time;
- if (!midi_opened[event_rec[2]])
- {
- int mode;
- int dev = event_rec[2];
+ if (this_time != prev_input_time)
+ {
+ unsigned char tmp_event[8];
- if (dev >= max_mididev)
- {
- printk ("Sequencer Error: Nonexistent MIDI device %d\n", dev);
- return -ENXIO;
- }
+ tmp_event[0] = EV_TIMING;
+ tmp_event[1] = TMR_WAIT_ABS;
+ tmp_event[2] = 0;
+ tmp_event[3] = 0;
+ *(unsigned int *) &tmp_event[4] = this_time;
- mode = file->mode & O_ACCMODE;
+ seq_copy_to_input(tmp_event, 8);
+ prev_input_time = this_time;
+ }
+ seq_copy_to_input(event_rec, len);
+}
- if ((err = midi_devs[dev]->open (dev, mode,
- sequencer_midi_input, sequencer_midi_output)) < 0)
- {
- seq_reset ();
- printk ("Sequencer Error: Unable to open Midi #%d\n", dev);
- return err;
- }
+int
+sequencer_write(int dev, struct fileinfo *file, const char *buf, int count)
+{
+ unsigned char event_rec[EV_SZ], ev_code;
+ int p = 0, c, ev_size;
+ int err;
+ int mode = file->mode & O_ACCMODE;
- midi_opened[dev] = 1;
- }
+ dev = dev >> 4;
- }
+ DEB(printk("sequencer_write(dev=%d, count=%d)\n", dev, count));
- if (!seq_queue (event_rec, (file->flags & (O_NONBLOCK) ?
- 1 : 0)))
- {
- int processed = count - c;
+ if (mode == OPEN_READ)
+ return -EIO;
- if (!seq_playing)
- seq_startplay ();
+ c = count;
- if (!processed && (file->flags & (O_NONBLOCK) ?
- 1 : 0))
- return -EAGAIN;
- else
- return processed;
- }
-
- p += ev_size;
- c -= ev_size;
- }
+ while (c >= 4)
+ {
+ copy_from_user((char *) event_rec, &(buf)[p], 4);
+ ev_code = event_rec[0];
+
+ if (ev_code == SEQ_FULLSIZE)
+ {
+ int err, fmt;
+
+ dev = *(unsigned short *) &event_rec[2];
+ if (dev < 0 || dev >= max_synthdev || synth_devs[dev] == NULL)
+ return -ENXIO;
+
+ if (!(synth_open_mask & (1 << dev)))
+ return -ENXIO;
+
+ fmt = (*(short *) &event_rec[0]) & 0xffff;
+ err = synth_devs[dev]->load_patch(dev, fmt, buf, p + 4, c, 0);
+ if (err < 0)
+ return err;
+
+ return err;
+ }
+ if (ev_code >= 128)
+ {
+ if (seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED)
+ {
+ printk("Sequencer: Invalid level 2 event %x\n", ev_code);
+ return -EINVAL;
+ }
+ ev_size = 8;
+
+ if (c < ev_size)
+ {
+ if (!seq_playing)
+ seq_startplay();
+ return count - c;
+ }
+ copy_from_user((char *) &event_rec[4], &(buf)[p + 4], 4);
+
+ } else
+ {
+ if (seq_mode == SEQ_2)
+ {
+ printk("Sequencer: 4 byte event in level 2 mode\n");
+ return -EINVAL;
+ }
+ ev_size = 4;
+
+ if (event_rec[0] != SEQ_MIDIPUTC)
+ obsolete_api_used = 1;
+ }
+
+ if (event_rec[0] == SEQ_MIDIPUTC)
+ {
+
+ if (!midi_opened[event_rec[2]])
+ {
+ int mode;
+ int dev = event_rec[2];
+
+ if (dev >= max_mididev)
+ {
+ printk("Sequencer Error: Nonexistent MIDI device %d\n", dev);
+ return -ENXIO;
+ }
+ mode = file->mode & O_ACCMODE;
+
+ if ((err = midi_devs[dev]->open(dev, mode,
+ sequencer_midi_input, sequencer_midi_output)) < 0)
+ {
+ seq_reset();
+ printk("Sequencer Error: Unable to open Midi #%d\n", dev);
+ return err;
+ }
+ midi_opened[dev] = 1;
+ }
+ }
+ if (!seq_queue(event_rec, (file->flags & (O_NONBLOCK) ?
+ 1 : 0)))
+ {
+ int processed = count - c;
+
+ if (!seq_playing)
+ seq_startplay();
+
+ if (!processed && (file->flags & (O_NONBLOCK) ?
+ 1 : 0))
+ return -EAGAIN;
+ else
+ return processed;
+ }
+ p += ev_size;
+ c -= ev_size;
+ }
- if (!seq_playing)
- seq_startplay ();
+ if (!seq_playing)
+ seq_startplay();
- return count;
+ return count;
}
static int
-seq_queue (unsigned char *note, char nonblock)
+seq_queue(unsigned char *note, char nonblock)
{
- /*
- * Test if there is space in the queue
- */
-
- if (qlen >= SEQ_MAX_QUEUE)
- if (!seq_playing)
- seq_startplay (); /*
- * Give chance to drain the queue
- */
+ /*
+ * Test if there is space in the queue
+ */
- if (!nonblock && qlen >= SEQ_MAX_QUEUE && !(seq_sleep_flag.opts & WK_SLEEP))
- {
- /*
- * Sleep until there is enough space on the queue
- */
+ if (qlen >= SEQ_MAX_QUEUE)
+ if (!seq_playing)
+ seq_startplay(); /*
+ * Give chance to drain the queue
+ */
- seq_sleep_flag.opts = WK_SLEEP;
- interruptible_sleep_on (&seq_sleeper);
- seq_sleep_flag.opts &= ~WK_SLEEP;;
- }
+ if (!nonblock && qlen >= SEQ_MAX_QUEUE && !(seq_sleep_flag.opts & WK_SLEEP))
+ {
+ /*
+ * Sleep until there is enough space on the queue
+ */
- if (qlen >= SEQ_MAX_QUEUE)
- {
- return 0; /*
+ seq_sleep_flag.opts = WK_SLEEP;
+ interruptible_sleep_on(&seq_sleeper);
+ seq_sleep_flag.opts &= ~WK_SLEEP;;
+ }
+ if (qlen >= SEQ_MAX_QUEUE)
+ {
+ return 0; /*
* To be sure
*/
- }
- memcpy (&queue[qtail * EV_SZ], note, EV_SZ);
+ }
+ memcpy(&queue[qtail * EV_SZ], note, EV_SZ);
- qtail = (qtail + 1) % SEQ_MAX_QUEUE;
- qlen++;
+ qtail = (qtail + 1) % SEQ_MAX_QUEUE;
+ qlen++;
- return 1;
+ return 1;
}
static int
-extended_event (unsigned char *q)
+extended_event(unsigned char *q)
{
- int dev = q[2];
+ int dev = q[2];
- if (dev < 0 || dev >= max_synthdev)
- return -ENXIO;
+ if (dev < 0 || dev >= max_synthdev)
+ return -ENXIO;
- if (!(synth_open_mask & (1 << dev)))
- return -ENXIO;
+ if (!(synth_open_mask & (1 << dev)))
+ return -ENXIO;
- switch (q[1])
- {
- case SEQ_NOTEOFF:
- synth_devs[dev]->kill_note (dev, q[3], q[4], q[5]);
- break;
+ switch (q[1])
+ {
+ case SEQ_NOTEOFF:
+ synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]);
+ break;
+
+ case SEQ_NOTEON:
+ if (q[4] > 127 && q[4] != 255)
+ return 0;
+
+ if (q[5] == 0)
+ {
+ synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]);
+ break;
+ }
+ synth_devs[dev]->start_note(dev, q[3], q[4], q[5]);
+ break;
+
+ case SEQ_PGMCHANGE:
+ synth_devs[dev]->set_instr(dev, q[3], q[4]);
+ break;
+
+ case SEQ_AFTERTOUCH:
+ synth_devs[dev]->aftertouch(dev, q[3], q[4]);
+ break;
+
+ case SEQ_BALANCE:
+ synth_devs[dev]->panning(dev, q[3], (char) q[4]);
+ break;
+
+ case SEQ_CONTROLLER:
+ synth_devs[dev]->controller(dev, q[3], q[4], (short) (q[5] | (q[6] << 8)));
+ break;
+
+ case SEQ_VOLMODE:
+ if (synth_devs[dev]->volume_method != NULL)
+ synth_devs[dev]->volume_method(dev, q[3]);
+ break;
+
+ default:
+ return -EINVAL;
+ }
- case SEQ_NOTEON:
- if (q[4] > 127 && q[4] != 255)
return 0;
-
- if (q[5] == 0)
- {
- synth_devs[dev]->kill_note (dev, q[3], q[4], q[5]);
- break;
- }
- synth_devs[dev]->start_note (dev, q[3], q[4], q[5]);
- break;
-
- case SEQ_PGMCHANGE:
- synth_devs[dev]->set_instr (dev, q[3], q[4]);
- break;
-
- case SEQ_AFTERTOUCH:
- synth_devs[dev]->aftertouch (dev, q[3], q[4]);
- break;
-
- case SEQ_BALANCE:
- synth_devs[dev]->panning (dev, q[3], (char) q[4]);
- break;
-
- case SEQ_CONTROLLER:
- synth_devs[dev]->controller (dev, q[3], q[4], (short) (q[5] | (q[6] << 8)));
- break;
-
- case SEQ_VOLMODE:
- if (synth_devs[dev]->volume_method != NULL)
- synth_devs[dev]->volume_method (dev, q[3]);
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
}
static int
-find_voice (int dev, int chn, int note)
+find_voice(int dev, int chn, int note)
{
- unsigned short key;
- int i;
+ unsigned short key;
+ int i;
- key = (chn << 8) | (note + 1);
+ key = (chn << 8) | (note + 1);
- for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
- if (synth_devs[dev]->alloc.map[i] == key)
- return i;
+ for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
+ if (synth_devs[dev]->alloc.map[i] == key)
+ return i;
- return -1;
+ return -1;
}
static int
-alloc_voice (int dev, int chn, int note)
+alloc_voice(int dev, int chn, int note)
{
- unsigned short key;
- int voice;
+ unsigned short key;
+ int voice;
- key = (chn << 8) | (note + 1);
+ key = (chn << 8) | (note + 1);
- voice = synth_devs[dev]->alloc_voice (dev, chn, note,
- &synth_devs[dev]->alloc);
- synth_devs[dev]->alloc.map[voice] = key;
- synth_devs[dev]->alloc.alloc_times[voice] =
- synth_devs[dev]->alloc.timestamp++;
- return voice;
+ voice = synth_devs[dev]->alloc_voice(dev, chn, note,
+ &synth_devs[dev]->alloc);
+ synth_devs[dev]->alloc.map[voice] = key;
+ synth_devs[dev]->alloc.alloc_times[voice] =
+ synth_devs[dev]->alloc.timestamp++;
+ return voice;
}
static void
-seq_chn_voice_event (unsigned char *event_rec)
+seq_chn_voice_event(unsigned char *event_rec)
{
#define dev event_rec[1]
#define cmd event_rec[2]
@@ -509,73 +501,70 @@ seq_chn_voice_event (unsigned char *event_rec)
#define note event_rec[4]
#define parm event_rec[5]
- int voice = -1;
+ int voice = -1;
- if ((int) dev > max_synthdev)
- return;
- if (!(synth_open_mask & (1 << dev)))
- return;
- if (!synth_devs[dev])
- return;
-
- if (seq_mode == SEQ_2)
- {
- if (synth_devs[dev]->alloc_voice)
- voice = find_voice (dev, chn, note);
+ if ((int) dev > max_synthdev || synth_devs[dev] == NULL)
+ return;
+ if (!(synth_open_mask & (1 << dev)))
+ return;
+ if (!synth_devs[dev])
+ return;
- if (cmd == MIDI_NOTEON && parm == 0)
- {
- cmd = MIDI_NOTEOFF;
- parm = 64;
- }
- }
-
- switch (cmd)
- {
- case MIDI_NOTEON:
- if (note > 127 && note != 255) /* Not a seq2 feature */
- return;
-
- if (voice == -1 && seq_mode == SEQ_2 && synth_devs[dev]->alloc_voice)
- { /* Internal synthesizer (FM, GUS, etc) */
- voice = alloc_voice (dev, chn, note);
- }
-
- if (voice == -1)
- voice = chn;
-
- if (seq_mode == SEQ_2 && (int) dev < num_synths)
- {
- /*
- * The MIDI channel 10 is a percussive channel. Use the note
- * number to select the proper patch (128 to 255) to play.
- */
-
- if (chn == 9)
- {
- synth_devs[dev]->set_instr (dev, voice, 128 + note);
- synth_devs[dev]->chn_info[chn].pgm_num = 128 + note;
- }
- synth_devs[dev]->setup_voice (dev, voice, chn);
- }
-
- synth_devs[dev]->start_note (dev, voice, note, parm);
- break;
-
- case MIDI_NOTEOFF:
- if (voice == -1)
- voice = chn;
- synth_devs[dev]->kill_note (dev, voice, note, parm);
- break;
-
- case MIDI_KEY_PRESSURE:
- if (voice == -1)
- voice = chn;
- synth_devs[dev]->aftertouch (dev, voice, parm);
- break;
-
- default:;
- }
+ if (seq_mode == SEQ_2)
+ {
+ if (synth_devs[dev]->alloc_voice)
+ voice = find_voice(dev, chn, note);
+
+ if (cmd == MIDI_NOTEON && parm == 0)
+ {
+ cmd = MIDI_NOTEOFF;
+ parm = 64;
+ }
+ }
+ switch (cmd)
+ {
+ case MIDI_NOTEON:
+ if (note > 127 && note != 255) /* Not a seq2 feature */
+ return;
+
+ if (voice == -1 && seq_mode == SEQ_2 && synth_devs[dev]->alloc_voice)
+ { /* Internal synthesizer (FM, GUS, etc) */
+ voice = alloc_voice(dev, chn, note);
+ }
+ if (voice == -1)
+ voice = chn;
+
+ if (seq_mode == SEQ_2 && (int) dev < num_synths)
+ {
+ /*
+ * The MIDI channel 10 is a percussive channel. Use the note
+ * number to select the proper patch (128 to 255) to play.
+ */
+
+ if (chn == 9)
+ {
+ synth_devs[dev]->set_instr(dev, voice, 128 + note);
+ synth_devs[dev]->chn_info[chn].pgm_num = 128 + note;
+ }
+ synth_devs[dev]->setup_voice(dev, voice, chn);
+ }
+ synth_devs[dev]->start_note(dev, voice, note, parm);
+ break;
+
+ case MIDI_NOTEOFF:
+ if (voice == -1)
+ voice = chn;
+ synth_devs[dev]->kill_note(dev, voice, note, parm);
+ break;
+
+ case MIDI_KEY_PRESSURE:
+ if (voice == -1)
+ voice = chn;
+ synth_devs[dev]->aftertouch(dev, voice, parm);
+ break;
+
+ default:;
+ }
#undef dev
#undef cmd
#undef chn
@@ -585,1422 +574,1422 @@ seq_chn_voice_event (unsigned char *event_rec)
static void
-seq_chn_common_event (unsigned char *event_rec)
+seq_chn_common_event(unsigned char *event_rec)
{
- unsigned char dev = event_rec[1];
- unsigned char cmd = event_rec[2];
- unsigned char chn = event_rec[3];
- unsigned char p1 = event_rec[4];
-
- /* unsigned char p2 = event_rec[5]; */
- unsigned short w14 = *(short *) &event_rec[6];
-
- if ((int) dev > max_synthdev)
- return;
- if (!(synth_open_mask & (1 << dev)))
- return;
- if (!synth_devs[dev])
- return;
-
- switch (cmd)
- {
- case MIDI_PGM_CHANGE:
- if (seq_mode == SEQ_2)
- {
- synth_devs[dev]->chn_info[chn].pgm_num = p1;
- if ((int) dev >= num_synths)
- synth_devs[dev]->set_instr (dev, chn, p1);
- }
- else
- synth_devs[dev]->set_instr (dev, chn, p1);
+ unsigned char dev = event_rec[1];
+ unsigned char cmd = event_rec[2];
+ unsigned char chn = event_rec[3];
+ unsigned char p1 = event_rec[4];
- break;
+ /* unsigned char p2 = event_rec[5]; */
+ unsigned short w14 = *(short *) &event_rec[6];
- case MIDI_CTL_CHANGE:
- if (seq_mode == SEQ_2)
- {
- if (chn > 15 || p1 > 127)
- break;
-
- synth_devs[dev]->chn_info[chn].controllers[p1] = w14 & 0x7f;
-
- if (p1 < 32) /* Setting MSB should clear LSB to 0 */
- synth_devs[dev]->chn_info[chn].controllers[p1 + 32] = 0;
-
- if ((int) dev < num_synths)
- {
- int val = w14 & 0x7f;
- int i, key;
-
- if (p1 < 64) /* Combine MSB and LSB */
- {
- val = ((synth_devs[dev]->
- chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7)
- | (synth_devs[dev]->
- chn_info[chn].controllers[p1 | 32] & 0x7f);
- p1 &= ~32;
- }
-
- /* Handle all playing notes on this channel */
-
- key = ((int) chn << 8);
-
- for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
- if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key)
- synth_devs[dev]->controller (dev, i, p1, val);
- }
- else
- synth_devs[dev]->controller (dev, chn, p1, w14);
- }
- else /* Mode 1 */
- synth_devs[dev]->controller (dev, chn, p1, w14);
- break;
-
- case MIDI_PITCH_BEND:
- if (seq_mode == SEQ_2)
- {
- synth_devs[dev]->chn_info[chn].bender_value = w14;
-
- if ((int) dev < num_synths)
- { /* Handle all playing notes on this channel */
- int i, key;
-
- key = (chn << 8);
-
- for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
- if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key)
- synth_devs[dev]->bender (dev, i, w14);
- }
- else
- synth_devs[dev]->bender (dev, chn, w14);
- }
- else /* MODE 1 */
- synth_devs[dev]->bender (dev, chn, w14);
- break;
-
- default:;
- }
+ if ((int) dev > max_synthdev || synth_devs[dev] == NULL)
+ return;
+ if (!(synth_open_mask & (1 << dev)))
+ return;
+ if (!synth_devs[dev])
+ return;
+
+ switch (cmd)
+ {
+ case MIDI_PGM_CHANGE:
+ if (seq_mode == SEQ_2)
+ {
+ synth_devs[dev]->chn_info[chn].pgm_num = p1;
+ if ((int) dev >= num_synths)
+ synth_devs[dev]->set_instr(dev, chn, p1);
+ } else
+ synth_devs[dev]->set_instr(dev, chn, p1);
+
+ break;
+
+ case MIDI_CTL_CHANGE:
+ if (seq_mode == SEQ_2)
+ {
+ if (chn > 15 || p1 > 127)
+ break;
+
+ synth_devs[dev]->chn_info[chn].controllers[p1] = w14 & 0x7f;
+
+ if (p1 < 32) /* Setting MSB should clear LSB to 0 */
+ synth_devs[dev]->chn_info[chn].controllers[p1 + 32] = 0;
+
+ if ((int) dev < num_synths)
+ {
+ int val = w14 & 0x7f;
+ int i, key;
+
+ if (p1 < 64) /* Combine MSB and LSB */
+ {
+ val = ((synth_devs[dev]->
+ chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7)
+ | (synth_devs[dev]->
+ chn_info[chn].controllers[p1 | 32] & 0x7f);
+ p1 &= ~32;
+ }
+ /* Handle all playing notes on this channel */
+
+ key = ((int) chn << 8);
+
+ for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
+ if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key)
+ synth_devs[dev]->controller(dev, i, p1, val);
+ } else
+ synth_devs[dev]->controller(dev, chn, p1, w14);
+ } else /* Mode 1 */
+ synth_devs[dev]->controller(dev, chn, p1, w14);
+ break;
+
+ case MIDI_PITCH_BEND:
+ if (seq_mode == SEQ_2)
+ {
+ synth_devs[dev]->chn_info[chn].bender_value = w14;
+
+ if ((int) dev < num_synths)
+ { /* Handle all playing notes on this channel */
+ int i, key;
+
+ key = (chn << 8);
+
+ for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
+ if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key)
+ synth_devs[dev]->bender(dev, i, w14);
+ } else
+ synth_devs[dev]->bender(dev, chn, w14);
+ } else /* MODE 1 */
+ synth_devs[dev]->bender(dev, chn, w14);
+ break;
+
+ default:;
+ }
}
static int
-seq_timing_event (unsigned char *event_rec)
+seq_timing_event(unsigned char *event_rec)
{
- unsigned char cmd = event_rec[1];
- unsigned int parm = *(int *) &event_rec[4];
-
- if (seq_mode == SEQ_2)
- {
- int ret;
-
- if ((ret = tmr->event (tmr_no, event_rec)) == TIMER_ARMED)
- {
- if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
- {
- unsigned long flags;
-
- save_flags (flags);
- cli ();
- if ((seq_sleep_flag.opts & WK_SLEEP))
- {
- {
- seq_sleep_flag.opts = WK_WAKEUP;
- wake_up (&seq_sleeper);
- };
- }
- restore_flags (flags);
- }
- }
- return ret;
- }
-
- switch (cmd)
- {
- case TMR_WAIT_REL:
- parm += prev_event_time;
-
- /*
- * NOTE! No break here. Execution of TMR_WAIT_REL continues in the
- * next case (TMR_WAIT_ABS)
- */
-
- case TMR_WAIT_ABS:
- if (parm > 0)
- {
- long time;
-
- time = parm;
- prev_event_time = time;
-
- seq_playing = 1;
- request_sound_timer (time);
-
- if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
- {
- unsigned long flags;
+ unsigned char cmd = event_rec[1];
+ unsigned int parm = *(int *) &event_rec[4];
- save_flags (flags);
- cli ();
- if ((seq_sleep_flag.opts & WK_SLEEP))
- {
- {
- seq_sleep_flag.opts = WK_WAKEUP;
- wake_up (&seq_sleeper);
- };
- }
- restore_flags (flags);
- }
-
- return TIMER_ARMED;
- }
- break;
-
- case TMR_START:
- seq_time = jiffies;
- prev_input_time = 0;
- prev_event_time = 0;
- break;
-
- case TMR_STOP:
- break;
-
- case TMR_CONTINUE:
- break;
-
- case TMR_TEMPO:
- break;
-
- case TMR_ECHO:
- if (seq_mode == SEQ_2)
- seq_copy_to_input (event_rec, 8);
- else
- {
- parm = (parm << 8 | SEQ_ECHO);
- seq_copy_to_input ((unsigned char *) &parm, 4);
- }
- break;
-
- default:;
- }
+ if (seq_mode == SEQ_2)
+ {
+ int ret;
+
+ if ((ret = tmr->event(tmr_no, event_rec)) == TIMER_ARMED)
+ {
+ if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
+ {
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if ((seq_sleep_flag.opts & WK_SLEEP))
+ {
+ {
+ seq_sleep_flag.opts = WK_WAKEUP;
+ wake_up(&seq_sleeper);
+ };
+ }
+ restore_flags(flags);
+ }
+ }
+ return ret;
+ }
+ switch (cmd)
+ {
+ case TMR_WAIT_REL:
+ parm += prev_event_time;
+
+ /*
+ * NOTE! No break here. Execution of TMR_WAIT_REL continues in the
+ * next case (TMR_WAIT_ABS)
+ */
+
+ case TMR_WAIT_ABS:
+ if (parm > 0)
+ {
+ long time;
+
+ time = parm;
+ prev_event_time = time;
+
+ seq_playing = 1;
+ if (softsynthp != NULL)
+ softsynthp(SSYN_REQUEST, time, 0, 0);
+ else
+ request_sound_timer(time);
+
+ if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
+ {
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if ((seq_sleep_flag.opts & WK_SLEEP))
+ {
+ {
+ seq_sleep_flag.opts = WK_WAKEUP;
+ wake_up(&seq_sleeper);
+ };
+ }
+ restore_flags(flags);
+ }
+ return TIMER_ARMED;
+ }
+ break;
+
+ case TMR_START:
+ if (softsynthp != NULL)
+ {
+ softsynthp(SSYN_START, 0, 0, 0);
+ seq_time = 0;
+ } else
+ seq_time = jiffies;
+ prev_input_time = 0;
+ prev_event_time = 0;
+ break;
+
+ case TMR_STOP:
+ break;
+
+ case TMR_CONTINUE:
+ break;
+
+ case TMR_TEMPO:
+ break;
+
+ case TMR_ECHO:
+ if (seq_mode == SEQ_2)
+ seq_copy_to_input(event_rec, 8);
+ else
+ {
+ parm = (parm << 8 | SEQ_ECHO);
+ seq_copy_to_input((unsigned char *) &parm, 4);
+ }
+ break;
+
+ default:;
+ }
- return TIMER_NOT_ARMED;
+ return TIMER_NOT_ARMED;
}
static void
-seq_local_event (unsigned char *event_rec)
+seq_local_event(unsigned char *event_rec)
{
- unsigned char cmd = event_rec[1];
- unsigned int parm = *((unsigned int *) &event_rec[4]);
+ unsigned char cmd = event_rec[1];
+ unsigned int parm = *((unsigned int *) &event_rec[4]);
- switch (cmd)
- {
- case LOCL_STARTAUDIO:
+ switch (cmd)
+ {
+ case LOCL_STARTAUDIO:
#ifdef CONFIG_AUDIO
- DMAbuf_start_devices (parm);
+ DMAbuf_start_devices(parm);
#endif
- break;
+ break;
- default:;
- }
+ default:;
+ }
}
static void
-seq_sysex_message (unsigned char *event_rec)
+seq_sysex_message(unsigned char *event_rec)
{
- int dev = event_rec[1];
- int i, l = 0;
- unsigned char *buf = &event_rec[2];
-
- if ((int) dev > max_synthdev)
- return;
- if (!(synth_open_mask & (1 << dev)))
- return;
- if (!synth_devs[dev])
- return;
-
- l = 0;
- for (i = 0; i < 6 && buf[i] != 0xff; i++)
- l = i + 1;
-
- if (!synth_devs[dev]->send_sysex)
- return;
- if (l > 0)
- synth_devs[dev]->send_sysex (dev, buf, l);
+ int dev = event_rec[1];
+ int i, l = 0;
+ unsigned char *buf = &event_rec[2];
+
+ if ((int) dev > max_synthdev)
+ return;
+ if (!(synth_open_mask & (1 << dev)))
+ return;
+ if (!synth_devs[dev])
+ return;
+
+ l = 0;
+ for (i = 0; i < 6 && buf[i] != 0xff; i++)
+ l = i + 1;
+
+ if (!synth_devs[dev]->send_sysex)
+ return;
+ if (l > 0)
+ synth_devs[dev]->send_sysex(dev, buf, l);
}
static int
-play_event (unsigned char *q)
+play_event(unsigned char *q)
{
- /*
- * NOTE! This routine returns
- * 0 = normal event played.
- * 1 = Timer armed. Suspend playback until timer callback.
- * 2 = MIDI output buffer full. Restore queue and suspend until timer
- */
- unsigned int *delay;
-
- switch (q[0])
- {
- case SEQ_NOTEOFF:
- if (synth_open_mask & (1 << 0))
- if (synth_devs[0])
- synth_devs[0]->kill_note (0, q[1], 255, q[3]);
- break;
-
- case SEQ_NOTEON:
- if (q[4] < 128 || q[4] == 255)
- if (synth_open_mask & (1 << 0))
- if (synth_devs[0])
- synth_devs[0]->start_note (0, q[1], q[2], q[3]);
- break;
-
- case SEQ_WAIT:
- delay = (unsigned int *) q; /*
- * Bytes 1 to 3 are containing the *
- * delay in 'ticks'
- */
- *delay = (*delay >> 8) & 0xffffff;
-
- if (*delay > 0)
- {
- long time;
-
- seq_playing = 1;
- time = *delay;
- prev_event_time = time;
-
- request_sound_timer (time);
-
- if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
- {
- unsigned long flags;
-
- save_flags (flags);
- cli ();
- if ((seq_sleep_flag.opts & WK_SLEEP))
- {
- {
- seq_sleep_flag.opts = WK_WAKEUP;
- wake_up (&seq_sleeper);
- };
- }
- restore_flags (flags);
- }
- /*
- * The timer is now active and will reinvoke this function
- * after the timer expires. Return to the caller now.
- */
- return 1;
- }
- break;
-
- case SEQ_PGMCHANGE:
- if (synth_open_mask & (1 << 0))
- if (synth_devs[0])
- synth_devs[0]->set_instr (0, q[1], q[2]);
- break;
-
- case SEQ_SYNCTIMER: /*
- * Reset timer
+ /*
+ * NOTE! This routine returns
+ * 0 = normal event played.
+ * 1 = Timer armed. Suspend playback until timer callback.
+ * 2 = MIDI output buffer full. Restore queue and suspend until timer
+ */
+ unsigned int *delay;
+
+ switch (q[0])
+ {
+ case SEQ_NOTEOFF:
+ if (synth_open_mask & (1 << 0))
+ if (synth_devs[0])
+ synth_devs[0]->kill_note(0, q[1], 255, q[3]);
+ break;
+
+ case SEQ_NOTEON:
+ if (q[4] < 128 || q[4] == 255)
+ if (synth_open_mask & (1 << 0))
+ if (synth_devs[0])
+ synth_devs[0]->start_note(0, q[1], q[2], q[3]);
+ break;
+
+ case SEQ_WAIT:
+ delay = (unsigned int *) q; /*
+ * Bytes 1 to 3 are containing the *
+ * delay in 'ticks'
+ */
+ *delay = (*delay >> 8) & 0xffffff;
+
+ if (*delay > 0)
+ {
+ long time;
+
+ seq_playing = 1;
+ time = *delay;
+ prev_event_time = time;
+
+ if (softsynthp != NULL)
+ softsynthp(SSYN_REQUEST, time, 0, 0);
+ else
+ request_sound_timer(time);
+
+ if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
+ {
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if ((seq_sleep_flag.opts & WK_SLEEP))
+ {
+ {
+ seq_sleep_flag.opts = WK_WAKEUP;
+ wake_up(&seq_sleeper);
+ };
+ }
+ restore_flags(flags);
+ }
+ /*
+ * The timer is now active and will reinvoke this function
+ * after the timer expires. Return to the caller now.
+ */
+ return 1;
+ }
+ break;
+
+ case SEQ_PGMCHANGE:
+ if (synth_open_mask & (1 << 0))
+ if (synth_devs[0])
+ synth_devs[0]->set_instr(0, q[1], q[2]);
+ break;
+
+ case SEQ_SYNCTIMER: /*
+ * Reset timer
*/
- seq_time = jiffies;
- prev_input_time = 0;
- prev_event_time = 0;
- break;
-
- case SEQ_MIDIPUTC: /*
+ if (softsynthp != NULL)
+ seq_time = 0;
+ else
+ seq_time = jiffies;
+ prev_input_time = 0;
+ prev_event_time = 0;
+ if (softsynthp != NULL)
+ softsynthp(SSYN_START, 0, 0, 0);
+ break;
+
+ case SEQ_MIDIPUTC: /*
* Put a midi character
*/
- if (midi_opened[q[2]])
- {
- int dev;
-
- dev = q[2];
-
- if (dev < 0 || dev >= num_midis)
- break;
-
- if (!midi_devs[dev]->outputc (dev, q[1]))
- {
- /*
- * Output FIFO is full. Wait one timer cycle and try again.
- */
-
- seq_playing = 1;
- request_sound_timer (-1);
- return 2;
- }
- else
- midi_written[dev] = 1;
- }
- break;
-
- case SEQ_ECHO:
- seq_copy_to_input (q, 4); /*
- * Echo back to the process
- */
- break;
-
- case SEQ_PRIVATE:
- if ((int) q[1] < max_synthdev)
- synth_devs[q[1]]->hw_control (q[1], q);
- break;
-
- case SEQ_EXTENDED:
- extended_event (q);
- break;
-
- case EV_CHN_VOICE:
- seq_chn_voice_event (q);
- break;
-
- case EV_CHN_COMMON:
- seq_chn_common_event (q);
- break;
-
- case EV_TIMING:
- if (seq_timing_event (q) == TIMER_ARMED)
- {
- return 1;
- }
- break;
-
- case EV_SEQ_LOCAL:
- seq_local_event (q);
- break;
-
- case EV_SYSEX:
- seq_sysex_message (q);
- break;
-
- default:;
- }
+ if (midi_opened[q[2]])
+ {
+ int dev;
+
+ dev = q[2];
+
+ if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
+ break;
+
+ if (!midi_devs[dev]->outputc(dev, q[1]))
+ {
+ /*
+ * Output FIFO is full. Wait one timer cycle and try again.
+ */
+
+ seq_playing = 1;
+ if (softsynthp != NULL)
+ softsynthp(SSYN_REQUEST, -1, 0, 0);
+ else
+ request_sound_timer(-1);
+ return 2;
+ } else
+ midi_written[dev] = 1;
+ }
+ break;
+
+ case SEQ_ECHO:
+ seq_copy_to_input(q, 4); /*
+ * Echo back to the process
+ */
+ break;
+
+ case SEQ_PRIVATE:
+ if ((int) q[1] < max_synthdev)
+ synth_devs[q[1]]->hw_control(q[1], q);
+ break;
+
+ case SEQ_EXTENDED:
+ extended_event(q);
+ break;
+
+ case EV_CHN_VOICE:
+ seq_chn_voice_event(q);
+ break;
+
+ case EV_CHN_COMMON:
+ seq_chn_common_event(q);
+ break;
+
+ case EV_TIMING:
+ if (seq_timing_event(q) == TIMER_ARMED)
+ {
+ return 1;
+ }
+ break;
+
+ case EV_SEQ_LOCAL:
+ seq_local_event(q);
+ break;
+
+ case EV_SYSEX:
+ seq_sysex_message(q);
+ break;
+
+ default:;
+ }
- return 0;
+ return 0;
}
static void
-seq_startplay (void)
+seq_startplay(void)
{
- unsigned long flags;
- int this_one, action;
-
- while (qlen > 0)
- {
-
- save_flags (flags);
- cli ();
- qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE;
- qlen--;
- restore_flags (flags);
-
- seq_playing = 1;
-
- if ((action = play_event (&queue[this_one * EV_SZ])))
- { /* Suspend playback. Next timer routine invokes this routine again */
- if (action == 2)
- {
- qlen++;
- qhead = this_one;
- }
- return;
- }
+ unsigned long flags;
+ int this_one, action;
- }
+ while (qlen > 0)
+ {
- seq_playing = 0;
+ save_flags(flags);
+ cli();
+ qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE;
+ qlen--;
+ restore_flags(flags);
+
+ seq_playing = 1;
+
+ if ((action = play_event(&queue[this_one * EV_SZ])))
+ { /* Suspend playback. Next timer routine invokes this routine again */
+ if (action == 2)
+ {
+ qlen++;
+ qhead = this_one;
+ }
+ return;
+ }
+ }
- if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
- {
- unsigned long flags;
+ seq_playing = 0;
- save_flags (flags);
- cli ();
- if ((seq_sleep_flag.opts & WK_SLEEP))
- {
+ if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
{
- seq_sleep_flag.opts = WK_WAKEUP;
- wake_up (&seq_sleeper);
- };
- }
- restore_flags (flags);
- }
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if ((seq_sleep_flag.opts & WK_SLEEP))
+ {
+ {
+ seq_sleep_flag.opts = WK_WAKEUP;
+ wake_up(&seq_sleeper);
+ };
+ }
+ restore_flags(flags);
+ }
}
static void
-reset_controllers (int dev, unsigned char *controller, int update_dev)
+reset_controllers(int dev, unsigned char *controller, int update_dev)
{
- int i;
+ int i;
- for (i = 0; i < 128; i++)
- controller[i] = ctrl_def_values[i];
+ for (i = 0; i < 128; i++)
+ controller[i] = ctrl_def_values[i];
}
static void
-setup_mode2 (void)
+setup_mode2(void)
{
- int dev;
-
- max_synthdev = num_synths;
-
- for (dev = 0; dev < num_midis; dev++)
- if (midi_devs[dev]->converter != NULL)
- {
- synth_devs[max_synthdev++] =
- midi_devs[dev]->converter;
- }
+ int dev;
- for (dev = 0; dev < max_synthdev; dev++)
- {
- int chn;
+ max_synthdev = num_synths;
- synth_devs[dev]->sysex_ptr = 0;
- synth_devs[dev]->emulation = 0;
+ for (dev = 0; dev < num_midis; dev++)
+ if (midi_devs[dev] && midi_devs[dev]->converter != NULL)
+ {
+ synth_devs[max_synthdev++] =
+ midi_devs[dev]->converter;
+ }
+ for (dev = 0; dev < max_synthdev; dev++)
+ {
+ int chn;
+
+ synth_devs[dev]->sysex_ptr = 0;
+ synth_devs[dev]->emulation = 0;
+
+ for (chn = 0; chn < 16; chn++)
+ {
+ synth_devs[dev]->chn_info[chn].pgm_num = 0;
+ reset_controllers(dev,
+ synth_devs[dev]->chn_info[chn].controllers,
+ 0);
+ synth_devs[dev]->chn_info[chn].bender_value = (1 << 7); /* Neutral */
+ synth_devs[dev]->chn_info[chn].bender_range = 200;
+ }
+ }
- for (chn = 0; chn < 16; chn++)
- {
- synth_devs[dev]->chn_info[chn].pgm_num = 0;
- reset_controllers (dev,
- synth_devs[dev]->chn_info[chn].controllers,
- 0);
- synth_devs[dev]->chn_info[chn].bender_value = (1 << 7); /* Neutral */
- synth_devs[dev]->chn_info[chn].bender_range = 200;
- }
- }
-
- max_mididev = 0;
- seq_mode = SEQ_2;
+ max_mididev = 0;
+ seq_mode = SEQ_2;
}
int
-sequencer_open (int dev, struct fileinfo *file)
+sequencer_open(int dev, struct fileinfo *file)
{
- int retval, mode, i;
- int level, tmp;
- unsigned long flags;
+ int retval, mode, i;
+ int level, tmp;
+ unsigned long flags;
- if (!sequencer_ok)
- sequencer_init ();
+ if (!sequencer_ok)
+ sequencer_init();
- level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1;
+ level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1;
- dev = dev >> 4;
- mode = file->mode & O_ACCMODE;
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
- DEB (printk ("sequencer_open(dev=%d)\n", dev));
+ DEB(printk("sequencer_open(dev=%d)\n", dev));
+
+ if (!sequencer_ok)
+ {
+ printk("Soundcard: Sequencer not initialized\n");
+ return -ENXIO;
+ }
+ if (dev) /* Patch manager device (obsolete) */
+ {
+ return -ENXIO;
+ }
+ if (mode == OPEN_READ)
+ if (!num_midis)
+ {
+ printk("Sequencer: No MIDI devices. Input not possible\n");
+ sequencer_busy = 0;
+ return -ENXIO;
+ }
+ save_flags(flags);
+ cli();
+ if (sequencer_busy)
+ {
+ restore_flags(flags);
+ return -EBUSY;
+ }
+ sequencer_busy = 1;
+ obsolete_api_used = 0;
+ restore_flags(flags);
- if (!sequencer_ok)
- {
- printk ("Soundcard: Sequencer not initialized\n");
- return -ENXIO;
- }
+ max_mididev = num_midis;
+ max_synthdev = num_synths;
+ pre_event_timeout = 0;
+ seq_mode = SEQ_1;
- if (dev) /* Patch manager device (obsolete) */
- {
- return -ENXIO;
- }
+ if (pending_timer != -1)
+ {
+ tmr_no = pending_timer;
+ pending_timer = -1;
+ }
+ if (tmr_no == -1) /* Not selected yet */
+ {
+ int i, best;
+
+ best = -1;
+ for (i = 0; i < num_sound_timers; i++)
+ if (sound_timer_devs[i] && sound_timer_devs[i]->priority > best)
+ {
+ tmr_no = i;
+ best = sound_timer_devs[i]->priority;
+ }
+ if (tmr_no == -1) /* Should not be */
+ tmr_no = 0;
+ }
+ tmr = sound_timer_devs[tmr_no];
- if (mode == OPEN_READ)
- if (!num_midis)
- {
- printk ("Sequencer: No MIDI devices. Input not possible\n");
- sequencer_busy = 0;
- return -ENXIO;
- }
-
- save_flags (flags);
- cli ();
- if (sequencer_busy)
- {
- restore_flags (flags);
- return -EBUSY;
- }
- sequencer_busy = 1;
- obsolete_api_used = 0;
- restore_flags (flags);
-
- max_mididev = num_midis;
- max_synthdev = num_synths;
- pre_event_timeout = 0;
- seq_mode = SEQ_1;
-
- if (pending_timer != -1)
- {
- tmr_no = pending_timer;
- pending_timer = -1;
- }
-
- if (tmr_no == -1) /* Not selected yet */
- {
- int i, best;
-
- best = -1;
- for (i = 0; i < num_sound_timers; i++)
- if (sound_timer_devs[i]->priority > best)
+ if (level == 2)
{
- tmr_no = i;
- best = sound_timer_devs[i]->priority;
+ if (tmr == NULL)
+ {
+ printk("sequencer: No timer for level 2\n");
+ sequencer_busy = 0;
+ return -ENXIO;
+ }
+ setup_mode2();
}
+ if (!max_synthdev && !max_mididev)
+ return -ENXIO;
- if (tmr_no == -1) /* Should not be */
- tmr_no = 0;
- }
+ synth_open_mask = 0;
- tmr = sound_timer_devs[tmr_no];
+ for (i = 0; i < max_mididev; i++)
+ {
+ midi_opened[i] = 0;
+ midi_written[i] = 0;
+ }
- if (level == 2)
- {
- if (tmr == NULL)
- {
- printk ("sequencer: No timer for level 2\n");
- sequencer_busy = 0;
- return -ENXIO;
- }
- setup_mode2 ();
- }
-
- if (!max_synthdev && !max_mididev)
- return -ENXIO;
-
- synth_open_mask = 0;
-
- for (i = 0; i < max_mididev; i++)
- {
- midi_opened[i] = 0;
- midi_written[i] = 0;
- }
-
- for (i = 0; i < max_synthdev; i++)
- {
- if ((tmp = synth_devs[i]->open (i, mode)) < 0)
- {
- printk ("Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp);
- if (synth_devs[i]->midi_dev)
- printk ("(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev);
- }
- else
- {
- synth_open_mask |= (1 << i);
- if (synth_devs[i]->midi_dev)
- midi_opened[synth_devs[i]->midi_dev] = 1;
- }
- }
+ for (i = 0; i < max_synthdev; i++)
+ {
+ if ((tmp = synth_devs[i]->open(i, mode)) < 0)
+ {
+ printk("Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp);
+ if (synth_devs[i]->midi_dev)
+ printk("(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev);
+ } else
+ {
+ synth_open_mask |= (1 << i);
+ if (synth_devs[i]->midi_dev)
+ midi_opened[synth_devs[i]->midi_dev] = 1;
+ }
+ }
- seq_time = jiffies;
+ if (softsynthp != NULL)
+ seq_time = 0;
+ else
+ seq_time = jiffies;
- prev_input_time = 0;
- prev_event_time = 0;
+ prev_input_time = 0;
+ prev_event_time = 0;
+ if (softsynthp != NULL)
+ softsynthp(SSYN_START, 0, 0, 0);
- if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE))
- { /*
+ if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE))
+ { /*
* Initialize midi input devices
*/
- for (i = 0; i < max_mididev; i++)
- if (!midi_opened[i])
+ for (i = 0; i < max_mididev; i++)
+ if (!midi_opened[i])
+ {
+ if ((retval = midi_devs[i]->open(i, mode,
+ sequencer_midi_input, sequencer_midi_output)) >= 0)
+ midi_opened[i] = 1;
+ }
+ }
+ if (seq_mode == SEQ_2)
{
- if ((retval = midi_devs[i]->open (i, mode,
- sequencer_midi_input, sequencer_midi_output)) >= 0)
- midi_opened[i] = 1;
+ tmr->open(tmr_no, seq_mode);
}
- }
-
- if (seq_mode == SEQ_2)
- {
- tmr->open (tmr_no, seq_mode);
- }
-
- seq_sleep_flag.opts = WK_NONE;
- midi_sleep_flag.opts = WK_NONE;
- output_threshold = SEQ_MAX_QUEUE / 2;
+ seq_sleep_flag.opts = WK_NONE;
+ midi_sleep_flag.opts = WK_NONE;
+ output_threshold = SEQ_MAX_QUEUE / 2;
- return 0;
+ return 0;
}
void
-seq_drain_midi_queues (void)
+seq_drain_midi_queues(void)
{
- int i, n;
-
- /*
- * Give the Midi drivers time to drain their output queues
- */
-
- n = 1;
+ int i, n;
- while (!(current->signal & ~current->blocked) && n)
- {
- n = 0;
+ /*
+ * Give the Midi drivers time to drain their output queues
+ */
- for (i = 0; i < max_mididev; i++)
- if (midi_opened[i] && midi_written[i])
- if (midi_devs[i]->buffer_status != NULL)
- if (midi_devs[i]->buffer_status (i))
- n++;
-
- /*
- * Let's have a delay
- */
- if (n)
- {
+ n = 1;
+ while (!(current->signal & ~current->blocked) && n)
{
- unsigned long tlimit;
-
- if (HZ / 10)
- current->timeout = tlimit = jiffies + (HZ / 10);
- else
- tlimit = (unsigned long) -1;
- seq_sleep_flag.opts = WK_SLEEP;
- interruptible_sleep_on (&seq_sleeper);
- if (!(seq_sleep_flag.opts & WK_WAKEUP))
- {
- if (jiffies >= tlimit)
- seq_sleep_flag.opts |= WK_TIMEOUT;
- }
- seq_sleep_flag.opts &= ~WK_SLEEP;
- };
- }
- }
+ n = 0;
+
+ for (i = 0; i < max_mididev; i++)
+ if (midi_opened[i] && midi_written[i])
+ if (midi_devs[i]->buffer_status != NULL)
+ if (midi_devs[i]->buffer_status(i))
+ n++;
+
+ /*
+ * Let's have a delay
+ */
+ if (n)
+ {
+
+ {
+ unsigned long tlimit;
+
+ if (HZ / 10)
+ current->timeout = tlimit = jiffies + (HZ / 10);
+ else
+ tlimit = (unsigned long) -1;
+ seq_sleep_flag.opts = WK_SLEEP;
+ interruptible_sleep_on(&seq_sleeper);
+ if (!(seq_sleep_flag.opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ seq_sleep_flag.opts |= WK_TIMEOUT;
+ }
+ seq_sleep_flag.opts &= ~WK_SLEEP;
+ };
+ }
+ }
}
void
-sequencer_release (int dev, struct fileinfo *file)
+sequencer_release(int dev, struct fileinfo *file)
{
- int i;
- int mode = file->mode & O_ACCMODE;
-
- dev = dev >> 4;
+ int i;
+ int mode = file->mode & O_ACCMODE;
- DEB (printk ("sequencer_release(dev=%d)\n", dev));
+ dev = dev >> 4;
- /*
- * Wait until the queue is empty (if we don't have nonblock)
- */
+ DEB(printk("sequencer_release(dev=%d)\n", dev));
- if (mode != OPEN_READ && !(file->flags & (O_NONBLOCK) ?
- 1 : 0))
- while (!(current->signal & ~current->blocked) && qlen > 0)
- {
- seq_sync ();
+ /*
+ * Wait until the queue is empty (if we don't have nonblock)
+ */
- {
- unsigned long tlimit;
-
- if (3 * HZ)
- current->timeout = tlimit = jiffies + (3 * HZ);
- else
- tlimit = (unsigned long) -1;
- seq_sleep_flag.opts = WK_SLEEP;
- interruptible_sleep_on (&seq_sleeper);
- if (!(seq_sleep_flag.opts & WK_WAKEUP))
- {
- if (jiffies >= tlimit)
- seq_sleep_flag.opts |= WK_TIMEOUT;
- }
- seq_sleep_flag.opts &= ~WK_SLEEP;
- }; /* Extra delay */
- }
-
- if (mode != OPEN_READ)
- seq_drain_midi_queues (); /*
- * Ensure the output queues are empty
- */
- seq_reset ();
- if (mode != OPEN_READ)
- seq_drain_midi_queues (); /*
- * Flush the all notes off messages
- */
+ if (mode != OPEN_READ && !(file->flags & (O_NONBLOCK) ?
+ 1 : 0))
+ while (!(current->signal & ~current->blocked) && qlen > 0)
+ {
+ seq_sync();
+
+ {
+ unsigned long tlimit;
+
+ if (3 * HZ)
+ current->timeout = tlimit = jiffies + (3 * HZ);
+ else
+ tlimit = (unsigned long) -1;
+ seq_sleep_flag.opts = WK_SLEEP;
+ interruptible_sleep_on(&seq_sleeper);
+ if (!(seq_sleep_flag.opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ seq_sleep_flag.opts |= WK_TIMEOUT;
+ }
+ seq_sleep_flag.opts &= ~WK_SLEEP;
+ }; /* Extra delay */
+ }
+ if (mode != OPEN_READ)
+ seq_drain_midi_queues(); /*
+ * Ensure the output queues are empty
+ */
+ seq_reset();
+ if (mode != OPEN_READ)
+ seq_drain_midi_queues(); /*
+ * Flush the all notes off messages
+ */
- for (i = 0; i < max_synthdev; i++)
- {
- if (synth_open_mask & (1 << i)) /*
- * Actually opened
- */
- if (synth_devs[i])
+ for (i = 0; i < max_synthdev; i++)
{
- synth_devs[i]->close (i);
-
- if (synth_devs[i]->midi_dev)
- midi_opened[synth_devs[i]->midi_dev] = 0;
+ if (synth_open_mask & (1 << i)) /*
+ * Actually opened
+ */
+ if (synth_devs[i])
+ {
+ synth_devs[i]->close(i);
+
+ if (synth_devs[i]->midi_dev)
+ midi_opened[synth_devs[i]->midi_dev] = 0;
+ }
}
- }
- for (i = 0; i < max_mididev; i++)
- {
- if (midi_opened[i])
- midi_devs[i]->close (i);
- }
+ for (i = 0; i < max_mididev; i++)
+ {
+ if (midi_opened[i])
+ midi_devs[i]->close(i);
+ }
- if (seq_mode == SEQ_2)
- tmr->close (tmr_no);
+ if (seq_mode == SEQ_2)
+ tmr->close(tmr_no);
- if (obsolete_api_used)
- printk ("/dev/music: Obsolete (4 byte) API was used by this program\n");
- sequencer_busy = 0;
+ if (obsolete_api_used)
+ printk("/dev/music: Obsolete (4 byte) API was used by this program\n");
+ sequencer_busy = 0;
}
static int
-seq_sync (void)
+seq_sync(void)
{
- unsigned long flags;
-
- if (qlen && !seq_playing && !(current->signal & ~current->blocked))
- seq_startplay ();
-
- save_flags (flags);
- cli ();
- if (qlen > 0)
- {
+ unsigned long flags;
- {
- unsigned long tlimit;
+ if (qlen && !seq_playing && !(current->signal & ~current->blocked))
+ seq_startplay();
- if (HZ)
- current->timeout = tlimit = jiffies + (HZ);
- else
- tlimit = (unsigned long) -1;
- seq_sleep_flag.opts = WK_SLEEP;
- interruptible_sleep_on (&seq_sleeper);
- if (!(seq_sleep_flag.opts & WK_WAKEUP))
+ save_flags(flags);
+ cli();
+ if (qlen > 0)
{
- if (jiffies >= tlimit)
- seq_sleep_flag.opts |= WK_TIMEOUT;
+
+ {
+ unsigned long tlimit;
+
+ if (HZ)
+ current->timeout = tlimit = jiffies + (HZ);
+ else
+ tlimit = (unsigned long) -1;
+ seq_sleep_flag.opts = WK_SLEEP;
+ interruptible_sleep_on(&seq_sleeper);
+ if (!(seq_sleep_flag.opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ seq_sleep_flag.opts |= WK_TIMEOUT;
+ }
+ seq_sleep_flag.opts &= ~WK_SLEEP;
+ };
}
- seq_sleep_flag.opts &= ~WK_SLEEP;
- };
- }
- restore_flags (flags);
+ restore_flags(flags);
- return qlen;
+ return qlen;
}
static void
-midi_outc (int dev, unsigned char data)
+midi_outc(int dev, unsigned char data)
{
- /*
- * NOTE! Calls sleep(). Don't call this from interrupt.
- */
-
- int n;
- unsigned long flags;
-
- /*
- * This routine sends one byte to the Midi channel.
- * If the output FIFO is full, it waits until there
- * is space in the queue
- */
+ /*
+ * NOTE! Calls sleep(). Don't call this from interrupt.
+ */
- n = 3 * HZ; /* Timeout */
+ int n;
+ unsigned long flags;
- save_flags (flags);
- cli ();
- while (n && !midi_devs[dev]->outputc (dev, data))
- {
+ /*
+ * This routine sends one byte to the Midi channel.
+ * If the output FIFO is full, it waits until there
+ * is space in the queue
+ */
- {
- unsigned long tlimit;
+ n = 3 * HZ; /* Timeout */
- if (4)
- current->timeout = tlimit = jiffies + (4);
- else
- tlimit = (unsigned long) -1;
- seq_sleep_flag.opts = WK_SLEEP;
- interruptible_sleep_on (&seq_sleeper);
- if (!(seq_sleep_flag.opts & WK_WAKEUP))
+ save_flags(flags);
+ cli();
+ while (n && !midi_devs[dev]->outputc(dev, data))
{
- if (jiffies >= tlimit)
- seq_sleep_flag.opts |= WK_TIMEOUT;
+
+ {
+ unsigned long tlimit;
+
+ if (4)
+ current->timeout = tlimit = jiffies + (4);
+ else
+ tlimit = (unsigned long) -1;
+ seq_sleep_flag.opts = WK_SLEEP;
+ interruptible_sleep_on(&seq_sleeper);
+ if (!(seq_sleep_flag.opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ seq_sleep_flag.opts |= WK_TIMEOUT;
+ }
+ seq_sleep_flag.opts &= ~WK_SLEEP;
+ };
+ n--;
}
- seq_sleep_flag.opts &= ~WK_SLEEP;
- };
- n--;
- }
- restore_flags (flags);
+ restore_flags(flags);
}
static void
-seq_reset (void)
+seq_reset(void)
{
- /*
- * NOTE! Calls sleep(). Don't call this from interrupt.
- */
+ /*
+ * NOTE! Calls sleep(). Don't call this from interrupt.
+ */
- int i;
- int chn;
- unsigned long flags;
+ int i;
+ int chn;
+ unsigned long flags;
- sound_stop_timer ();
+ if (softsynthp != NULL)
+ softsynthp(SSYN_STOP, 0, 0, 0);
+ else
+ sound_stop_timer();
- seq_time = jiffies;
- prev_input_time = 0;
- prev_event_time = 0;
+ seq_time = jiffies;
+ prev_input_time = 0;
+ prev_event_time = 0;
- qlen = qhead = qtail = 0;
- iqlen = iqhead = iqtail = 0;
+ qlen = qhead = qtail = 0;
+ iqlen = iqhead = iqtail = 0;
- for (i = 0; i < max_synthdev; i++)
- if (synth_open_mask & (1 << i))
- if (synth_devs[i])
- synth_devs[i]->reset (i);
+ for (i = 0; i < max_synthdev; i++)
+ if (synth_open_mask & (1 << i))
+ if (synth_devs[i])
+ synth_devs[i]->reset(i);
- if (seq_mode == SEQ_2)
- {
+ if (seq_mode == SEQ_2)
+ {
- for (chn = 0; chn < 16; chn++)
- for (i = 0; i < max_synthdev; i++)
- if (synth_open_mask & (1 << i))
- if (synth_devs[i])
- {
- synth_devs[i]->controller (i, chn, 123, 0); /* All notes off */
- synth_devs[i]->controller (i, chn, 121, 0); /* Reset all ctl */
- synth_devs[i]->bender (i, chn, 1 << 13); /* Bender off */
- }
-
- }
- else
- /* seq_mode == SEQ_1 */
- {
- for (i = 0; i < max_mididev; i++)
- if (midi_written[i]) /*
- * Midi used. Some notes may still be playing
- */
+ for (chn = 0; chn < 16; chn++)
+ for (i = 0; i < max_synthdev; i++)
+ if (synth_open_mask & (1 << i))
+ if (synth_devs[i])
+ {
+ synth_devs[i]->controller(i, chn, 123, 0); /* All notes off */
+ synth_devs[i]->controller(i, chn, 121, 0); /* Reset all ctl */
+ synth_devs[i]->bender(i, chn, 1 << 13); /* Bender off */
+ }
+ } else
+ /* seq_mode == SEQ_1 */
+ {
+ for (i = 0; i < max_mididev; i++)
+ if (midi_written[i]) /*
+ * Midi used. Some notes may still be playing
+ */
+ {
+ /*
+ * Sending just a ACTIVE SENSING message should be enough to stop all
+ * playing notes. Since there are devices not recognizing the
+ * active sensing, we have to send some all notes off messages also.
+ */
+ midi_outc(i, 0xfe);
+
+ for (chn = 0; chn < 16; chn++)
+ {
+ midi_outc(i,
+ (unsigned char) (0xb0 + (chn & 0x0f))); /* control change */
+ midi_outc(i, 0x7b); /* All notes off */
+ midi_outc(i, 0); /* Dummy parameter */
+ }
+
+ midi_devs[i]->close(i);
+
+ midi_written[i] = 0;
+ midi_opened[i] = 0;
+ }
+ }
+
+ seq_playing = 0;
+
+ save_flags(flags);
+ cli();
+ if ((seq_sleep_flag.opts & WK_SLEEP))
{
- /*
- * Sending just a ACTIVE SENSING message should be enough to stop all
- * playing notes. Since there are devices not recognizing the
- * active sensing, we have to send some all notes off messages also.
- */
- midi_outc (i, 0xfe);
-
- for (chn = 0; chn < 16; chn++)
- {
- midi_outc (i,
- (unsigned char) (0xb0 + (chn & 0x0f))); /* control change */
- midi_outc (i, 0x7b); /* All notes off */
- midi_outc (i, 0); /* Dummy parameter */
- }
-
- midi_devs[i]->close (i);
-
- midi_written[i] = 0;
- midi_opened[i] = 0;
+ /* printk( "Sequencer Warning: Unexpected sleeping process - Waking up\n"); */
+ {
+ seq_sleep_flag.opts = WK_WAKEUP;
+ wake_up(&seq_sleeper);
+ };
}
- }
-
- seq_playing = 0;
-
- save_flags (flags);
- cli ();
- if ((seq_sleep_flag.opts & WK_SLEEP))
- {
- /* printk ("Sequencer Warning: Unexpected sleeping process - Waking up\n"); */
- {
- seq_sleep_flag.opts = WK_WAKEUP;
- wake_up (&seq_sleeper);
- };
- }
- restore_flags (flags);
+ restore_flags(flags);
}
static void
-seq_panic (void)
+seq_panic(void)
{
- /*
- * This routine is called by the application in case the user
- * wants to reset the system to the default state.
- */
-
- seq_reset ();
-
- /*
- * Since some of the devices don't recognize the active sensing and
- * all notes off messages, we have to shut all notes manually.
- *
- * TO BE IMPLEMENTED LATER
- */
-
- /*
- * Also return the controllers to their default states
- */
+ /*
+ * This routine is called by the application in case the user
+ * wants to reset the system to the default state.
+ */
+
+ seq_reset();
+
+ /*
+ * Since some of the devices don't recognize the active sensing and
+ * all notes off messages, we have to shut all notes manually.
+ *
+ * TO BE IMPLEMENTED LATER
+ */
+
+ /*
+ * Also return the controllers to their default states
+ */
}
int
-sequencer_ioctl (int dev, struct fileinfo *file,
- unsigned int cmd, caddr_t arg)
+sequencer_ioctl(int dev, struct fileinfo *file,
+ unsigned int cmd, caddr_t arg)
{
- int midi_dev, orig_dev, val;
- int mode = file->mode & O_ACCMODE;
-
- orig_dev = dev = dev >> 4;
-
- switch (cmd)
- {
- case SNDCTL_TMR_TIMEBASE:
- case SNDCTL_TMR_TEMPO:
- case SNDCTL_TMR_START:
- case SNDCTL_TMR_STOP:
- case SNDCTL_TMR_CONTINUE:
- case SNDCTL_TMR_METRONOME:
- case SNDCTL_TMR_SOURCE:
-
- if (seq_mode != SEQ_2)
- return -EINVAL;
- return tmr->ioctl (tmr_no, cmd, arg);
- break;
-
- case SNDCTL_TMR_SELECT:
-
- if (seq_mode != SEQ_2)
- return -EINVAL;
- pending_timer = *(int *) arg;
-
- if (pending_timer < 0 || pending_timer >= num_sound_timers)
- {
- pending_timer = -1;
- return -EINVAL;
- }
-
- return (*(int *) arg = pending_timer);
- break;
-
- case SNDCTL_SEQ_PANIC:
- seq_panic ();
- break;
-
- case SNDCTL_SEQ_SYNC:
-
- if (mode == OPEN_READ)
- return 0;
- while (qlen > 0 && !(current->signal & ~current->blocked))
- seq_sync ();
- if (qlen)
- return -EINTR;
- else
- return 0;
- break;
+ int midi_dev, orig_dev, val;
+ int mode = file->mode & O_ACCMODE;
- case SNDCTL_SEQ_RESET:
+ orig_dev = dev = dev >> 4;
- seq_reset ();
- return 0;
- break;
-
- case SNDCTL_SEQ_TESTMIDI:
- midi_dev = *(int *) arg;
- if (midi_dev < 0 || midi_dev >= max_mididev)
- return -ENXIO;
-
- if (!midi_opened[midi_dev])
- {
- int err, mode;
+ switch (cmd)
+ {
+ case SNDCTL_TMR_TIMEBASE:
+ case SNDCTL_TMR_TEMPO:
+ case SNDCTL_TMR_START:
+ case SNDCTL_TMR_STOP:
+ case SNDCTL_TMR_CONTINUE:
+ case SNDCTL_TMR_METRONOME:
+ case SNDCTL_TMR_SOURCE:
+
+ if (seq_mode != SEQ_2)
+ return -EINVAL;
+ return tmr->ioctl(tmr_no, cmd, arg);
+ break;
+
+ case SNDCTL_TMR_SELECT:
+
+ if (seq_mode != SEQ_2)
+ return -EINVAL;
+ pending_timer = *(int *) arg;
+
+ if (pending_timer < 0 || pending_timer >= num_sound_timers || sound_timer_devs[pending_timer] == NULL)
+ {
+ pending_timer = -1;
+ return -EINVAL;
+ }
+ return (*(int *) arg = pending_timer);
+ break;
+
+ case SNDCTL_SEQ_PANIC:
+ seq_panic();
+ break;
+
+ case SNDCTL_SEQ_SYNC:
+
+ if (mode == OPEN_READ)
+ return 0;
+ while (qlen > 0 && !(current->signal & ~current->blocked))
+ seq_sync();
+ if (qlen)
+ return -EINTR;
+ else
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_RESET:
+
+ seq_reset();
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_TESTMIDI:
+ midi_dev = *(int *) arg;
+ if (midi_dev < 0 || midi_dev >= max_mididev)
+ return -ENXIO;
+
+ if (!midi_opened[midi_dev])
+ {
+ int err, mode;
+
+ mode = file->mode & O_ACCMODE;
+ if ((err = midi_devs[midi_dev]->open(midi_dev, mode,
+ sequencer_midi_input,
+ sequencer_midi_output)) < 0)
+ return err;
+ }
+ midi_opened[midi_dev] = 1;
+
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_GETINCOUNT:
+ if (mode == OPEN_WRITE)
+ return 0;
+ return (*(int *) arg = iqlen);
+ break;
+
+ case SNDCTL_SEQ_GETOUTCOUNT:
+
+ if (mode == OPEN_READ)
+ return 0;
+ return (*(int *) arg = SEQ_MAX_QUEUE - qlen);
+ break;
+
+ case SNDCTL_SEQ_GETTIME:
+ if (seq_mode == SEQ_2)
+ return tmr->ioctl(tmr_no, cmd, arg);
+
+ if (softsynthp != NULL)
+ return (*(int *) arg = softsynthp(SSYN_GETTIME, 0, 0, 0));
+ else
+ return (*(int *) arg = jiffies - seq_time);
+ break;
+
+ case SNDCTL_SEQ_CTRLRATE:
+ /*
+ * If *arg == 0, just return the current rate
+ */
+ if (seq_mode == SEQ_2)
+ return tmr->ioctl(tmr_no, cmd, arg);
+
+ val = *(int *) arg;
+ if (val != 0)
+ return -EINVAL;
+
+ return (*(int *) arg = HZ);
+ break;
+
+ case SNDCTL_SEQ_RESETSAMPLES:
+ case SNDCTL_SYNTH_REMOVESAMPLE:
+ case SNDCTL_SYNTH_CONTROL:
+ {
+ int err;
+
+ dev = *(int *) arg;
+ if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
+ {
+ return -ENXIO;
+ }
+ if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+ {
+ return -EBUSY;
+ }
+ err = synth_devs[dev]->ioctl(dev, cmd, arg);
+ return err;
+ }
+ break;
+
+ case SNDCTL_SEQ_NRSYNTHS:
+ return (*(int *) arg = max_synthdev);
+ break;
+
+ case SNDCTL_SEQ_NRMIDIS:
+ return (*(int *) arg = max_mididev);
+ break;
+
+ case SNDCTL_SYNTH_MEMAVL:
+ {
+ int dev;
- mode = file->mode & O_ACCMODE;
- if ((err = midi_devs[midi_dev]->open (midi_dev, mode,
- sequencer_midi_input,
- sequencer_midi_output)) < 0)
- return err;
- }
+ dev = *(int *) arg;
- midi_opened[midi_dev] = 1;
+ if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
+ return -ENXIO;
- return 0;
- break;
+ if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+ return -EBUSY;
- case SNDCTL_SEQ_GETINCOUNT:
- if (mode == OPEN_WRITE)
- return 0;
- return (*(int *) arg = iqlen);
- break;
+ return (*(int *) arg = synth_devs[dev]->ioctl(dev, cmd, arg));
+ }
+ break;
- case SNDCTL_SEQ_GETOUTCOUNT:
+ case SNDCTL_FM_4OP_ENABLE:
+ {
+ int dev;
- if (mode == OPEN_READ)
- return 0;
- return (*(int *) arg = SEQ_MAX_QUEUE - qlen);
- break;
+ dev = *(int *) arg;
- case SNDCTL_SEQ_GETTIME:
- if (seq_mode == SEQ_2)
- return tmr->ioctl (tmr_no, cmd, arg);
+ if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
+ return -ENXIO;
- return (*(int *) arg = jiffies - seq_time);
- break;
+ if (!(synth_open_mask & (1 << dev)))
+ return -ENXIO;
- case SNDCTL_SEQ_CTRLRATE:
- /*
- * If *arg == 0, just return the current rate
- */
- if (seq_mode == SEQ_2)
- return tmr->ioctl (tmr_no, cmd, arg);
+ synth_devs[dev]->ioctl(dev, cmd, arg);
+ return 0;
+ }
+ break;
- val = *(int *) arg;
- if (val != 0)
- return -EINVAL;
+ case SNDCTL_SYNTH_INFO:
+ {
+ struct synth_info inf;
+ int dev;
- return (*(int *) arg = HZ);
- break;
+ memcpy((char *) &inf, (&((char *) arg)[0]), sizeof(inf));
+ dev = inf.device;
- case SNDCTL_SEQ_RESETSAMPLES:
- case SNDCTL_SYNTH_REMOVESAMPLE:
- case SNDCTL_SYNTH_CONTROL:
- {
- int err;
+ if (dev < 0 || dev >= max_synthdev)
+ return -ENXIO;
- dev = *(int *) arg;
- if (dev < 0 || dev >= num_synths)
- {
- return -ENXIO;
- }
+ if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+ return -EBUSY;
- if (!(synth_open_mask & (1 << dev)) && !orig_dev)
- {
- return -EBUSY;
- }
+ return synth_devs[dev]->ioctl(dev, cmd, arg);
+ }
+ break;
- err = synth_devs[dev]->ioctl (dev, cmd, arg);
- return err;
- }
- break;
- case SNDCTL_SEQ_NRSYNTHS:
- return (*(int *) arg = max_synthdev);
- break;
+ /* Like SYNTH_INFO but returns ID in the name field */
+ case SNDCTL_SYNTH_ID:
+ {
+ struct synth_info inf;
+ int dev;
- case SNDCTL_SEQ_NRMIDIS:
- return (*(int *) arg = max_mididev);
- break;
+ memcpy((char *) &inf, (&((char *) arg)[0]), sizeof(inf));
+ dev = inf.device;
- case SNDCTL_SYNTH_MEMAVL:
- {
- int dev;
+ if (dev < 0 || dev >= max_synthdev)
+ return -ENXIO;
- dev = *(int *) arg;
+ if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+ return -EBUSY;
- if (dev < 0 || dev >= num_synths)
- return -ENXIO;
+ memcpy((char *) &inf, (char *) synth_devs[dev]->info, sizeof(inf));
+ strcpy(inf.name, synth_devs[dev]->id);
+ inf.device = dev;
+ memcpy((&((char *) arg)[0]), (char *) &inf, sizeof(inf));
+ return 0;
+ }
+ break;
- if (!(synth_open_mask & (1 << dev)) && !orig_dev)
- return -EBUSY;
+ case SNDCTL_SEQ_OUTOFBAND:
+ {
+ struct seq_event_rec event_rec;
+ unsigned long flags;
- return (*(int *) arg = synth_devs[dev]->ioctl (dev, cmd, arg));
- }
- break;
+ memcpy((char *) &event_rec, (&((char *) arg)[0]), sizeof(event_rec));
- case SNDCTL_FM_4OP_ENABLE:
- {
- int dev;
+ save_flags(flags);
+ cli();
+ play_event(event_rec.arr);
+ restore_flags(flags);
- dev = *(int *) arg;
+ return 0;
+ }
+ break;
- if (dev < 0 || dev >= num_synths)
- return -ENXIO;
+ case SNDCTL_MIDI_INFO:
+ {
+ struct midi_info inf;
+ int dev;
+ char *pp;
- if (!(synth_open_mask & (1 << dev)))
- return -ENXIO;
+ memcpy((char *) &inf, (&((char *) arg)[0]), sizeof(inf));
+ dev = inf.device;
- synth_devs[dev]->ioctl (dev, cmd, arg);
- return 0;
- }
- break;
+ if (dev < 0 || dev >= max_mididev)
+ return -ENXIO;
- case SNDCTL_SYNTH_INFO:
- {
- struct synth_info inf;
- int dev;
+ midi_devs[dev]->info.device = dev;
+ pp = (char *) &midi_devs[dev]->info;
+ memcpy((&((char *) arg)[0]), pp, sizeof(inf));
+ return 0;
+ }
+ break;
- memcpy ((char *) &inf, (&((char *) arg)[0]), sizeof (inf));
- dev = inf.device;
+ case SNDCTL_SEQ_THRESHOLD:
+ {
+ int tmp;
- if (dev < 0 || dev >= max_synthdev)
- return -ENXIO;
+ tmp = *(int *) arg;
- if (!(synth_open_mask & (1 << dev)) && !orig_dev)
- return -EBUSY;
+ if (tmp < 1)
+ tmp = 1;
+ if (tmp >= SEQ_MAX_QUEUE)
+ tmp = SEQ_MAX_QUEUE - 1;
+ output_threshold = tmp;
+ return 0;
+ }
+ break;
- return synth_devs[dev]->ioctl (dev, cmd, arg);
- }
- break;
+ case SNDCTL_MIDI_PRETIME:
+ {
+ int val;
+ val = *(int *) arg;
- /* Like SYNTH_INFO but returns ID in the name field */
- case SNDCTL_SYNTH_ID:
- {
- struct synth_info inf;
- int dev;
+ if (val < 0)
+ val = 0;
- memcpy ((char *) &inf, (&((char *) arg)[0]), sizeof (inf));
- dev = inf.device;
+ val = (HZ * val) / 10;
+ pre_event_timeout = val;
+ return (*(int *) arg = val);
+ }
+ break;
- if (dev < 0 || dev >= max_synthdev)
- return -ENXIO;
+ default:
+ if (mode == OPEN_READ)
+ return -EIO;
- if (!(synth_open_mask & (1 << dev)) && !orig_dev)
- return -EBUSY;
+ if (!synth_devs[0])
+ return -ENXIO;
+ if (!(synth_open_mask & (1 << 0)))
+ return -ENXIO;
+ return synth_devs[0]->ioctl(0, cmd, arg);
+ break;
+ }
- memcpy ((char *) &inf, (char *) synth_devs[dev]->info, sizeof (inf));
- strcpy (inf.name, synth_devs[dev]->id);
- memcpy ((&((char *) arg)[0]), (char *) &inf, sizeof (inf));
- return 0;
- }
- break;
+ return -EINVAL;
+}
- case SNDCTL_SEQ_OUTOFBAND:
- {
- struct seq_event_rec event_rec;
+int
+sequencer_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait)
+{
unsigned long flags;
- memcpy ((char *) &event_rec, (&((char *) arg)[0]), sizeof (event_rec));
-
- save_flags (flags);
- cli ();
- play_event (event_rec.arr);
- restore_flags (flags);
-
- return 0;
- }
- break;
-
- case SNDCTL_MIDI_INFO:
- {
- struct midi_info inf;
- int dev;
- char *pp;
+ dev = dev >> 4;
- memcpy ((char *) &inf, (&((char *) arg)[0]), sizeof (inf));
- dev = inf.device;
-
- if (dev < 0 || dev >= max_mididev)
- return -ENXIO;
-
- pp = (char *) &midi_devs[dev]->info;
- memcpy ((&((char *) arg)[0]), pp, sizeof (inf));
- return 0;
- }
- break;
-
- case SNDCTL_SEQ_THRESHOLD:
- {
- int tmp;
-
- tmp = *(int *) arg;
+ switch (sel_type)
+ {
+ case SEL_IN:
+ save_flags(flags);
+ cli();
+ if (!iqlen)
+ {
+
+ midi_sleep_flag.opts = WK_SLEEP;
+ poll_wait(&midi_sleeper, wait);
+ restore_flags(flags);
+ return 0;
+ }
+ restore_flags(flags);
+ return 1;
+ break;
+
+ case SEL_OUT:
+ save_flags(flags);
+ cli();
+ if ((SEQ_MAX_QUEUE - qlen) < output_threshold)
+ {
+
+ seq_sleep_flag.opts = WK_SLEEP;
+ poll_wait(&seq_sleeper, wait);
+ restore_flags(flags);
+ return 0;
+ }
+ restore_flags(flags);
+ return 1;
+ break;
+
+ case SEL_EX:
+ return 0;
+ }
- if (tmp < 1)
- tmp = 1;
- if (tmp >= SEQ_MAX_QUEUE)
- tmp = SEQ_MAX_QUEUE - 1;
- output_threshold = tmp;
return 0;
- }
- break;
-
- case SNDCTL_MIDI_PRETIME:
- {
- int val;
-
- val = *(int *) arg;
-
- if (val < 0)
- val = 0;
-
- val = (HZ * val) / 10;
- pre_event_timeout = val;
- return (*(int *) arg = val);
- }
- break;
-
- default:
- if (mode == OPEN_READ)
- return -EIO;
+}
- if (!synth_devs[0])
- return -ENXIO;
- if (!(synth_open_mask & (1 << 0)))
- return -ENXIO;
- return synth_devs[0]->ioctl (0, cmd, arg);
- break;
- }
- return -EINVAL;
+void
+sequencer_timer(unsigned long dummy)
+{
+ seq_startplay();
}
int
-sequencer_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait)
+note_to_freq(int note_num)
{
- unsigned long flags;
- dev = dev >> 4;
+ /*
+ * This routine converts a midi note to a frequency (multiplied by 1000)
+ */
- switch (sel_type)
- {
- case SEL_IN:
- save_flags (flags);
- cli ();
- if (!iqlen)
+ int note, octave, note_freq;
+ static int notes[] =
{
+ 261632, 277189, 293671, 311132, 329632, 349232,
+ 369998, 391998, 415306, 440000, 466162, 493880
+ };
- midi_sleep_flag.opts = WK_SLEEP;
- poll_wait (&midi_sleeper, wait);
- restore_flags (flags);
- return 0;
- }
- restore_flags (flags);
- return 1;
- break;
-
- case SEL_OUT:
- save_flags (flags);
- cli ();
- if ((SEQ_MAX_QUEUE - qlen) < output_threshold)
- {
+#define BASE_OCTAVE 5
- seq_sleep_flag.opts = WK_SLEEP;
- poll_wait (&seq_sleeper, wait);
- restore_flags (flags);
- return 0;
- }
- restore_flags (flags);
- return 1;
- break;
+ octave = note_num / 12;
+ note = note_num % 12;
- case SEL_EX:
- return 0;
- }
+ note_freq = notes[note];
- return 0;
-}
+ if (octave < BASE_OCTAVE)
+ note_freq >>= (BASE_OCTAVE - octave);
+ else if (octave > BASE_OCTAVE)
+ note_freq <<= (octave - BASE_OCTAVE);
+ /*
+ * note_freq >>= 1;
+ */
-void
-sequencer_timer (unsigned long dummy)
-{
- seq_startplay ();
+ return note_freq;
}
-int
-note_to_freq (int note_num)
+unsigned long
+compute_finetune(unsigned long base_freq, int bend, int range,
+ int vibrato_cents)
{
+ unsigned long amount;
+ int negative, semitones, cents, multiplier = 1;
- /*
- * This routine converts a midi note to a frequency (multiplied by 1000)
- */
+ if (!bend)
+ return base_freq;
+ if (!range)
+ return base_freq;
- int note, octave, note_freq;
- static int notes[] =
- {
- 261632, 277189, 293671, 311132, 329632, 349232,
- 369998, 391998, 415306, 440000, 466162, 493880
- };
+ if (!base_freq)
+ return base_freq;
-#define BASE_OCTAVE 5
+ if (range >= 8192)
+ range = 8192;
- octave = note_num / 12;
- note = note_num % 12;
+ bend = bend * range / 8192; /* Convert to cents */
+ bend += vibrato_cents;
- note_freq = notes[note];
+ if (!bend)
+ return base_freq;
- if (octave < BASE_OCTAVE)
- note_freq >>= (BASE_OCTAVE - octave);
- else if (octave > BASE_OCTAVE)
- note_freq <<= (octave - BASE_OCTAVE);
+ negative = bend < 0 ? 1 : 0;
- /*
- * note_freq >>= 1;
- */
+ if (bend < 0)
+ bend *= -1;
+ if (bend > range)
+ bend = range;
- return note_freq;
-}
+ /*
+ if (bend > 2399)
+ bend = 2399;
+ */
+ while (bend > 2399)
+ {
+ multiplier *= 4;
+ bend -= 2400;
+ }
-unsigned long
-compute_finetune (unsigned long base_freq, int bend, int range,
- int vibrato_cents)
-{
- unsigned long amount;
- int negative, semitones, cents, multiplier = 1;
-
- if (!bend)
- return base_freq;
- if (!range)
- return base_freq;
-
- if (!base_freq)
- return base_freq;
-
- if (range >= 8192)
- range = 8192;
-
- bend = bend * range / 8192; /* Convert to cents */
- bend += vibrato_cents;
-
- if (!bend)
- return base_freq;
-
- negative = bend < 0 ? 1 : 0;
-
- if (bend < 0)
- bend *= -1;
- if (bend > range)
- bend = range;
-
- /*
- if (bend > 2399)
- bend = 2399;
- */
- while (bend > 2399)
- {
- multiplier *= 4;
- bend -= 2400;
- }
-
- semitones = bend / 100;
- if (semitones > 99)
- semitones = 99;
- cents = bend % 100;
-
- amount = (int) (semitone_tuning[semitones] * multiplier * cent_tuning[cents])
- / 10000;
-
- if (negative)
- return (base_freq * 10000) / amount; /* Bend down */
- else
- return (base_freq * amount) / 10000; /* Bend up */
+ semitones = bend / 100;
+ if (semitones > 99)
+ semitones = 99;
+ cents = bend % 100;
+
+ amount = (int) (semitone_tuning[semitones] * multiplier * cent_tuning[cents])
+ / 10000;
+
+ if (negative)
+ return (base_freq * 10000) / amount; /* Bend down */
+ else
+ return (base_freq * amount) / 10000; /* Bend up */
}
void
-sequencer_init (void)
+sequencer_init(void)
{
- if (sequencer_ok)
- return;
+ if (sequencer_ok)
+ return;
#ifdef CONFIG_MIDI
- MIDIbuf_init ();
+ MIDIbuf_init();
#endif
- queue = (unsigned char *) (sound_mem_blocks[sound_nblocks] = vmalloc (SEQ_MAX_QUEUE * EV_SZ));
- sound_mem_sizes[sound_nblocks] = SEQ_MAX_QUEUE * EV_SZ;
- if (sound_nblocks < 1024)
- sound_nblocks++;;
- if (queue == NULL)
- {
- printk ("Sound: Can't allocate memory for sequencer output queue\n");
- return;
- }
-
-
- iqueue = (unsigned char *) (sound_mem_blocks[sound_nblocks] = vmalloc (SEQ_MAX_QUEUE * IEV_SZ));
- sound_mem_sizes[sound_nblocks] = SEQ_MAX_QUEUE * IEV_SZ;
- if (sound_nblocks < 1024)
- sound_nblocks++;;
- if (iqueue == NULL)
- {
- printk ("Sound: Can't allocate memory for sequencer input queue\n");
- return;
- }
-
-
- sequencer_ok = 1;
+ queue = (unsigned char *) (sound_mem_blocks[sound_nblocks] = vmalloc(SEQ_MAX_QUEUE * EV_SZ));
+ sound_mem_sizes[sound_nblocks] = SEQ_MAX_QUEUE * EV_SZ;
+ if (sound_nblocks < 1024)
+ sound_nblocks++;;
+ if (queue == NULL)
+ {
+ printk("Sound: Can't allocate memory for sequencer output queue\n");
+ return;
+ }
+ iqueue = (unsigned char *) (sound_mem_blocks[sound_nblocks] = vmalloc(SEQ_MAX_QUEUE * IEV_SZ));
+ sound_mem_sizes[sound_nblocks] = SEQ_MAX_QUEUE * IEV_SZ;
+ if (sound_nblocks < 1024)
+ sound_nblocks++;;
+ if (iqueue == NULL)
+ {
+ printk("Sound: Can't allocate memory for sequencer input queue\n");
+ return;
+ }
+ sequencer_ok = 1;
}
#endif
diff --git a/drivers/sound/softoss.c b/drivers/sound/softoss.c
new file mode 100644
index 000000000..a47133a8a
--- /dev/null
+++ b/drivers/sound/softoss.c
@@ -0,0 +1,1580 @@
+/*
+ * sound/softoss.c
+ *
+ * Software based MIDI synthsesizer driver.
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+
+/*
+ * When POLLED_MODE is defined, the resampling loop is run using a timer
+ * callback routine. Normally the resampling loop is executed inside
+ * audio buffer interrupt handler which doesn't work with single mode DMA.
+ */
+#define SOFTSYN_MAIN
+#undef POLLED_MODE
+#define HANDLE_LFO
+
+#define ENVELOPE_SCALE 8
+#define NO_SAMPLE 0xffff
+
+#include "sound_config.h"
+#include "soundmodule.h"
+
+#if defined(CONFIG_SOFTOSS) || defined(MODULE)
+#include "softoss.h"
+#include <linux/ultrasound.h>
+
+int softsynth_disabled = 0;
+
+static volatile int intr_pending = 0;
+
+#ifdef POLLED_MODE
+
+static struct timer_list poll_timer =
+{NULL, NULL, 0, 0, softsyn_poll};
+
+#else
+#endif
+
+#ifdef HANDLE_LFO
+/*
+ * LFO table. Playback at 128 Hz gives 1 Hz LFO frequency.
+ */
+static int tremolo_table[128] =
+{
+ 0, 39, 158, 355, 630, 982, 1411, 1915,
+ 2494, 3146, 3869, 4662, 5522, 6448, 7438, 8489,
+ 9598, 10762, 11980, 13248, 14563, 15922, 17321, 18758,
+ 20228, 21729, 23256, 24806, 26375, 27960, 29556, 31160,
+ 32768, 34376, 35980, 37576, 39161, 40730, 42280, 43807,
+ 45308, 46778, 48215, 49614, 50973, 52288, 53556, 54774,
+ 55938, 57047, 58098, 59088, 60014, 60874, 61667, 62390,
+ 63042, 63621, 64125, 64554, 64906, 65181, 65378, 65497,
+ 65536, 65497, 65378, 65181, 64906, 64554, 64125, 63621,
+ 63042, 62390, 61667, 60874, 60014, 59087, 58098, 57047,
+ 55938, 54774, 53556, 52288, 50973, 49614, 48215, 46778,
+ 45308, 43807, 42280, 40730, 39161, 37576, 35980, 34376,
+ 32768, 31160, 29556, 27960, 26375, 24806, 23256, 21729,
+ 20228, 18758, 17321, 15922, 14563, 13248, 11980, 10762,
+ 9598, 8489, 7438, 6448, 5522, 4662, 3869, 3146,
+ 2494, 1915, 1411, 982, 630, 355, 158, 39
+};
+
+static int vibrato_table[128] =
+{
+ 0, 1608, 3212, 4808, 6393, 7962, 9512, 11039,
+ 12540, 14010, 15447, 16846, 18205, 19520, 20788, 22006,
+ 23170, 24279, 25330, 26320, 27246, 28106, 28899, 29622,
+ 30274, 30853, 31357, 31786, 32138, 32413, 32610, 32729,
+ 32768, 32729, 32610, 32413, 32138, 31786, 31357, 30853,
+ 30274, 29622, 28899, 28106, 27246, 26320, 25330, 24279,
+ 23170, 22006, 20788, 19520, 18205, 16846, 15447, 14010,
+ 12540, 11039, 9512, 7962, 6393, 4808, 3212, 1608,
+ 0, -1608, -3212, -4808, -6393, -7962, -9512, -11039,
+ -12540, -14010, -15447, -16846, -18205, -19520, -20788, -22006,
+ -23170, -24279, -25330, -26320, -27246, -28106, -28899, -29622,
+ -30274, -30853, -31357, -31786, -32138, -32413, -32610, -32729,
+ -32768, -32729, -32610, -32413, -32138, -31786, -31357, -30853,
+ -30274, -29622, -28899, -28106, -27246, -26320, -25330, -24279,
+ -23170, -22006, -20788, -19520, -18205, -16846, -15447, -14010,
+ -12540, -11039, -9512, -7962, -6393, -4808, -3212, -1608
+};
+
+#endif
+
+static unsigned long last_resample_jiffies;
+static unsigned long resample_counter;
+
+extern int *sound_osp;
+
+static volatile int is_running = 0;
+static int softsynth_loaded = 0;
+
+static struct synth_info softsyn_info =
+{"SoftOSS", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, 0, 16, 0, MAX_PATCH};
+
+static struct softsyn_devc sdev_info =
+{0};
+softsyn_devc *devc = &sdev_info;
+
+static struct voice_alloc_info *voice_alloc;
+
+static int softsyn_open(int synthdev, int mode);
+static void init_voice(softsyn_devc * devc, int voice);
+static void compute_step(int voice);
+
+static volatile int tmr_running = 0;
+static int voice_limit = 24;
+
+static void
+set_max_voices(int nr)
+{
+ int i;
+
+ if (nr < 4)
+ nr = 4;
+
+ if (nr > voice_limit)
+ nr = voice_limit;
+
+ voice_alloc->max_voice = devc->maxvoice = nr;
+ devc->afterscale = 5;
+
+ for (i = 31; i > 0; i--)
+ if (nr & (1 << i))
+ {
+ devc->afterscale = i + 1;
+ return;
+ }
+}
+
+static void
+update_vibrato(int voice)
+{
+ voice_info *v = &softoss_voices[voice];
+
+#ifdef HANDLE_LFO
+ int x;
+
+ x = vibrato_table[v->vibrato_phase >> 8];
+ v->vibrato_phase = (v->vibrato_phase + v->vibrato_step) & 0x7fff;
+
+ x = (x * v->vibrato_depth) >> 15;
+ v->vibrato_level = (x * 600) >> 8;
+
+ compute_step(voice);
+#else
+ v->vibrato_level = 0;
+#endif
+}
+
+#ifdef HANDLE_LFO
+static void
+update_tremolo(int voice)
+{
+ voice_info *v = &softoss_voices[voice];
+ int x;
+
+ x = tremolo_table[v->tremolo_phase >> 8];
+ v->tremolo_phase = (v->tremolo_phase + v->tremolo_step) & 0x7fff;
+
+ v->tremolo_level = (x * v->tremolo_depth) >> 20;
+}
+#endif
+
+static void
+start_vibrato(int voice)
+{
+ voice_info *v = &softoss_voices[voice];
+ int rate;
+
+ if (!v->vibrato_depth)
+ return;
+
+ rate = v->vibrato_rate * 6 * 128;
+ v->vibrato_step = (rate * devc->control_rate) / devc->speed;
+
+ devc->vibratomap |= (1 << voice); /* Enable vibrato */
+}
+
+static void
+start_tremolo(int voice)
+{
+ voice_info *v = &softoss_voices[voice];
+ int rate;
+
+ if (!v->tremolo_depth)
+ return;
+
+ rate = v->tremolo_rate * 6 * 128;
+ v->tremolo_step = (rate * devc->control_rate) / devc->speed;
+
+ devc->tremolomap |= (1 << voice); /* Enable tremolo */
+}
+
+static void
+update_volume(int voice)
+{
+ voice_info *v = &softoss_voices[voice];
+ unsigned int vol;
+
+/*
+ * Compute plain volume
+ */
+
+ vol = (v->velocity * v->expression_vol * v->main_vol) >> 12;
+
+#ifdef HANDLE_LFO
+/*
+ * Handle LFO
+ */
+
+ if (devc->tremolomap & (1 << voice))
+ {
+ int t;
+
+ t = 32768 - v->tremolo_level;
+ vol = (vol * t) >> 15;
+ update_tremolo(voice);
+ }
+#endif
+/*
+ * Envelope
+ */
+ if (v->mode & WAVE_ENVELOPES && !v->percussive_voice)
+ vol = (vol * (v->envelope_vol >> 16)) >> 19;
+ else
+ vol >>= 4;
+
+/*
+ * Handle panning
+ */
+
+ if (v->panning < 0) /* Pan left */
+ v->rightvol = (vol * (128 + v->panning)) / 128;
+ else
+ v->rightvol = vol;
+
+ if (v->panning > 0) /* Pan right */
+ v->leftvol = (vol * (128 - v->panning)) / 128;
+ else
+ v->leftvol = vol;
+}
+
+static void
+step_envelope(int voice, int do_release, int velocity)
+{
+ voice_info *v = &softoss_voices[voice];
+ int r, rate, time, dif;
+ unsigned int vol;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ if (!voice_active[voice] || v->sample == NULL)
+ {
+ restore_flags(flags);
+ return;
+ }
+ if (!do_release)
+ if (v->mode & WAVE_SUSTAIN_ON && v->envelope_phase == 2)
+ { /* Stop envelope until note off */
+ v->envelope_volstep = 0;
+ v->envelope_time = 0x7fffffff;
+ if (v->mode & WAVE_VIBRATO)
+ start_vibrato(voice);
+ if (v->mode & WAVE_TREMOLO)
+ start_tremolo(voice);
+ restore_flags(flags);
+ return;
+ }
+ if (do_release)
+ v->envelope_phase = 3;
+ else
+ v->envelope_phase++;
+
+ if (v->envelope_phase >= 5) /* Finished */
+ {
+ init_voice(devc, voice);
+ restore_flags(flags);
+ return;
+ }
+ vol = v->envelope_target = v->sample->env_offset[v->envelope_phase] << 22;
+
+
+ rate = v->sample->env_rate[v->envelope_phase];
+ r = 3 - ((rate >> 6) & 0x3);
+ r *= 3;
+ r = (int) (rate & 0x3f) << r;
+ rate = (((r * 44100) / devc->speed) * devc->control_rate) << 8;
+
+ if (rate < (1 << 20)) /* Avoid infinitely "releasing" voices */
+ rate = 1 << 20;
+
+ dif = (v->envelope_vol - vol);
+ if (dif < 0)
+ dif *= -1;
+ if (dif < rate * 2) /* Too close */
+ {
+ step_envelope(voice, 0, 60);
+ restore_flags(flags);
+ return;
+ }
+ if (vol > v->envelope_vol)
+ {
+ v->envelope_volstep = rate;
+ time = (vol - v->envelope_vol) / rate;
+ } else
+ {
+ v->envelope_volstep = -rate;
+ time = (v->envelope_vol - vol) / rate;
+ }
+
+ time--;
+ if (time <= 0)
+ time = 1;
+
+ v->envelope_time = time;
+
+
+ restore_flags(flags);
+}
+
+static void
+step_envelope_lfo(int voice)
+{
+ voice_info *v = &softoss_voices[voice];
+
+/*
+ * Update pitch (vibrato) LFO
+ */
+
+ if (devc->vibratomap & (1 << voice))
+ update_vibrato(voice);
+
+/*
+ * Update envelope
+ */
+
+ if (v->mode & WAVE_ENVELOPES)
+ {
+ v->envelope_vol += v->envelope_volstep;
+ /* Overshoot protection */
+ if (v->envelope_vol < 0)
+ {
+ v->envelope_vol = v->envelope_target;
+ v->envelope_volstep = 0;
+ }
+ if (v->envelope_time-- <= 0)
+ {
+ v->envelope_vol = v->envelope_target;
+ step_envelope(voice, 0, 60);
+ }
+ }
+}
+
+static void
+compute_step(int voice)
+{
+ voice_info *v = &softoss_voices[voice];
+
+ /*
+ * Since the pitch bender may have been set before playing the note, we
+ * have to calculate the bending now.
+ */
+
+ v->current_freq = compute_finetune(v->orig_freq,
+ v->bender,
+ v->bender_range,
+ v->vibrato_level);
+ v->step = (((v->current_freq << 9) + (devc->speed >> 1)) / devc->speed);
+
+ if (v->mode & WAVE_LOOP_BACK)
+ v->step *= -1; /* Reversed playback */
+}
+
+static void
+init_voice(softsyn_devc * devc, int voice)
+{
+ voice_info *v = &softoss_voices[voice];
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ voice_active[voice] = 0;
+ devc->vibratomap &= ~(1 << voice);
+ devc->tremolomap &= ~(1 << voice);
+ v->mode = 0;
+ v->wave = NULL;
+ v->sample = NULL;
+ v->ptr = 0;
+ v->step = 0;
+ v->startloop = 0;
+ v->startbackloop = 0;
+ v->endloop = 0;
+ v->looplen = 0;
+ v->bender = 0;
+ v->bender_range = 200;
+ v->panning = 0;
+ v->main_vol = 127;
+ v->expression_vol = 127;
+ v->patch_vol = 127;
+ v->percussive_voice = 0;
+ v->sustain_mode = 0;
+ v->envelope_phase = 1;
+ v->envelope_vol = 1 << 24;
+ v->envelope_volstep = 256;
+ v->envelope_time = 0;
+ v->vibrato_phase = 0;
+ v->vibrato_step = 0;
+ v->vibrato_level = 0;
+ v->vibrato_rate = 0;
+ v->vibrato_depth = 0;
+ v->tremolo_phase = 0;
+ v->tremolo_step = 0;
+ v->tremolo_level = 0;
+ v->tremolo_rate = 0;
+ v->tremolo_depth = 0;
+ voice_alloc->map[voice] = 0;
+ voice_alloc->alloc_times[voice] = 0;
+ restore_flags(flags);
+}
+
+static void
+reset_samples(softsyn_devc * devc)
+{
+ int i;
+
+ for (i = 0; i < MAX_VOICE; i++)
+ voice_active[i] = 0;
+ for (i = 0; i < devc->maxvoice; i++)
+ {
+ init_voice(devc, i);
+ softoss_voices[i].instr = 0;
+ }
+
+ devc->ram_used = 0;
+
+ for (i = 0; i < MAX_PATCH; i++)
+ devc->programs[i] = NO_SAMPLE;
+
+ for (i = 0; i < devc->nrsamples; i++)
+ {
+ vfree(devc->samples[i]);
+ vfree(devc->wave[i]);
+ devc->samples[i] = NULL;
+ devc->wave[i] = NULL;
+ }
+
+ devc->nrsamples = 0;
+}
+
+static void
+init_engine(softsyn_devc * devc)
+{
+ int i, fz, srate, sz = devc->channels;
+
+ set_max_voices(devc->default_max_voices);
+ voice_alloc->timestamp = 0;
+
+ if (devc->bits == 16)
+ sz *= 2;
+
+ fz = devc->fragsize / sz; /* Samples per fragment */
+ devc->samples_per_fragment = fz;
+
+ devc->usecs = 0;
+ devc->usecs_per_frag = (1000000 * fz) / devc->speed;
+
+ for (i = 0; i < devc->maxvoice; i++)
+ {
+ init_voice(devc, i);
+ softoss_voices[i].instr = 0;
+ }
+
+ devc->engine_state = ES_STOPPED;
+
+/*
+ * Initialize delay
+ */
+
+ for (i = 0; i < DELAY_SIZE; i++)
+ left_delay[i] = right_delay[i] = 0;
+ delayp = 0;
+ srate = (devc->speed / 10000); /* 1 to 4 */
+ if (srate <= 0)
+ srate = 1;
+ devc->delay_size = (DELAY_SIZE * srate) / 4;
+ if (devc->delay_size == 0 || devc->delay_size > DELAY_SIZE)
+ devc->delay_size = DELAY_SIZE;
+}
+
+void
+softsyn_control_loop(void)
+{
+ int voice;
+
+/*
+ * Recompute envlope, LFO, etc.
+ */
+ for (voice = 0; voice < devc->maxvoice; voice++)
+ if (voice_active[voice])
+ {
+ update_volume(voice);
+ step_envelope_lfo(voice);
+ } else
+ voice_alloc->map[voice] = 0;
+}
+
+static void start_engine(softsyn_devc * devc);
+
+static void
+do_resample(int dummy)
+{
+ struct dma_buffparms *dmap = audio_devs[devc->audiodev]->dmap_out;
+ struct voice_info *vinfo;
+ unsigned long flags, jif;
+
+ int voice, loops;
+ short *buf;
+
+ if (softsynth_disabled)
+ return;
+
+ save_flags(flags);
+ cli();
+
+ if (is_running)
+ {
+ printk("SoftOSS: Playback overrun\n");
+ restore_flags(flags);
+ return;
+ }
+ jif = jiffies;
+ if (jif == last_resample_jiffies)
+ {
+ if (resample_counter++ > 50)
+ {
+ for (voice = 0; voice < devc->maxvoice; voice++)
+ init_voice(devc, voice);
+ voice_limit--;
+ resample_counter = 0;
+ printk("SoftOSS: CPU overload. Limiting # of voices to %d\n", voice_limit);
+
+ if (voice_limit < 10)
+ {
+ voice_limit = 10;
+ devc->speed = (devc->speed * 2) / 3;
+
+ printk("SoftOSS: Dropping sampling rate and stopping the device.\n");
+ softsynth_disabled = 1;
+ }
+ }
+ } else
+ {
+ last_resample_jiffies = jif;
+ resample_counter = 0;
+ }
+
+ /* is_running = 1; */
+
+ if (dmap->qlen > devc->max_playahead)
+ {
+ printk("SoftOSS: audio buffers full\n");
+ is_running = 0;
+ restore_flags(flags);
+ return;
+ }
+/*
+ * First verify that all active voices are valid (do this just once per block).
+ */
+ for (voice = 0; voice < devc->maxvoice; voice++)
+ if (voice_active[voice])
+ {
+ int ptr;
+
+ vinfo = &softoss_voices[voice];
+ ptr = vinfo->ptr >> 9;
+
+ if (vinfo->wave == NULL ||
+ ptr < 0 ||
+ ptr > vinfo->sample->len)
+ init_voice(devc, voice);
+ else if (!(vinfo->mode & WAVE_LOOPING) &&
+ (vinfo->ptr + vinfo->step) > vinfo->endloop)
+ voice_active[voice] = 0;
+ }
+/*
+ * Start the resampling process
+ */
+
+ loops = devc->samples_per_fragment;
+ buf = (short *) (dmap->raw_buf + (dmap->qtail * dmap->fragment_size));
+
+ softsynth_resample_loop(buf, loops); /* In Xsoftsynth_rs.c */
+
+ dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+ dmap->qlen++;
+ dmap->user_counter += dmap->fragment_size;
+
+ devc->usecs += devc->usecs_per_frag;
+
+ if (tmr_running)
+ {
+ sound_timer_interrupt();
+ }
+/*
+ * Execute timer
+ */
+
+ if (!tmr_running)
+ if (devc->usecs >= devc->next_event_usecs)
+ {
+ devc->next_event_usecs = ~0;
+ sequencer_timer(0);
+ }
+ is_running = 0;
+ restore_flags(flags);
+}
+
+static void
+delayed_resample(int dummy)
+{
+ struct dma_buffparms *dmap = audio_devs[devc->audiodev]->dmap_out;
+ int n = 0;
+
+ if (is_running)
+ return;
+
+ while (devc->engine_state != ES_STOPPED &&
+ dmap->qlen < devc->max_playahead && n++ < 2)
+ do_resample(0);
+ intr_pending = 0;
+}
+
+#ifdef POLLED_MODE
+static void
+softsyn_poll(unsigned long dummy)
+{
+ delayed_resample(0);
+
+ if (devc->engine_state != ES_STOPPED)
+
+ {
+ poll_timer.expires = (1) + jiffies;
+ add_timer(&poll_timer);
+ };
+}
+#else
+static void
+softsyn_callback(int dev, int parm)
+{
+ delayed_resample(0);
+}
+#endif
+
+static void
+start_engine(softsyn_devc * devc)
+{
+ struct dma_buffparms *dmap;
+
+ if (!devc->audio_opened)
+ if (softsyn_open(devc->synthdev, 0) < 0)
+ return;
+
+ if (devc->audiodev >= num_audiodevs)
+ return;
+
+ dmap = audio_devs[devc->audiodev]->dmap_out;
+
+ devc->usecs = 0;
+ devc->next_event_usecs = ~0;
+ devc->control_rate = 64;
+ devc->control_counter = 0;
+
+ if (devc->engine_state == ES_STOPPED)
+ {
+ int trig, n = 0;
+
+ trig = 0;
+ dma_ioctl(devc->audiodev, SNDCTL_DSP_SETTRIGGER, (caddr_t) & trig);
+#ifdef POLLED_MODE
+ ;
+
+ {
+ poll_timer.expires = (1) + jiffies;
+ add_timer(&poll_timer);
+ }; /* Start polling */
+#else
+ dmap->audio_callback = softsyn_callback;
+ dmap->qhead = dmap->qtail = dmap->qlen = 0;
+#endif
+
+ while (dmap->qlen < devc->max_playahead && n++ < 2)
+ do_resample(0);
+
+ devc->engine_state = ES_STARTED;
+ last_resample_jiffies = jiffies;
+ resample_counter = 0;
+
+ trig = PCM_ENABLE_OUTPUT;
+ if (dma_ioctl(devc->audiodev, SNDCTL_DSP_SETTRIGGER,
+ (caddr_t) & trig) < 0)
+ {
+ printk("SoftOSS: Trigger failed\n");
+ }
+ }
+}
+
+static void
+stop_engine(softsyn_devc * devc)
+{
+}
+
+static void
+request_engine(softsyn_devc * devc, int ticks)
+{
+ if (ticks < 0) /* Relative time */
+ devc->next_event_usecs = devc->usecs - ticks * (1000000 / HZ);
+ else
+ devc->next_event_usecs = ticks * (1000000 / HZ);
+}
+
+/*
+ * Softsync hook serves mode1 (timing) calls made by sequencer.c
+ */
+static int
+softsynth_hook(int cmd, int parm1, int parm2, unsigned long parm3)
+{
+ switch (cmd)
+ {
+ case SSYN_START:
+ start_engine(devc);
+ break;
+
+ case SSYN_STOP:
+ stop_engine(devc);
+ break;
+
+ case SSYN_REQUEST:
+ request_engine(devc, parm1);
+ break;
+
+ case SSYN_GETTIME:
+ return devc->usecs / (1000000 / HZ);
+ break;
+
+ default:
+ printk("SoftOSS: Unknown request %d\n", cmd);
+ }
+
+ return 0;
+}
+
+static int
+softsyn_ioctl(int dev,
+ unsigned int cmd, caddr_t arg)
+{
+ switch (cmd)
+ {
+
+ case SNDCTL_SYNTH_INFO:
+ softsyn_info.nr_voices = devc->maxvoice;
+
+ memcpy((&((char *) arg)[0]), (char *) &softsyn_info, sizeof(softsyn_info));
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_RESETSAMPLES:
+ stop_engine(devc);
+ reset_samples(devc);
+ return 0;
+ break;
+
+ case SNDCTL_SYNTH_MEMAVL:
+ return devc->ram_size - devc->ram_used;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+}
+
+static int
+softsyn_kill_note(int devno, int voice, int note, int velocity)
+{
+ if (voice < 0 || voice > devc->maxvoice)
+ return 0;
+ voice_alloc->map[voice] = 0xffff; /* Releasing */
+
+ if (softoss_voices[voice].sustain_mode & 1) /* Sustain controller on */
+ {
+ softoss_voices[voice].sustain_mode = 3; /* Note off pending */
+ return 0;
+ }
+ if (velocity > 127 || softoss_voices[voice].mode & WAVE_FAST_RELEASE)
+ {
+ init_voice(devc, voice); /* Mark it inactive */
+ return 0;
+ }
+ if (softoss_voices[voice].mode & WAVE_ENVELOPES)
+ step_envelope(voice, 1, velocity); /* Enter sustain phase */
+ else
+ init_voice(devc, voice); /* Mark it inactive */
+ return 0;
+}
+
+static int
+softsyn_set_instr(int dev, int voice, int instr)
+{
+ if (voice < 0 || voice > devc->maxvoice)
+ return 0;
+
+ if (instr < 0 || instr > MAX_PATCH)
+ {
+ printk("SoftOSS: Invalid instrument number %d\n", instr);
+ return 0;
+ }
+ softoss_voices[voice].instr = instr;
+
+ return 0;
+}
+
+static int
+softsyn_start_note(int dev, int voice, int note, int volume)
+{
+ int instr = 0;
+ int best_sample, best_delta, delta_freq, selected;
+ unsigned long note_freq, freq, base_note, flags;
+ voice_info *v = &softoss_voices[voice];
+
+ struct patch_info *sample;
+
+ if (voice < 0 || voice > devc->maxvoice)
+ return 0;
+
+ if (volume == 0) /* Actually note off */
+ softsyn_kill_note(dev, voice, note, volume);
+
+ save_flags(flags);
+ cli();
+
+ if (note == 255)
+ { /* Just volume update */
+ v->velocity = volume;
+ if (voice_active[voice])
+ update_volume(voice);
+ restore_flags(flags);
+ return 0;
+ }
+ voice_active[voice] = 0; /* Stop the voice for a while */
+ devc->vibratomap &= ~(1 << voice);
+ devc->tremolomap &= ~(1 << voice);
+
+ instr = v->instr;
+ if (instr < 0 || instr > MAX_PATCH || devc->programs[instr] == NO_SAMPLE)
+ {
+ printk("SoftOSS: Undefined MIDI instrument %d\n", instr);
+ restore_flags(flags);
+ return 0;
+ }
+ instr = devc->programs[instr];
+
+ if (instr < 0 || instr >= devc->nrsamples)
+ {
+ printk("SoftOSS: Corrupted MIDI instrument %d (%d)\n", v->instr, instr);
+ restore_flags(flags);
+ return 0;
+ }
+ note_freq = note_to_freq(note);
+
+ selected = -1;
+
+ best_sample = instr;
+ best_delta = 1000000;
+
+ while (instr != NO_SAMPLE && instr >= 0 && selected == -1)
+ {
+ delta_freq = note_freq - devc->samples[instr]->base_note;
+
+ if (delta_freq < 0)
+ delta_freq = -delta_freq;
+ if (delta_freq < best_delta)
+ {
+ best_sample = instr;
+ best_delta = delta_freq;
+ }
+ if (devc->samples[instr]->low_note <= note_freq &&
+ note_freq <= devc->samples[instr]->high_note)
+ selected = instr;
+ else
+ instr = devc->samples[instr]->key; /* Link to next sample */
+
+ if (instr < 0 || instr >= devc->nrsamples)
+ instr = NO_SAMPLE;
+ }
+
+ if (selected == -1)
+ instr = best_sample;
+ else
+ instr = selected;
+
+ if (instr < 0 || instr == NO_SAMPLE || instr > devc->nrsamples)
+ {
+ printk("SoftOSS: Unresolved MIDI instrument %d\n", v->instr);
+ restore_flags(flags);
+ return 0;
+ }
+ sample = devc->samples[instr];
+ v->sample = sample;
+
+ if (v->percussive_voice) /* No key tracking */
+ {
+ v->orig_freq = sample->base_freq; /* Fixed pitch */
+ } else
+ {
+ base_note = sample->base_note / 100;
+ note_freq /= 100;
+
+ freq = sample->base_freq * note_freq / base_note;
+ v->orig_freq = freq;
+ }
+
+ if (!(sample->mode & WAVE_LOOPING))
+ {
+ sample->loop_end = sample->len;
+ }
+ v->wave = devc->wave[instr];
+
+ if (volume < 0)
+ volume = 0;
+ else if (volume > 127)
+ volume = 127;
+
+ v->ptr = 0;
+ v->startloop = sample->loop_start * 512;
+ v->startbackloop = 0;
+ v->endloop = sample->loop_end * 512;
+ v->looplen = (sample->loop_end - sample->loop_start) * 512;
+ v->leftvol = 64;
+ v->rightvol = 64;
+ v->patch_vol = sample->volume;
+ v->velocity = volume;
+ v->mode = sample->mode;
+ v->vibrato_phase = 0;
+ v->vibrato_step = 0;
+ v->vibrato_level = 0;
+ v->vibrato_rate = 0;
+ v->vibrato_depth = 0;
+ v->tremolo_phase = 0;
+ v->tremolo_step = 0;
+ v->tremolo_level = 0;
+ v->tremolo_rate = 0;
+ v->tremolo_depth = 0;
+
+ if (!(v->mode & WAVE_LOOPING))
+ v->mode &= ~(WAVE_BIDIR_LOOP | WAVE_LOOP_BACK);
+ else if (v->mode & WAVE_LOOP_BACK)
+ {
+ v->ptr = sample->len;
+ v->startbackloop = v->startloop;
+ }
+ if (v->mode & WAVE_VIBRATO)
+ {
+ v->vibrato_rate = sample->vibrato_rate;
+ v->vibrato_depth = sample->vibrato_depth;
+ }
+ if (v->mode & WAVE_TREMOLO)
+ {
+ v->tremolo_rate = sample->tremolo_rate;
+ v->tremolo_depth = sample->tremolo_depth;
+ }
+ if (v->mode & WAVE_ENVELOPES)
+ {
+ v->envelope_phase = -1;
+ v->envelope_vol = 0;
+ step_envelope(voice, 0, 60);
+ }
+ update_volume(voice);
+ compute_step(voice);
+
+ voice_active[voice] = 1; /* Mark it active */
+
+ restore_flags(flags);
+ return 0;
+}
+
+static int
+softsyn_open(int synthdev, int mode)
+{
+ int err;
+ extern int softoss_dev;
+ int frags = 0x7fff0007; /* fragment size of 128 bytes */
+
+ if (devc->audio_opened) /* Already opened */
+ return 0;
+
+ softsynth_disabled = 0;
+ devc->finfo.mode = OPEN_WRITE;
+ devc->finfo.flags = 0;
+
+ if (softoss_dev >= num_audiodevs)
+ softoss_dev = num_audiodevs - 1;
+
+ if (softoss_dev < 0)
+ softoss_dev = 0;
+ if (softoss_dev >= num_audiodevs)
+ return -ENXIO;
+ devc->audiodev = softoss_dev;
+
+ if (!(audio_devs[devc->audiodev]->format_mask & AFMT_S16_LE))
+ {
+ printk("SoftOSS: The audio device doesn't support 16 bits\n");
+ return -ENXIO;
+ }
+ if ((err = audio_open((devc->audiodev << 4) | SND_DEV_DSP16, &devc->finfo)) < 0)
+ return err;
+
+ devc->speed = audio_devs[devc->audiodev]->d->set_speed(
+ devc->audiodev, devc->speed);
+ devc->channels = audio_devs[devc->audiodev]->d->set_channels(
+ devc->audiodev, devc->channels);
+ devc->bits = audio_devs[devc->audiodev]->d->set_bits(
+ devc->audiodev, devc->bits);
+
+
+ DDB(printk("SoftOSS: Using audio dev %d, speed %d, bits %d, channels %d\n", devc->audiodev, devc->speed, devc->bits, devc->channels));
+
+ dma_ioctl(devc->audiodev, SNDCTL_DSP_SETFRAGMENT, (caddr_t) & frags);
+ dma_ioctl(devc->audiodev, SNDCTL_DSP_GETBLKSIZE, (caddr_t) & devc->fragsize);
+
+ if (devc->bits != 16 || devc->channels != 2)
+ {
+ audio_release((devc->audiodev << 4) | SND_DEV_DSP16, &devc->finfo);
+ printk("SoftOSS: A 16 bit stereo soundcard is required\n");
+ return 0;
+ }
+ if (devc->max_playahead >= audio_devs[devc->audiodev]->dmap_out->nbufs)
+ devc->max_playahead = audio_devs[devc->audiodev]->dmap_out->nbufs;
+
+ DDB(printk("SoftOSS: Using %d fragments of %d bytes\n", devc->max_playahead, devc->fragsize));
+
+ init_engine(devc);
+ devc->audio_opened = 1;
+ devc->sequencer_mode = mode;
+ return 0;
+}
+
+static void
+softsyn_close(int synthdev)
+{
+ devc->engine_state = ES_STOPPED;
+#ifdef POLLED_MODE
+ del_timer(&poll_timer);;
+#endif
+ dma_ioctl(devc->audiodev, SNDCTL_DSP_RESET, 0);
+ if (devc->audio_opened)
+ audio_release((devc->audiodev << 4) | SND_DEV_DSP16, &devc->finfo);
+ devc->audio_opened = 0;
+}
+
+static void
+softsyn_hw_control(int dev, unsigned char *event_rec)
+{
+ int voice, cmd;
+ unsigned short p1, p2;
+ unsigned int plong;
+
+ cmd = event_rec[2];
+ voice = event_rec[3];
+ p1 = *(unsigned short *) &event_rec[4];
+ p2 = *(unsigned short *) &event_rec[6];
+ plong = *(unsigned int *) &event_rec[4];
+
+ switch (cmd)
+ {
+
+ case _GUS_NUMVOICES:
+ set_max_voices(p1);
+ break;
+
+
+ default:;
+ }
+}
+
+static int
+softsyn_load_patch(int dev, int format, const char *addr,
+ int offs, int count, int pmgr_flag)
+{
+ struct patch_info *patch = NULL;
+
+ int i, p, instr;
+ long sizeof_patch;
+ int memlen, adj;
+ unsigned short data;
+ short *wave = NULL;
+
+ sizeof_patch = (long) &patch->data[0] - (long) patch; /* Header size */
+
+ if (format != GUS_PATCH)
+ {
+ printk("SoftOSS: Invalid patch format (key) 0x%x\n", format);
+ return -EINVAL;
+ }
+ if (count < sizeof_patch)
+ {
+ printk("SoftOSS: Patch header too short\n");
+ return -EINVAL;
+ }
+ count -= sizeof_patch;
+
+ if (devc->nrsamples >= MAX_SAMPLE)
+ {
+ printk("SoftOSS: Sample table full\n");
+ return -ENOSPC;
+ }
+ /*
+ * Copy the header from user space but ignore the first bytes which have
+ * been transferred already.
+ */
+
+ patch = vmalloc(sizeof(*patch));
+
+ if (patch == NULL)
+ {
+ printk("SoftOSS: Out of memory\n");
+ return -ENOSPC;
+ }
+ copy_from_user(&((char *) patch)[offs], &(addr)[offs], sizeof_patch - offs);
+
+ if (patch->mode & WAVE_ROM)
+ {
+ vfree(patch);
+ return -EINVAL;
+ }
+ instr = patch->instr_no;
+
+ if (instr < 0 || instr > MAX_PATCH)
+ {
+ printk("SoftOSS: Invalid patch number %d\n", instr);
+ vfree(patch);
+ return -EINVAL;
+ }
+ if (count < patch->len)
+ {
+ printk("SoftOSS: Patch record too short (%d<%d)\n", count, (int) patch->len);
+ patch->len = count;
+ }
+ if (patch->len <= 0 || patch->len > (devc->ram_size - devc->ram_used))
+ {
+ printk("SoftOSS: Invalid sample length %d\n", (int) patch->len);
+ vfree(patch);
+ return -EINVAL;
+ }
+ if (patch->mode & WAVE_LOOPING)
+ {
+ if (patch->loop_start < 0 || patch->loop_start >= patch->len)
+ {
+ printk("SoftOSS: Invalid loop start %d\n", patch->loop_start);
+ vfree(patch);
+ return -EINVAL;
+ }
+ if (patch->loop_end < patch->loop_start || patch->loop_end > patch->len)
+ {
+ printk("SoftOSS: Invalid loop start or end point (%d, %d)\n", patch->loop_start, patch->loop_end);
+ vfree(patch);
+ return -EINVAL;
+ }
+ }
+/*
+ * Next load the wave data to memory
+ */
+
+ memlen = patch->len;
+ adj = 1;
+
+ if (!(patch->mode & WAVE_16_BITS))
+ memlen *= 2;
+ else
+ adj = 2;
+
+ wave = vmalloc(memlen);
+
+ if (wave == NULL)
+ {
+ printk("SoftOSS: Can't allocate %d bytes of mem for a sample\n", memlen);
+ vfree(patch);
+ return -ENOSPC;
+ }
+ p = 0;
+ for (i = 0; i < memlen / 2; i++) /* Handle words */
+ {
+ unsigned char tmp;
+
+ data = 0;
+
+ if (patch->mode & WAVE_16_BITS)
+ {
+ get_user(*(unsigned char *) &tmp, (unsigned char *) &((addr)[sizeof_patch + p++])); /* Get lsb */
+ data = tmp;
+ get_user(*(unsigned char *) &tmp, (unsigned char *) &((addr)[sizeof_patch + p++])); /* Get msb */
+ if (patch->mode & WAVE_UNSIGNED)
+ tmp ^= 0x80; /* Convert to signed */
+ data |= (tmp << 8);
+ } else
+ {
+ get_user(*(unsigned char *) &tmp, (unsigned char *) &((addr)[sizeof_patch + p++]));
+ if (patch->mode & WAVE_UNSIGNED)
+ tmp ^= 0x80; /* Convert to signed */
+ data = (tmp << 8); /* Convert to 16 bits */
+ }
+
+ wave[i] = (short) data;
+ }
+
+ devc->ram_used += patch->len;
+/*
+ * Convert pointers to 16 bit indexes
+ */
+ patch->len /= adj;
+ patch->loop_start /= adj;
+ patch->loop_end /= adj;
+
+/*
+ * Finally link the loaded patch to the chain
+ */
+
+ patch->key = devc->programs[instr];
+ devc->programs[instr] = devc->nrsamples;
+ devc->wave[devc->nrsamples] = (short *) wave;
+ devc->samples[devc->nrsamples++] = patch;
+
+ return 0;
+}
+
+static void
+softsyn_panning(int dev, int voice, int pan)
+{
+ if (voice < 0 || voice > devc->maxvoice)
+ return;
+
+ if (pan < -128)
+ pan = -128;
+ if (pan > 127)
+ pan = 127;
+
+ softoss_voices[voice].panning = pan;
+ if (voice_active[voice])
+ update_volume(voice);
+}
+
+static void
+softsyn_volume_method(int dev, int mode)
+{
+}
+
+static void
+softsyn_aftertouch(int dev, int voice, int pressure)
+{
+ if (voice < 0 || voice > devc->maxvoice)
+ return;
+
+ if (voice_active[voice])
+ update_volume(voice);
+}
+
+static void
+softsyn_controller(int dev, int voice, int ctrl_num, int value)
+{
+ unsigned long flags;
+
+ if (voice < 0 || voice > devc->maxvoice)
+ return;
+ save_flags(flags);
+ cli();
+
+ switch (ctrl_num)
+ {
+ case CTRL_PITCH_BENDER:
+ softoss_voices[voice].bender = value;
+
+ if (voice_active[voice])
+ compute_step(voice); /* Update pitch */
+ break;
+
+
+ case CTRL_PITCH_BENDER_RANGE:
+ softoss_voices[voice].bender_range = value;
+ break;
+ case CTL_EXPRESSION:
+ value /= 128;
+ case CTRL_EXPRESSION:
+ softoss_voices[voice].expression_vol = value;
+ if (voice_active[voice])
+ update_volume(voice);
+ break;
+
+ case CTL_PAN:
+ softsyn_panning(dev, voice, (value * 2) - 128);
+ break;
+
+ case CTL_MAIN_VOLUME:
+ value = (value * 100) / 16383;
+
+ case CTRL_MAIN_VOLUME:
+ softoss_voices[voice].main_vol = value;
+ if (voice_active[voice])
+ update_volume(voice);
+ break;
+
+ default:
+ break;
+ }
+
+ restore_flags(flags);
+}
+
+static void
+softsyn_bender(int dev, int voice, int value)
+{
+ if (voice < 0 || voice > devc->maxvoice)
+ return;
+
+ softoss_voices[voice].bender = value - 8192;
+ if (voice_active[voice])
+ compute_step(voice); /* Update pitch */
+}
+
+static int
+softsyn_alloc_voice(int dev, int chn, int note, struct voice_alloc_info *alloc)
+{
+ int i, p, best = -1, best_time = 0x7fffffff;
+
+ p = alloc->ptr;
+ /*
+ * First look for a completely stopped voice
+ */
+
+ for (i = 0; i < alloc->max_voice; i++)
+ {
+ if (alloc->map[p] == 0)
+ {
+ alloc->ptr = p;
+ voice_active[p] = 0;
+ return p;
+ }
+ if (alloc->alloc_times[p] < best_time)
+ {
+ best = p;
+ best_time = alloc->alloc_times[p];
+ }
+ p = (p + 1) % alloc->max_voice;
+ }
+
+ /*
+ * Then look for a releasing voice
+ */
+
+ for (i = 0; i < alloc->max_voice; i++)
+ {
+ if (alloc->map[p] == 0xffff)
+ {
+ alloc->ptr = p;
+ voice_active[p] = 0;
+ return p;
+ }
+ p = (p + 1) % alloc->max_voice;
+ }
+
+ if (best >= 0)
+ p = best;
+
+ alloc->ptr = p;
+ voice_active[p] = 0;
+ return p;
+}
+
+static void
+softsyn_setup_voice(int dev, int voice, int chn)
+{
+ unsigned long flags;
+
+ struct channel_info *info =
+ &synth_devs[dev]->chn_info[chn];
+
+ save_flags(flags);
+ cli();
+ /* init_voice(devc, voice); */
+ softsyn_set_instr(dev, voice, info->pgm_num);
+
+ softoss_voices[voice].expression_vol =
+ info->controllers[CTL_EXPRESSION]; /* Just MSB */
+ softoss_voices[voice].main_vol =
+ (info->controllers[CTL_MAIN_VOLUME] * 100) / (unsigned) 128;
+ softsyn_panning(dev, voice, (info->controllers[CTL_PAN] * 2) - 128);
+ softoss_voices[voice].bender = 0; /* info->bender_value; */
+ softoss_voices[voice].bender_range = info->bender_range;
+
+ if (chn == 9)
+ softoss_voices[voice].percussive_voice = 1;
+ restore_flags(flags);
+}
+
+static void
+softsyn_reset(int devno)
+{
+ int i;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ for (i = 0; i < devc->maxvoice; i++)
+ init_voice(devc, i);
+ restore_flags(flags);
+}
+
+static struct synth_operations softsyn_operations =
+{
+ "SoftOSS",
+ &softsyn_info,
+ 0,
+ SYNTH_TYPE_SAMPLE,
+ 0,
+ softsyn_open,
+ softsyn_close,
+ softsyn_ioctl,
+ softsyn_kill_note,
+ softsyn_start_note,
+ softsyn_set_instr,
+ softsyn_reset,
+ softsyn_hw_control,
+ softsyn_load_patch,
+ softsyn_aftertouch,
+ softsyn_controller,
+ softsyn_panning,
+ softsyn_volume_method,
+ softsyn_bender,
+ softsyn_alloc_voice,
+ softsyn_setup_voice
+};
+
+/*
+ * Timer stuff (for /dev/music).
+ */
+
+static unsigned int
+soft_tmr_start(int dev, unsigned int usecs)
+{
+ tmr_running = 1;
+ start_engine(devc);
+ return devc->usecs_per_frag;
+}
+
+static void
+soft_tmr_disable(int dev)
+{
+ stop_engine(devc);
+ tmr_running = 0;
+}
+
+static void
+soft_tmr_restart(int dev)
+{
+ tmr_running = 1;
+}
+
+static struct sound_lowlev_timer soft_tmr =
+{
+ 0,
+ 9999,
+ soft_tmr_start,
+ soft_tmr_disable,
+ soft_tmr_restart
+};
+
+int
+probe_softsyn(struct address_info *hw_config)
+{
+ int i;
+
+ if (softsynth_loaded)
+ return 0;
+
+ devc->ram_size = 8 * 1024 * 1024;
+ devc->ram_used = 0;
+ devc->nrsamples = 0;
+ for (i = 0; i < MAX_PATCH; i++)
+ {
+ devc->programs[i] = NO_SAMPLE;
+ devc->wave[i] = NULL;
+ }
+
+ devc->maxvoice = DEFAULT_VOICES;
+
+ devc->audiodev = 0;
+ devc->audio_opened = 0;
+ devc->channels = 2;
+ devc->bits = 16;
+ devc->max_playahead = 32;
+
+#ifdef SOFTOSS_RATE
+ devc->speed = SOFTOSS_RATE;
+#else
+ devc->speed = 32000;
+#endif
+
+#ifdef SOFTOSS_VOICES
+ devc->default_max_voices = SOFTOSS_VOICES;
+#else
+ devc->default_max_voices = 32;
+#endif
+
+ softsynth_loaded = 1;
+ return 1;
+}
+
+void
+attach_softsyn_card(struct address_info *hw_config)
+{
+
+ voice_alloc = &softsyn_operations.alloc;
+ synth_devs[devc->synthdev = num_synths++] = &softsyn_operations;
+ sequencer_init();
+ sound_timer_init(&soft_tmr, "SoftOSS");
+ devc->timerdev = num_sound_timers;
+ softsynthp = softsynth_hook;
+
+#ifndef POLLED_MODE
+#endif
+}
+
+void
+unload_softsyn(struct address_info *hw_config)
+{
+ if (!softsynth_loaded)
+ return;
+#ifndef POLLED_MODE
+#endif
+
+ softsynthp = NULL;
+ softsynth_loaded = 0;
+ reset_samples(devc);
+}
+
+#ifdef MODULE
+
+static struct address_info config;
+
+int
+init_module(void)
+{
+ printk("SoftOSS driver Copyright (C) by Hannu Savolainen 1993-1997\n");
+ if (!probe_softsyn(&config))
+ return -ENODEV;
+ attach_softsyn_card(&config);
+ SOUND_LOCK;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unload_softsyn(&config);
+ sound_unload_synthdev(devc->synthdev);
+ sound_unload_timerdev(devc->timerdev);
+ SOUND_LOCK_END;
+}
+#endif
+#endif
diff --git a/drivers/sound/softoss.h b/drivers/sound/softoss.h
new file mode 100644
index 000000000..f0b07e8d0
--- /dev/null
+++ b/drivers/sound/softoss.h
@@ -0,0 +1,161 @@
+/*
+ * softoss.h - Definitions for Software MIDI Synthesizer.
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+
+
+/*
+ * Sequencer mode1 timer calls made by sequencer.c
+ */
+extern int (*softsynthp) (int cmd, int parm1, int parm2, unsigned long parm3);
+
+#define SSYN_START 1
+#define SSYN_REQUEST 2 /* parm1 = time */
+#define SSYN_STOP 3
+#define SSYN_GETTIME 4 /* Returns number of ticks since reset */
+
+#define MAX_PATCH 256
+#define MAX_SAMPLE 512
+#define MAX_VOICE 32
+#define DEFAULT_VOICES 16
+
+typedef struct voice_info
+{
+/*
+ * Don't change anything in the beginning of this struct. These fields are used
+ * by the resampling loop which may have been written in assembly for some
+ * architectures. Any change may make the resampling code incompatible
+ */
+ int instr;
+ short *wave;
+ struct patch_info *sample;
+
+ unsigned int ptr; int step; /* Pointer to the wave data and pointer increment */
+
+ int mode;
+ int startloop, startbackloop, endloop, looplen;
+
+ unsigned int leftvol, rightvol;
+/***** Don't change anything above this */
+
+ volatile unsigned long orig_freq, current_freq;
+ volatile int bender, bender_range, panning;
+ volatile int main_vol, expression_vol, patch_vol, velocity;
+
+/* Envelope parameters */
+
+ int envelope_phase;
+ volatile int envelope_vol;
+ volatile int envelope_volstep;
+ int envelope_time; /* Number of remaining envelope steps */
+ unsigned int envelope_target;
+ int percussive_voice;
+ int sustain_mode; /* 0=off, 1=sustain on, 2=sustain on+key released */
+
+/* Vibrato */
+ int vibrato_rate;
+ int vibrato_depth;
+ int vibrato_phase;
+ int vibrato_step;
+ int vibrato_level;
+
+/* Tremolo */
+ int tremolo_rate;
+ int tremolo_depth;
+ int tremolo_phase;
+ int tremolo_step;
+ int tremolo_level;
+} voice_info;
+
+extern voice_info softoss_voices[MAX_VOICE]; /* Voice spesific info */
+
+typedef struct softsyn_devc
+{
+/*
+ * Don't change anything in the beginning of this struct. These fields are used
+ * by the resampling loop which may have been written in assembly for some
+ * architectures. Any change may make the resampling code incompatible
+ */
+ int maxvoice; /* # of voices to be processed */
+ int afterscale;
+ int delay_size;
+ int control_rate, control_counter;
+/***** Don't change anything above this */
+
+ int ram_size;
+ int ram_used;
+
+ int synthdev;
+ int timerdev;
+ int sequencer_mode;
+/*
+ * Audio parameters
+ */
+
+ int audiodev;
+ int audio_opened;
+ int speed;
+ int channels;
+ int bits;
+ int default_max_voices;
+ int max_playahead;
+ struct fileinfo finfo;
+ int fragsize;
+ int samples_per_fragment;
+
+/*
+ * Sample storage
+ */
+ int nrsamples;
+ struct patch_info *samples[MAX_SAMPLE];
+ short *wave[MAX_SAMPLE];
+
+/*
+ * Programs
+ */
+ int programs[MAX_SAMPLE];
+
+/*
+ * Timer parameters
+ */
+ volatile unsigned long usecs;
+ volatile unsigned long usecs_per_frag;
+ volatile unsigned long next_event_usecs;
+
+/*
+ * Engine state
+ */
+
+ volatile int engine_state;
+#define ES_STOPPED 0
+#define ES_STARTED 1
+
+ /* Voice spesific bitmaps */
+ volatile int tremolomap;
+ volatile int vibratomap;
+
+} softsyn_devc;
+
+void softsynth_resample_loop(short *buf, int loops);
+extern void softsyn_control_loop(void);
+
+#define DELAY_SIZE 4096
+
+#ifdef SOFTSYN_MAIN
+ short voice_active[MAX_VOICE] = {0};
+ voice_info softoss_voices[MAX_VOICE] = {{0}}; /* Voice spesific info */
+ int left_delay[DELAY_SIZE]={0}, right_delay[DELAY_SIZE]={0};
+ int delayp=0;
+#else
+ extern softsyn_devc *devc;
+
+ extern int left_delay[DELAY_SIZE], right_delay[DELAY_SIZE];
+ extern int delayp;
+ extern short voice_active[MAX_VOICE];
+#endif
diff --git a/drivers/sound/softoss_rs.c b/drivers/sound/softoss_rs.c
new file mode 100644
index 000000000..f8dac8ed5
--- /dev/null
+++ b/drivers/sound/softoss_rs.c
@@ -0,0 +1,136 @@
+
+/*
+ * sound/softoss_rs.c
+ *
+ * Software based MIDI synthsesizer driver, the actual mixing loop.
+ * Keep the loop as simple as possible to make it easier to rewrite this
+ * routine in assembly.
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+#include <linux/config.h>
+
+
+#include "sound_config.h"
+
+#if defined(CONFIG_SOFTOSS) || defined(MODULE)
+#include "softoss.h"
+
+void
+softsynth_resample_loop(short *buf, int loops)
+{
+ int iloop, voice;
+ volatile voice_info *v;
+
+#ifdef OSS_BIG_ENDIAN
+ unsigned char *cbuf = (unsigned char *) buf;
+
+#endif
+
+ for (iloop = 0; iloop < loops; iloop++)
+ { /* Mix one sample */
+
+ int accum, left = 0, right = 0;
+ int ix, position;
+
+ for (voice = 0; voice < devc->maxvoice; voice++)
+ if (voice_active[voice])
+ { /* Compute voice */
+
+ v = &softoss_voices[voice];
+#ifdef SOFTOSS_TEST
+ ix = iloop << 3;
+ position = v->ptr;
+#else
+ ix = (position = v->ptr) >> 9;
+#endif
+ /* Interpolation (resolution of 512 steps) */
+ {
+ int fract = v->ptr & 0x1f; /* 9 bits */
+
+ /* This method works with less arithmetic operations */
+ register int v1 = v->wave[ix];
+
+ accum = v1 + ((((v->wave[ix + 1] - v1)) * (fract)) >> 9);
+ }
+
+ left += (accum * v->leftvol);
+ right += (accum * v->rightvol);
+
+ /* Update sample pointer */
+
+ position += v->step;
+ if (position <= v->endloop)
+ v->ptr = position;
+ else if (v->mode & WAVE_LOOPING)
+ {
+ if (v->mode & WAVE_BIDIR_LOOP)
+ {
+ v->mode ^= WAVE_LOOP_BACK; /* Turn around */
+ v->step *= -1;
+ } else
+ {
+ position -= v->looplen;
+ v->ptr = position;
+ }
+ }
+ /* else leave the voice looping the current sample */
+
+ if (v->mode & WAVE_LOOP_BACK && position < v->startloop)
+ {
+ if (v->mode & WAVE_BIDIR_LOOP)
+ {
+ v->mode ^= WAVE_LOOP_BACK; /* Turn around */
+ v->step *= -1;
+ } else
+ {
+ position += v->looplen;
+ v->ptr = position;
+ }
+ }
+ } /* Compute voice */
+#if 1 /* Delay */
+ left += left_delay[delayp];
+ right += right_delay[delayp];
+
+ left_delay[delayp] = right >> 2;
+ right_delay[delayp] = left >> 2;
+ delayp = (delayp + 1) % devc->delay_size;
+#endif
+
+#define AFTERSCALE devc->afterscale;
+
+ left >>= AFTERSCALE;
+ right >>= AFTERSCALE;
+
+ if (left > 32767)
+ left = 32767;
+ if (left < -32768)
+ left = -32768;
+ if (right > 32767)
+ right = 32767;
+ if (right < -32768)
+ right = -32768;
+
+#ifdef OSS_BIG_ENDIAN
+ *cbuf++ = left & 0xff;
+ *cbuf++ = (left >> 8) & 0xff;
+ *cbuf++ = right & 0xff;
+ *cbuf++ = (right >> 8) & 0xff;
+#else
+ *buf++ = left;
+ *buf++ = right;
+#endif
+ if (devc->control_counter++ >= devc->control_rate)
+ {
+ devc->control_counter = 0;
+ softsyn_control_loop();
+ }
+ } /* Mix one sample */
+}
+#endif
diff --git a/drivers/sound/sound_calls.h b/drivers/sound/sound_calls.h
index 8bb54fa9d..829cfcf91 100644
--- a/drivers/sound/sound_calls.h
+++ b/drivers/sound/sound_calls.h
@@ -41,6 +41,7 @@ void audio_init_devices (void);
int audio_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait);
void reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording);
+int dma_ioctl (int dev, unsigned int cmd, caddr_t arg);
/*
* System calls for the /dev/sequencer
@@ -83,7 +84,7 @@ void MIDIbuf_init(void);
*/
/* From soundcard.c */
-#ifndef __bsdi__
+#if !defined(__bsdi__) && !defined(__NjetBSD__)
void tenmicrosec(int *osp);
#endif
void request_sound_timer (int count);
@@ -107,7 +108,7 @@ int sound_ioctl_sw (int dev, struct fileinfo *file,
/* From opl3.c */
int opl3_detect (int ioaddr, int *osp);
-void opl3_init(int ioaddr, int *osp);
+int opl3_init(int ioaddr, int *osp);
/* From sb_card.c */
void attach_sb_card(struct address_info *hw_config);
@@ -160,7 +161,7 @@ int probe_gus_db16(struct address_info *hw_config);
/* From gus_wave.c */
int gus_wave_detect(int baseaddr);
void gus_wave_init(struct address_info *hw_config);
-void gus_wave_unload (void);
+void gus_wave_unload (struct address_info *hw_config);
void gus_voice_irq(void);
void gus_write8(int reg, unsigned int data);
void guswave_dma_irq(void);
@@ -169,7 +170,7 @@ int gus_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg);
void gus_timer_command (unsigned int addr, unsigned int val);
/* From gus_midi.c */
-void gus_midi_init(void);
+void gus_midi_init(struct address_info *hw_config);
void gus_midi_interrupt(int dummy);
/* From mpu401.c */
@@ -185,14 +186,14 @@ int probe_uart6850(struct address_info *hw_config);
void enable_opl3_mode(int left, int right, int both);
/* From ics2101.c */
-void ics2101_mixer_init(void);
+int ics2101_mixer_init(void);
/* From sound_timer.c */
void sound_timer_interrupt(void);
void sound_timer_syncinterval(unsigned int new_usecs);
/* From ad1848.c */
-void ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture, int share_dma, int *osp);
+int ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture, int share_dma, int *osp);
void ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int share_dma);
int ad1848_detect (int io_base, int *flags, int *osp);
diff --git a/drivers/sound/sound_config.h b/drivers/sound/sound_config.h
index baa04be61..9ca2f3d8a 100644
--- a/drivers/sound/sound_config.h
+++ b/drivers/sound/sound_config.h
@@ -11,7 +11,8 @@
*/
-#include "local.h"
+#include "local.h.master"
+
#include "os.h"
#include "soundvers.h"
@@ -115,7 +116,9 @@ struct address_info {
int driver_use_1; /* Driver defined field 1 */
int driver_use_2; /* Driver defined field 2 */
int *osp; /* OS specific info */
- int card_subtype; /* Driver spesific. Usually 0 */
+ int card_subtype; /* Driver specific. Usually 0 */
+ void *memptr; /* Module memory chainer */
+ int slots[6]; /* To remember driver slot ids */
};
#define SYNTH_MAX_VOICES 32
@@ -145,6 +148,7 @@ struct channel_info {
#define WK_SIGNAL 0x04
#define WK_SLEEP 0x08
#define WK_SELECT 0x10
+#define WK_ABORT 0x20
#define OPEN_READ PCM_ENABLE_INPUT
#define OPEN_WRITE PCM_ENABLE_OUTPUT
@@ -161,5 +165,17 @@ struct channel_info {
#define DDB(x) {}
#endif
+#ifndef MDB
+#ifdef MODULE
+#define MDB(x) x
+#else
+#define MDB(x)
+#endif
+#endif
+
#define TIMER_ARMED 121234
#define TIMER_NOT_ARMED 1
+
+
+
+
diff --git a/drivers/sound/sound_firmware.c b/drivers/sound/sound_firmware.c
new file mode 100644
index 000000000..4dedda0d9
--- /dev/null
+++ b/drivers/sound/sound_firmware.c
@@ -0,0 +1,58 @@
+#define __KERNEL_SYSCALLS__
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/unistd.h>
+#include <asm/uaccess.h>
+
+static int errno;
+
+static int do_mod_firmware_load(const char *fn, char **fp)
+{
+ int fd;
+ long l;
+ char *dp;
+
+ fd = open(fn, 0, 0);
+ if (fd == -1)
+ {
+ printk(KERN_INFO "Unable to load '%s'.\n", fn);
+ return 0;
+ }
+ l = lseek(fd, 0L, 2);
+ if (l <= 0 || l > 65535)
+ {
+ printk(KERN_INFO "Invalid firmware '%s'\n", fn);
+ sys_close(fd);
+ return 0;
+ }
+ lseek(fd, 0L, 0);
+ dp = kmalloc(l, GFP_KERNEL);
+ if (dp == NULL)
+ {
+ printk(KERN_INFO "Out of memory loading '%s'.\n", fn);
+ sys_close(fd);
+ return 0;
+ }
+ if (read(fd, dp, l) != l)
+ {
+ printk(KERN_INFO "Failed to read '%s'.\n", fn);
+ kfree(dp);
+ sys_close(fd);
+ return 0;
+ }
+ close(fd);
+ *fp = dp;
+ return (int) l;
+}
+
+int mod_firmware_load(const char *fn, char **fp)
+{
+ int r;
+ unsigned long fs = get_fs();
+
+ set_fs(get_ds());
+ r = do_mod_firmware_load(fn, fp);
+ set_fs(fs);
+ return r;
+}
diff --git a/drivers/sound/sound_firmware.h b/drivers/sound/sound_firmware.h
new file mode 100644
index 000000000..0a0cbfdfb
--- /dev/null
+++ b/drivers/sound/sound_firmware.h
@@ -0,0 +1,2 @@
+extern int mod_firmware_load(const char *fn, char **fp);
+
diff --git a/drivers/sound/sound_pnp.c b/drivers/sound/sound_pnp.c
deleted file mode 100644
index ffaa5b644..000000000
--- a/drivers/sound/sound_pnp.c
+++ /dev/null
@@ -1,1513 +0,0 @@
-/*
- * sound/sound_pnp.c
- *
- * PnP soundcard support (Linux spesific)
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-#include <linux/config.h>
-
-#include "sound_config.h"
-
-#if defined(CONFIG_SPNP)
-
-
-static struct wait_queue *maui_sleeper = NULL;
-static volatile struct snd_wait maui_sleep_flag =
-{0};
-
-extern unsigned long init_pnp (unsigned long, int *);
-
-#include "pnp.h"
-extern int *sound_osp;
-
-extern int (*pnp_ioctl) (unsigned int cmd, caddr_t arg);
-
-extern int sound_pnp_port;
-static int disabled_devices[] =
-{
- PNP_DEVID ('G', 'R', 'V', 0x0003), /* GUS SB emulation */
- PNP_DEVID ('G', 'R', 'V', 0x0004), /* GUS MPU emulation */
- 0
-};
-
-static int special_devices[] =
-{
- PNP_DEVID ('C', 'S', 'C', 0x0010), /* CS4232/6 control port */
- PNP_DEVID ('C', 'S', 'C', 0x0002), /* CS4232/6 control port */
- 0
-};
-
-static int pnp_sig = 0;
-
-static void
-pnp_delay (long t)
-{
- t = (t * HZ) / 1000000; /* Convert to jiffies */
-
-
- {
- unsigned long tlimit;
-
- if (t)
- current->timeout = tlimit = jiffies + (t);
- else
- tlimit = (unsigned long) -1;
- maui_sleep_flag.opts = WK_SLEEP;
- interruptible_sleep_on (&maui_sleeper);
- if (!(maui_sleep_flag.opts & WK_WAKEUP))
- {
- if (jiffies >= tlimit)
- maui_sleep_flag.opts |= WK_TIMEOUT;
- }
- maui_sleep_flag.opts &= ~WK_SLEEP;
- };
-}
-
-void
-cs4232_pnp (void *parm)
-{
- struct pnp_dev *dev = (struct pnp_dev *) parm;
- char *name;
- int old_num_mixers = num_mixers;
- int is_4232 = 0; /* CS4232 (not CS4236 or something better) */
-
- int portmask = 0xff;
- int irqmask = 0x01, dmamask = 0x03;
- int opl3_driver, wss_driver;
-
-
- if (pnp_trace)
- printk ("CS4232/6 driver waking up\n");
-
- if (dev->card->key == (PNP_DEVID ('C', 'S', 'C', 0x4232)))
- is_4232 = 1;
-
-#ifndef USE_HOT_PNP_INIT
- if (is_4232) /* CS4232 may cause lockups */
- if (pnp_get_port (dev, 0) == NO_PORT ||
- pnp_get_port (dev, 1) == NO_PORT ||
- pnp_get_irq (dev, 0) == NO_IRQ ||
- pnp_get_dma (dev, 0) == NO_DMA
- )
- {
- printk ("Sound: CS4232 in PnP mode requires prior initialization by PnP BIOS\n");
- return;
- }
-#endif
-
- if (dev->card && dev->card->name)
- name = dev->card->name;
- else
- name = "PnP WSS";
-
- if ((wss_driver = sndtable_identify_card ("AD1848")))
- portmask |= 0x01; /* MSS */
- else
- printk ("Sound: PnP MSS/WSS device detected but no driver enabled\n");
-
- if ((opl3_driver = sndtable_identify_card ("OPL3")))
- portmask |= 0x02; /* OPL3 */
- else
- printk ("Sound: PnP OPL3/4 device detected but no driver enabled\n");
-
- /* printk ("WSS driver %d, OPL3 driver %d\n", wss_driver, opl3_driver); */
-
- if (!portmask) /* No drivers available */
- return;
-
- if (!is_4232)
- if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
- {
- printk ("sound_pnp: Failed to find free resources\n");
- return;
- }
-
- {
- struct address_info hw_config;
- int wss_base, opl3_base;
- int irq;
- int dma1, dma2;
-
- if (pnp_trace)
- printk ("Device activation OK\n");
- wss_base = pnp_get_port (dev, 0);
- opl3_base = pnp_get_port (dev, 1);
- irq = pnp_get_irq (dev, 0);
- dma1 = pnp_get_dma (dev, 0);
- dma2 = pnp_get_dma (dev, 1);
-
- pnp_delay (1000000);
-
- if (pnp_trace)
- {
- printk ("I/O0 %03x\n", wss_base);
- printk ("I/O1 %03x\n", opl3_base);
- printk ("IRQ %d\n", irq);
- printk ("DMA0 %d\n", dma1);
- printk ("DMA1 %d\n", dma2);
- }
-
- if (opl3_base && opl3_driver)
- {
- hw_config.io_base = opl3_base;
- hw_config.irq = 0;
- hw_config.dma = -1;
- hw_config.dma2 = -1;
- hw_config.always_detect = 0;
- hw_config.name = "";
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (opl3_driver, &hw_config);
-
- }
-
- if (wss_base && wss_driver)
- {
- hw_config.io_base = wss_base;
- hw_config.irq = irq;
- hw_config.dma = dma1;
- hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
- hw_config.always_detect = 0;
- hw_config.name = name;
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (wss_driver, &hw_config);
-
-
- if (num_mixers > old_num_mixers)
- { /* Assume the mixer map is as suggested in the CS4232 spec */
- AD1848_REROUTE (SOUND_MIXER_LINE1, SOUND_MIXER_LINE);
- AD1848_REROUTE (SOUND_MIXER_LINE2, SOUND_MIXER_CD);
- AD1848_REROUTE (SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* FM */
- }
- }
- }
-}
-
-void
-opti82C924_pnp (void *parm)
-{
- struct pnp_dev *dev = (struct pnp_dev *) parm;
- char *name;
-
- int portmask = 0xff, irqmask = 0x01, dmamask = 0x03;
- int opl3_driver, wss_driver;
-
- if (pnp_trace)
- printk ("OPTi 82C924 driver waking up\n");
-
- if (dev->card && dev->card->name)
- name = dev->card->name;
- else
- name = "PnP WSS";
-
- if ((wss_driver = sndtable_identify_card ("AD1848")))
- portmask |= 0x01; /* MSS */
- else
- printk ("Sound: PnP MSS/WSS device detected but no driver enabled\n");
-
- if ((opl3_driver = sndtable_identify_card ("OPL3")))
- portmask |= 0x02; /* OPL3 */
- else
- printk ("Sound: PnP OPL3/4 device detected but no driver enabled\n");
-
- /* printk ("WSS driver %d, OPL3 driver %d\n", wss_driver, opl3_driver); */
-
- if (!portmask) /* No drivers available */
- return;
-
- if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
- printk ("sound_pnp: Failed to find free resources\n");
- else
- {
- struct address_info hw_config;
- int wss_base, opl3_base;
- int irq;
- int dma1, dma2;
-
- if (pnp_trace)
- printk ("Device activation OK\n");
- wss_base = pnp_get_port (dev, 1);
- opl3_base = pnp_get_port (dev, 2);
- irq = pnp_get_irq (dev, 0);
- dma1 = pnp_get_dma (dev, 0);
- dma2 = pnp_get_dma (dev, 1);
-
- pnp_delay (2000000);
-
- if (pnp_trace)
- {
- printk ("I/O0 %03x\n", wss_base);
- printk ("I/O1 %03x\n", opl3_base);
- printk ("IRQ %d\n", irq);
- printk ("DMA0 %d\n", dma1);
- printk ("DMA1 %d\n", dma2);
- }
-
- if (opl3_base && opl3_driver)
- {
- hw_config.io_base = opl3_base + 8;
- hw_config.irq = 0;
- hw_config.dma = -1;
- hw_config.dma2 = -1;
- hw_config.always_detect = 0;
- hw_config.name = "";
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (opl3_driver, &hw_config);
-
- }
-
- if (wss_base && wss_driver)
- {
- hw_config.io_base = wss_base + 4;
- hw_config.irq = irq;
- hw_config.dma = dma1;
- hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
- hw_config.always_detect = 0;
- hw_config.name = name;
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (wss_driver, &hw_config);
-
- }
- }
-}
-
-void
-opl3sa2_pnp (void *parm)
-{
- struct pnp_dev *dev = (struct pnp_dev *) parm;
- char *name;
-
- int portmask = 0x00, irqmask = 0x01, dmamask = 0x03;
- int opl3_driver, wss_driver, mpu_driver;
-
- if (pnp_trace)
- printk ("OPL3-SA2 driver waking up\n");
-
- if (dev->card && dev->card->name)
- name = dev->card->name;
- else
- name = "PnP WSS";
-
- if ((wss_driver = sndtable_identify_card ("AD1848")))
- portmask |= 0x02; /* MSS */
- else
- printk ("Sound: PnP MSS/WSS device detected but no driver enabled\n");
-
- if ((opl3_driver = sndtable_identify_card ("OPL3")))
- portmask |= 0x04; /* OPL3 */
- else
- printk ("Sound: PnP OPL3/4 device detected but no driver enabled\n");
-
- if ((mpu_driver = sndtable_identify_card ("UART401")))
- portmask |= 0x08; /* OPL3 */
- else
- printk ("Sound: PnP UART401 device detected but no driver enabled\n");
-
- /* printk ("WSS driver %d, OPL3 driver %d\n", wss_driver, opl3_driver); */
-
- if (!portmask) /* No drivers available */
- return;
-
- if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
- printk ("sound_pnp: Failed to find free resources\n");
- else
- {
- struct address_info hw_config;
- int wss_base, opl3_base, mpu_base;
- int irq;
- int dma1, dma2;
-
- if (pnp_trace)
- printk ("Device activation OK\n");
- wss_base = pnp_get_port (dev, 1);
- opl3_base = pnp_get_port (dev, 2);
- mpu_base = pnp_get_port (dev, 3);
- irq = pnp_get_irq (dev, 0);
- dma1 = pnp_get_dma (dev, 0);
- dma2 = pnp_get_dma (dev, 1);
-
- pnp_delay (1000000);
-
- if (pnp_trace)
- {
- printk ("I/O0 %03x\n", wss_base);
- printk ("I/O1 %03x\n", opl3_base);
- printk ("I/O3 %03x\n", mpu_base);
- printk ("IRQ %d\n", irq);
- printk ("DMA0 %d\n", dma1);
- printk ("DMA1 %d\n", dma2);
- }
-
- if (opl3_base && opl3_driver)
- {
- hw_config.io_base = opl3_base;
- hw_config.irq = 0;
- hw_config.dma = -1;
- hw_config.dma2 = -1;
- hw_config.always_detect = 0;
- hw_config.name = "";
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (opl3_driver, &hw_config);
-
- }
-
- if (wss_base && wss_driver)
- {
- hw_config.io_base = wss_base + 4;
- hw_config.irq = irq;
- hw_config.dma = dma1;
- hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
- hw_config.always_detect = 0;
- hw_config.name = name;
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (wss_driver, &hw_config);
-
- }
-
- if (mpu_base && mpu_driver)
- {
- hw_config.io_base = mpu_base;
- hw_config.irq = 0;
- hw_config.dma = -1;
- hw_config.dma2 = -1;
- hw_config.always_detect = 0;
- hw_config.name = "";
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (mpu_driver, &hw_config);
-
- }
- }
-}
-
-static unsigned char
-C931_read (int base, int reg)
-{
- unsigned char data;
- unsigned long flags;
-
- save_flags (flags);
- cli ();
- outb ((0xE4), base);
- outb ((reg), base + 2);
- data = inb (base + 3);
- restore_flags (flags);
- return data;
-}
-
-static void
-C931_write (int base, int reg, unsigned char data)
-{
- unsigned long flags;
-
- save_flags (flags);
- cli ();
- outb ((0xE4), base);
- outb ((reg), base + 2);
- outb ((data), base + 3);
- restore_flags (flags);
-}
-
-void
-opti82C931_pnp (void *parm)
-{
- struct pnp_dev *dev = (struct pnp_dev *) parm;
- char *name;
-
- int portmask = 0xff, irqmask = 0x01, dmamask = 0x03;
- int opl3_driver, wss_driver;
-
- if (pnp_trace)
- printk ("OPTi 82C931 driver waking up\n");
-
- if (dev->card && dev->card->name)
- name = dev->card->name;
- else
- name = "PnP WSS";
-
- if ((wss_driver = sndtable_identify_card ("AD1848")))
- portmask |= 0x01; /* MSS */
- else
- printk ("Sound: PnP MSS/WSS device detected but no driver enabled\n");
-
- if ((opl3_driver = sndtable_identify_card ("OPL3")))
- portmask |= 0x02; /* OPL3 */
- else
- printk ("Sound: PnP OPL3/4 device detected but no driver enabled\n");
-
- /* printk ("WSS driver %d, OPL3 driver %d\n", wss_driver, opl3_driver); */
-
- if (!portmask) /* No drivers available */
- return;
-
- if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
- printk ("sound_pnp: Failed to find free resources\n");
- else
- {
- struct address_info hw_config;
- int wss_base, opl3_base, master_ctl;
- int irq;
- int dma1, dma2;
-
- if (pnp_trace)
- printk ("Device activation OK\n");
- wss_base = pnp_get_port (dev, 0);
- opl3_base = pnp_get_port (dev, 1);
- master_ctl = pnp_get_port (dev, 3);
- irq = pnp_get_irq (dev, 0);
- dma1 = pnp_get_dma (dev, 0);
- dma2 = pnp_get_dma (dev, 1);
-
- if (pnp_trace)
- {
- int i;
-
- printk ("I/O0 %03x\n", wss_base);
- printk ("I/O1 %03x\n", opl3_base);
- printk ("Master control port %x\n", master_ctl);
- for (i = 0; i < 4; i++)
- printk ("Port %x = %x\n", master_ctl + i, inb (master_ctl + i));
- printk ("IRQ %d\n", irq);
- printk ("DMA0 %d\n", dma1);
- printk ("DMA1 %d\n", dma2);
- }
- {
- unsigned char tmp;
-
- tmp = C931_read (master_ctl, 5) | 0x20; /* Enable codec registers I16 to I31 */
- C931_write (master_ctl, 5, tmp);
-
- tmp = 0x82; /* MPU and WSS enabled, SB disabled */
- C931_write (master_ctl, 6, tmp);
- }
-
- pnp_delay (2000000);
-
- if (opl3_base && opl3_driver)
- {
- hw_config.io_base = opl3_base + 8;
- hw_config.irq = 0;
- hw_config.dma = -1;
- hw_config.dma2 = -1;
- hw_config.always_detect = 0;
- hw_config.name = "";
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (opl3_driver, &hw_config);
-
- }
-
- if (wss_base && wss_driver)
- {
- hw_config.io_base = wss_base;
- hw_config.irq = irq;
- hw_config.dma = dma1;
- hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
- hw_config.always_detect = 0;
- hw_config.name = name;
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (wss_driver, &hw_config);
-
- ad1848_control (AD1848_SET_C930_PWD, master_ctl);
- }
- }
-}
-
-void
-opti82C924mpu_pnp (void *parm)
-{
- struct pnp_dev *dev = (struct pnp_dev *) parm;
- char *name;
-
- int portmask = 0xff, irqmask = 0x01, dmamask = 0x03;
- int mpu_driver;
-
- if (pnp_trace)
- printk ("OPTi 82C924/C925/C931 MPU driver waking up\n");
-
- if (dev->card && dev->card->name)
- name = dev->card->name;
- else
- name = "PnP MPU";
-
- if ((mpu_driver = sndtable_identify_card ("UART401")))
- portmask |= 0x01; /* MPU401 */
- else
- printk ("Sound: PnP MPU device detected but no driver enabled\n");
-
- /* printk ("MPU driver %d\n", mpu_driver); */
-
- if (!portmask) /* No drivers available */
- return;
-
- if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
- printk ("sound_pnp: Failed to find free resources\n");
- else
- {
- struct address_info hw_config;
- int mpu_base;
- int irq;
-
- if (pnp_trace)
- printk ("Device activation OK\n");
- mpu_base = pnp_get_port (dev, 0);
- irq = pnp_get_irq (dev, 0);
-
- pnp_delay (1000000);
-
- if (pnp_trace)
- {
- printk ("I/O %03x\n", mpu_base);
- printk ("IRQ %d\n", irq);
- }
-
- if (mpu_base && mpu_driver)
- {
- hw_config.io_base = mpu_base;
- hw_config.irq = irq;
- hw_config.dma = -1;
- hw_config.dma2 = -1;
- hw_config.always_detect = 0;
- hw_config.name = name;
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (mpu_driver, &hw_config);
-
- }
- }
-}
-
-void
-cs4236mpu_pnp (void *parm)
-{
- struct pnp_dev *dev = (struct pnp_dev *) parm;
- char *name;
-
- int portmask = 0xff, irqmask = 0x01, dmamask = 0x03;
- int mpu_driver;
-
- if (dev->card->key == (PNP_DEVID ('C', 'S', 'C', 0x4232))) /* CS4232 */
- return;
-
- if (pnp_trace)
- printk ("CS4236 MPU driver waking up\n");
-
- if (dev->card && dev->card->name)
- name = dev->card->name;
- else
- name = "PnP MPU";
-
- if ((mpu_driver = sndtable_identify_card ("UART401")))
- portmask |= 0x01; /* MPU401 */
- else
- printk ("Sound: CS4236 PnP MPU device detected but no driver enabled\n");
-
- /* printk ("MPU driver %d\n", mpu_driver); */
-
- if (!portmask) /* No drivers available */
- return;
-
- if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
- printk ("sound_pnp: Failed to find free resources\n");
- else
- {
- struct address_info hw_config;
- int mpu_base;
- int irq;
-
- if (pnp_trace)
- printk ("Device activation OK\n");
- mpu_base = pnp_get_port (dev, 0);
- irq = pnp_get_irq (dev, 0);
-
- pnp_delay (1500000);
-
- if (pnp_trace)
- {
- printk ("I/O %03x\n", mpu_base);
- printk ("IRQ %d\n", irq);
- }
-
- if (mpu_base && mpu_driver)
- {
- hw_config.io_base = mpu_base;
- hw_config.irq = irq;
- hw_config.dma = -1;
- hw_config.dma2 = -1;
- hw_config.always_detect = 0;
- hw_config.name = name;
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (mpu_driver, &hw_config);
-
- }
- }
-}
-
-void
-soundscape_pnp (void *parm)
-{
- struct pnp_dev *dev = (struct pnp_dev *) parm;
- char *name;
-
- int portmask = 0xff, irqmask = 0x03, dmamask = 0x01;
- int sscape_driver, wss_driver;
-
- if (pnp_trace)
- printk ("Soundscape PnP driver waking up\n");
-
- if (dev->card && dev->card->name)
- name = dev->card->name;
- else
- name = "SoundScape PnP";
-
- if ((sscape_driver = sndtable_identify_card ("SSCAPE")))
- portmask |= 0x01; /* MPU401 */
- else
- printk ("Sound: Soundscape PnP device detected but no driver enabled\n");
-
- /* printk ("Soundscape driver %d\n", sscape_driver); */
-
- if ((wss_driver = sndtable_identify_card ("SSCAPEMSS")))
- portmask |= 0x01;
- else
- printk ("Sound: Soundscape codec device detected but no driver enabled\n");
-
- if (!portmask) /* No drivers available */
- return;
-
- if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
- printk ("sound_pnp: Failed to find free resources\n");
- else
- {
- struct address_info hw_config;
- int sscape_base;
- int irq, irq2, dma, dma2;
-
- if (pnp_trace)
- printk ("Device activation OK\n");
- sscape_base = pnp_get_port (dev, 0);
- irq = pnp_get_irq (dev, 0);
- irq2 = pnp_get_irq (dev, 1);
- dma = pnp_get_dma (dev, 0);
- dma2 = pnp_get_dma (dev, 1);
-
- pnp_delay (1000000);
-
- if (pnp_trace)
- {
- printk ("I/O %03x\n", sscape_base);
- printk ("IRQ %d\n", irq);
- printk ("IRQ2 %d\n", irq2);
- printk ("DMA %d\n", dma);
- printk ("DMA2 %d\n", dma2);
- }
-
- if (sscape_base && sscape_driver)
- {
- hw_config.io_base = sscape_base;
- hw_config.irq = irq;
- hw_config.dma = dma;
- hw_config.dma2 = dma2;
- hw_config.always_detect = 0;
- hw_config.name = name;
- hw_config.driver_use_1 = 0x12345678;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (sscape_driver, &hw_config);
- }
-
- if (sscape_base && wss_driver)
- {
- hw_config.io_base = sscape_base + 8; /* The codec base */
- hw_config.irq = irq2;
- hw_config.dma = dma;
- hw_config.dma2 = -1;
- hw_config.always_detect = 0;
- hw_config.name = name;
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (wss_driver, &hw_config);
- ad1848_control (AD1848_SET_XTAL, 1); /* 14.3 MHz is used */
- }
- }
-}
-
-void
-soundscape_vivo (void *parm)
-{
- struct pnp_dev *dev = (struct pnp_dev *) parm;
- char *name;
-
- int portmask = 0x07, irqmask = 0x03, dmamask = 0x03;
- int mpu_driver, wss_driver, vivo_driver;
- int is_vivo_classic = 0;
-
- if (pnp_trace)
- printk ("Soundscape VIVO driver waking up\n");
-
- if (dev->card->key == (PNP_DEVID ('E', 'N', 'S', 0x4080)))
- is_vivo_classic = 1;
-
- if (dev->card && dev->card->name)
- name = dev->card->name;
- else
- name = "SoundScape VIVO";
-
- if ((mpu_driver = sndtable_identify_card ("UART401")))
- portmask |= 0x01; /* MPU401 */
-
- /* printk ("MPU driver %d\n", mpu_driver); */
-
- if ((wss_driver = sndtable_identify_card ("AD1848")))
- portmask |= 0x02;
- else
- printk ("Sound: Soundscape codec device detected but no driver enabled\n");
-
- if ((vivo_driver = sndtable_identify_card ("VIVO")))
- portmask |= 0x07;
- else
- printk ("Sound: Soundscape VIVO/OTTO device detected but no driver installed\n");
-
- if (!portmask) /* No drivers available */
- return;
-
- if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
- printk ("sound_pnp: Failed to find free resources\n");
- else
- {
- struct address_info hw_config;
- int mpu_base, mss_base, otto_base;
- int irq, irq2, dma, dma2;
-
- if (pnp_trace)
- printk ("Device activation OK\n");
- mpu_base = pnp_get_port (dev, 0);
- mss_base = pnp_get_port (dev, 1);
- otto_base = pnp_get_port (dev, 2);
- irq = pnp_get_irq (dev, 0);
- irq2 = pnp_get_irq (dev, 1);
- dma = pnp_get_dma (dev, 0);
- dma2 = pnp_get_dma (dev, 1);
- if (dma2 == NO_DMA)
- dma2 = dma;
-
- if (pnp_trace)
- {
- printk ("I/O %03x\n", mpu_base);
- printk ("MSS I/O %03x\n", mss_base);
- printk ("OTTO I/O %03x\n", otto_base);
- printk ("IRQ %d\n", irq);
- printk ("IRQ2 %d\n", irq2);
- printk ("DMA %d\n", dma);
- printk ("DMA2 %d\n", dma2);
- }
-
-
- if (mss_base && wss_driver)
- {
- hw_config.io_base = mss_base + 4; /* The codec base */
- hw_config.irq = irq;
- hw_config.dma = dma;
- hw_config.dma2 = dma2;
- hw_config.always_detect = 0;
- hw_config.name = name;
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (wss_driver, &hw_config);
- }
-
- if (otto_base && vivo_driver)
- {
- hw_config.io_base = otto_base;
- hw_config.irq = irq2;
- hw_config.dma = -1;
- hw_config.dma2 = -1;
- hw_config.always_detect = 0;
- hw_config.name = name;
- hw_config.driver_use_1 = mpu_base;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (vivo_driver, &hw_config);
-
- if (is_vivo_classic)
- {
- /*
- * The original VIVO uses XCTL0 pin of AD1845 as a synth (un)mute bit. Turn it
- * on _after_ the synth is initialized. Btw, XCTL1 controls 30 dB mic boost
- * circuit.
- */
-
- ad1848_control (AD1848_SET_XCTL0, 1); /* Unmute */
- }
- AD1848_REROUTE (SOUND_MIXER_LINE1, SOUND_MIXER_SYNTH); /* AUX1 is OTTO input */
- AD1848_REROUTE (SOUND_MIXER_LINE3, SOUND_MIXER_LINE); /* Line in */
-
- }
- }
-}
-
-void
-gus_pnp (void *parm)
-{
- struct pnp_dev *dev = (struct pnp_dev *) parm;
- char *name;
-
- int portmask = 0x00, irqmask = 0x01, dmamask = 0x03;
- int gus_driver, wss_driver;
-
- if (pnp_trace)
- printk ("GUS PnP driver waking up\n");
-
- if (dev->card && dev->card->name)
- name = dev->card->name;
- else
- name = "Ultrasound PnP";
-
- if ((gus_driver = sndtable_identify_card ("GUSPNP")))
- portmask |= 0x07;
- else
- printk ("Sound: GUS PnP device detected but no driver enabled\n");
-
- if ((wss_driver = sndtable_identify_card ("AD1848")))
- portmask |= 0x01; /* MAX */
- else
- printk ("Sound: GUS PnP codec device detected but no driver enabled\n");
-
- if (!portmask) /* No drivers available */
- return;
-
- if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
- printk ("sound_pnp: Failed to find free resources\n");
- else
- {
- struct address_info hw_config;
- int gus_base;
- int irq;
- int dma1, dma2;
-
- gus_base = pnp_get_port (dev, 0);
-
- irq = pnp_get_irq (dev, 0);
- dma1 = pnp_get_dma (dev, 0);
- dma2 = pnp_get_dma (dev, 1);
-
- if (pnp_trace)
- printk ("Device activation OK (P%x I%d D%d d%d)\n",
- gus_base, irq, dma1, dma2);
-
- if (gus_base && gus_driver)
- {
-
- hw_config.io_base = gus_base;
- hw_config.irq = irq;
- hw_config.dma = dma1;
- hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
- hw_config.always_detect = 0;
- hw_config.name = name;
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (gus_driver, &hw_config);
- }
- }
-}
-
-void
-sb_pnp (void *parm)
-{
- struct pnp_dev *dev = (struct pnp_dev *) parm;
- char *name;
-
- int portmask = 0x00, irqmask = 0x01, dmamask = 0x03;
- int sb_driver, mpu_driver, opl3_driver;
-
- if (pnp_trace)
- printk ("SB PnP driver waking up\n");
- pnp_delay (1000000);
-
- if (dev->card && dev->card->name)
- name = dev->card->name;
- else
- name = "SoundBlaster PnP";
-
- if ((sb_driver = sndtable_identify_card ("SBPNP")))
- portmask |= 0x01;
- else
- printk ("Sound: SB PnP device detected but no driver enabled\n");
-
- if ((mpu_driver = sndtable_identify_card ("SBMPU")))
- portmask |= 0x02;
- else
- printk ("Sound: SB PnP device detected but SB MPU driver not enabled\n");
-
- if ((opl3_driver = sndtable_identify_card ("OPL3")))
- portmask |= 0x04;
- else
- printk ("Sound: SB PnP device detected but OPL3 driver not enabled\n");
-
- if (!portmask) /* No drivers available */
- return;
-
- if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
- printk ("sound_pnp: Failed to find free resources\n");
- else
- {
- struct address_info hw_config;
- int sb_base, mpu_base, opl3_base;
- int irq;
- int dma1, dma2;
-
- if (pnp_trace)
- printk ("Device activation OK\n");
- sb_base = pnp_get_port (dev, 0);
- mpu_base = pnp_get_port (dev, 1);
- opl3_base = pnp_get_port (dev, 2);
-
- irq = pnp_get_irq (dev, 0);
- dma1 = pnp_get_dma (dev, 0);
- dma2 = pnp_get_dma (dev, 1);
-
- if (sb_base && sb_driver)
- {
- hw_config.io_base = sb_base;
- hw_config.irq = irq;
- hw_config.dma = dma1;
- hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
- hw_config.always_detect = 0;
- hw_config.name = name;
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (sb_driver, &hw_config);
- }
-
- if (opl3_base && opl3_driver)
- {
- hw_config.io_base = opl3_base;
- hw_config.irq = 0;
- hw_config.dma = -1;
- hw_config.dma2 = -1;
- hw_config.always_detect = 0;
- hw_config.name = "";
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (opl3_driver, &hw_config);
-
- }
-
- if (mpu_base && mpu_driver)
- {
- hw_config.io_base = mpu_base;
- hw_config.irq = irq;
- hw_config.dma = -1;
- hw_config.dma2 = -1;
- hw_config.always_detect = 0;
- hw_config.name = "";
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (mpu_driver, &hw_config);
-
- }
- }
-}
-
-void
-als_pnp (void *parm)
-{
- struct pnp_dev *dev = (struct pnp_dev *) parm;
- char *name;
-
- int portmask = 0x00, irqmask = 0x01, dmamask = 0x03;
- int sb_driver;
-
- if (pnp_trace)
- printk ("ALS### PnP driver waking up\n");
-
- if (dev->card && dev->card->name)
- name = dev->card->name;
- else
- name = "SB16 clone";
-
- if ((sb_driver = sndtable_identify_card ("SBPNP")))
- portmask |= 0x01;
- else
- printk ("Sound: ALS PnP device detected but no driver enabled\n");
-
- if (!portmask) /* No drivers available */
- return;
-
- if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
- printk ("sound_pnp: Failed to find free resources\n");
- else
- {
- struct address_info hw_config;
- int sb_base;
- int irq;
- int dma1, dma2;
-
- if (pnp_trace)
- printk ("Device activation OK\n");
- sb_base = pnp_get_port (dev, 0);
-
- irq = pnp_get_irq (dev, 0);
- dma1 = pnp_get_dma (dev, 1);
- dma2 = pnp_get_dma (dev, 0);
-
- if (sb_base && sb_driver)
- {
- hw_config.io_base = sb_base;
- hw_config.irq = irq;
- hw_config.dma = dma1;
- hw_config.dma2 = dma2;
- hw_config.always_detect = 0;
- hw_config.name = name;
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (sb_driver, &hw_config);
- }
- }
-}
-
-void
-als_pnp_mpu (void *parm)
-{
- struct pnp_dev *dev = (struct pnp_dev *) parm;
- char *name;
-
- int portmask = 0x00, irqmask = 0x01, dmamask = 0x03;
- int mpu_driver;
-
- if (pnp_trace)
- printk ("ALS### PnP MPU driver waking up\n");
-
- if (dev->card && dev->card->name)
- name = dev->card->name;
- else
- name = "SB16 clone";
-
- if ((mpu_driver = sndtable_identify_card ("UART401")))
- portmask |= 0x01;
- else
- printk ("Sound: ALS PnP device detected but no MPU driver enabled\n");
-
- if (!portmask) /* No drivers available */
- return;
-
- if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
- printk ("sound_pnp: Failed to find free resources\n");
- else
- {
- struct address_info hw_config;
- int mpu_base;
- int irq;
-
- if (pnp_trace)
- printk ("Device activation OK\n");
- mpu_base = pnp_get_port (dev, 0);
-
- irq = pnp_get_irq (dev, 0);
-
- if (mpu_base && mpu_driver)
- {
- hw_config.io_base = mpu_base;
- hw_config.irq = irq;
- hw_config.dma = -1;
- hw_config.dma2 = -1;
- hw_config.always_detect = 0;
- hw_config.name = name;
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (mpu_driver, &hw_config);
- }
- }
-}
-
-void
-als_pnp_opl (void *parm)
-{
- struct pnp_dev *dev = (struct pnp_dev *) parm;
- char *name;
-
- int portmask = 0x00, irqmask = 0x01, dmamask = 0x03;
- int opl3_driver;
-
- if (pnp_trace)
- printk ("ALS### PnP OPL3 driver waking up\n");
-
- if (dev->card && dev->card->name)
- name = dev->card->name;
- else
- name = "SB16 clone";
-
- if ((opl3_driver = sndtable_identify_card ("OPL3")))
- portmask |= 0x01;
- else
- printk ("Sound: ALS PnP device detected but no OPL3 driver enabled\n");
-
- if (!portmask) /* No drivers available */
- return;
-
- if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
- printk ("sound_pnp: Failed to find free resources\n");
- else
- {
- struct address_info hw_config;
- int opl3_base;
- int irq;
-
- if (pnp_trace)
- printk ("Device activation OK\n");
- opl3_base = pnp_get_port (dev, 0);
-
- irq = pnp_get_irq (dev, 0);
-
- if (opl3_base && opl3_driver)
- {
- hw_config.io_base = opl3_base;
- hw_config.irq = 0;
- hw_config.dma = -1;
- hw_config.dma2 = -1;
- hw_config.always_detect = 0;
- hw_config.name = name;
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = sound_osp;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (opl3_driver, &hw_config);
- }
- }
-}
-
-void
-ess_pnp (void *parm)
-{
- struct pnp_dev *dev = (struct pnp_dev *) parm;
- char *name;
-
- int portmask = 0x03, irqmask = 0x01, dmamask = 0x03;
- int sb_driver, mpu_driver, opl3_driver;
-
- if (pnp_trace)
- printk ("ESS PnP driver waking up\n");
-
- if (pnp_trace)
- {
- printk ("ESS1868: IRQB,IRQA = %x\n", pnp_readreg (dev, 0x20));
- printk ("ESS1868: IRQD,IRQC = %x\n", pnp_readreg (dev, 0x21));
- printk ("ESS1868: IRQF,IRQE = %x\n", pnp_readreg (dev, 0x22));
- printk ("ESS1868: DRQB,DRQA = %x\n", pnp_readreg (dev, 0x23));
- printk ("ESS1868: DRQD,DRQC = %x\n", pnp_readreg (dev, 0x24));
- printk ("ESS1868: Configuration ROM Header 0 = %x\n", pnp_readreg (dev, 0x25));
- printk ("ESS1868: Configuration ROM Header 1 = %x\n", pnp_readreg (dev, 0x26));
- printk ("ESS1868: HW Volume IRQ = %x\n", pnp_readreg (dev, 0x27));
- printk ("ESS1868: MPU401 IRQ = %x\n", pnp_readreg (dev, 0x28));
- }
-
- if (pnp_readreg (dev, 0x27) & 0x01) /* MPU401 is at logical device #3 */
- printk ("Nonstandard ESS1868 implementation - contact support@4front-tech.com\n");
-
- if (dev->card && dev->card->name)
- name = dev->card->name;
- else
- name = "ESS AudioDrive PnP";
-
- if ((sb_driver = sndtable_identify_card ("SBLAST")))
- portmask |= 0x01;
- else
- printk ("Sound: SB PnP device detected but no driver enabled\n");
-
- if ((mpu_driver = sndtable_identify_card ("SBMPU")))
- portmask |= 0x02;
- else
- printk ("Sound: SB PnP device detected but SB MPU driver not enabled\n");
-
- if ((opl3_driver = sndtable_identify_card ("OPL3")))
- portmask |= 0x04;
- else
- printk ("Sound: SB PnP device detected but OPL3 driver not enabled\n");
-
- if (!portmask) /* No drivers available */
- return;
-
- if (!pnp_allocate_device (pnp_sig, dev, portmask, irqmask, dmamask, 0x00))
- printk ("sound_pnp: Failed to find free resources\n");
- else
- {
- struct address_info hw_config;
- int sb_base, mpu_base, opl3_base;
- int irq;
- int dma1, dma2;
-
- if (pnp_trace)
- printk ("Device activation OK\n");
- sb_base = pnp_get_port (dev, 0);
- opl3_base = pnp_get_port (dev, 1);
- mpu_base = pnp_get_port (dev, 2);
-
- irq = pnp_get_irq (dev, 0);
- dma1 = pnp_get_dma (dev, 0);
- /* dma2 = pnp_get_dma (dev, 1); */ dma2 = -1;
-
- if (pnp_trace)
- {
- printk ("ESS PnP at %x/%x/%x, %d, %d/%d\n",
- sb_base, mpu_base, opl3_base,
- irq, dma1, dma2);
- }
-
- if (sb_base && sb_driver)
- {
- hw_config.io_base = sb_base;
- hw_config.irq = irq;
- hw_config.dma = dma1;
- hw_config.dma2 = (dma2 == NO_DMA) ? dma1 : dma2;
- hw_config.always_detect = 0;
- hw_config.name = name;
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = NULL;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (sb_driver, &hw_config);
- }
-
- if (opl3_base && opl3_driver)
- {
- hw_config.io_base = opl3_base;
- hw_config.irq = 0;
- hw_config.dma = -1;
- hw_config.dma2 = -1;
- hw_config.always_detect = 0;
- hw_config.name = "";
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = NULL;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (opl3_driver, &hw_config);
-
- }
-
- if (mpu_base && mpu_driver)
- {
- hw_config.io_base = mpu_base;
- hw_config.irq = -irq;
- hw_config.dma = -1;
- hw_config.dma2 = -1;
- hw_config.always_detect = 0;
- hw_config.name = "";
- hw_config.driver_use_1 = 0;
- hw_config.driver_use_2 = 0;
- hw_config.osp = NULL;
- hw_config.card_subtype = 0;
-
- sndtable_start_card (mpu_driver, &hw_config);
-
- }
- }
-}
-
-static struct pnp_sounddev pnp_devs[] =
-{
- {PNP_DEVID ('C', 'S', 'C', 0x0000), cs4232_pnp, "CS4232"},
- {PNP_DEVID ('C', 'S', 'C', 0x0003), cs4236mpu_pnp, "CS4236MPU"},
- {PNP_DEVID ('G', 'R', 'V', 0x0000), gus_pnp, "GUS"},
- {PNP_DEVID ('R', 'V', 'L', 0x0010), gus_pnp, "WAVXTREME"},
- {PNP_DEVID ('A', 'D', 'V', 0x0010), gus_pnp, "IWAVE"},
- {PNP_DEVID ('D', 'X', 'P', 0x0010), gus_pnp, "IWAVE"},
- {PNP_DEVID ('Y', 'M', 'H', 0x0021), opl3sa2_pnp, "OPL3SA2"},
- {PNP_DEVID ('O', 'P', 'T', 0x0000), opti82C924_pnp, "82C924"},
- {PNP_DEVID ('O', 'P', 'T', 0x9250), opti82C924_pnp, "82C925"},
- {PNP_DEVID ('O', 'P', 'T', 0x9310), opti82C931_pnp, "82C931"},
- {PNP_DEVID ('O', 'P', 'T', 0x0002), opti82C924mpu_pnp, "82C924MPU"},
- {PNP_DEVID ('E', 'N', 'S', 0x0000), soundscape_pnp, "SSCAPE"},
- {PNP_DEVID ('N', 'E', 'C', 0x0000), soundscape_pnp, "NEC"},
- {PNP_DEVID ('E', 'N', 'S', 0x1010), soundscape_vivo, "SSCAPE"},
- {PNP_DEVID ('E', 'N', 'S', 0x1011), soundscape_vivo, "SSCAPE"},
- {PNP_DEVID ('C', 'T', 'L', 0x0031), sb_pnp, "SB"},
- {PNP_DEVID ('C', 'T', 'L', 0x0001), sb_pnp, "SB"},
- {PNP_DEVID ('C', 'T', 'L', 0x0041), sb_pnp, "SB"}, /* SB32 (new revision) */
- {PNP_DEVID ('C', 'T', 'L', 0x0042), sb_pnp, "SB"}, /* SB64 */
- {PNP_DEVID ('C', 'T', 'L', 0x0044), sb_pnp, "SB"}, /* SB64 Gold */
- {PNP_DEVID ('@', '@', '@', 0x0001), als_pnp, "SB"},
- {PNP_DEVID ('@', 'X', '@', 0x0001), als_pnp_mpu, "SB"},
- {PNP_DEVID ('@', 'H', '@', 0x0001), als_pnp_opl, "SB"},
- {PNP_DEVID ('E', 'S', 'S', 0x1868), ess_pnp, "ESS"}
-};
-
-static int nr_pnpdevs = sizeof (pnp_devs) / sizeof (struct pnp_sounddev);
-
-static int
-pnp_activate (int id, struct pnp_dev *dev)
-{
- int i;
-
- for (i = 0; i < nr_pnpdevs; i++)
- if (pnp_devs[i].id == id)
- {
-
- if (pnp_trace)
- printk ("PnP dev: %08x, %s\n", id,
- pnp_devid2asc (id));
-
- pnp_devs[i].setup ((void *) dev);
- return 1;
- }
-
- return 0;
-}
-
-void
-cs423x_special (struct pnp_dev *dev)
-{
-}
-
-void
-sound_pnp_init (int *osp)
-{
-
- struct pnp_dev *dev;
-
- if (pnp_sig == 0)
- init_pnp (0, osp);
-
- if (pnp_sig == 0)
- if ((pnp_sig = pnp_connect ("sound")) == -1)
- {
- printk ("Sound: Can't connect to kernel PnP services.\n");
- return;
- }
-
-/*
- * First handle some special configuration ports.
- */
- dev = NULL;
- while ((dev = pnp_get_next_device (pnp_sig, dev)) != NULL)
- {
- int i;
-
- for (i = 0; special_devices[i] != 0; i++)
- if (special_devices[i] == dev->key)
- switch (i)
- {
- case 0:
- case 1:
- cs423x_special (dev);
- break;
- }
- }
-
-/*
- * Next disable some unused sound devices so that they don't consume
- * valuable IRQ and DMA resources.
- */
- dev = NULL;
- while ((dev = pnp_get_next_device (pnp_sig, dev)) != NULL)
- {
- int i;
-
- for (i = 0; disabled_devices[i] != 0; i++)
- if (disabled_devices[i] == dev->key)
- pnp_enable_device (dev, 0); /* Disable it */
- }
-
-/*
- * Then initialize drivers for known PnP devices.
- */
- dev = NULL;
- while ((dev = pnp_get_next_device (pnp_sig, dev)) != NULL)
- {
- if (!pnp_activate (dev->key, dev))
- {
- /* Scan all compatible devices */
-
- int i;
-
- for (i = 0; i < dev->ncompat; i++)
- if (pnp_activate (dev->compat_keys[i], dev))
- break;
- }
- }
-}
-
-void
-sound_pnp_disconnect (void)
-{
- pnp_disconnect (pnp_sig);
-}
-
-
-#endif
diff --git a/drivers/sound/sound_switch.c b/drivers/sound/sound_switch.c
index bacdccb66..8e8c553bf 100644
--- a/drivers/sound/sound_switch.c
+++ b/drivers/sound/sound_switch.c
@@ -33,696 +33,702 @@ static int status_busy = 0;
int
*
-load_mixer_volumes (char *name, int *levels, int present)
+load_mixer_volumes(char *name, int *levels, int present)
{
- int i, n;
-
- for (i = 0; i < num_mixer_volumes; i++)
- if (strcmp (name, mixer_vols[i].name) == 0)
- {
- if (present)
- mixer_vols[i].num = i;
- return mixer_vols[i].levels;
- }
-
- if (num_mixer_volumes >= MAX_MIXER_DEV)
- {
- printk ("Sound: Too many mixers (%s)\n", name);
- return levels;
- }
-
- n = num_mixer_volumes++;
+ int i, n;
+
+ for (i = 0; i < num_mixer_volumes; i++)
+ if (strcmp(name, mixer_vols[i].name) == 0)
+ {
+ if (present)
+ mixer_vols[i].num = i;
+ return mixer_vols[i].levels;
+ }
+ if (num_mixer_volumes >= MAX_MIXER_DEV)
+ {
+ printk("Sound: Too many mixers (%s)\n", name);
+ return levels;
+ }
+ n = num_mixer_volumes++;
- strcpy (mixer_vols[n].name, name);
+ strcpy(mixer_vols[n].name, name);
- if (present)
- mixer_vols[n].num = n;
- else
- mixer_vols[n].num = -1;
+ if (present)
+ mixer_vols[n].num = n;
+ else
+ mixer_vols[n].num = -1;
- for (i = 0; i < 32; i++)
- mixer_vols[n].levels[i] = levels[i];
- return mixer_vols[n].levels;
+ for (i = 0; i < 32; i++)
+ mixer_vols[n].levels[i] = levels[i];
+ return mixer_vols[n].levels;
}
static int
-set_mixer_levels (caddr_t arg)
+set_mixer_levels(caddr_t arg)
{
- mixer_vol_table *buf = NULL;
- int err = 0;
+ mixer_vol_table *buf = NULL;
+ int err = 0;
- if ((buf = (mixer_vol_table *) vmalloc (sizeof (mixer_vol_table))) == NULL)
- return -ENOSPC;
+ if ((buf = (mixer_vol_table *) vmalloc(sizeof(mixer_vol_table))) == NULL)
+ return -ENOSPC;
- memcpy ((char *) buf, (&((char *) arg)[0]), sizeof (*buf));
+ memcpy((char *) buf, (&((char *) arg)[0]), sizeof(*buf));
- load_mixer_volumes (buf->name, buf->levels, 0);
+ load_mixer_volumes(buf->name, buf->levels, 0);
- memcpy ((&((char *) arg)[0]), (char *) buf, sizeof (*buf));
- vfree (buf);
+ memcpy((&((char *) arg)[0]), (char *) buf, sizeof(*buf));
+ vfree(buf);
- return err;
+ return err;
}
static int
-get_mixer_levels (caddr_t arg)
+get_mixer_levels(caddr_t arg)
{
- mixer_vol_table *buf = NULL;
- int n, err = 0;
+ mixer_vol_table *buf = NULL;
+ int n, err = 0;
- if ((buf = (mixer_vol_table *) vmalloc (sizeof (mixer_vol_table))) == NULL)
- return -ENOSPC;
+ if ((buf = (mixer_vol_table *) vmalloc(sizeof(mixer_vol_table))) == NULL)
+ return -ENOSPC;
- memcpy ((char *) buf, (&((char *) arg)[0]), sizeof (*buf));
+ memcpy((char *) buf, (&((char *) arg)[0]), sizeof(*buf));
- n = buf->num;
- if (n < 0 || n >= num_mixer_volumes)
- err = -EINVAL;
- else
- {
- memcpy ((char *) buf, (char *) &mixer_vols[n], sizeof (*buf));
- }
+ n = buf->num;
+ if (n < 0 || n >= num_mixer_volumes)
+ err = -EINVAL;
+ else
+ {
+ memcpy((char *) buf, (char *) &mixer_vols[n], sizeof(*buf));
+ }
- memcpy ((&((char *) arg)[0]), (char *) buf, sizeof (*buf));
- vfree (buf);
+ memcpy((&((char *) arg)[0]), (char *) buf, sizeof(*buf));
+ vfree(buf);
- return err;
+ return err;
}
static int
-put_status (char *s)
+put_status(char *s)
{
- int l = strlen (s);
+ int l = strlen(s);
- if (status_len + l >= 4000)
- return 0;
+ if (status_len + l >= 4000)
+ return 0;
- memcpy (&status_buf[status_len], s, l);
- status_len += l;
+ memcpy(&status_buf[status_len], s, l);
+ status_len += l;
- return 1;
+ return 1;
}
static int
-put_status_int (unsigned int val, int radix)
+put_status_int(unsigned int val, int radix)
{
- int l, v;
+ int l, v;
- static char hx[] = "0123456789abcdef";
- char buf[11];
+ static char hx[] = "0123456789abcdef";
+ char buf[11];
- if (!val)
- return put_status ("0");
+ if (!val)
+ return put_status("0");
- l = 0;
- buf[10] = 0;
+ l = 0;
+ buf[10] = 0;
- while (val)
- {
- v = val % radix;
- val = val / radix;
+ while (val)
+ {
+ v = val % radix;
+ val = val / radix;
- buf[9 - l] = hx[v];
- l++;
- }
+ buf[9 - l] = hx[v];
+ l++;
+ }
- if (status_len + l >= 4000)
- return 0;
+ if (status_len + l >= 4000)
+ return 0;
- memcpy (&status_buf[status_len], &buf[10 - l], l);
- status_len += l;
+ memcpy(&status_buf[status_len], &buf[10 - l], l);
+ status_len += l;
- return 1;
+ return 1;
}
static void
-init_status (void)
+init_status(void)
{
- /*
- * Write the status information to the status_buf and update status_len.
- * There is a limit of 4000 bytes for the data.
- */
+ /*
+ * Write the status information to the status_buf and update status_len.
+ * There is a limit of 4000 bytes for the data.
+ */
- int i;
+ int i;
- status_ptr = 0;
+ status_ptr = 0;
#ifdef SOUND_UNAME_A
- put_status ("OSS/Free" SOUND_VERSION_STRING
- " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY ",\n"
- SOUND_UNAME_A ")"
- "\n");
+ put_status("OSS/Free" SOUND_VERSION_STRING
+ " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY ",\n"
+ SOUND_UNAME_A ")"
+ "\n");
#else
- put_status ("OSS/Free:" SOUND_VERSION_STRING
- " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY "@"
- SOUND_CONFIG_HOST "." SOUND_CONFIG_DOMAIN ")"
- "\n");
+ put_status("OSS/Free:" SOUND_VERSION_STRING
+#if 0
+ " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY "@"
+ SOUND_CONFIG_HOST "." SOUND_CONFIG_DOMAIN ")"
+#endif
+ "\n");
#endif
#ifdef MODULE
- put_status ("Load type: Driver loaded as a module.\n");
+ put_status("Load type: Driver loaded as a module.\n");
#else
- put_status ("Load type: Driver compiled into kernel\n");
-#endif
- put_status ("Kernel: ");
- put_status (system_utsname.sysname);
- put_status (" ");
- put_status (system_utsname.nodename);
- put_status (" ");
- put_status (system_utsname.release);
- put_status (" ");
- put_status (system_utsname.version);
- put_status (" ");
- put_status (system_utsname.machine);
- put_status ("\n");
-#ifdef MODULE
- put_status ("Driver loaded as a module\n");
+ put_status("Load type: Driver compiled into kernel\n");
#endif
-
-
- if (!put_status ("Config options: "))
- return;
- if (!put_status_int (SELECTED_SOUND_OPTIONS, 16))
- return;
-
- if (!put_status ("\n\nInstalled drivers: \n"))
- return;
-
- for (i = 0; i < num_sound_drivers; i++)
- if (sound_drivers[i].card_type != 0)
- {
- if (!put_status ("Type "))
- return;
- if (!put_status_int (sound_drivers[i].card_type, 10))
- return;
- if (!put_status (": "))
- return;
- if (!put_status (sound_drivers[i].name))
- return;
-
- if (!put_status ("\n"))
- return;
- }
-
-
- if (!put_status ("\nCard config: \n"))
- return;
-
- for (i = 0; i < num_sound_cards; i++)
- if (snd_installed_cards[i].card_type != 0)
- {
- int drv, tmp;
-
- if (!snd_installed_cards[i].enabled)
- if (!put_status ("("))
- return;
-
- /*
- * if (!put_status_int(snd_installed_cards[i].card_type, 10)) return;
- * if (!put_status (": ")) return;
- */
-
- if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) != -1)
- if (!put_status (sound_drivers[drv].name))
- return;
-
- if (snd_installed_cards[i].config.io_base)
- {
- if (!put_status (" at 0x"))
- return;
- if (!put_status_int (snd_installed_cards[i].config.io_base, 16))
- return;
- }
-
- tmp = snd_installed_cards[i].config.irq;
- if (tmp != 0)
- {
- if (!put_status (" irq "))
- return;
- if (tmp < 0)
- tmp = -tmp;
- if (!put_status_int (tmp, 10))
- return;
- }
-
- if (snd_installed_cards[i].config.dma != -1)
+ put_status("Kernel: ");
+ put_status(system_utsname.sysname);
+ put_status(" ");
+ put_status(system_utsname.nodename);
+ put_status(" ");
+ put_status(system_utsname.release);
+ put_status(" ");
+ put_status(system_utsname.version);
+ put_status(" ");
+ put_status(system_utsname.machine);
+ put_status("\n");
+
+
+ if (!put_status("Config options: "))
+ return;
+ if (!put_status_int(SELECTED_SOUND_OPTIONS, 16))
+ return;
+
+ if (!put_status("\n\nInstalled drivers: \n"))
+ return;
+
+ for (i = 0; i < num_sound_drivers; i++)
+ if (sound_drivers[i].card_type != 0)
+ {
+ if (!put_status("Type "))
+ return;
+ if (!put_status_int(sound_drivers[i].card_type, 10))
+ return;
+ if (!put_status(": "))
+ return;
+ if (!put_status(sound_drivers[i].name))
+ return;
+
+ if (!put_status("\n"))
+ return;
+ }
+ if (!put_status("\nCard config: \n"))
+ return;
+
+ for (i = 0; i < num_sound_cards; i++)
+ if (snd_installed_cards[i].card_type != 0)
+ {
+ int drv, tmp;
+
+ if (!snd_installed_cards[i].enabled)
+ if (!put_status("("))
+ return;
+
+ /*
+ * if (!put_status_int(snd_installed_cards[i].card_type, 10)) return;
+ * if (!put_status (": ")) return;
+ */
+
+ if ((drv = snd_find_driver(snd_installed_cards[i].card_type)) != -1)
+ if (!put_status(sound_drivers[drv].name))
+ return;
+
+ if (snd_installed_cards[i].config.io_base)
+ {
+ if (!put_status(" at 0x"))
+ return;
+ if (!put_status_int(snd_installed_cards[i].config.io_base, 16))
+ return;
+ }
+ tmp = snd_installed_cards[i].config.irq;
+ if (tmp != 0)
+ {
+ if (!put_status(" irq "))
+ return;
+ if (tmp < 0)
+ tmp = -tmp;
+ if (!put_status_int(tmp, 10))
+ return;
+ }
+ if (snd_installed_cards[i].config.dma != -1)
+ {
+ if (!put_status(" drq "))
+ return;
+ if (!put_status_int(snd_installed_cards[i].config.dma, 10))
+ return;
+ if (snd_installed_cards[i].config.dma2 != -1)
+ {
+ if (!put_status(","))
+ return;
+ if (!put_status_int(snd_installed_cards[i].config.dma2, 10))
+ return;
+ }
+ }
+ if (!snd_installed_cards[i].enabled)
+ if (!put_status(")"))
+ return;
+
+ if (!put_status("\n"))
+ return;
+ }
+ if (!sound_started)
{
- if (!put_status (" drq "))
- return;
- if (!put_status_int (snd_installed_cards[i].config.dma, 10))
- return;
- if (snd_installed_cards[i].config.dma2 != -1)
- {
- if (!put_status (","))
+ put_status("\n\n***** Sound driver not started *****\n\n");
return;
- if (!put_status_int (snd_installed_cards[i].config.dma2, 10))
- return;
- }
}
-
- if (!snd_installed_cards[i].enabled)
- if (!put_status (")"))
- return;
-
- if (!put_status ("\n"))
- return;
- }
-
- if (!sound_started)
- {
- put_status ("\n\n***** Sound driver not started *****\n\n");
- return;
- }
-
#ifndef CONFIG_AUDIO
- if (!put_status ("\nAudio devices: NOT ENABLED IN CONFIG\n"))
- return;
+ if (!put_status("\nAudio devices: NOT ENABLED IN CONFIG\n"))
+ return;
#else
- if (!put_status ("\nAudio devices:\n"))
- return;
-
- for (i = 0; i < num_audiodevs; i++)
- {
- if (!put_status_int (i, 10))
- return;
- if (!put_status (": "))
- return;
- if (!put_status (audio_devs[i]->name))
- return;
-
- if (audio_devs[i]->flags & DMA_DUPLEX)
- if (!put_status (" (DUPLEX)"))
- return;
-
- if (!put_status ("\n"))
- return;
- }
+ if (!put_status("\nAudio devices:\n"))
+ return;
+
+ for (i = 0; i < num_audiodevs; i++)
+ {
+ if (audio_devs[i] == NULL)
+ continue;
+ if (!put_status_int(i, 10))
+ return;
+ if (!put_status(": "))
+ return;
+ if (!put_status(audio_devs[i]->name))
+ return;
+
+ if (audio_devs[i]->flags & DMA_DUPLEX)
+ if (!put_status(" (DUPLEX)"))
+ return;
+
+ if (!put_status("\n"))
+ return;
+ }
#endif
#ifndef CONFIG_SEQUENCER
- if (!put_status ("\nSynth devices: NOT ENABLED IN CONFIG\n"))
- return;
+ if (!put_status("\nSynth devices: NOT ENABLED IN CONFIG\n"))
+ return;
#else
- if (!put_status ("\nSynth devices:\n"))
- return;
-
- for (i = 0; i < num_synths; i++)
- {
- if (!put_status_int (i, 10))
- return;
- if (!put_status (": "))
- return;
- if (!put_status (synth_devs[i]->info->name))
- return;
- if (!put_status ("\n"))
- return;
- }
+ if (!put_status("\nSynth devices:\n"))
+ return;
+
+ for (i = 0; i < num_synths; i++)
+ {
+ if (synth_devs[i] == NULL)
+ continue;
+ if (!put_status_int(i, 10))
+ return;
+ if (!put_status(": "))
+ return;
+ if (!put_status(synth_devs[i]->info->name))
+ return;
+ if (!put_status("\n"))
+ return;
+ }
#endif
#ifndef CONFIG_MIDI
- if (!put_status ("\nMidi devices: NOT ENABLED IN CONFIG\n"))
- return;
+ if (!put_status("\nMidi devices: NOT ENABLED IN CONFIG\n"))
+ return;
#else
- if (!put_status ("\nMidi devices:\n"))
- return;
-
- for (i = 0; i < num_midis; i++)
- {
- if (!put_status_int (i, 10))
- return;
- if (!put_status (": "))
- return;
- if (!put_status (midi_devs[i]->info.name))
- return;
- if (!put_status ("\n"))
- return;
- }
+ if (!put_status("\nMidi devices:\n"))
+ return;
+
+ for (i = 0; i < num_midis; i++)
+ {
+ if (midi_devs[i] == NULL)
+ continue;
+ if (!put_status_int(i, 10))
+ return;
+ if (!put_status(": "))
+ return;
+ if (!put_status(midi_devs[i]->info.name))
+ return;
+ if (!put_status("\n"))
+ return;
+ }
#endif
#ifdef CONFIG_SEQUENCER
- if (!put_status ("\nTimers:\n"))
- return;
-
- for (i = 0; i < num_sound_timers; i++)
- {
- if (!put_status_int (i, 10))
- return;
- if (!put_status (": "))
- return;
- if (!put_status (sound_timer_devs[i]->info.name))
- return;
- if (!put_status ("\n"))
- return;
- }
+ if (!put_status("\nTimers:\n"))
+ return;
+
+ for (i = 0; i < num_sound_timers; i++)
+ {
+ if (sound_timer_devs[i] == NULL)
+ continue;
+ if (!put_status_int(i, 10))
+ return;
+ if (!put_status(": "))
+ return;
+ if (!put_status(sound_timer_devs[i]->info.name))
+ return;
+ if (!put_status("\n"))
+ return;
+ }
#endif
- if (!put_status ("\nMixers:\n"))
- return;
-
- for (i = 0; i < num_mixers; i++)
- {
- if (!put_status_int (i, 10))
- return;
- if (!put_status (": "))
- return;
- if (!put_status (mixer_devs[i]->name))
- return;
- if (!put_status ("\n"))
- return;
- }
+ if (!put_status("\nMixers:\n"))
+ return;
+
+ for (i = 0; i < num_mixers; i++)
+ {
+ if (mixer_devs[i] == NULL)
+ continue;
+ if (!put_status_int(i, 10))
+ return;
+ if (!put_status(": "))
+ return;
+ if (!put_status(mixer_devs[i]->name))
+ return;
+ if (!put_status("\n"))
+ return;
+ }
}
static int
-read_status (char *buf, int count)
+read_status(char *buf, int count)
{
- /*
- * Return at most 'count' bytes from the status_buf.
- */
- int l, c;
+ /*
+ * Return at most 'count' bytes from the status_buf.
+ */
+ int l, c;
- l = count;
- c = status_len - status_ptr;
+ l = count;
+ c = status_len - status_ptr;
- if (l > c)
- l = c;
- if (l <= 0)
- return 0;
+ if (l > c)
+ l = c;
+ if (l <= 0)
+ return 0;
- {
- char *fixit = &status_buf[status_ptr];
+ {
+ char *fixit = &status_buf[status_ptr];
- copy_to_user (&(buf)[0], fixit, l);
- };
- status_ptr += l;
+ copy_to_user(&(buf)[0], fixit, l);
+ };
+ status_ptr += l;
- return l;
+ return l;
}
int
-sound_read_sw (int dev, struct fileinfo *file, char *buf, int count)
+sound_read_sw(int dev, struct fileinfo *file, char *buf, int count)
{
- DEB (printk ("sound_read_sw(dev=%d, count=%d)\n", dev, count));
+ DEB(printk("sound_read_sw(dev=%d, count=%d)\n", dev, count));
- switch (dev & 0x0f)
- {
- case SND_DEV_STATUS:
- return read_status (buf, count);
- break;
+ switch (dev & 0x0f)
+ {
+ case SND_DEV_STATUS:
+ return read_status(buf, count);
+ break;
#ifdef CONFIG_AUDIO
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- return audio_read (dev, file, buf, count);
- break;
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return audio_read(dev, file, buf, count);
+ break;
#endif
#ifdef CONFIG_SEQUENCER
- case SND_DEV_SEQ:
- case SND_DEV_SEQ2:
- return sequencer_read (dev, file, buf, count);
- break;
+ case SND_DEV_SEQ:
+ case SND_DEV_SEQ2:
+ return sequencer_read(dev, file, buf, count);
+ break;
#endif
#ifdef CONFIG_MIDI
- case SND_DEV_MIDIN:
- return MIDIbuf_read (dev, file, buf, count);
+ case SND_DEV_MIDIN:
+ return MIDIbuf_read(dev, file, buf, count);
#endif
- default:;
- }
+ default:;
+ }
- return -EINVAL;
+ return -EINVAL;
}
int
-sound_write_sw (int dev, struct fileinfo *file, const char *buf, int count)
+sound_write_sw(int dev, struct fileinfo *file, const char *buf, int count)
{
- DEB (printk ("sound_write_sw(dev=%d, count=%d)\n", dev, count));
+ DEB(printk("sound_write_sw(dev=%d, count=%d)\n", dev, count));
- switch (dev & 0x0f)
- {
+ switch (dev & 0x0f)
+ {
#ifdef CONFIG_SEQUENCER
- case SND_DEV_SEQ:
- case SND_DEV_SEQ2:
- return sequencer_write (dev, file, buf, count);
- break;
+ case SND_DEV_SEQ:
+ case SND_DEV_SEQ2:
+ return sequencer_write(dev, file, buf, count);
+ break;
#endif
#ifdef CONFIG_AUDIO
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- return audio_write (dev, file, buf, count);
- break;
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return audio_write(dev, file, buf, count);
+ break;
#endif
#ifdef CONFIG_MIDI
- case SND_DEV_MIDIN:
- return MIDIbuf_write (dev, file, buf, count);
+ case SND_DEV_MIDIN:
+ return MIDIbuf_write(dev, file, buf, count);
#endif
- }
+ }
- return -EINVAL;
+ return -EINVAL;
}
int
-sound_open_sw (int dev, struct fileinfo *file)
+sound_open_sw(int dev, struct fileinfo *file)
{
- int retval;
-
- DEB (printk ("sound_open_sw(dev=%d)\n", dev));
-
- if ((dev >= SND_NDEVS) || (dev < 0))
- {
- printk ("Invalid minor device %d\n", dev);
- return -ENXIO;
- }
-
-
- switch (dev & 0x0f)
- {
- case SND_DEV_STATUS:
- if (status_busy)
- return -EBUSY;
- status_busy = 1;
- if ((status_buf = (char *) vmalloc (4000)) == NULL)
- return -EIO;
- status_len = status_ptr = 0;
- init_status ();
- break;
-
- case SND_DEV_CTL:
- if ((dev & 0xf0) && ((dev & 0xf0) >> 4) >= num_mixers)
- return -ENXIO;
- return 0;
- break;
+ int retval;
+
+ DEB(printk("sound_open_sw(dev=%d)\n", dev));
+
+ if ((dev >= SND_NDEVS) || (dev < 0))
+ {
+ printk("Invalid minor device %d\n", dev);
+ return -ENXIO;
+ }
+ switch (dev & 0x0f)
+ {
+ case SND_DEV_STATUS:
+ if (status_busy)
+ return -EBUSY;
+ status_busy = 1;
+ if ((status_buf = (char *) vmalloc(4000)) == NULL)
+ {
+ status_busy = 0;
+ return -EIO;
+ }
+ status_len = status_ptr = 0;
+ init_status();
+ break;
+
+ case SND_DEV_CTL:
+ if ((dev & 0xf0) && ((dev & 0xf0) >> 4) >= num_mixers)
+ return -ENXIO;
+ return 0;
+ break;
#ifdef CONFIG_SEQUENCER
- case SND_DEV_SEQ:
- case SND_DEV_SEQ2:
- if ((retval = sequencer_open (dev, file)) < 0)
- return retval;
- break;
+ case SND_DEV_SEQ:
+ case SND_DEV_SEQ2:
+ if ((retval = sequencer_open(dev, file)) < 0)
+ return retval;
+ break;
#endif
#ifdef CONFIG_MIDI
- case SND_DEV_MIDIN:
- if ((retval = MIDIbuf_open (dev, file)) < 0)
- return retval;
- break;
+ case SND_DEV_MIDIN:
+ if ((retval = MIDIbuf_open(dev, file)) < 0)
+ return retval;
+ break;
#endif
#ifdef CONFIG_AUDIO
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- if ((retval = audio_open (dev, file)) < 0)
- return retval;
- break;
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ if ((retval = audio_open(dev, file)) < 0)
+ return retval;
+ break;
#endif
- default:
- printk ("Invalid minor device %d\n", dev);
- return -ENXIO;
- }
+ default:
+ printk("Invalid minor device %d\n", dev);
+ return -ENXIO;
+ }
- in_use++;
+ in_use++;
- return 0;
+ return 0;
}
void
-sound_release_sw (int dev, struct fileinfo *file)
+sound_release_sw(int dev, struct fileinfo *file)
{
- DEB (printk ("sound_release_sw(dev=%d)\n", dev));
+ DEB(printk("sound_release_sw(dev=%d)\n", dev));
- switch (dev & 0x0f)
- {
- case SND_DEV_STATUS:
- if (status_buf)
- vfree (status_buf);
- status_buf = NULL;
- status_busy = 0;
- break;
+ switch (dev & 0x0f)
+ {
+ case SND_DEV_STATUS:
+ if (status_buf)
+ vfree(status_buf);
+ status_buf = NULL;
+ status_busy = 0;
+ break;
- case SND_DEV_CTL:
- break;
+ case SND_DEV_CTL:
+ break;
#ifdef CONFIG_SEQUENCER
- case SND_DEV_SEQ:
- case SND_DEV_SEQ2:
- sequencer_release (dev, file);
- break;
+ case SND_DEV_SEQ:
+ case SND_DEV_SEQ2:
+ sequencer_release(dev, file);
+ break;
#endif
#ifdef CONFIG_MIDI
- case SND_DEV_MIDIN:
- MIDIbuf_release (dev, file);
- break;
+ case SND_DEV_MIDIN:
+ MIDIbuf_release(dev, file);
+ break;
#endif
#ifdef CONFIG_AUDIO
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- audio_release (dev, file);
- break;
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ audio_release(dev, file);
+ break;
#endif
- default:
- printk ("Sound error: Releasing unknown device 0x%02x\n", dev);
- }
- in_use--;
+ default:
+ printk("Sound error: Releasing unknown device 0x%02x\n", dev);
+ }
+ in_use--;
}
static int
-get_mixer_info (int dev, caddr_t arg)
+get_mixer_info(int dev, caddr_t arg)
{
- mixer_info info;
+ mixer_info info;
+ int i;
- if (dev < 0 || dev >= num_mixers)
- return -ENXIO;
+ if (dev < 0 || dev >= num_mixers)
+ return -ENXIO;
- strcpy (info.id, mixer_devs[dev]->id);
- strcpy (info.name, mixer_devs[dev]->name);
- info.modify_counter = mixer_devs[dev]->modify_counter;
+ strcpy(info.id, mixer_devs[dev]->id);
+ for (i = 0; i < 32 && mixer_devs[dev]->name; i++)
+ info.name[i] = mixer_devs[dev]->name[i];
+ info.name[i] = 0;
+ info.modify_counter = mixer_devs[dev]->modify_counter;
- memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
- return 0;
+ memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info));
+ return 0;
}
static int
-get_old_mixer_info (int dev, caddr_t arg)
+get_old_mixer_info(int dev, caddr_t arg)
{
- _old_mixer_info info;
+ _old_mixer_info info;
+ int i;
- if (dev < 0 || dev >= num_mixers)
- return -ENXIO;
+ if (dev < 0 || dev >= num_mixers)
+ return -ENXIO;
- strcpy (info.id, mixer_devs[dev]->id);
- strcpy (info.name, mixer_devs[dev]->name);
+ strcpy(info.id, mixer_devs[dev]->id);
+ for (i = 0; i < 32 && mixer_devs[dev]->name; i++)
+ info.name[i] = mixer_devs[dev]->name[i];
+ info.name[i] = 0;
- memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
- return 0;
+ memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info));
+ return 0;
}
static int
-sound_mixer_ioctl (int mixdev,
- unsigned int cmd, caddr_t arg)
+sound_mixer_ioctl(int mixdev,
+ unsigned int cmd, caddr_t arg)
{
- if (cmd == SOUND_MIXER_INFO)
- return get_mixer_info (mixdev, arg);
- if (cmd == SOUND_OLD_MIXER_INFO)
- return get_old_mixer_info (mixdev, arg);
+ if (cmd == SOUND_MIXER_INFO)
+ return get_mixer_info(mixdev, arg);
+ if (cmd == SOUND_OLD_MIXER_INFO)
+ return get_old_mixer_info(mixdev, arg);
- if (_SIOC_DIR (cmd) & _SIOC_WRITE)
- mixer_devs[mixdev]->modify_counter++;
+ if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+ mixer_devs[mixdev]->modify_counter++;
- return mixer_devs[mixdev]->ioctl (mixdev, cmd, arg);
+ return mixer_devs[mixdev]->ioctl(mixdev, cmd, arg);
}
int
-sound_ioctl_sw (int dev, struct fileinfo *file,
- unsigned int cmd, caddr_t arg)
+sound_ioctl_sw(int dev, struct fileinfo *file,
+ unsigned int cmd, caddr_t arg)
{
- DEB (printk ("sound_ioctl_sw(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
+ DEB(printk("sound_ioctl_sw(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
- if (cmd == OSS_GETVERSION)
- return (*(int *) arg = SOUND_VERSION);
+ if (cmd == OSS_GETVERSION)
+ return (*(int *) arg = SOUND_VERSION);
- if (((cmd >> 8) & 0xff) == 'M' && num_mixers > 0) /* Mixer ioctl */
- if ((dev & 0x0f) != SND_DEV_CTL)
- {
- int dtype = dev & 0x0f;
- int mixdev;
+ if (((cmd >> 8) & 0xff) == 'M' && num_mixers > 0) /* Mixer ioctl */
+ if ((dev & 0x0f) != SND_DEV_CTL)
+ {
+ int dtype = dev & 0x0f;
+ int mixdev;
- switch (dtype)
- {
+ switch (dtype)
+ {
#ifdef CONFIG_AUDIO
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- mixdev = audio_devs[dev >> 4]->mixer_dev;
- if (mixdev < 0 || mixdev >= num_mixers)
- return -ENXIO;
- return sound_mixer_ioctl (mixdev, cmd, arg);
- break;
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ mixdev = audio_devs[dev >> 4]->mixer_dev;
+ if (mixdev < 0 || mixdev >= num_mixers)
+ return -ENXIO;
+ return sound_mixer_ioctl(mixdev, cmd, arg);
+ break;
#endif
- default:
- return sound_mixer_ioctl (dev, cmd, arg);
- }
- }
-
- switch (dev & 0x0f)
- {
+ default:
+ return sound_mixer_ioctl(dev, cmd, arg);
+ }
+ }
+ switch (dev & 0x0f)
+ {
- case SND_DEV_CTL:
- if (cmd == SOUND_MIXER_GETLEVELS)
- return get_mixer_levels (arg);
- if (cmd == SOUND_MIXER_SETLEVELS)
- return set_mixer_levels (arg);
+ case SND_DEV_CTL:
+ if (cmd == SOUND_MIXER_GETLEVELS)
+ return get_mixer_levels(arg);
+ if (cmd == SOUND_MIXER_SETLEVELS)
+ return set_mixer_levels(arg);
- if (!num_mixers)
- return -ENXIO;
+ if (!num_mixers)
+ return -ENXIO;
- dev = dev >> 4;
+ dev = dev >> 4;
- if (dev >= num_mixers)
- return -ENXIO;
+ if (dev >= num_mixers)
+ return -ENXIO;
- return sound_mixer_ioctl (dev, cmd, arg);
- break;
+ return sound_mixer_ioctl(dev, cmd, arg);
+ break;
#ifdef CONFIG_SEQUENCER
- case SND_DEV_SEQ:
- case SND_DEV_SEQ2:
- return sequencer_ioctl (dev, file, cmd, arg);
- break;
+ case SND_DEV_SEQ:
+ case SND_DEV_SEQ2:
+ return sequencer_ioctl(dev, file, cmd, arg);
+ break;
#endif
#ifdef CONFIG_AUDIO
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- return audio_ioctl (dev, file, cmd, arg);
- break;
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return audio_ioctl(dev, file, cmd, arg);
+ break;
#endif
#ifdef CONFIG_MIDI
- case SND_DEV_MIDIN:
- return MIDIbuf_ioctl (dev, file, cmd, arg);
- break;
+ case SND_DEV_MIDIN:
+ return MIDIbuf_ioctl(dev, file, cmd, arg);
+ break;
#endif
- }
+ }
- return -EINVAL;
+ return -EINVAL;
}
diff --git a/drivers/sound/sound_syms.c b/drivers/sound/sound_syms.c
new file mode 100644
index 000000000..2b80bd165
--- /dev/null
+++ b/drivers/sound/sound_syms.c
@@ -0,0 +1,78 @@
+/*
+ * The sound core exports the following symbols to the rest of
+ * modulespace.
+ *
+ * (C) Copyright 1997 Alan Cox, Licensed under the GNU GPL
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include "sound_config.h"
+#define _MIDI_SYNTH_C_
+#include "midi_synth.h"
+#include <linux/notifier.h>
+#include "sound_firmware.h"
+
+extern struct notifier_block *sound_locker;
+
+EXPORT_SYMBOL(mixer_devs);
+EXPORT_SYMBOL(audio_devs);
+EXPORT_SYMBOL(num_audiodevs);
+
+EXPORT_SYMBOL(note_to_freq);
+EXPORT_SYMBOL(compute_finetune);
+EXPORT_SYMBOL(seq_copy_to_input);
+EXPORT_SYMBOL(sequencer_timer);
+
+EXPORT_SYMBOL(sound_install_audiodrv);
+EXPORT_SYMBOL(sound_install_mixer);
+EXPORT_SYMBOL(sound_alloc_dma);
+EXPORT_SYMBOL(sound_free_dma);
+EXPORT_SYMBOL(snd_set_irq_handler);
+EXPORT_SYMBOL(snd_release_irq);
+EXPORT_SYMBOL(sound_alloc_audiodev);
+EXPORT_SYMBOL(sound_alloc_mididev);
+EXPORT_SYMBOL(sound_alloc_mixerdev);
+EXPORT_SYMBOL(sound_alloc_timerdev);
+EXPORT_SYMBOL(sound_alloc_synthdev);
+EXPORT_SYMBOL(sound_unload_audiodev);
+EXPORT_SYMBOL(sound_unload_mididev);
+EXPORT_SYMBOL(sound_unload_mixerdev);
+EXPORT_SYMBOL(sound_unload_timerdev);
+EXPORT_SYMBOL(sound_unload_synthdev);
+
+EXPORT_SYMBOL(DMAbuf_start_dma);
+EXPORT_SYMBOL(DMAbuf_inputintr);
+EXPORT_SYMBOL(DMAbuf_outputintr);
+EXPORT_SYMBOL(dma_ioctl);
+
+EXPORT_SYMBOL(conf_printf2);
+
+EXPORT_SYMBOL(sound_timer_init);
+EXPORT_SYMBOL(sound_timer_interrupt);
+EXPORT_SYMBOL(sound_timer_syncinterval);
+
+/* Locking */
+EXPORT_SYMBOL(sound_locker);
+
+/* MIDI symbols */
+EXPORT_SYMBOL(midi_devs);
+EXPORT_SYMBOL(num_midis);
+EXPORT_SYMBOL(synth_devs);
+EXPORT_SYMBOL(num_synths);
+
+EXPORT_SYMBOL(do_midi_msg);
+EXPORT_SYMBOL(midi_synth_open);
+EXPORT_SYMBOL(midi_synth_close);
+EXPORT_SYMBOL(midi_synth_ioctl);
+EXPORT_SYMBOL(midi_synth_kill_note);
+EXPORT_SYMBOL(midi_synth_start_note);
+EXPORT_SYMBOL(midi_synth_set_instr);
+EXPORT_SYMBOL(midi_synth_reset);
+EXPORT_SYMBOL(midi_synth_hw_control);
+EXPORT_SYMBOL(midi_synth_aftertouch);
+EXPORT_SYMBOL(midi_synth_controller);
+EXPORT_SYMBOL(midi_synth_panning);
+EXPORT_SYMBOL(midi_synth_setup_voice);
+EXPORT_SYMBOL(midi_synth_send_sysex);
+EXPORT_SYMBOL(midi_synth_bender);
diff --git a/drivers/sound/sound_timer.c b/drivers/sound/sound_timer.c
index e72fef192..5fafccc42 100644
--- a/drivers/sound/sound_timer.c
+++ b/drivers/sound/sound_timer.c
@@ -1,3 +1,5 @@
+
+
/*
* sound/sound_timer.c
*/
@@ -13,7 +15,7 @@
#include "sound_config.h"
-#if defined(CONFIG_SEQUENCER)
+#if defined(CONFIG_SEQUENCER) || defined(CONFIG_SEQUENCER_MODULE)
static volatile int initialized = 0, opened = 0, tmr_running = 0;
static volatile time_t tmr_offs, tmr_ctr;
@@ -27,320 +29,315 @@ static volatile unsigned long usecs_per_tmr; /* Length of the current interval *
static struct sound_lowlev_timer *tmr = NULL;
static unsigned long
-tmr2ticks (int tmr_value)
+tmr2ticks(int tmr_value)
{
- /*
- * Convert timer ticks to MIDI ticks
- */
+ /*
+ * Convert timer ticks to MIDI ticks
+ */
- unsigned long tmp;
- unsigned long scale;
+ unsigned long tmp;
+ unsigned long scale;
- tmp = tmr_value * usecs_per_tmr; /* Convert to usecs */
+ tmp = tmr_value * usecs_per_tmr; /* Convert to usecs */
- scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */
+ scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */
- return (tmp + (scale / 2)) / scale;
+ return (tmp + (scale / 2)) / scale;
}
static void
-reprogram_timer (void)
+reprogram_timer(void)
{
- unsigned long usecs_per_tick;
+ unsigned long usecs_per_tick;
- usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
+ usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
- /*
- * Don't kill the system by setting too high timer rate
- */
- if (usecs_per_tick < 2000)
- usecs_per_tick = 2000;
+ /*
+ * Don't kill the system by setting too high timer rate
+ */
+ if (usecs_per_tick < 2000)
+ usecs_per_tick = 2000;
- usecs_per_tmr = tmr->tmr_start (tmr->dev, usecs_per_tick);
+ usecs_per_tmr = tmr->tmr_start(tmr->dev, usecs_per_tick);
}
void
-sound_timer_syncinterval (unsigned int new_usecs)
+sound_timer_syncinterval(unsigned int new_usecs)
{
/*
* This routine is called by the hardware level if
* the clock frequency has changed for some reason.
*/
- tmr_offs = tmr_ctr;
- ticks_offs += tmr2ticks (tmr_ctr);
- tmr_ctr = 0;
+ tmr_offs = tmr_ctr;
+ ticks_offs += tmr2ticks(tmr_ctr);
+ tmr_ctr = 0;
- usecs_per_tmr = new_usecs;
+ usecs_per_tmr = new_usecs;
}
static void
-tmr_reset (void)
+tmr_reset(void)
{
- unsigned long flags;
-
- save_flags (flags);
- cli ();
- tmr_offs = 0;
- ticks_offs = 0;
- tmr_ctr = 0;
- next_event_time = (unsigned long) -1;
- prev_event_time = 0;
- curr_ticks = 0;
- restore_flags (flags);
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ tmr_offs = 0;
+ ticks_offs = 0;
+ tmr_ctr = 0;
+ next_event_time = (unsigned long) -1;
+ prev_event_time = 0;
+ curr_ticks = 0;
+ restore_flags(flags);
}
static int
-timer_open (int dev, int mode)
+timer_open(int dev, int mode)
{
- if (opened)
- return -EBUSY;
+ if (opened)
+ return -EBUSY;
- tmr_reset ();
- curr_tempo = 60;
- curr_timebase = 100;
- opened = 1;
- reprogram_timer ();
+ tmr_reset();
+ curr_tempo = 60;
+ curr_timebase = 100;
+ opened = 1;
+ reprogram_timer();
- return 0;
+ return 0;
}
static void
-timer_close (int dev)
+timer_close(int dev)
{
- opened = tmr_running = 0;
- tmr->tmr_disable (tmr->dev);
+ opened = tmr_running = 0;
+ tmr->tmr_disable(tmr->dev);
}
static int
-timer_event (int dev, unsigned char *event)
+timer_event(int dev, unsigned char *event)
{
- unsigned char cmd = event[1];
- unsigned long parm = *(int *) &event[4];
-
- switch (cmd)
- {
- case TMR_WAIT_REL:
- parm += prev_event_time;
- case TMR_WAIT_ABS:
- if (parm > 0)
- {
- long time;
-
- if (parm <= curr_ticks) /* It's the time */
- return TIMER_NOT_ARMED;
-
- time = parm;
- next_event_time = prev_event_time = time;
-
- return TIMER_ARMED;
- }
- break;
-
- case TMR_START:
- tmr_reset ();
- tmr_running = 1;
- reprogram_timer ();
- break;
-
- case TMR_STOP:
- tmr_running = 0;
- break;
-
- case TMR_CONTINUE:
- tmr_running = 1;
- reprogram_timer ();
- break;
-
- case TMR_TEMPO:
- if (parm)
- {
- if (parm < 8)
- parm = 8;
- if (parm > 250)
- parm = 250;
- tmr_offs = tmr_ctr;
- ticks_offs += tmr2ticks (tmr_ctr);
- tmr_ctr = 0;
- curr_tempo = parm;
- reprogram_timer ();
- }
- break;
-
- case TMR_ECHO:
- seq_copy_to_input (event, 8);
- break;
-
- default:;
- }
-
- return TIMER_NOT_ARMED;
+ unsigned char cmd = event[1];
+ unsigned long parm = *(int *) &event[4];
+
+ switch (cmd)
+ {
+ case TMR_WAIT_REL:
+ parm += prev_event_time;
+ case TMR_WAIT_ABS:
+ if (parm > 0)
+ {
+ long time;
+
+ if (parm <= curr_ticks) /* It's the time */
+ return TIMER_NOT_ARMED;
+
+ time = parm;
+ next_event_time = prev_event_time = time;
+
+ return TIMER_ARMED;
+ }
+ break;
+
+ case TMR_START:
+ tmr_reset();
+ tmr_running = 1;
+ reprogram_timer();
+ break;
+
+ case TMR_STOP:
+ tmr_running = 0;
+ break;
+
+ case TMR_CONTINUE:
+ tmr_running = 1;
+ reprogram_timer();
+ break;
+
+ case TMR_TEMPO:
+ if (parm)
+ {
+ if (parm < 8)
+ parm = 8;
+ if (parm > 250)
+ parm = 250;
+ tmr_offs = tmr_ctr;
+ ticks_offs += tmr2ticks(tmr_ctr);
+ tmr_ctr = 0;
+ curr_tempo = parm;
+ reprogram_timer();
+ }
+ break;
+
+ case TMR_ECHO:
+ seq_copy_to_input(event, 8);
+ break;
+
+ default:;
+ }
+
+ return TIMER_NOT_ARMED;
}
static unsigned long
-timer_get_time (int dev)
+timer_get_time(int dev)
{
- if (!opened)
- return 0;
+ if (!opened)
+ return 0;
- return curr_ticks;
+ return curr_ticks;
}
static int
-timer_ioctl (int dev,
- unsigned int cmd, caddr_t arg)
+timer_ioctl(int dev,
+ unsigned int cmd, caddr_t arg)
{
- int val;
-
- switch (cmd)
- {
- case SNDCTL_TMR_SOURCE:
- return (*(int *) arg = TMR_INTERNAL);
- break;
-
- case SNDCTL_TMR_START:
- tmr_reset ();
- tmr_running = 1;
- return 0;
- break;
-
- case SNDCTL_TMR_STOP:
- tmr_running = 0;
- return 0;
- break;
-
- case SNDCTL_TMR_CONTINUE:
- tmr_running = 1;
- return 0;
- break;
-
- case SNDCTL_TMR_TIMEBASE:
- val = *(int *) arg;
-
- if (val)
- {
- if (val < 1)
- val = 1;
- if (val > 1000)
- val = 1000;
- curr_timebase = val;
- }
-
- return (*(int *) arg = curr_timebase);
- break;
-
- case SNDCTL_TMR_TEMPO:
- val = *(int *) arg;
-
- if (val)
- {
- if (val < 8)
- val = 8;
- if (val > 250)
- val = 250;
- tmr_offs = tmr_ctr;
- ticks_offs += tmr2ticks (tmr_ctr);
- tmr_ctr = 0;
- curr_tempo = val;
- reprogram_timer ();
- }
-
- return (*(int *) arg = curr_tempo);
- break;
-
- case SNDCTL_SEQ_CTRLRATE:
- val = *(int *) arg;
-
- if (val != 0) /* Can't change */
- return -EINVAL;
-
- return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60);
- break;
-
- case SNDCTL_SEQ_GETTIME:
- return (*(int *) arg = curr_ticks);
- break;
-
- case SNDCTL_TMR_METRONOME:
- /* NOP */
- break;
-
- default:;
- }
+ int val;
+
+ switch (cmd)
+ {
+ case SNDCTL_TMR_SOURCE:
+ return (*(int *) arg = TMR_INTERNAL);
+ break;
+
+ case SNDCTL_TMR_START:
+ tmr_reset();
+ tmr_running = 1;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_STOP:
+ tmr_running = 0;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_CONTINUE:
+ tmr_running = 1;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_TIMEBASE:
+ val = *(int *) arg;
+
+ if (val)
+ {
+ if (val < 1)
+ val = 1;
+ if (val > 1000)
+ val = 1000;
+ curr_timebase = val;
+ }
+ return (*(int *) arg = curr_timebase);
+ break;
+
+ case SNDCTL_TMR_TEMPO:
+ val = *(int *) arg;
+
+ if (val)
+ {
+ if (val < 8)
+ val = 8;
+ if (val > 250)
+ val = 250;
+ tmr_offs = tmr_ctr;
+ ticks_offs += tmr2ticks(tmr_ctr);
+ tmr_ctr = 0;
+ curr_tempo = val;
+ reprogram_timer();
+ }
+ return (*(int *) arg = curr_tempo);
+ break;
+
+ case SNDCTL_SEQ_CTRLRATE:
+ val = *(int *) arg;
+
+ if (val != 0) /* Can't change */
+ return -EINVAL;
+
+ return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60);
+ break;
+
+ case SNDCTL_SEQ_GETTIME:
+ return (*(int *) arg = curr_ticks);
+ break;
+
+ case SNDCTL_TMR_METRONOME:
+ /* NOP */
+ break;
+
+ default:;
+ }
- return -EINVAL;
+ return -EINVAL;
}
static void
-timer_arm (int dev, long time)
+timer_arm(int dev, long time)
{
- if (time < 0)
- time = curr_ticks + 1;
- else if (time <= curr_ticks) /* It's the time */
- return;
+ if (time < 0)
+ time = curr_ticks + 1;
+ else if (time <= curr_ticks) /* It's the time */
+ return;
- next_event_time = prev_event_time = time;
+ next_event_time = prev_event_time = time;
- return;
+ return;
}
static struct sound_timer_operations sound_timer =
{
- {"Sound Timer", 0},
- 1, /* Priority */
- 0, /* Local device link */
- timer_open,
- timer_close,
- timer_event,
- timer_get_time,
- timer_ioctl,
- timer_arm
+ {"Sound Timer", 0},
+ 1, /* Priority */
+ 0, /* Local device link */
+ timer_open,
+ timer_close,
+ timer_event,
+ timer_get_time,
+ timer_ioctl,
+ timer_arm
};
void
-sound_timer_interrupt (void)
+sound_timer_interrupt(void)
{
- if (!opened)
- return;
+ if (!opened)
+ return;
- tmr->tmr_restart (tmr->dev);
+ tmr->tmr_restart(tmr->dev);
- if (!tmr_running)
- return;
+ if (!tmr_running)
+ return;
- tmr_ctr++;
- curr_ticks = ticks_offs + tmr2ticks (tmr_ctr);
+ tmr_ctr++;
+ curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
- if (curr_ticks >= next_event_time)
- {
- next_event_time = (unsigned long) -1;
- sequencer_timer (0);
- }
+ if (curr_ticks >= next_event_time)
+ {
+ next_event_time = (unsigned long) -1;
+ sequencer_timer(0);
+ }
}
-void
-sound_timer_init (struct sound_lowlev_timer *t, char *name)
+void
+sound_timer_init(struct sound_lowlev_timer *t, char *name)
{
- int n;
-
- if (initialized)
- {
- if (t->priority <= tmr->priority)
- return; /* There is already a similar or better timer */
- tmr = t;
- return;
- }
- initialized = 1;
-
- tmr = t;
-
- if (num_sound_timers >= MAX_TIMER_DEV)
- n = 0; /* Overwrite the system timer */
- else
- n = num_sound_timers++;
-
- strcpy (sound_timer.info.name, name);
-
- sound_timer_devs[n] = &sound_timer;
+ int n;
+
+ if (initialized)
+ {
+ if (t->priority <= tmr->priority)
+ return; /* There is already a similar or better timer */
+ tmr = t;
+ return;
+ }
+ initialized = 1;
+
+ tmr = t;
+
+ n = sound_alloc_timerdev();
+ if (n == -1)
+ n = 0; /* Overwrite the system timer */
+ strcpy(sound_timer.info.name, name);
+ sound_timer_devs[n] = &sound_timer;
}
#endif
diff --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c
index 450197a34..f6d17e4e8 100644
--- a/drivers/sound/soundcard.c
+++ b/drivers/sound/soundcard.c
@@ -25,11 +25,19 @@
#include <linux/wait.h>
#include <linux/malloc.h>
#include <linux/ioport.h>
-#endif /* __KERNEL__ */
+#endif /* __KERNEL__ */
#include <linux/delay.h>
-#include <linux/major.h>
+#define SOUND_CORE
+
+#include "soundmodule.h"
+#include <linux/major.h>
+#ifdef MODULE
+#define modular 1
+#else
+#define modular 0
+#endif
static int chrdev_registered = 0;
static int sound_major = SOUND_MAJOR;
@@ -56,296 +64,270 @@ static char dma_alloc_map[8] =
-static long
-sound_read (struct inode *inode, struct file *file, char *buf, unsigned long count)
+static ssize_t sound_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
- int dev;
+ int dev;
- dev = MINOR (inode->i_rdev);
+ dev = MINOR(file->f_dentry->d_inode->i_rdev);
- files[dev].flags = file->f_flags;
+ files[dev].flags = file->f_flags;
- return sound_read_sw (dev, &files[dev], buf, count);
+ return sound_read_sw(dev, &files[dev], buf, count);
}
-static long
-sound_write (struct inode *inode, struct file *file, const char *buf, unsigned long count)
+static ssize_t sound_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
- int dev;
+ int dev;
- dev = MINOR (inode->i_rdev);
+ dev = MINOR(file->f_dentry->d_inode->i_rdev);
- files[dev].flags = file->f_flags;
+ files[dev].flags = file->f_flags;
- return sound_write_sw (dev, &files[dev], buf, count);
+ return sound_write_sw(dev, &files[dev], buf, count);
}
-static long long sound_lseek (struct file *file, long long offset, int orig)
+static long long sound_lseek(struct file *file, long long offset, int orig)
{
- return -EPERM;
+ return -ESPIPE;
}
-static int
-sound_open (struct inode *inode, struct file *file)
+static int sound_open(struct inode *inode, struct file *file)
{
- int dev, retval;
- struct fileinfo tmp_file;
-
- if (is_unloading)
- {
- printk ("Sound: Driver partially removed. Can't open device\n");
- return -EBUSY;
- }
-
- dev = MINOR (inode->i_rdev);
+ int dev, retval;
+ struct fileinfo tmp_file;
- if (!soundcard_configured && dev != SND_DEV_CTL && dev != SND_DEV_STATUS)
- {
- printk ("SoundCard Error: The soundcard system has not been configured\n");
- return -ENXIO;
- }
+ if (is_unloading)
+ {
+/* printk(KERN_ERR "Sound: Driver partially removed. Can't open device\n");*/
+ return -EBUSY;
+ }
+ dev = MINOR(inode->i_rdev);
- tmp_file.mode = 0;
- tmp_file.flags = file->f_flags;
+ if (!soundcard_configured && dev != SND_DEV_CTL && dev != SND_DEV_STATUS)
+ {
+/* printk("SoundCard Error: The soundcard system has not been configured\n");*/
+ return -ENXIO;
+ }
+ tmp_file.mode = 0;
+ tmp_file.flags = file->f_flags;
- if ((tmp_file.flags & O_ACCMODE) == O_RDWR)
- tmp_file.mode = OPEN_READWRITE;
- if ((tmp_file.flags & O_ACCMODE) == O_RDONLY)
- tmp_file.mode = OPEN_READ;
- if ((tmp_file.flags & O_ACCMODE) == O_WRONLY)
- tmp_file.mode = OPEN_WRITE;
+ if ((tmp_file.flags & O_ACCMODE) == O_RDWR)
+ tmp_file.mode = OPEN_READWRITE;
+ if ((tmp_file.flags & O_ACCMODE) == O_RDONLY)
+ tmp_file.mode = OPEN_READ;
+ if ((tmp_file.flags & O_ACCMODE) == O_WRONLY)
+ tmp_file.mode = OPEN_WRITE;
- if ((retval = sound_open_sw (dev, &tmp_file)) < 0)
- return retval;
+ if ((retval = sound_open_sw(dev, &tmp_file)) < 0)
+ return retval;
#ifdef MODULE
- MOD_INC_USE_COUNT;
+ SOUND_INC_USE_COUNT;
#endif
- memcpy ((char *) &files[dev], (char *) &tmp_file, sizeof (tmp_file));
- return retval;
+ memcpy((char *) &files[dev], (char *) &tmp_file, sizeof(tmp_file));
+ return retval;
}
-static int
-sound_release (struct inode *inode, struct file *file)
+static int sound_release(struct inode *inode, struct file *file)
{
- int dev;
+ int dev;
- dev = MINOR (inode->i_rdev);
+ dev = MINOR(inode->i_rdev);
- files[dev].flags = file->f_flags;
+ files[dev].flags = file->f_flags;
- sound_release_sw (dev, &files[dev]);
+ sound_release_sw(dev, &files[dev]);
#ifdef MODULE
- MOD_DEC_USE_COUNT;
+ SOUND_DEC_USE_COUNT;
#endif
- return 0;
+ return 0;
}
-static int
-sound_ioctl (struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int sound_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
{
- int dev, err;
- int len = 0;
- int alloced = 0;
- char *ptr = (char *) arg;
-
- dev = MINOR (inode->i_rdev);
-
- files[dev].flags = file->f_flags;
-
- if (_SIOC_DIR (cmd) != _SIOC_NONE && _SIOC_DIR (cmd) != 0)
- {
- /*
- * Have to validate the address given by the process.
- */
+ int dev, err;
+ int len = 0;
+ int alloced = 0;
+ char *ptr = (char *) arg;
- len = _SIOC_SIZE (cmd);
- if (len < 1 || len > 65536 || arg == 0)
- return -EFAULT;
+ dev = MINOR(inode->i_rdev);
- ptr = vmalloc (len);
- alloced = 1;
- if (ptr == NULL)
- return -EFAULT;
+ files[dev].flags = file->f_flags;
- if (_SIOC_DIR (cmd) & _SIOC_WRITE)
+ if (_SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0)
{
- if ((err = verify_area (VERIFY_READ, (void *) arg, len)) < 0)
- return err;
- copy_from_user (ptr, (char *) arg, len);
+ /*
+ * Have to validate the address given by the process.
+ */
+
+ len = _SIOC_SIZE(cmd);
+ if (len < 1 || len > 65536 || arg == 0)
+ return -EFAULT;
+
+ ptr = vmalloc(len);
+ alloced = 1;
+ if (ptr == NULL)
+ return -EFAULT;
+
+ if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+ {
+ if ((err = verify_area(VERIFY_READ, (void *) arg, len)) < 0)
+ return err;
+ copy_from_user(ptr, (char *) arg, len);
+ }
+ if (_SIOC_DIR(cmd) & _SIOC_READ)
+ {
+ if ((err = verify_area(VERIFY_WRITE, (void *) arg, len)) < 0)
+ return err;
+ }
}
+ err = sound_ioctl_sw(dev, &files[dev], cmd, (caddr_t) ptr);
- if (_SIOC_DIR (cmd) & _SIOC_READ)
- {
- if ((err = verify_area (VERIFY_WRITE, (void *) arg, len)) < 0)
- return err;
- }
-
- }
-
- err = sound_ioctl_sw (dev, &files[dev], cmd, (caddr_t) ptr);
+ if (_SIOC_DIR(cmd) & _SIOC_READ)
+ copy_to_user((char *) arg, ptr, len);
- if (_SIOC_DIR (cmd) & _SIOC_READ)
- {
- copy_to_user ((char *) arg, ptr, len);
- }
+ if (ptr != NULL && alloced)
+ vfree(ptr);
- if (ptr != NULL && alloced)
- vfree (ptr);
-
- return ((err < 0) ? err : 0);
+ return ((err < 0) ? err : 0);
}
-static int
-sound_select (struct inode *inode, struct file *file, int sel_type, poll_table * wait)
+static int sound_select(struct inode *inode, struct file *file, int sel_type, poll_table * wait)
{
- int dev;
+ int dev;
- dev = MINOR (inode->i_rdev);
+ dev = MINOR(inode->i_rdev);
- files[dev].flags = file->f_flags;
+ files[dev].flags = file->f_flags;
- DEB (printk ("sound_select(dev=%d, type=0x%x)\n", dev, sel_type));
+ DEB(printk("sound_select(dev=%d, type=0x%x)\n", dev, sel_type));
- switch (dev & 0x0f)
- {
-#ifdef CONFIG_SEQUENCER
- case SND_DEV_SEQ:
- case SND_DEV_SEQ2:
- return sequencer_select (dev, &files[dev], sel_type, wait);
- break;
+ switch (dev & 0x0f)
+ {
+#if defined(CONFIG_SEQUENCER) || defined(MODULE)
+ case SND_DEV_SEQ:
+ case SND_DEV_SEQ2:
+ return sequencer_select(dev, &files[dev], sel_type, wait);
+ break;
#endif
-#ifdef CONFIG_MIDI
- case SND_DEV_MIDIN:
- return MIDIbuf_select (dev, &files[dev], sel_type, wait);
- break;
+#if defined(CONFIG_MIDI)
+ case SND_DEV_MIDIN:
+ return MIDIbuf_select(dev, &files[dev], sel_type, wait);
+ break;
#endif
-#ifdef CONFIG_AUDIO
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- return DMAbuf_select (dev >> 4, &files[dev], sel_type, wait);
- break;
+#if defined(CONFIG_AUDIO) || defined(MODULE)
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return DMAbuf_select(dev >> 4, &files[dev], sel_type, wait);
+ break;
#endif
- default:
- return 0;
- }
+ default:
+ return 0;
+ }
- return 0;
+ return 0;
}
-static unsigned int
-sound_poll (struct file *file, poll_table * wait)
+static unsigned int sound_poll(struct file *file, poll_table * wait)
{
- struct inode *inode;
- int ret = 0;
+ struct inode *inode;
+ int ret = 0;
- inode = file->f_dentry->d_inode;
+ inode = file->f_dentry->d_inode;
- if (sound_select (inode, file, SEL_IN, wait))
- ret |= POLLIN;
- if (sound_select (inode, file, SEL_OUT, wait))
- ret |= POLLOUT;
- return ret;
+ if (sound_select(inode, file, SEL_IN, wait))
+ ret |= POLLIN;
+ if (sound_select(inode, file, SEL_OUT, wait))
+ ret |= POLLOUT;
+ return ret;
}
-static int
-sound_mmap (struct file *file, struct vm_area_struct *vma)
+static int sound_mmap(struct file *file, struct vm_area_struct *vma)
{
- int dev, dev_class;
- unsigned long size;
- struct dma_buffparms *dmap = NULL;
-
- dev = MINOR (file->f_dentry->d_inode->i_rdev);
-
- files[dev].flags = file->f_flags;
-
- dev_class = dev & 0x0f;
- dev >>= 4;
-
- if (dev_class != SND_DEV_DSP && dev_class != SND_DEV_DSP16 && dev_class != SND_DEV_AUDIO)
- {
- printk ("Sound: mmap() not supported for other than audio devices\n");
- return -EINVAL;
- }
-
- if (vma->vm_flags & VM_WRITE) /* Map write and read/write to the output buf */
- {
- dmap = audio_devs[dev]->dmap_out;
- }
- else if (vma->vm_flags & VM_READ)
- {
- dmap = audio_devs[dev]->dmap_in;
- }
- else
- {
- printk ("Sound: Undefined mmap() access\n");
- return -EINVAL;
- }
-
- if (dmap == NULL)
- {
- printk ("Sound: mmap() error. dmap == NULL\n");
- return -EIO;
- }
-
- if (dmap->raw_buf == NULL)
- {
- printk ("Sound: mmap() called when raw_buf == NULL\n");
- return -EIO;
- }
-
- if (dmap->mapping_flags)
- {
- printk ("Sound: mmap() called twice for the same DMA buffer\n");
- return -EIO;
- }
-
- if (vma->vm_offset != 0)
- {
- printk ("Sound: mmap() offset must be 0.\n");
- return -EINVAL;
- }
-
- size = vma->vm_end - vma->vm_start;
-
- if (size != dmap->bytes_in_use)
- {
- printk ("Sound: mmap() size = %ld. Should be %d\n",
- size, dmap->bytes_in_use);
- }
-
- if (remap_page_range (vma->vm_start, virt_to_phys (dmap->raw_buf),
- vma->vm_end - vma->vm_start,
- vma->vm_page_prot))
- return -EAGAIN;
-
- vma->vm_dentry = dget(file->f_dentry);
-
- dmap->mapping_flags |= DMA_MAP_MAPPED;
-
- memset (dmap->raw_buf,
- dmap->neutral_byte,
- dmap->bytes_in_use);
- return 0;
+ int dev, dev_class;
+ unsigned long size;
+ struct dma_buffparms *dmap = NULL;
+
+ dev = MINOR(file->f_dentry->d_inode->i_rdev);
+
+ files[dev].flags = file->f_flags;
+
+ dev_class = dev & 0x0f;
+ dev >>= 4;
+
+ if (dev_class != SND_DEV_DSP && dev_class != SND_DEV_DSP16 && dev_class != SND_DEV_AUDIO)
+ {
+/* printk("Sound: mmap() not supported for other than audio devices\n");*/
+ return -EINVAL;
+ }
+ if (vma->vm_flags & VM_WRITE) /* Map write and read/write to the output buf */
+ dmap = audio_devs[dev]->dmap_out;
+ else if (vma->vm_flags & VM_READ)
+ dmap = audio_devs[dev]->dmap_in;
+ else
+ {
+/* printk("Sound: Undefined mmap() access\n");*/
+ return -EINVAL;
+ }
+
+ if (dmap == NULL)
+ {
+/* printk("Sound: mmap() error. dmap == NULL\n");*/
+ return -EIO;
+ }
+ if (dmap->raw_buf == NULL)
+ {
+/* printk("Sound: mmap() called when raw_buf == NULL\n");*/
+ return -EIO;
+ }
+ if (dmap->mapping_flags)
+ {
+/* printk("Sound: mmap() called twice for the same DMA buffer\n");*/
+ return -EIO;
+ }
+ if (vma->vm_offset != 0)
+ {
+/* printk("Sound: mmap() offset must be 0.\n");*/
+ return -EINVAL;
+ }
+ size = vma->vm_end - vma->vm_start;
+
+ if (size != dmap->bytes_in_use)
+ {
+ printk(KERN_WARNING "Sound: mmap() size = %ld. Should be %d\n", size, dmap->bytes_in_use);
+ }
+ if (remap_page_range(vma->vm_start, virt_to_phys(dmap->raw_buf),
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot))
+ return -EAGAIN;
+
+ vma->vm_dentry = dget(file->f_dentry);
+
+ dmap->mapping_flags |= DMA_MAP_MAPPED;
+
+ memset(dmap->raw_buf,
+ dmap->neutral_byte,
+ dmap->bytes_in_use);
+ return 0;
}
static struct file_operations sound_fops =
{
- sound_lseek,
- sound_read,
- sound_write,
- NULL, /* sound_readdir */
- sound_poll,
- sound_ioctl,
- sound_mmap,
- sound_open,
- sound_release
+ sound_lseek,
+ sound_read,
+ sound_write,
+ NULL, /* sound_readdir */
+ sound_poll,
+ sound_ioctl,
+ sound_mmap,
+ sound_open,
+ sound_release
};
#ifdef MODULE
@@ -353,26 +335,28 @@ static void
#else
void
#endif
-soundcard_init (void)
+soundcard_init(void)
{
#ifndef MODULE
- register_chrdev (sound_major, "sound", &sound_fops);
- chrdev_registered = 1;
+ register_chrdev(sound_major, "sound", &sound_fops);
+ chrdev_registered = 1;
#endif
- soundcard_configured = 1;
+ soundcard_configured = 1;
- sndtable_init (); /* Initialize call tables and detect cards */
+ sndtable_init(); /* Initialize call tables and detect cards */
- if (sndtable_get_cardcount () == 0)
- return; /* No cards detected */
+#ifdef FIXME
+ if (sndtable_get_cardcount() == 0)
+ return; /* No cards detected */
+#endif
-#ifdef CONFIG_AUDIO
- if (num_audiodevs) /* Audio devices present */
- {
- audio_init_devices ();
- }
+#if defined(CONFIG_AUDIO)
+ if (num_audiodevs || modular) /* Audio devices present */
+ {
+ audio_init_devices();
+ }
#endif
@@ -382,17 +366,19 @@ static unsigned int irqs = 0;
#ifdef MODULE
static void
-free_all_irqs (void)
+free_all_irqs(void)
{
- int i;
-
- for (i = 0; i < 31; i++)
- if (irqs & (1ul << i))
- {
- printk ("Sound warning: IRQ%d was left allocated - fixed.\n", i);
- snd_release_irq (i);
- }
- irqs = 0;
+ int i;
+
+ for (i = 0; i < 31; i++)
+ {
+ if (irqs & (1ul << i))
+ {
+ printk(KERN_WARNING "Sound warning: IRQ%d was left allocated - fixed.\n", i);
+ snd_release_irq(i);
+ }
+ }
+ irqs = 0;
}
char kernel_version[] = UTS_RELEASE;
@@ -404,277 +390,249 @@ static int debugmem = 0; /* switched off by default */
static int sound[20] =
{0};
-int
-init_module (void)
+int init_module(void)
{
- int err;
- int ints[21];
- int i;
-
- if (0 < 0)
- {
- printk ("Sound: Incompatible kernel (wrapper) version\n");
- return -EINVAL;
- }
-
- /*
- * "sound=" command line handling by Harald Milz.
- */
- i = 0;
- while (i < 20 && sound[i])
- ints[i + 1] = sound[i++];
- ints[0] = i;
-
- if (i)
- sound_setup ("sound=", ints);
-
- err = register_chrdev (sound_major, "sound", &sound_fops);
- if (err)
- {
- printk ("sound: driver already loaded/included in kernel\n");
- return err;
- }
-
- chrdev_registered = 1;
- soundcard_init ();
-
- if (sound_nblocks >= 1024)
- printk ("Sound warning: Deallocation table was too small.\n");
-
- return 0;
+ int err;
+ int ints[21];
+ int i;
+
+ /*
+ * "sound=" command line handling by Harald Milz.
+ */
+ i = 0;
+ while (i < 20 && sound[i])
+ ints[i + 1] = sound[i++];
+ ints[0] = i;
+
+ if (i)
+ sound_setup("sound=", ints);
+
+ err = register_chrdev(sound_major, "sound", &sound_fops);
+ if (err)
+ {
+ printk(KERN_ERR "sound: driver already loaded/included in kernel\n");
+ return err;
+ }
+ chrdev_registered = 1;
+ soundcard_init();
+
+ if (sound_nblocks >= 1024)
+ printk(KERN_ERR "Sound warning: Deallocation table was too small.\n");
+
+ return 0;
}
#ifdef MODULE
-void
-cleanup_module (void)
+void cleanup_module(void)
{
- int i;
-
- if (MOD_IN_USE)
- {
- return;
- }
+ int i;
- if (chrdev_registered)
- unregister_chrdev (sound_major, "sound");
+ if (MOD_IN_USE)
+ {
+ return;
+ }
+ if (chrdev_registered)
+ unregister_chrdev(sound_major, "sound");
-#ifdef CONFIG_SEQUENCER
- sound_stop_timer ();
+#if defined(CONFIG_SEQUENCER) || defined(MODULE)
+ sound_stop_timer();
#endif
#ifdef CONFIG_LOWLEVEL_SOUND
- {
- extern void sound_unload_lowlevel_drivers (void);
+ {
+ extern void sound_unload_lowlevel_drivers(void);
- sound_unload_lowlevel_drivers ();
- }
+ sound_unload_lowlevel_drivers();
+ }
#endif
- sound_unload_drivers ();
+ sound_unload_drivers();
- free_all_irqs (); /* If something was left allocated by accident */
+ free_all_irqs(); /* If something was left allocated by accident */
- for (i = 0; i < 8; i++)
- if (dma_alloc_map[i] != DMA_MAP_UNAVAIL)
- {
- printk ("Sound: Hmm, DMA%d was left allocated - fixed\n", i);
- sound_free_dma (i);
- }
-
-
- for (i = 0; i < sound_nblocks; i++)
- {
- vfree (sound_mem_blocks[i]);
- }
+ for (i = 0; i < 8; i++)
+ {
+ if (dma_alloc_map[i] != DMA_MAP_UNAVAIL)
+ {
+ printk(KERN_ERR "Sound: Hmm, DMA%d was left allocated - fixed\n", i);
+ sound_free_dma(i);
+ }
+ }
+ for (i = 0; i < sound_nblocks; i++)
+ {
+ vfree(sound_mem_blocks[i]);
+ }
}
#endif
-void
-tenmicrosec (int *osp)
+void tenmicrosec(int *osp)
{
- udelay (10);
+ udelay(10);
}
-int
-snd_set_irq_handler (int interrupt_level, void (*iproc) (int, void *, struct pt_regs *), char *name, int *osp)
+int snd_set_irq_handler(int interrupt_level, void (*iproc) (int, void *, struct pt_regs *), char *name, int *osp)
{
- int retcode;
- unsigned long flags;
-
- save_flags (flags);
- cli ();
- retcode = request_irq (interrupt_level, iproc, 0, name, NULL);
- if (retcode < 0)
- {
- printk ("Sound: IRQ%d already in use\n", interrupt_level);
- }
- else
- irqs |= (1ul << interrupt_level);
-
- restore_flags (flags);
- return retcode;
+ int retcode;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ retcode = request_irq(interrupt_level, iproc, 0, name, NULL);
+
+ if (retcode < 0)
+ {
+ printk(KERN_ERR "Sound: IRQ%d already in use\n", interrupt_level);
+ }
+ else
+ irqs |= (1ul << interrupt_level);
+
+ restore_flags(flags);
+ return retcode;
}
-void
-snd_release_irq (int vect)
+void snd_release_irq(int vect)
{
- if (!(irqs & (1ul << vect)))
- return;
+ if (!(irqs & (1ul << vect)))
+ return;
- irqs &= ~(1ul << vect);
- free_irq (vect, NULL);
+ irqs &= ~(1ul << vect);
+ free_irq(vect, NULL);
}
-int
-sound_alloc_dma (int chn, char *deviceID)
+int sound_alloc_dma(int chn, char *deviceID)
{
- int err;
+ int err;
- if ((err = request_dma (chn, deviceID)) != 0)
- return err;
+ if ((err = request_dma(chn, deviceID)) != 0)
+ return err;
- dma_alloc_map[chn] = DMA_MAP_FREE;
+ dma_alloc_map[chn] = DMA_MAP_FREE;
- return 0;
+ return 0;
}
-int
-sound_open_dma (int chn, char *deviceID)
+int sound_open_dma(int chn, char *deviceID)
{
- unsigned long flags;
-
- if (chn < 0 || chn > 7 || chn == 4)
- {
- printk ("sound_open_dma: Invalid DMA channel %d\n", chn);
- return 1;
- }
-
- save_flags (flags);
- cli ();
-
- if (dma_alloc_map[chn] != DMA_MAP_FREE)
- {
- printk ("sound_open_dma: DMA channel %d busy or not allocated (%d)\n", chn, dma_alloc_map[chn]);
- restore_flags (flags);
- return 1;
- }
-
- dma_alloc_map[chn] = DMA_MAP_BUSY;
- restore_flags (flags);
- return 0;
+ unsigned long flags;
+
+ if (chn < 0 || chn > 7 || chn == 4)
+ {
+ printk(KERN_ERR "sound_open_dma: Invalid DMA channel %d\n", chn);
+ return 1;
+ }
+ save_flags(flags);
+ cli();
+
+ if (dma_alloc_map[chn] != DMA_MAP_FREE)
+ {
+ printk("sound_open_dma: DMA channel %d busy or not allocated (%d)\n", chn, dma_alloc_map[chn]);
+ restore_flags(flags);
+ return 1;
+ }
+ dma_alloc_map[chn] = DMA_MAP_BUSY;
+ restore_flags(flags);
+ return 0;
}
-void
-sound_free_dma (int chn)
+void sound_free_dma(int chn)
{
- if (dma_alloc_map[chn] == DMA_MAP_UNAVAIL)
- {
- /* printk ("sound_free_dma: Bad access to DMA channel %d\n", chn); */
- return;
- }
- free_dma (chn);
- dma_alloc_map[chn] = DMA_MAP_UNAVAIL;
+ if (dma_alloc_map[chn] == DMA_MAP_UNAVAIL)
+ {
+ /* printk( "sound_free_dma: Bad access to DMA channel %d\n", chn); */
+ return;
+ }
+ free_dma(chn);
+ dma_alloc_map[chn] = DMA_MAP_UNAVAIL;
}
-void
-sound_close_dma (int chn)
+void sound_close_dma(int chn)
{
- unsigned long flags;
-
- save_flags (flags);
- cli ();
-
- if (dma_alloc_map[chn] != DMA_MAP_BUSY)
- {
- printk ("sound_close_dma: Bad access to DMA channel %d\n", chn);
- restore_flags (flags);
- return;
- }
- dma_alloc_map[chn] = DMA_MAP_FREE;
- restore_flags (flags);
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ if (dma_alloc_map[chn] != DMA_MAP_BUSY)
+ {
+ printk(KERN_ERR "sound_close_dma: Bad access to DMA channel %d\n", chn);
+ restore_flags(flags);
+ return;
+ }
+ dma_alloc_map[chn] = DMA_MAP_FREE;
+ restore_flags(flags);
}
-#ifdef CONFIG_SEQUENCER
+#if defined(CONFIG_SEQUENCER) || defined(MODULE)
-static void
-do_sequencer_timer (unsigned long dummy)
+static void do_sequencer_timer(unsigned long dummy)
{
- sequencer_timer (0);
+ sequencer_timer(0);
}
static struct timer_list seq_timer =
{NULL, NULL, 0, 0, do_sequencer_timer};
-void
-request_sound_timer (int count)
+void request_sound_timer(int count)
{
- extern unsigned long seq_time;
+ extern unsigned long seq_time;
- if (count < 0)
- {
-
- {
- seq_timer.expires = (-count) + jiffies;
- add_timer (&seq_timer);
- };
- return;
- }
-
- count += seq_time;
-
- count -= jiffies;
+ if (count < 0)
+ {
+ seq_timer.expires = (-count) + jiffies;
+ add_timer(&seq_timer);
+ return;
+ }
+ count += seq_time;
- if (count < 1)
- count = 1;
+ count -= jiffies;
+ if (count < 1)
+ count = 1;
- {
- seq_timer.expires = (count) + jiffies;
- add_timer (&seq_timer);
- };
+ seq_timer.expires = (count) + jiffies;
+ add_timer(&seq_timer);
}
-void
-sound_stop_timer (void)
+void sound_stop_timer(void)
{
- del_timer (&seq_timer);;
+ del_timer(&seq_timer);;
}
#endif
#ifdef CONFIG_AUDIO
-static int dma_buffsize = DSP_BUFFSIZE;
+static int dma_buffsize = DSP_BUFFSIZE;
int
-sound_alloc_dmap (int dev, struct dma_buffparms *dmap, int chan)
+sound_alloc_dmap(int dev, struct dma_buffparms *dmap, int chan)
{
- char *start_addr, *end_addr;
- int i, dma_pagesize;
+ char *start_addr, *end_addr;
+ int i, dma_pagesize;
- dmap->mapping_flags &= ~DMA_MAP_MAPPED;
+ dmap->mapping_flags &= ~DMA_MAP_MAPPED;
- if (dmap->raw_buf != NULL)
- return 0; /* Already done */
+ if (dmap->raw_buf != NULL)
+ return 0; /* Already done */
- if (dma_buffsize < 4096)
- dma_buffsize = 4096;
+ if (dma_buffsize < 4096)
+ dma_buffsize = 4096;
- if (chan < 4)
- dma_pagesize = 64 * 1024;
- else
- dma_pagesize = 128 * 1024;
+ if (chan < 4)
+ dma_pagesize = 64 * 1024;
+ else
+ dma_pagesize = 128 * 1024;
- dmap->raw_buf = NULL;
+ dmap->raw_buf = NULL;
- dmap->buffsize = dma_buffsize;
+ dmap->buffsize = dma_buffsize;
- if (dmap->buffsize > dma_pagesize)
- dmap->buffsize = dma_pagesize;
+ if (dmap->buffsize > dma_pagesize)
+ dmap->buffsize = dma_pagesize;
- start_addr = NULL;
+ start_addr = NULL;
/*
* Now loop until we get a free buffer. Try to get smaller buffer if
@@ -682,151 +640,142 @@ sound_alloc_dmap (int dev, struct dma_buffparms *dmap, int chan)
* reasons.
*/
- while (start_addr == NULL && dmap->buffsize > PAGE_SIZE)
- {
- int sz, size;
+ while (start_addr == NULL && dmap->buffsize > PAGE_SIZE)
+ {
+ int sz, size;
- for (sz = 0, size = PAGE_SIZE;
- size < dmap->buffsize;
- sz++, size <<= 1);
+ for (sz = 0, size = PAGE_SIZE;
+ size < dmap->buffsize;
+ sz++, size <<= 1);
- dmap->buffsize = PAGE_SIZE * (1 << sz);
+ dmap->buffsize = PAGE_SIZE * (1 << sz);
- if ((start_addr = (char *) __get_free_pages (GFP_ATOMIC, sz, MAX_DMA_ADDRESS)) == NULL)
- dmap->buffsize /= 2;
- }
+ if ((start_addr = (char *) __get_free_pages(GFP_ATOMIC, sz, MAX_DMA_ADDRESS)) == NULL)
+ dmap->buffsize /= 2;
+ }
+
+ if (start_addr == NULL)
+ {
+ printk(KERN_WARNING "Sound error: Couldn't allocate DMA buffer\n");
+ return -ENOMEM;
+ }
+ else
+ {
+ /* make some checks */
+ end_addr = start_addr + dmap->buffsize - 1;
- if (start_addr == NULL)
- {
- printk ("Sound error: Couldn't allocate DMA buffer\n");
- return -ENOMEM;
- }
- else
- {
- /* make some checks */
- end_addr = start_addr + dmap->buffsize - 1;
+ if (debugmem)
+ printk(KERN_DEBUG "sound: start 0x%lx, end 0x%lx\n", (long) start_addr, (long) end_addr);
- if (debugmem)
- printk ("sound: start 0x%lx, end 0x%lx\n",
- (long) start_addr, (long) end_addr);
+ /* now check if it fits into the same dma-pagesize */
- /* now check if it fits into the same dma-pagesize */
+ if (((long) start_addr & ~(dma_pagesize - 1))
+ != ((long) end_addr & ~(dma_pagesize - 1))
+ || end_addr >= (char *) (MAX_DMA_ADDRESS))
+ {
+ printk(KERN_ERR "sound: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, dmap->buffsize);
+ return -EFAULT;
+ }
+ }
+ dmap->raw_buf = start_addr;
+ dmap->raw_buf_phys = virt_to_bus(start_addr);
- if (((long) start_addr & ~(dma_pagesize - 1))
- != ((long) end_addr & ~(dma_pagesize - 1))
- || end_addr >= (char *) (MAX_DMA_ADDRESS))
+ for (i = MAP_NR(start_addr); i <= MAP_NR(end_addr); i++)
{
- printk (
- "sound: Got invalid address 0x%lx for %db DMA-buffer\n",
- (long) start_addr,
- dmap->buffsize);
- return -EFAULT;
+ set_bit(PG_reserved, &mem_map[i].flags);;
}
- }
- dmap->raw_buf = start_addr;
- dmap->raw_buf_phys = virt_to_bus (start_addr);
- for (i = MAP_NR (start_addr); i <= MAP_NR (end_addr); i++)
- {
- set_bit (PG_reserved, &mem_map[i].flags);;
- }
-
- return 0;
+ return 0;
}
-void
-sound_free_dmap (int dev, struct dma_buffparms *dmap, int chan)
+void sound_free_dmap(int dev, struct dma_buffparms *dmap, int chan)
{
- int sz, size, i;
- unsigned long start_addr, end_addr;
+ int sz, size, i;
+ unsigned long start_addr, end_addr;
- if (dmap->raw_buf == NULL)
- return;
+ if (dmap->raw_buf == NULL)
+ return;
- if (dmap->mapping_flags & DMA_MAP_MAPPED)
- return; /* Don't free mmapped buffer. Will use it next time */
+ if (dmap->mapping_flags & DMA_MAP_MAPPED)
+ return; /* Don't free mmapped buffer. Will use it next time */
- for (sz = 0, size = PAGE_SIZE;
- size < dmap->buffsize;
- sz++, size <<= 1);
+ for (sz = 0, size = PAGE_SIZE;
+ size < dmap->buffsize;
+ sz++, size <<= 1);
- start_addr = (unsigned long) dmap->raw_buf;
- end_addr = start_addr + dmap->buffsize;
+ start_addr = (unsigned long) dmap->raw_buf;
+ end_addr = start_addr + dmap->buffsize;
- for (i = MAP_NR (start_addr); i <= MAP_NR (end_addr); i++)
- {
- clear_bit (PG_reserved, &mem_map[i].flags);;
- }
+ for (i = MAP_NR(start_addr); i <= MAP_NR(end_addr); i++)
+ {
+ clear_bit(PG_reserved, &mem_map[i].flags);;
+ }
- free_pages ((unsigned long) dmap->raw_buf, sz);
- dmap->raw_buf = NULL;
+ free_pages((unsigned long) dmap->raw_buf, sz);
+ dmap->raw_buf = NULL;
}
/* Intel version !!!!!!!!! */
-int
-sound_start_dma (int dev, struct dma_buffparms *dmap, int chan,
- unsigned long physaddr,
- int count, int dma_mode, int autoinit)
+
+int sound_start_dma(int dev, struct dma_buffparms *dmap, int chan,
+ unsigned long physaddr,
+ int count, int dma_mode, int autoinit)
{
- unsigned long flags;
-
- /* printk("Start DMA%d %d, %d\n", chan, (int)(physaddr-dmap->raw_buf_phys), count); */
- if (autoinit)
- dma_mode |= DMA_AUTOINIT;
- save_flags (flags);
- cli ();
- disable_dma (chan);
- clear_dma_ff (chan);
- set_dma_mode (chan, dma_mode);
- set_dma_addr (chan, physaddr);
- set_dma_count (chan, count);
- enable_dma (chan);
- restore_flags (flags);
-
- return 0;
+ unsigned long flags;
+
+ /* printk( "Start DMA%d %d, %d\n", chan, (int)(physaddr-dmap->raw_buf_phys), count); */
+ if (autoinit)
+ dma_mode |= DMA_AUTOINIT;
+ save_flags(flags);
+ cli();
+ disable_dma(chan);
+ clear_dma_ff(chan);
+ set_dma_mode(chan, dma_mode);
+ set_dma_addr(chan, physaddr);
+ set_dma_count(chan, count);
+ enable_dma(chan);
+ restore_flags(flags);
+
+ return 0;
}
#endif
-void
-conf_printf (char *name, struct address_info *hw_config)
+void conf_printf(char *name, struct address_info *hw_config)
{
- if (!trace_init)
- return;
+ if (!trace_init)
+ return;
- printk ("<%s> at 0x%03x", name, hw_config->io_base);
+ printk("<%s> at 0x%03x", name, hw_config->io_base);
- if (hw_config->irq)
- printk (" irq %d", (hw_config->irq > 0) ? hw_config->irq : -hw_config->irq);
+ if (hw_config->irq)
+ printk(" irq %d", (hw_config->irq > 0) ? hw_config->irq : -hw_config->irq);
- if (hw_config->dma != -1 || hw_config->dma2 != -1)
- {
- printk (" dma %d", hw_config->dma);
- if (hw_config->dma2 != -1)
- printk (",%d", hw_config->dma2);
- }
-
- printk ("\n");
+ if (hw_config->dma != -1 || hw_config->dma2 != -1)
+ {
+ printk(" dma %d", hw_config->dma);
+ if (hw_config->dma2 != -1)
+ printk(",%d", hw_config->dma2);
+ }
+ printk("\n");
}
-void
-conf_printf2 (char *name, int base, int irq, int dma, int dma2)
+void conf_printf2(char *name, int base, int irq, int dma, int dma2)
{
- if (!trace_init)
- return;
+ if (!trace_init)
+ return;
- printk ("<%s> at 0x%03x", name, base);
+ printk("<%s> at 0x%03x", name, base);
- if (irq)
- printk (" irq %d", (irq > 0) ? irq : -irq);
+ if (irq)
+ printk(" irq %d", (irq > 0) ? irq : -irq);
- if (dma != -1 || dma2 != -1)
- {
- printk (" dma %d", dma);
- if (dma2 != -1)
- printk (",%d", dma2);
- }
-
- printk ("\n");
+ if (dma != -1 || dma2 != -1)
+ {
+ printk(" dma %d", dma);
+ if (dma2 != -1)
+ printk(",%d", dma2);
+ }
+ printk("\n");
}
diff --git a/drivers/sound/soundmodule.h b/drivers/sound/soundmodule.h
new file mode 100644
index 000000000..d590acc0b
--- /dev/null
+++ b/drivers/sound/soundmodule.h
@@ -0,0 +1,41 @@
+#ifndef _SOUNDMODULE_H
+#define _SOUNDMODULE_H
+
+#ifdef MODULE
+
+#include <linux/notifier.h>
+
+#ifdef SOUND_CORE
+
+struct notifier_block *sound_locker=(struct notifier_block *)0;
+
+#define SOUND_INC_USE_COUNT notifier_call_chain(&sound_locker, 1, 0)
+#define SOUND_DEC_USE_COUNT notifier_call_chain(&sound_locker, 0, 0)
+
+#else
+
+#define SOUND_LOCK notifier_chain_register(&sound_locker, &sound_notifier)
+#define SOUND_LOCK_END notifier_chain_unregister(&sound_locker, &sound_notifier)
+
+extern struct notifier_block *sound_locker;
+
+
+static int my_notifier_call(struct notifier_block *b, unsigned long foo, void *bar)
+{
+ if(foo)
+ MOD_INC_USE_COUNT;
+ else
+ MOD_DEC_USE_COUNT;
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block sound_notifier=
+{
+ my_notifier_call,
+ (void *)0,
+ 0
+};
+
+#endif
+#endif
+#endif
diff --git a/drivers/sound/soundvers.h b/drivers/sound/soundvers.h
index 5b9b6a5c6..b3a8356c2 100644
--- a/drivers/sound/soundvers.h
+++ b/drivers/sound/soundvers.h
@@ -1,2 +1,2 @@
-#define SOUND_VERSION_STRING "3.8a"
+#define SOUND_VERSION_STRING "3.8s-971110"
#define SOUND_INTERNAL_VERSION 0x030804
diff --git a/drivers/sound/sscape.c b/drivers/sound/sscape.c
index c5cf7de3f..befd924d0 100644
--- a/drivers/sound/sscape.c
+++ b/drivers/sound/sscape.c
@@ -11,11 +11,12 @@
* for more info.
*/
#include <linux/config.h>
-
+#include <linux/module.h>
#include "sound_config.h"
+#include "soundmodule.h"
-#if defined(CONFIG_SSCAPEHW)
+#if defined(CONFIG_SSCAPEHW) || defined(MODULE)
#include "coproc.h"
@@ -73,25 +74,28 @@
#define CMD_ACK 0x80
typedef struct sscape_info
- {
- int base, irq, dma;
- int ok; /* Properly detected */
- int failed;
- int dma_allocated;
- int codec_audiodev;
- int opened;
- int *osp;
- }
-sscape_info;
-
-static struct sscape_info adev_info =
-{0};
+{
+ int base, irq, dma;
+ int ok; /* Properly detected */
+ int failed;
+ int dma_allocated;
+ int codec_audiodev;
+ int opened;
+ int *osp;
+ int my_audiodev;
+} sscape_info;
+
+static struct sscape_info adev_info = {
+ 0
+};
+
static struct sscape_info *devc = &adev_info;
static int sscape_mididev = -1;
static struct wait_queue *sscape_sleeper = NULL;
-static volatile struct snd_wait sscape_sleep_flag =
-{0};
+static volatile struct snd_wait sscape_sleep_flag = {
+ 0
+};
/* Some older cards have assigned interrupt bits differently than new ones */
static char valid_interrupts_old[] =
@@ -110,165 +114,153 @@ static char old_hardware = 0;
#endif
-static unsigned char
-sscape_read (struct sscape_info *devc, int reg)
+static unsigned char sscape_read(struct sscape_info *devc, int reg)
{
- unsigned long flags;
- unsigned char val;
-
- save_flags (flags);
- cli ();
- outb ((reg), PORT (ODIE_ADDR));
- val = inb (PORT (ODIE_DATA));
- restore_flags (flags);
- return val;
+ unsigned long flags;
+ unsigned char val;
+
+ save_flags(flags);
+ cli();
+ outb((reg), PORT(ODIE_ADDR));
+ val = inb(PORT(ODIE_DATA));
+ restore_flags(flags);
+ return val;
}
static void
-sscape_write (struct sscape_info *devc, int reg, int data)
+sscape_write(struct sscape_info *devc, int reg, int data)
{
- unsigned long flags;
+ unsigned long flags;
- save_flags (flags);
- cli ();
- outb ((reg), PORT (ODIE_ADDR));
- outb ((data), PORT (ODIE_DATA));
- restore_flags (flags);
+ save_flags(flags);
+ cli();
+ outb((reg), PORT(ODIE_ADDR));
+ outb((data), PORT(ODIE_DATA));
+ restore_flags(flags);
}
static void
-host_open (struct sscape_info *devc)
+host_open(struct sscape_info *devc)
{
- outb ((0x00), PORT (HOST_CTRL)); /* Put the board to the host mode */
+ outb((0x00), PORT(HOST_CTRL)); /* Put the board to the host mode */
}
static void
-host_close (struct sscape_info *devc)
+host_close(struct sscape_info *devc)
{
- outb ((0x03), PORT (HOST_CTRL)); /* Put the board to the MIDI mode */
+ outb((0x03), PORT(HOST_CTRL)); /* Put the board to the MIDI mode */
}
static int
-host_write (struct sscape_info *devc, unsigned char *data, int count)
+host_write(struct sscape_info *devc, unsigned char *data, int count)
{
- unsigned long flags;
- int i, timeout_val;
-
- save_flags (flags);
- cli ();
+ unsigned long flags;
+ int i, timeout_val;
- /*
- * Send the command and data bytes
- */
+ save_flags(flags);
+ cli();
- for (i = 0; i < count; i++)
- {
- for (timeout_val = 10000; timeout_val > 0; timeout_val--)
- if (inb (PORT (HOST_CTRL)) & TX_READY)
- break;
+ /*
+ * Send the command and data bytes
+ */
- if (timeout_val <= 0)
- {
- restore_flags (flags);
- return 0;
- }
-
- outb ((data[i]), PORT (HOST_DATA));
- }
+ for (i = 0; i < count; i++)
+ {
+ for (timeout_val = 10000; timeout_val > 0; timeout_val--)
+ if (inb(PORT(HOST_CTRL)) & TX_READY)
+ break;
+
+ if (timeout_val <= 0)
+ {
+ restore_flags(flags);
+ return 0;
+ }
+ outb((data[i]), PORT(HOST_DATA));
+ }
- restore_flags (flags);
+ restore_flags(flags);
- return 1;
+ return 1;
}
static int
-host_read (struct sscape_info *devc)
+host_read(struct sscape_info *devc)
{
- unsigned long flags;
- int timeout_val;
- unsigned char data;
-
- save_flags (flags);
- cli ();
+ unsigned long flags;
+ int timeout_val;
+ unsigned char data;
- /*
- * Read a byte
- */
+ save_flags(flags);
+ cli();
- for (timeout_val = 10000; timeout_val > 0; timeout_val--)
- if (inb (PORT (HOST_CTRL)) & RX_READY)
- break;
+ /*
+ * Read a byte
+ */
- if (timeout_val <= 0)
- {
- restore_flags (flags);
- return -1;
- }
+ for (timeout_val = 10000; timeout_val > 0; timeout_val--)
+ if (inb(PORT(HOST_CTRL)) & RX_READY)
+ break;
- data = inb (PORT (HOST_DATA));
+ if (timeout_val <= 0)
+ {
+ restore_flags(flags);
+ return -1;
+ }
+ data = inb(PORT(HOST_DATA));
- restore_flags (flags);
+ restore_flags(flags);
- return data;
+ return data;
}
-static int
-host_command1 (struct sscape_info *devc, int cmd)
-{
- unsigned char buf[10];
-
- buf[0] = (unsigned char) (cmd & 0xff);
-
- return host_write (devc, buf, 1);
-}
static int
-host_command2 (struct sscape_info *devc, int cmd, int parm1)
+host_command2(struct sscape_info *devc, int cmd, int parm1)
{
- unsigned char buf[10];
+ unsigned char buf[10];
- buf[0] = (unsigned char) (cmd & 0xff);
- buf[1] = (unsigned char) (parm1 & 0xff);
+ buf[0] = (unsigned char) (cmd & 0xff);
+ buf[1] = (unsigned char) (parm1 & 0xff);
- return host_write (devc, buf, 2);
+ return host_write(devc, buf, 2);
}
static int
-host_command3 (struct sscape_info *devc, int cmd, int parm1, int parm2)
+host_command3(struct sscape_info *devc, int cmd, int parm1, int parm2)
{
- unsigned char buf[10];
+ unsigned char buf[10];
- buf[0] = (unsigned char) (cmd & 0xff);
- buf[1] = (unsigned char) (parm1 & 0xff);
- buf[2] = (unsigned char) (parm2 & 0xff);
+ buf[0] = (unsigned char) (cmd & 0xff);
+ buf[1] = (unsigned char) (parm1 & 0xff);
+ buf[2] = (unsigned char) (parm2 & 0xff);
- return host_write (devc, buf, 3);
+ return host_write(devc, buf, 3);
}
static void
-set_mt32 (struct sscape_info *devc, int value)
+set_mt32(struct sscape_info *devc, int value)
{
- host_open (devc);
- host_command2 (devc, CMD_SET_MT32,
- value ? 1 : 0);
- if (host_read (devc) != CMD_ACK)
- {
- /* printk ("SNDSCAPE: Setting MT32 mode failed\n"); */
- }
- host_close (devc);
+ host_open(devc);
+ host_command2(devc, CMD_SET_MT32,
+ value ? 1 : 0);
+ if (host_read(devc) != CMD_ACK)
+ {
+ /* printk( "SNDSCAPE: Setting MT32 mode failed\n"); */
+ }
+ host_close(devc);
}
static void
-set_control (struct sscape_info *devc, int ctrl, int value)
+set_control(struct sscape_info *devc, int ctrl, int value)
{
- host_open (devc);
- host_command3 (devc, CMD_SET_CONTROL, ctrl, value);
- if (host_read (devc) != CMD_ACK)
- {
- /* printk ("SNDSCAPE: Setting control (%d) failed\n", ctrl); */
- }
- host_close (devc);
+ host_open(devc);
+ host_command3(devc, CMD_SET_CONTROL, ctrl, value);
+ if (host_read(devc) != CMD_ACK)
+ {
+ /* printk( "SNDSCAPE: Setting control (%d) failed\n", ctrl); */
+ }
+ host_close(devc);
}
@@ -276,378 +268,370 @@ set_control (struct sscape_info *devc, int ctrl, int value)
static void
-do_dma (struct sscape_info *devc, int dma_chan, unsigned long buf, int blk_size, int mode)
+do_dma(struct sscape_info *devc, int dma_chan, unsigned long buf, int blk_size, int mode)
{
- unsigned char temp;
-
- if (dma_chan != SSCAPE_DMA_A)
- {
- printk ("SSCAPE: Tried to use DMA channel != A. Why?\n");
- return;
- }
-
- audio_devs[devc->codec_audiodev]->flags &= ~DMA_AUTOMODE;
- DMAbuf_start_dma (devc->codec_audiodev,
- buf,
- blk_size, mode);
- audio_devs[devc->codec_audiodev]->flags |= DMA_AUTOMODE;
-
- temp = devc->dma << 4; /* Setup DMA channel select bits */
- if (devc->dma <= 3)
- temp |= 0x80; /* 8 bit DMA channel */
-
- temp |= 1; /* Trigger DMA */
- sscape_write (devc, GA_DMAA_REG, temp);
- temp &= 0xfe; /* Clear DMA trigger */
- sscape_write (devc, GA_DMAA_REG, temp);
+ unsigned char temp;
+
+ if (dma_chan != SSCAPE_DMA_A)
+ {
+ printk("SSCAPE: Tried to use DMA channel != A. Why?\n");
+ return;
+ }
+ audio_devs[devc->codec_audiodev]->flags &= ~DMA_AUTOMODE;
+ DMAbuf_start_dma(devc->codec_audiodev,
+ buf,
+ blk_size, mode);
+ audio_devs[devc->codec_audiodev]->flags |= DMA_AUTOMODE;
+
+ temp = devc->dma << 4; /* Setup DMA channel select bits */
+ if (devc->dma <= 3)
+ temp |= 0x80; /* 8 bit DMA channel */
+
+ temp |= 1; /* Trigger DMA */
+ sscape_write(devc, GA_DMAA_REG, temp);
+ temp &= 0xfe; /* Clear DMA trigger */
+ sscape_write(devc, GA_DMAA_REG, temp);
}
static int
-verify_mpu (struct sscape_info *devc)
+verify_mpu(struct sscape_info *devc)
{
- /*
- * The SoundScape board could be in three modes (MPU, 8250 and host).
- * If the card is not in the MPU mode, enabling the MPU driver will
- * cause infinite loop (the driver believes that there is always some
- * received data in the buffer.
- *
- * Detect this by looking if there are more than 10 received MIDI bytes
- * (0x00) in the buffer.
- */
-
- int i;
-
- for (i = 0; i < 10; i++)
- {
- if (inb (devc->base + HOST_CTRL) & 0x80)
- return 1;
+ /*
+ * The SoundScape board could be in three modes (MPU, 8250 and host).
+ * If the card is not in the MPU mode, enabling the MPU driver will
+ * cause infinite loop (the driver believes that there is always some
+ * received data in the buffer.
+ *
+ * Detect this by looking if there are more than 10 received MIDI bytes
+ * (0x00) in the buffer.
+ */
- if (inb (devc->base) != 0x00)
- return 1;
- }
+ int i;
+
+ for (i = 0; i < 10; i++)
+ {
+ if (inb(devc->base + HOST_CTRL) & 0x80)
+ return 1;
+
+ if (inb(devc->base) != 0x00)
+ return 1;
+ }
- printk ("SoundScape: The device is not in the MPU-401 mode\n");
- return 0;
+ printk("SoundScape: The device is not in the MPU-401 mode\n");
+ return 0;
}
static int
-sscape_coproc_open (void *dev_info, int sub_device)
+sscape_coproc_open(void *dev_info, int sub_device)
{
- if (sub_device == COPR_MIDI)
- {
- set_mt32 (devc, 0);
- if (!verify_mpu (devc))
- return -EIO;
- }
-
- sscape_sleep_flag.opts = WK_NONE;
- return 0;
+ if (sub_device == COPR_MIDI)
+ {
+ set_mt32(devc, 0);
+ if (!verify_mpu(devc))
+ return -EIO;
+ }
+ sscape_sleep_flag.opts = WK_NONE;
+ return 0;
}
static void
-sscape_coproc_close (void *dev_info, int sub_device)
+sscape_coproc_close(void *dev_info, int sub_device)
{
- struct sscape_info *devc = dev_info;
- unsigned long flags;
-
- save_flags (flags);
- cli ();
- if (devc->dma_allocated)
- {
- sscape_write (devc, GA_DMAA_REG, 0x20); /* DMA channel disabled */
- devc->dma_allocated = 0;
- }
- sscape_sleep_flag.opts = WK_NONE;
- restore_flags (flags);
-
- return;
+ struct sscape_info *devc = dev_info;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (devc->dma_allocated)
+ {
+ sscape_write(devc, GA_DMAA_REG, 0x20); /* DMA channel disabled */
+ devc->dma_allocated = 0;
+ }
+ sscape_sleep_flag.opts = WK_NONE;
+ restore_flags(flags);
+
+ return;
}
static void
-sscape_coproc_reset (void *dev_info)
+sscape_coproc_reset(void *dev_info)
{
}
static int
-sscape_download_boot (struct sscape_info *devc, unsigned char *block, int size, int flag)
+sscape_download_boot(struct sscape_info *devc, unsigned char *block, int size, int flag)
{
- unsigned long flags;
- unsigned char temp;
- volatile int done, timeout_val;
- static unsigned char codec_dma_bits = 0;
-
- if (flag & CPF_FIRST)
- {
- /*
- * First block. Have to allocate DMA and to reset the board
- * before continuing.
- */
-
- save_flags (flags);
- cli ();
- codec_dma_bits = sscape_read (devc, GA_CDCFG_REG);
-
- if (devc->dma_allocated == 0)
- {
- devc->dma_allocated = 1;
- }
- restore_flags (flags);
-
- sscape_write (devc, GA_HMCTL_REG,
- (temp = sscape_read (devc, GA_HMCTL_REG)) & 0x3f); /*Reset */
-
- for (timeout_val = 10000; timeout_val > 0; timeout_val--)
- sscape_read (devc, GA_HMCTL_REG); /* Delay */
+ unsigned long flags;
+ unsigned char temp;
+ volatile int done, timeout_val;
+ static unsigned char codec_dma_bits = 0;
- /* Take board out of reset */
- sscape_write (devc, GA_HMCTL_REG,
- (temp = sscape_read (devc, GA_HMCTL_REG)) | 0x80);
- }
-
- /*
- * Transfer one code block using DMA
- */
- if (audio_devs[devc->codec_audiodev]->dmap_out->raw_buf == NULL)
- {
- printk ("SSCAPE: Error: DMA buffer not available\n");
- return 0;
- }
-
- memcpy (audio_devs[devc->codec_audiodev]->dmap_out->raw_buf, block, size);
+ if (flag & CPF_FIRST)
+ {
+ /*
+ * First block. Have to allocate DMA and to reset the board
+ * before continuing.
+ */
+
+ save_flags(flags);
+ cli();
+ codec_dma_bits = sscape_read(devc, GA_CDCFG_REG);
+
+ if (devc->dma_allocated == 0)
+ {
+ devc->dma_allocated = 1;
+ }
+ restore_flags(flags);
+
+ sscape_write(devc, GA_HMCTL_REG,
+ (temp = sscape_read(devc, GA_HMCTL_REG)) & 0x3f); /*Reset */
+
+ for (timeout_val = 10000; timeout_val > 0; timeout_val--)
+ sscape_read(devc, GA_HMCTL_REG); /* Delay */
+
+ /* Take board out of reset */
+ sscape_write(devc, GA_HMCTL_REG,
+ (temp = sscape_read(devc, GA_HMCTL_REG)) | 0x80);
+ }
+ /*
+ * Transfer one code block using DMA
+ */
+ if (audio_devs[devc->codec_audiodev]->dmap_out->raw_buf == NULL)
+ {
+ printk("SSCAPE: Error: DMA buffer not available\n");
+ return 0;
+ }
+ memcpy(audio_devs[devc->codec_audiodev]->dmap_out->raw_buf, block, size);
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
/******** INTERRUPTS DISABLED NOW ********/
- do_dma (devc, SSCAPE_DMA_A,
- audio_devs[devc->codec_audiodev]->dmap_out->raw_buf_phys,
- size, DMA_MODE_WRITE);
-
- /*
- * Wait until transfer completes.
- */
- sscape_sleep_flag.opts = WK_NONE;
- done = 0;
- timeout_val = 30;
- while (!done && timeout_val-- > 0)
- {
- int resid;
-
- {
- unsigned long tlimit;
-
- if (HZ / 50)
- current->timeout = tlimit = jiffies + (HZ / 50);
- else
- tlimit = (unsigned long) -1;
- sscape_sleep_flag.opts = WK_SLEEP;
- interruptible_sleep_on (&sscape_sleeper);
- if (!(sscape_sleep_flag.opts & WK_WAKEUP))
+ do_dma(devc, SSCAPE_DMA_A,
+ audio_devs[devc->codec_audiodev]->dmap_out->raw_buf_phys,
+ size, DMA_MODE_WRITE);
+
+ /*
+ * Wait until transfer completes.
+ */
+ sscape_sleep_flag.opts = WK_NONE;
+ done = 0;
+ timeout_val = 30;
+ while (!done && timeout_val-- > 0)
{
- if (jiffies >= tlimit)
- sscape_sleep_flag.opts |= WK_TIMEOUT;
+ int resid;
+
+ {
+ unsigned long tlimit;
+
+ if (HZ / 50)
+ current->timeout = tlimit = jiffies + (HZ / 50);
+ else
+ tlimit = (unsigned long) -1;
+ sscape_sleep_flag.opts = WK_SLEEP;
+ interruptible_sleep_on(&sscape_sleeper);
+ if (!(sscape_sleep_flag.opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ sscape_sleep_flag.opts |= WK_TIMEOUT;
+ }
+ sscape_sleep_flag.opts &= ~WK_SLEEP;
+ };
+ clear_dma_ff(devc->dma);
+ if ((resid = get_dma_residue(devc->dma)) == 0)
+ {
+ done = 1;
+ }
}
- sscape_sleep_flag.opts &= ~WK_SLEEP;
- };
- clear_dma_ff (devc->dma);
- if ((resid = get_dma_residue (devc->dma)) == 0)
- {
- done = 1;
- }
- }
-
- restore_flags (flags);
- if (!done)
- return 0;
-
- if (flag & CPF_LAST)
- {
- /*
- * Take the board out of reset
- */
- outb ((0x00), PORT (HOST_CTRL));
- outb ((0x00), PORT (MIDI_CTRL));
-
- temp = sscape_read (devc, GA_HMCTL_REG);
- temp |= 0x40;
- sscape_write (devc, GA_HMCTL_REG, temp); /* Kickstart the board */
-
- /*
- * Wait until the ODB wakes up
- */
-
- save_flags (flags);
- cli ();
- done = 0;
- timeout_val = 5 * HZ;
- while (!done && timeout_val-- > 0)
- {
- unsigned char x;
+ restore_flags(flags);
+ if (!done)
+ return 0;
+ if (flag & CPF_LAST)
{
- unsigned long tlimit;
-
- if (1)
- current->timeout = tlimit = jiffies + (1);
- else
- tlimit = (unsigned long) -1;
- sscape_sleep_flag.opts = WK_SLEEP;
- interruptible_sleep_on (&sscape_sleeper);
- if (!(sscape_sleep_flag.opts & WK_WAKEUP))
- {
- if (jiffies >= tlimit)
- sscape_sleep_flag.opts |= WK_TIMEOUT;
- }
- sscape_sleep_flag.opts &= ~WK_SLEEP;
- };
- x = inb (PORT (HOST_DATA));
- if (x == 0xff || x == 0xfe) /* OBP startup acknowledge */
- {
- DDB (printk ("Soundscape: Acknowledge = %x\n", x));
- done = 1;
- }
- }
- sscape_write (devc, GA_CDCFG_REG, codec_dma_bits);
-
- restore_flags (flags);
- if (!done)
- {
- printk ("SoundScape: The OBP didn't respond after code download\n");
- return 0;
- }
-
- save_flags (flags);
- cli ();
- done = 0;
- timeout_val = 5 * HZ;
- while (!done && timeout_val-- > 0)
- {
-
- {
- unsigned long tlimit;
-
- if (1)
- current->timeout = tlimit = jiffies + (1);
- else
- tlimit = (unsigned long) -1;
- sscape_sleep_flag.opts = WK_SLEEP;
- interruptible_sleep_on (&sscape_sleeper);
- if (!(sscape_sleep_flag.opts & WK_WAKEUP))
- {
- if (jiffies >= tlimit)
- sscape_sleep_flag.opts |= WK_TIMEOUT;
- }
- sscape_sleep_flag.opts &= ~WK_SLEEP;
- };
- if (inb (PORT (HOST_DATA)) == 0xfe) /* Host startup acknowledge */
- done = 1;
- }
- restore_flags (flags);
- if (!done)
- {
- printk ("SoundScape: OBP Initialization failed.\n");
- return 0;
- }
-
- printk ("SoundScape board initialized OK\n");
- set_control (devc, CTL_MASTER_VOL, 100);
- set_control (devc, CTL_SYNTH_VOL, 100);
+ /*
+ * Take the board out of reset
+ */
+ outb((0x00), PORT(HOST_CTRL));
+ outb((0x00), PORT(MIDI_CTRL));
+
+ temp = sscape_read(devc, GA_HMCTL_REG);
+ temp |= 0x40;
+ sscape_write(devc, GA_HMCTL_REG, temp); /* Kickstart the board */
+
+ /*
+ * Wait until the ODB wakes up
+ */
+
+ save_flags(flags);
+ cli();
+ done = 0;
+ timeout_val = 5 * HZ;
+ while (!done && timeout_val-- > 0)
+ {
+ unsigned char x;
+
+
+ {
+ unsigned long tlimit;
+
+ if (1)
+ current->timeout = tlimit = jiffies + (1);
+ else
+ tlimit = (unsigned long) -1;
+ sscape_sleep_flag.opts = WK_SLEEP;
+ interruptible_sleep_on(&sscape_sleeper);
+ if (!(sscape_sleep_flag.opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ sscape_sleep_flag.opts |= WK_TIMEOUT;
+ }
+ sscape_sleep_flag.opts &= ~WK_SLEEP;
+ };
+ x = inb(PORT(HOST_DATA));
+ if (x == 0xff || x == 0xfe) /* OBP startup acknowledge */
+ {
+ DDB(printk("Soundscape: Acknowledge = %x\n", x));
+ done = 1;
+ }
+ }
+ sscape_write(devc, GA_CDCFG_REG, codec_dma_bits);
+
+ restore_flags(flags);
+ if (!done)
+ {
+ printk("SoundScape: The OBP didn't respond after code download\n");
+ return 0;
+ }
+ save_flags(flags);
+ cli();
+ done = 0;
+ timeout_val = 5 * HZ;
+ while (!done && timeout_val-- > 0)
+ {
+
+ {
+ unsigned long tlimit;
+
+ if (1)
+ current->timeout = tlimit = jiffies + (1);
+ else
+ tlimit = (unsigned long) -1;
+ sscape_sleep_flag.opts = WK_SLEEP;
+ interruptible_sleep_on(&sscape_sleeper);
+ if (!(sscape_sleep_flag.opts & WK_WAKEUP))
+ {
+ if (jiffies >= tlimit)
+ sscape_sleep_flag.opts |= WK_TIMEOUT;
+ }
+ sscape_sleep_flag.opts &= ~WK_SLEEP;
+ };
+ if (inb(PORT(HOST_DATA)) == 0xfe) /* Host startup acknowledge */
+ done = 1;
+ }
+ restore_flags(flags);
+ if (!done)
+ {
+ printk("SoundScape: OBP Initialization failed.\n");
+ return 0;
+ }
+ printk("SoundScape board initialized OK\n");
+ set_control(devc, CTL_MASTER_VOL, 100);
+ set_control(devc, CTL_SYNTH_VOL, 100);
#ifdef SSCAPE_DEBUG3
- /*
- * Temporary debugging aid. Print contents of the registers after
- * downloading the code.
- */
- {
- int i;
-
- for (i = 0; i < 13; i++)
- printk ("I%d = %02x (new value)\n", i, sscape_read (devc, i));
- }
+ /*
+ * Temporary debugging aid. Print contents of the registers after
+ * downloading the code.
+ */
+ {
+ int i;
+
+ for (i = 0; i < 13; i++)
+ printk("I%d = %02x (new value)\n", i, sscape_read(devc, i));
+ }
#endif
- }
-
- return 1;
+ }
+ return 1;
}
static int
-download_boot_block (void *dev_info, copr_buffer * buf)
+download_boot_block(void *dev_info, copr_buffer * buf)
{
- if (buf->len <= 0 || buf->len > sizeof (buf->data))
- return -EINVAL;
+ if (buf->len <= 0 || buf->len > sizeof(buf->data))
+ return -EINVAL;
- if (!sscape_download_boot (devc, buf->data, buf->len, buf->flags))
- {
- printk ("SSCAPE: Unable to load microcode block to the OBP.\n");
- return -EIO;
- }
-
- return 0;
+ if (!sscape_download_boot(devc, buf->data, buf->len, buf->flags))
+ {
+ printk("SSCAPE: Unable to load microcode block to the OBP.\n");
+ return -EIO;
+ }
+ return 0;
}
static int
-sscape_coproc_ioctl (void *dev_info, unsigned int cmd, caddr_t arg, int local)
+sscape_coproc_ioctl(void *dev_info, unsigned int cmd, caddr_t arg, int local)
{
- switch (cmd)
- {
- case SNDCTL_COPR_RESET:
- sscape_coproc_reset (dev_info);
- return 0;
- break;
-
- case SNDCTL_COPR_LOAD:
- {
- copr_buffer *buf;
- int err;
-
- buf = (copr_buffer *) vmalloc (sizeof (copr_buffer));
- if (buf == NULL)
- return -ENOSPC;
- memcpy ((char *) buf, (&((char *) arg)[0]), sizeof (*buf));
- err = download_boot_block (dev_info, buf);
- vfree (buf);
- return err;
- }
- break;
-
- default:
- return -EINVAL;
- }
+ switch (cmd)
+ {
+ case SNDCTL_COPR_RESET:
+ sscape_coproc_reset(dev_info);
+ return 0;
+ break;
+
+ case SNDCTL_COPR_LOAD:
+ {
+ copr_buffer *buf;
+ int err;
+
+ buf = (copr_buffer *) vmalloc(sizeof(copr_buffer));
+ if (buf == NULL)
+ return -ENOSPC;
+ memcpy((char *) buf, (&((char *) arg)[0]), sizeof(*buf));
+ err = download_boot_block(dev_info, buf);
+ vfree(buf);
+ return err;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
}
static coproc_operations sscape_coproc_operations =
{
- "SoundScape M68K",
- sscape_coproc_open,
- sscape_coproc_close,
- sscape_coproc_ioctl,
- sscape_coproc_reset,
- &adev_info
+ "SoundScape M68K",
+ sscape_coproc_open,
+ sscape_coproc_close,
+ sscape_coproc_ioctl,
+ sscape_coproc_reset,
+ &adev_info
};
static int sscape_detected = 0;
void
-attach_sscape (struct address_info *hw_config)
+attach_sscape(struct address_info *hw_config)
{
#ifndef SSCAPE_REGS
- /*
- * Config register values for Spea/V7 Media FX and Ensoniq S-2000.
- * These values are card
- * dependent. If you have another SoundScape based card, you have to
- * find the correct values. Do the following:
- * - Compile this driver with SSCAPE_DEBUG1 defined.
- * - Shut down and power off your machine.
- * - Boot with DOS so that the SSINIT.EXE program is run.
- * - Warm boot to {Linux|SYSV|BSD} and write down the lines displayed
- * when detecting the SoundScape.
- * - Modify the following list to use the values printed during boot.
- * Undefine the SSCAPE_DEBUG1
- */
+ /*
+ * Config register values for Spea/V7 Media FX and Ensoniq S-2000.
+ * These values are card
+ * dependent. If you have another SoundScape based card, you have to
+ * find the correct values. Do the following:
+ * - Compile this driver with SSCAPE_DEBUG1 defined.
+ * - Shut down and power off your machine.
+ * - Boot with DOS so that the SSINIT.EXE program is run.
+ * - Warm boot to {Linux|SYSV|BSD} and write down the lines displayed
+ * when detecting the SoundScape.
+ * - Modify the following list to use the values printed during boot.
+ * Undefine the SSCAPE_DEBUG1
+ */
#define SSCAPE_REGS { \
/* I0 */ 0x00, \
0xf0, /* Note! Ignored. Set always to 0xf0 */ \
@@ -662,356 +646,400 @@ attach_sscape (struct address_info *hw_config)
}
#endif
- unsigned long flags;
- static unsigned char regs[10] = SSCAPE_REGS;
-
- int i, irq_bits = 0xff;
-
- if (sscape_detected != hw_config->io_base)
- return;
-
- request_region (devc->base + 2, 6, "SoundScape");
- if (old_hardware)
- {
- valid_interrupts = valid_interrupts_old;
- conf_printf ("Ensoniq SoundScape (old)", hw_config);
- }
- else
- conf_printf ("Ensoniq SoundScape", hw_config);
-
- for (i = 0; i < sizeof (valid_interrupts); i++)
- if (hw_config->irq == valid_interrupts[i])
- {
- irq_bits = i;
- break;
- }
-
- if (hw_config->irq > 15 || (regs[4] = irq_bits == 0xff))
- {
- printk ("Invalid IRQ%d\n", hw_config->irq);
- return;
- }
-
- save_flags (flags);
- cli ();
-
- for (i = 1; i < 10; i++)
- switch (i)
- {
- case 1: /* Host interrupt enable */
- sscape_write (devc, i, 0xf0); /* All interrupts enabled */
- break;
-
- case 2: /* DMA A status/trigger register */
- case 3: /* DMA B status/trigger register */
- sscape_write (devc, i, 0x20); /* DMA channel disabled */
- break;
-
- case 4: /* Host interrupt config reg */
- sscape_write (devc, i, 0xf0 | (irq_bits << 2) | irq_bits);
- break;
-
- case 5: /* Don't destroy CD-ROM DMA config bits (0xc0) */
- sscape_write (devc, i, (regs[i] & 0x3f) |
- (sscape_read (devc, i) & 0xc0));
- break;
-
- case 6: /* CD-ROM config (WSS codec actually) */
- sscape_write (devc, i, regs[i]);
- break;
-
- case 9: /* Master control reg. Don't modify CR-ROM bits. Disable SB emul */
- sscape_write (devc, i,
- (sscape_read (devc, i) & 0xf0) | 0x08);
- break;
-
- default:
- sscape_write (devc, i, regs[i]);
- }
-
- restore_flags (flags);
+ unsigned long flags;
+ static unsigned char regs[10] = SSCAPE_REGS;
+
+ int i, irq_bits = 0xff;
+
+ if (sscape_detected != hw_config->io_base)
+ return;
+
+ request_region(devc->base + 2, 6, "SoundScape");
+ if (old_hardware)
+ {
+ valid_interrupts = valid_interrupts_old;
+ conf_printf("Ensoniq SoundScape (old)", hw_config);
+ } else
+ conf_printf("Ensoniq SoundScape", hw_config);
+
+ for (i = 0; i < sizeof(valid_interrupts); i++)
+ if (hw_config->irq == valid_interrupts[i])
+ {
+ irq_bits = i;
+ break;
+ }
+ if (hw_config->irq > 15 || (regs[4] = irq_bits == 0xff))
+ {
+ printk("Invalid IRQ%d\n", hw_config->irq);
+ return;
+ }
+ save_flags(flags);
+ cli();
+
+ for (i = 1; i < 10; i++)
+ switch (i)
+ {
+ case 1: /* Host interrupt enable */
+ sscape_write(devc, i, 0xf0); /* All interrupts enabled */
+ break;
+
+ case 2: /* DMA A status/trigger register */
+ case 3: /* DMA B status/trigger register */
+ sscape_write(devc, i, 0x20); /* DMA channel disabled */
+ break;
+
+ case 4: /* Host interrupt config reg */
+ sscape_write(devc, i, 0xf0 | (irq_bits << 2) | irq_bits);
+ break;
+
+ case 5: /* Don't destroy CD-ROM DMA config bits (0xc0) */
+ sscape_write(devc, i, (regs[i] & 0x3f) |
+ (sscape_read(devc, i) & 0xc0));
+ break;
+
+ case 6: /* CD-ROM config (WSS codec actually) */
+ sscape_write(devc, i, regs[i]);
+ break;
+
+ case 9: /* Master control reg. Don't modify CR-ROM bits. Disable SB emul */
+ sscape_write(devc, i,
+ (sscape_read(devc, i) & 0xf0) | 0x08);
+ break;
+
+ default:
+ sscape_write(devc, i, regs[i]);
+ }
+
+ restore_flags(flags);
#ifdef SSCAPE_DEBUG2
- /*
- * Temporary debugging aid. Print contents of the registers after
- * changing them.
- */
- {
- int i;
-
- for (i = 0; i < 13; i++)
- printk ("I%d = %02x (new value)\n", i, sscape_read (devc, i));
- }
+ /*
+ * Temporary debugging aid. Print contents of the registers after
+ * changing them.
+ */
+ {
+ int i;
+
+ for (i = 0; i < 13; i++)
+ printk("I%d = %02x (new value)\n", i, sscape_read(devc, i));
+ }
#endif
#if defined(CONFIG_MIDI) && defined(CONFIG_MPU_EMU)
- if (probe_mpu401 (hw_config))
- hw_config->always_detect = 1;
- {
- int prev_devs;
-
- prev_devs = num_midis;
- hw_config->name = "SoundScape";
-
- hw_config->irq *= -1; /* Negative value signals IRQ sharing */
- attach_mpu401 (hw_config);
- hw_config->irq *= -1; /* Restore it */
-
- if (num_midis == (prev_devs + 1)) /* The MPU driver installed itself */
- {
- sscape_mididev = prev_devs;
- midi_devs[prev_devs]->coproc = &sscape_coproc_operations;
- }
- }
+ if (probe_mpu401(hw_config))
+ hw_config->always_detect = 1;
+ {
+ hw_config->name = "SoundScape";
+
+ hw_config->irq *= -1; /* Negative value signals IRQ sharing */
+ attach_mpu401(hw_config);
+ hw_config->irq *= -1; /* Restore it */
+
+ if (hw_config->slots[1] != -1) /* The MPU driver installed itself */
+ {
+ sscape_mididev = hw_config->slots[1];
+ midi_devs[hw_config->slots[1]]->coproc = &sscape_coproc_operations;
+ }
+ }
#endif
- sscape_write (devc, GA_INTENA_REG, 0x80); /* Master IRQ enable */
- devc->ok = 1;
- devc->failed = 0;
+ sscape_write(devc, GA_INTENA_REG, 0x80); /* Master IRQ enable */
+ devc->ok = 1;
+ devc->failed = 0;
}
static int
-detect_ga (sscape_info * devc)
+detect_ga(sscape_info * devc)
{
- unsigned char save;
-
- DDB (printk ("Entered Soundscape detect_ga(%x)\n", devc->base));
-
- if (check_region (devc->base, 8))
- return 0;
-
- /*
- * First check that the address register of "ODIE" is
- * there and that it has exactly 4 writable bits.
- * First 4 bits
- */
- if ((save = inb (PORT (ODIE_ADDR))) & 0xf0)
- {
- DDB (printk ("soundscape: Detect error A\n"));
- return 0;
- }
-
- outb ((0x00), PORT (ODIE_ADDR));
- if (inb (PORT (ODIE_ADDR)) != 0x00)
- {
- DDB (printk ("soundscape: Detect error B\n"));
- return 0;
- }
-
- outb ((0xff), PORT (ODIE_ADDR));
- if (inb (PORT (ODIE_ADDR)) != 0x0f)
- {
- DDB (printk ("soundscape: Detect error C\n"));
- return 0;
- }
-
- outb ((save), PORT (ODIE_ADDR));
-
- /*
- * Now verify that some indirect registers return zero on some bits.
- * This may break the driver with some future revisions of "ODIE" but...
- */
-
- if (sscape_read (devc, 0) & 0x0c)
- {
- DDB (printk ("soundscape: Detect error D (%x)\n", sscape_read (devc, 0)));
- return 0;
- }
-
- if (sscape_read (devc, 1) & 0x0f)
- {
- DDB (printk ("soundscape: Detect error E\n"));
- return 0;
- }
-
- if (sscape_read (devc, 5) & 0x0f)
- {
- DDB (printk ("soundscape: Detect error F\n"));
- return 0;
- }
-
- return 1;
-}
+ unsigned char save;
-int
-probe_sscape (struct address_info *hw_config)
-{
+ DDB(printk("Entered Soundscape detect_ga(%x)\n", devc->base));
- if (sscape_detected != 0 && sscape_detected != hw_config->io_base)
- return 0;
+ if (check_region(devc->base, 8))
+ return 0;
- devc->base = hw_config->io_base;
- devc->irq = hw_config->irq;
- devc->dma = hw_config->dma;
- devc->osp = hw_config->osp;
+ /*
+ * First check that the address register of "ODIE" is
+ * there and that it has exactly 4 writable bits.
+ * First 4 bits
+ */
+ if ((save = inb(PORT(ODIE_ADDR))) & 0xf0)
+ {
+ DDB(printk("soundscape: Detect error A\n"));
+ return 0;
+ }
+ outb((0x00), PORT(ODIE_ADDR));
+ if (inb(PORT(ODIE_ADDR)) != 0x00)
+ {
+ DDB(printk("soundscape: Detect error B\n"));
+ return 0;
+ }
+ outb((0xff), PORT(ODIE_ADDR));
+ if (inb(PORT(ODIE_ADDR)) != 0x0f)
+ {
+ DDB(printk("soundscape: Detect error C\n"));
+ return 0;
+ }
+ outb((save), PORT(ODIE_ADDR));
-#ifdef SSCAPE_DEBUG1
- /*
- * Temporary debugging aid. Print contents of the registers before
- * changing them.
- */
- {
- int i;
-
- for (i = 0; i < 13; i++)
- printk ("I%d = %02x (old value)\n", i, sscape_read (devc, i));
- }
-#endif
+ /*
+ * Now verify that some indirect registers return zero on some bits.
+ * This may break the driver with some future revisions of "ODIE" but...
+ */
+ if (sscape_read(devc, 0) & 0x0c)
+ {
+ DDB(printk("soundscape: Detect error D (%x)\n", sscape_read(devc, 0)));
+ return 0;
+ }
+ if (sscape_read(devc, 1) & 0x0f)
+ {
+ DDB(printk("soundscape: Detect error E\n"));
+ return 0;
+ }
+ if (sscape_read(devc, 5) & 0x0f)
+ {
+ DDB(printk("soundscape: Detect error F\n"));
+ return 0;
+ }
+ return 1;
+}
- devc->failed = 1;
+int
+probe_sscape(struct address_info *hw_config)
+{
- if (!detect_ga (devc))
- return 0;
+ if (sscape_detected != 0 && sscape_detected != hw_config->io_base)
+ return 0;
- if (old_hardware) /* Check that it's really an old Spea/Reveal card. */
- {
- unsigned char tmp;
- int cc;
+ devc->base = hw_config->io_base;
+ devc->irq = hw_config->irq;
+ devc->dma = hw_config->dma;
+ devc->osp = hw_config->osp;
- if (!((tmp = sscape_read (devc, GA_HMCTL_REG)) & 0xc0))
+#ifdef SSCAPE_DEBUG1
+ /*
+ * Temporary debugging aid. Print contents of the registers before
+ * changing them.
+ */
{
- sscape_write (devc, GA_HMCTL_REG, tmp | 0x80);
- for (cc = 0; cc < 200000; ++cc)
- inb (devc->base + ODIE_ADDR);
+ int i;
+
+ for (i = 0; i < 13; i++)
+ printk("I%d = %02x (old value)\n", i, sscape_read(devc, i));
}
- }
+#endif
+
+ devc->failed = 1;
- sscape_detected = hw_config->io_base;
+ if (!detect_ga(devc))
+ return 0;
- return 1;
+ if (old_hardware) /* Check that it's really an old Spea/Reveal card. */
+ {
+ unsigned char tmp;
+ int cc;
+
+ if (!((tmp = sscape_read(devc, GA_HMCTL_REG)) & 0xc0))
+ {
+ sscape_write(devc, GA_HMCTL_REG, tmp | 0x80);
+ for (cc = 0; cc < 200000; ++cc)
+ inb(devc->base + ODIE_ADDR);
+ }
+ }
+ sscape_detected = hw_config->io_base;
+
+ return 1;
}
int
-probe_ss_ms_sound (struct address_info *hw_config)
+probe_ss_ms_sound(struct address_info *hw_config)
{
- int i, irq_bits = 0xff;
- int ad_flags = 0;
-
- if (devc->failed)
- {
- printk ("Soundscape: Card not detected\n");
- return 0;
- }
-
- if (devc->ok == 0)
- {
- printk ("SoundScape: Invalid initialization order.\n");
- return 0;
- }
-
- for (i = 0; i < sizeof (valid_interrupts); i++)
- if (hw_config->irq == valid_interrupts[i])
- {
- irq_bits = i;
- break;
- }
- if (hw_config->irq > 15 || irq_bits == 0xff)
- {
- printk ("SoundScape: Invalid MSS IRQ%d\n", hw_config->irq);
- return 0;
- }
-
-
- if (old_hardware)
- ad_flags = 0x12345677; /* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */
- return ad1848_detect (hw_config->io_base, &ad_flags, hw_config->osp);
+ int i, irq_bits = 0xff;
+ int ad_flags = 0;
+
+ if (devc->failed)
+ {
+ printk("Soundscape: Card not detected\n");
+ return 0;
+ }
+ if (devc->ok == 0)
+ {
+ printk("SoundScape: Invalid initialization order.\n");
+ return 0;
+ }
+ for (i = 0; i < sizeof(valid_interrupts); i++)
+ if (hw_config->irq == valid_interrupts[i])
+ {
+ irq_bits = i;
+ break;
+ }
+ if (hw_config->irq > 15 || irq_bits == 0xff)
+ {
+ printk("SoundScape: Invalid MSS IRQ%d\n", hw_config->irq);
+ return 0;
+ }
+ if (old_hardware)
+ ad_flags = 0x12345677; /* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */
+ return ad1848_detect(hw_config->io_base, &ad_flags, hw_config->osp);
}
void
-attach_ss_ms_sound (struct address_info *hw_config)
+attach_ss_ms_sound(struct address_info *hw_config)
{
- /*
- * This routine configures the SoundScape card for use with the
- * Win Sound System driver. The AD1848 codec interface uses the CD-ROM
- * config registers of the "ODIE".
- */
+ /*
+ * This routine configures the SoundScape card for use with the
+ * Win Sound System driver. The AD1848 codec interface uses the CD-ROM
+ * config registers of the "ODIE".
+ */
+
+ int i, irq_bits = 0xff;
+
+ hw_config->dma = devc->dma; /* Share the DMA with the ODIE/OPUS chip */
+
+ /*
+ * Setup the DMA polarity.
+ */
+ sscape_write(devc, GA_DMACFG_REG, 0x50);
+
+ /*
+ * Take the gate-array off of the DMA channel.
+ */
+ sscape_write(devc, GA_DMAB_REG, 0x20);
+
+ /*
+ * Init the AD1848 (CD-ROM) config reg.
+ */
+
+ for (i = 0; i < sizeof(valid_interrupts); i++)
+ if (hw_config->irq == valid_interrupts[i])
+ {
+ irq_bits = i;
+ break;
+ }
+ sscape_write(devc, GA_CDCFG_REG, 0x89 | (hw_config->dma << 4) |
+ (irq_bits << 1));
+
+ if (hw_config->irq == devc->irq)
+ printk("SoundScape: Warning! The WSS mode can't share IRQ with MIDI\n");
+
+ hw_config->slots[0] = ad1848_init("SoundScape", hw_config->io_base,
+ hw_config->irq,
+ hw_config->dma,
+ hw_config->dma,
+ 0,
+ devc->osp);
+
+ if (hw_config->slots[0] != -1) /* The AD1848 driver installed itself */
+ {
+ audio_devs[hw_config->slots[0]]->coproc = &sscape_coproc_operations;
+ devc->codec_audiodev = hw_config->slots[0];
+ devc->my_audiodev = hw_config->slots[0];
- int i, irq_bits = 0xff;
+ /* Set proper routings here (what are they) */
+ AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE);
+ }
+#ifdef SSCAPE_DEBUG5
+ /*
+ * Temporary debugging aid. Print contents of the registers
+ * after the AD1848 device has been initialized.
+ */
+ {
+ int i;
+
+ for (i = 0; i < 13; i++)
+ printk("I%d = %02x\n", i, sscape_read(devc, i));
+ }
+#endif
- int prev_devs = num_audiodevs;
+}
- hw_config->dma = devc->dma; /* Share the DMA with the ODIE/OPUS chip */
+void
+unload_sscape(struct address_info *hw_config)
+{
+ release_region(devc->base + 2, 6);
+#if defined(CONFIG_MPU_EMU) && defined(CONFIG_MIDI)
+ unload_mpu401(hw_config);
+#endif
+}
- /*
- * Setup the DMA polarity.
- */
- sscape_write (devc, GA_DMACFG_REG, 0x50);
+void
+unload_ss_ms_sound(struct address_info *hw_config)
+{
+ ad1848_unload(hw_config->io_base,
+ hw_config->irq,
+ devc->dma,
+ devc->dma,
+ 0);
+ sound_unload_audiodev(hw_config->slots[0]);
+}
- /*
- * Take the gate-array off of the DMA channel.
- */
- sscape_write (devc, GA_DMAB_REG, 0x20);
+#ifdef MODULE
- /*
- * Init the AD1848 (CD-ROM) config reg.
- */
+int dma = -1;
+int irq = -1;
+int io = -1;
- for (i = 0; i < sizeof (valid_interrupts); i++)
- if (hw_config->irq == valid_interrupts[i])
- {
- irq_bits = i;
- break;
- }
+int mpu_irq = -1;
+int mpu_io = -1;
- sscape_write (devc, GA_CDCFG_REG, 0x89 | (hw_config->dma << 4) |
- (irq_bits << 1));
+static int mss = 0;
- if (hw_config->irq == devc->irq)
- printk ("SoundScape: Warning! The WSS mode can't share IRQ with MIDI\n");
+MODULE_PARM(dma, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(io, "i");
+MODULE_PARM(mpu_irq, "i");
+MODULE_PARM(mpu_io, "i");
+MODULE_PARM(mss, "i");
- ad1848_init ("SoundScape", hw_config->io_base,
- hw_config->irq,
- hw_config->dma,
- hw_config->dma,
- 0,
- devc->osp);
+struct address_info config;
+struct address_info mpu_config;
- if (num_audiodevs == (prev_devs + 1)) /* The AD1848 driver installed itself */
- {
- audio_devs[prev_devs]->coproc = &sscape_coproc_operations;
- devc->codec_audiodev = prev_devs;
+int
+init_module(void)
+{
+ printk("Soundscape driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+ if (dma == -1 || irq == -1 || io == -1)
+ {
+ printk("DMA, IRQ, and IO port must be specified.\n");
+ return -EINVAL;
+ }
+ if (mpu_irq == -1 && mpu_io != -1)
+ {
+ printk("MPU_IRQ must be specified if MPU_IO is set.\n");
+ return -EINVAL;
+ }
+ config.irq = irq;
+ config.dma = dma;
+ config.io_base = io;
- /* Set proper routings here (what are they) */
- AD1848_REROUTE (SOUND_MIXER_LINE1, SOUND_MIXER_LINE);
- }
+ mpu_config.irq = mpu_irq;
+ mpu_config.io_base = mpu_io;
-#ifdef SSCAPE_DEBUG5
- /*
- * Temporary debugging aid. Print contents of the registers
- * after the AD1848 device has been initialized.
- */
- {
- int i;
-
- for (i = 0; i < 13; i++)
- printk ("I%d = %02x\n", i, sscape_read (devc, i));
- }
-#endif
+ if (probe_sscape(&mpu_config) == 0)
+ return -ENODEV;
-}
+ attach_sscape(&mpu_config);
+
+ mss = probe_ss_ms_sound(&config);
-void
-unload_sscape (struct address_info *hw_config)
-{
- release_region (devc->base + 2, 6);
-#if defined(CONFIG_MPU_EMU) && defined(CONFIG_MIDI)
- unload_mpu401 (hw_config);
-#endif
+ if (mss)
+ attach_ss_ms_sound(&config);
+ SOUND_LOCK;
+ return 0;
}
-void
-unload_ss_ms_sound (struct address_info *hw_config)
+void
+cleanup_module(void)
{
- ad1848_unload (hw_config->io_base,
- hw_config->irq,
- devc->dma,
- devc->dma,
- 0);
+ if (mss)
+ unload_ss_ms_sound(&config);
+ SOUND_LOCK_END;
+ unload_sscape(&config);
}
-
+#endif
#endif
diff --git a/drivers/sound/sys_timer.c b/drivers/sound/sys_timer.c
index ca21cfa60..8097d1872 100644
--- a/drivers/sound/sys_timer.c
+++ b/drivers/sound/sys_timer.c
@@ -16,7 +16,7 @@
#include "sound_config.h"
-#ifdef CONFIG_SEQUENCER
+#if defined(CONFIG_SEQUENCER) || defined(MODULE)
static volatile int opened = 0, tmr_running = 0;
static volatile time_t tmr_offs, tmr_ctr;
@@ -26,281 +26,279 @@ static volatile unsigned long curr_ticks;
static volatile unsigned long next_event_time;
static unsigned long prev_event_time;
-static void poll_def_tmr (unsigned long dummy);
+static void poll_def_tmr(unsigned long dummy);
static struct timer_list def_tmr =
{NULL, NULL, 0, 0, poll_def_tmr};
static unsigned long
-tmr2ticks (int tmr_value)
+tmr2ticks(int tmr_value)
{
- /*
- * Convert system timer ticks (HZ) to MIDI ticks
- * (divide # of MIDI ticks/minute by # of system ticks/minute).
- */
+ /*
+ * Convert system timer ticks (HZ) to MIDI ticks
+ * (divide # of MIDI ticks/minute by # of system ticks/minute).
+ */
- return ((tmr_value * curr_tempo * curr_timebase) + (30 * 100)) / (60 * HZ);
+ return ((tmr_value * curr_tempo * curr_timebase) + (30 * 100)) / (60 * HZ);
}
static void
-poll_def_tmr (unsigned long dummy)
+poll_def_tmr(unsigned long dummy)
{
- if (opened)
- {
-
- {
- def_tmr.expires = (1) + jiffies;
- add_timer (&def_tmr);
- };
+ if (opened)
+ {
- if (tmr_running)
- {
- tmr_ctr++;
- curr_ticks = ticks_offs + tmr2ticks (tmr_ctr);
-
- if (curr_ticks >= next_event_time)
- {
- next_event_time = (unsigned long) -1;
- sequencer_timer (0);
- }
- }
- }
+ {
+ def_tmr.expires = (1) + jiffies;
+ add_timer(&def_tmr);
+ };
+
+ if (tmr_running)
+ {
+ tmr_ctr++;
+ curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
+
+ if (curr_ticks >= next_event_time)
+ {
+ next_event_time = (unsigned long) -1;
+ sequencer_timer(0);
+ }
+ }
+ }
}
static void
-tmr_reset (void)
+tmr_reset(void)
{
- unsigned long flags;
-
- save_flags (flags);
- cli ();
- tmr_offs = 0;
- ticks_offs = 0;
- tmr_ctr = 0;
- next_event_time = (unsigned long) -1;
- prev_event_time = 0;
- curr_ticks = 0;
- restore_flags (flags);
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ tmr_offs = 0;
+ ticks_offs = 0;
+ tmr_ctr = 0;
+ next_event_time = (unsigned long) -1;
+ prev_event_time = 0;
+ curr_ticks = 0;
+ restore_flags(flags);
}
static int
-def_tmr_open (int dev, int mode)
+def_tmr_open(int dev, int mode)
{
- if (opened)
- return -EBUSY;
+ if (opened)
+ return -EBUSY;
- tmr_reset ();
- curr_tempo = 60;
- curr_timebase = 100;
- opened = 1;
+ tmr_reset();
+ curr_tempo = 60;
+ curr_timebase = 100;
+ opened = 1;
- ;
+ ;
- {
- def_tmr.expires = (1) + jiffies;
- add_timer (&def_tmr);
- };
+ {
+ def_tmr.expires = (1) + jiffies;
+ add_timer(&def_tmr);
+ };
- return 0;
+ return 0;
}
static void
-def_tmr_close (int dev)
+def_tmr_close(int dev)
{
- opened = tmr_running = 0;
- del_timer (&def_tmr);;
+ opened = tmr_running = 0;
+ del_timer(&def_tmr);;
}
static int
-def_tmr_event (int dev, unsigned char *event)
+def_tmr_event(int dev, unsigned char *event)
{
- unsigned char cmd = event[1];
- unsigned long parm = *(int *) &event[4];
-
- switch (cmd)
- {
- case TMR_WAIT_REL:
- parm += prev_event_time;
- case TMR_WAIT_ABS:
- if (parm > 0)
- {
- long time;
-
- if (parm <= curr_ticks) /* It's the time */
- return TIMER_NOT_ARMED;
+ unsigned char cmd = event[1];
+ unsigned long parm = *(int *) &event[4];
- time = parm;
- next_event_time = prev_event_time = time;
-
- return TIMER_ARMED;
- }
- break;
-
- case TMR_START:
- tmr_reset ();
- tmr_running = 1;
- break;
-
- case TMR_STOP:
- tmr_running = 0;
- break;
-
- case TMR_CONTINUE:
- tmr_running = 1;
- break;
+ switch (cmd)
+ {
+ case TMR_WAIT_REL:
+ parm += prev_event_time;
+ case TMR_WAIT_ABS:
+ if (parm > 0)
+ {
+ long time;
+
+ if (parm <= curr_ticks) /* It's the time */
+ return TIMER_NOT_ARMED;
+
+ time = parm;
+ next_event_time = prev_event_time = time;
+
+ return TIMER_ARMED;
+ }
+ break;
+
+ case TMR_START:
+ tmr_reset();
+ tmr_running = 1;
+ break;
+
+ case TMR_STOP:
+ tmr_running = 0;
+ break;
+
+ case TMR_CONTINUE:
+ tmr_running = 1;
+ break;
+
+ case TMR_TEMPO:
+ if (parm)
+ {
+ if (parm < 8)
+ parm = 8;
+ if (parm > 360)
+ parm = 360;
+ tmr_offs = tmr_ctr;
+ ticks_offs += tmr2ticks(tmr_ctr);
+ tmr_ctr = 0;
+ curr_tempo = parm;
+ }
+ break;
+
+ case TMR_ECHO:
+ seq_copy_to_input(event, 8);
+ break;
+
+ default:;
+ }
- case TMR_TEMPO:
- if (parm)
- {
- if (parm < 8)
- parm = 8;
- if (parm > 360)
- parm = 360;
- tmr_offs = tmr_ctr;
- ticks_offs += tmr2ticks (tmr_ctr);
- tmr_ctr = 0;
- curr_tempo = parm;
- }
- break;
-
- case TMR_ECHO:
- seq_copy_to_input (event, 8);
- break;
-
- default:;
- }
-
- return TIMER_NOT_ARMED;
+ return TIMER_NOT_ARMED;
}
static unsigned long
-def_tmr_get_time (int dev)
+def_tmr_get_time(int dev)
{
- if (!opened)
- return 0;
+ if (!opened)
+ return 0;
- return curr_ticks;
+ return curr_ticks;
}
static int
-def_tmr_ioctl (int dev,
- unsigned int cmd, caddr_t arg)
+def_tmr_ioctl(int dev,
+ unsigned int cmd, caddr_t arg)
{
- switch (cmd)
- {
- case SNDCTL_TMR_SOURCE:
- return (*(int *) arg = TMR_INTERNAL);
- break;
-
- case SNDCTL_TMR_START:
- tmr_reset ();
- tmr_running = 1;
- return 0;
- break;
-
- case SNDCTL_TMR_STOP:
- tmr_running = 0;
- return 0;
- break;
-
- case SNDCTL_TMR_CONTINUE:
- tmr_running = 1;
- return 0;
- break;
-
- case SNDCTL_TMR_TIMEBASE:
- {
- int val;
-
- val = *(int *) arg;
-
- if (val)
+ switch (cmd)
{
- if (val < 1)
- val = 1;
- if (val > 1000)
- val = 1000;
- curr_timebase = val;
+ case SNDCTL_TMR_SOURCE:
+ return (*(int *) arg = TMR_INTERNAL);
+ break;
+
+ case SNDCTL_TMR_START:
+ tmr_reset();
+ tmr_running = 1;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_STOP:
+ tmr_running = 0;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_CONTINUE:
+ tmr_running = 1;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_TIMEBASE:
+ {
+ int val;
+
+ val = *(int *) arg;
+
+ if (val)
+ {
+ if (val < 1)
+ val = 1;
+ if (val > 1000)
+ val = 1000;
+ curr_timebase = val;
+ }
+ return (*(int *) arg = curr_timebase);
+ }
+ break;
+
+ case SNDCTL_TMR_TEMPO:
+ {
+ int val;
+
+ val = *(int *) arg;
+
+ if (val)
+ {
+ if (val < 8)
+ val = 8;
+ if (val > 250)
+ val = 250;
+ tmr_offs = tmr_ctr;
+ ticks_offs += tmr2ticks(tmr_ctr);
+ tmr_ctr = 0;
+ curr_tempo = val;
+ }
+ return (*(int *) arg = curr_tempo);
+ }
+ break;
+
+ case SNDCTL_SEQ_CTRLRATE:
+ {
+ int val;
+
+ val = *(int *) arg;
+ if (val != 0) /* Can't change */
+ return -EINVAL;
+
+ return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60);
+ }
+ break;
+
+ case SNDCTL_SEQ_GETTIME:
+ return (*(int *) arg = curr_ticks);
+ break;
+
+ case SNDCTL_TMR_METRONOME:
+ /* NOP */
+ break;
+
+ default:;
}
- return (*(int *) arg = curr_timebase);
- }
- break;
-
- case SNDCTL_TMR_TEMPO:
- {
- int val;
-
- val = *(int *) arg;
-
- if (val)
- {
- if (val < 8)
- val = 8;
- if (val > 250)
- val = 250;
- tmr_offs = tmr_ctr;
- ticks_offs += tmr2ticks (tmr_ctr);
- tmr_ctr = 0;
- curr_tempo = val;
- }
-
- return (*(int *) arg = curr_tempo);
- }
- break;
-
- case SNDCTL_SEQ_CTRLRATE:
- {
- int val;
-
- val = *(int *) arg;
- if (val != 0) /* Can't change */
- return -EINVAL;
-
- return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60);
- }
- break;
-
- case SNDCTL_SEQ_GETTIME:
- return (*(int *) arg = curr_ticks);
- break;
-
- case SNDCTL_TMR_METRONOME:
- /* NOP */
- break;
-
- default:;
- }
-
- return -EINVAL;
+ return -EINVAL;
}
static void
-def_tmr_arm (int dev, long time)
+def_tmr_arm(int dev, long time)
{
- if (time < 0)
- time = curr_ticks + 1;
- else if (time <= curr_ticks) /* It's the time */
- return;
+ if (time < 0)
+ time = curr_ticks + 1;
+ else if (time <= curr_ticks) /* It's the time */
+ return;
- next_event_time = prev_event_time = time;
+ next_event_time = prev_event_time = time;
- return;
+ return;
}
struct sound_timer_operations default_sound_timer =
{
- {"System clock", 0},
- 0, /* Priority */
- 0, /* Local device link */
- def_tmr_open,
- def_tmr_close,
- def_tmr_event,
- def_tmr_get_time,
- def_tmr_ioctl,
- def_tmr_arm
+ {"System clock", 0},
+ 0, /* Priority */
+ 0, /* Local device link */
+ def_tmr_open,
+ def_tmr_close,
+ def_tmr_event,
+ def_tmr_get_time,
+ def_tmr_ioctl,
+ def_tmr_arm
};
#endif
diff --git a/drivers/sound/trix.c b/drivers/sound/trix.c
index 5bb60ea6a..4c3d0fb2c 100644
--- a/drivers/sound/trix.c
+++ b/drivers/sound/trix.c
@@ -12,12 +12,20 @@
* for more info.
*/
#include <linux/config.h>
-
+#include <linux/module.h>
#include "sound_config.h"
+#include "soundmodule.h"
#include "sb.h"
+#include "sound_firmware.h"
-#if defined(CONFIG_TRIX)
+#if defined(CONFIG_TRIX) || defined (MODULE)
+
+#if defined(CONFIG_UART401) || defined(CONFIG_UART401_MODULE)
+#if defined(CONFIG_MIDI)
+#define DO_MIDI
+#endif
+#endif
#ifdef INCLUDE_TRIX_BOOT
#include "trix_boot.h"
@@ -35,100 +43,98 @@ static int mpu_initialized = 0;
static int *trix_osp = NULL;
static unsigned char
-trix_read (int addr)
+trix_read(int addr)
{
- outb (((unsigned char) addr), 0x390); /* MT-0002-PC ASIC address */
- return inb (0x391); /* MT-0002-PC ASIC data */
+ outb(((unsigned char) addr), 0x390); /* MT-0002-PC ASIC address */
+ return inb(0x391); /* MT-0002-PC ASIC data */
}
static void
-trix_write (int addr, int data)
+trix_write(int addr, int data)
{
- outb (((unsigned char) addr), 0x390); /* MT-0002-PC ASIC address */
- outb (((unsigned char) data), 0x391); /* MT-0002-PC ASIC data */
+ outb(((unsigned char) addr), 0x390); /* MT-0002-PC ASIC address */
+ outb(((unsigned char) data), 0x391); /* MT-0002-PC ASIC data */
}
static void
-download_boot (int base)
+download_boot(int base)
{
- int i = 0, n = trix_boot_len;
+ int i = 0, n = trix_boot_len;
- if (trix_boot_len == 0)
- return;
+ if (trix_boot_len == 0)
+ return;
- trix_write (0xf8, 0x00); /* ??????? */
- outb ((0x01), base + 6); /* Clear the internal data pointer */
- outb ((0x00), base + 6); /* Restart */
+ trix_write(0xf8, 0x00); /* ??????? */
+ outb((0x01), base + 6); /* Clear the internal data pointer */
+ outb((0x00), base + 6); /* Restart */
- /*
- * Write the boot code to the RAM upload/download register.
- * Each write increments the internal data pointer.
- */
- outb ((0x01), base + 6); /* Clear the internal data pointer */
- outb ((0x1A), 0x390); /* Select RAM download/upload port */
+ /*
+ * Write the boot code to the RAM upload/download register.
+ * Each write increments the internal data pointer.
+ */
+ outb((0x01), base + 6); /* Clear the internal data pointer */
+ outb((0x1A), 0x390); /* Select RAM download/upload port */
- for (i = 0; i < n; i++)
- outb ((trix_boot[i]), 0x391);
- for (i = n; i < 10016; i++) /* Clear up to first 16 bytes of data RAM */
- outb ((0x00), 0x391);
- outb ((0x00), base + 6); /* Reset */
- outb ((0x50), 0x390); /* ?????? */
+ for (i = 0; i < n; i++)
+ outb((trix_boot[i]), 0x391);
+ for (i = n; i < 10016; i++) /* Clear up to first 16 bytes of data RAM */
+ outb((0x00), 0x391);
+ outb((0x00), base + 6); /* Reset */
+ outb((0x50), 0x390); /* ?????? */
}
static int
-trix_set_wss_port (struct address_info *hw_config)
+trix_set_wss_port(struct address_info *hw_config)
{
- unsigned char addr_bits;
-
- if (check_region (0x390, 2))
- {
- printk ("AudioTrix: Config port I/O conflict\n");
- return 0;
- }
-
- if (kilroy_was_here) /* Already initialized */
- return 0;
-
- if (trix_read (0x15) != 0x71) /* No ASIC signature */
- {
- DDB (printk ("No AudioTrix ASIC signature found\n"));
- return 0;
- }
-
- kilroy_was_here = 1;
-
- /*
- * Reset some registers.
- */
-
- trix_write (0x13, 0);
- trix_write (0x14, 0);
-
- /*
- * Configure the ASIC to place the codec to the proper I/O location
- */
-
- switch (hw_config->io_base)
- {
- case 0x530:
- addr_bits = 0;
- break;
- case 0x604:
- addr_bits = 1;
- break;
- case 0xE80:
- addr_bits = 2;
- break;
- case 0xF40:
- addr_bits = 3;
- break;
- default:
- return 0;
- }
-
- trix_write (0x19, (trix_read (0x19) & 0x03) | addr_bits);
- return 1;
+ unsigned char addr_bits;
+
+ if (check_region(0x390, 2))
+ {
+ printk(KERN_ERR "AudioTrix: Config port I/O conflict\n");
+ return 0;
+ }
+ if (kilroy_was_here) /* Already initialized */
+ return 0;
+
+ if (trix_read(0x15) != 0x71) /* No ASIC signature */
+ {
+ MDB(printk("No AudioTrix ASIC signature found\n"));
+ return 0;
+ }
+ kilroy_was_here = 1;
+
+ /*
+ * Reset some registers.
+ */
+
+ trix_write(0x13, 0);
+ trix_write(0x14, 0);
+
+ /*
+ * Configure the ASIC to place the codec to the proper I/O location
+ */
+
+ switch (hw_config->io_base)
+ {
+ case 0x530:
+ addr_bits = 0;
+ break;
+ case 0x604:
+ addr_bits = 1;
+ break;
+ case 0xE80:
+ addr_bits = 2;
+ break;
+ case 0xF40:
+ addr_bits = 3;
+ break;
+ default:
+ return 0;
+ }
+
+ trix_write(0x19, (trix_read(0x19) & 0x03) | addr_bits);
+ return 1;
}
/*
@@ -137,339 +143,421 @@ trix_set_wss_port (struct address_info *hw_config)
*/
int
-probe_trix_wss (struct address_info *hw_config)
+probe_trix_wss(struct address_info *hw_config)
{
- int ret;
-
- /*
- * Check if the IO port returns valid signature. The original MS Sound
- * system returns 0x04 while some cards (AudioTrix Pro for example)
- * return 0x00.
- */
- if (check_region (hw_config->io_base, 8))
- {
- printk ("AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base);
- return 0;
- }
-
- trix_osp = hw_config->osp;
-
- if (!trix_set_wss_port (hw_config))
- return 0;
-
- if ((inb (hw_config->io_base + 3) & 0x3f) != 0x00)
- {
- DDB (printk ("No MSS signature detected on port 0x%x\n", hw_config->io_base));
- return 0;
- }
-
- if (hw_config->irq > 11)
- {
- printk ("AudioTrix: Bad WSS IRQ %d\n", hw_config->irq);
- return 0;
- }
-
- if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
- {
- printk ("AudioTrix: Bad WSS DMA %d\n", hw_config->dma);
- return 0;
- }
-
- if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma)
- if (hw_config->dma2 != 0 && hw_config->dma2 != 1 && hw_config->dma2 != 3)
- {
- printk ("AudioTrix: Bad capture DMA %d\n", hw_config->dma2);
- return 0;
- }
-
- /*
- * Check that DMA0 is not in use with a 8 bit board.
- */
-
- if (hw_config->dma == 0 && inb (hw_config->io_base + 3) & 0x80)
- {
- printk ("AudioTrix: Can't use DMA0 with a 8 bit card\n");
- return 0;
- }
-
- if (hw_config->irq > 7 && hw_config->irq != 9 && inb (hw_config->io_base + 3) & 0x80)
- {
- printk ("AudioTrix: Can't use IRQ%d with a 8 bit card\n", hw_config->irq);
- return 0;
- }
-
- ret = ad1848_detect (hw_config->io_base + 4, NULL, hw_config->osp);
-
- if (ret)
- {
+ int ret;
+
+ /*
+ * Check if the IO port returns valid signature. The original MS Sound
+ * system returns 0x04 while some cards (AudioTrix Pro for example)
+ * return 0x00.
+ */
+ if (check_region(hw_config->io_base, 8))
+ {
+ printk("AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base);
+ return 0;
+ }
+ trix_osp = hw_config->osp;
+
+ if (!trix_set_wss_port(hw_config))
+ return 0;
+
+ if ((inb(hw_config->io_base + 3) & 0x3f) != 0x00)
+ {
+ MDB(printk("No MSS signature detected on port 0x%x\n", hw_config->io_base));
+ return 0;
+ }
+ if (hw_config->irq > 11)
+ {
+ printk("AudioTrix: Bad WSS IRQ %d\n", hw_config->irq);
+ return 0;
+ }
+ if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
+ {
+ printk("AudioTrix: Bad WSS DMA %d\n", hw_config->dma);
+ return 0;
+ }
+ if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma)
+ if (hw_config->dma2 != 0 && hw_config->dma2 != 1 && hw_config->dma2 != 3)
+ {
+ printk("AudioTrix: Bad capture DMA %d\n", hw_config->dma2);
+ return 0;
+ }
+ /*
+ * Check that DMA0 is not in use with a 8 bit board.
+ */
+
+ if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80)
+ {
+ printk("AudioTrix: Can't use DMA0 with a 8 bit card slot\n");
+ return 0;
+ }
+ if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80)
+ {
+ printk("AudioTrix: Can't use IRQ%d with a 8 bit card slot\n", hw_config->irq);
+ return 0;
+ }
+ ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp);
+
+ if (ret)
+ {
#ifdef TRIX_ENABLE_JOYSTICK
- trix_write (0x15, 0x80);
+ trix_write(0x15, 0x80);
#endif
- request_region (0x390, 2, "AudioTrix");
- }
-
- return ret;
+ request_region(0x390, 2, "AudioTrix");
+ }
+ return ret;
}
void
-attach_trix_wss (struct address_info *hw_config)
+attach_trix_wss(struct address_info *hw_config)
{
- static unsigned char interrupt_bits[12] =
- {0, 0, 0, 0, 0, 0, 0, 0x08, 0, 0x10, 0x18, 0x20};
- char bits;
-
- static unsigned char dma_bits[4] =
- {1, 2, 0, 3};
-
- int config_port = hw_config->io_base + 0;
- int dma1 = hw_config->dma, dma2 = hw_config->dma2;
- int old_num_mixers = num_mixers;
-
- trix_osp = hw_config->osp;
-
- if (!kilroy_was_here)
- {
- DDB (printk ("AudioTrix: Attach called but not probed yet???\n"));
- return;
- }
-
- /*
- * Set the IRQ and DMA addresses.
- */
-
- bits = interrupt_bits[hw_config->irq];
- if (bits == 0)
- {
- printk ("AudioTrix: Bad IRQ (%d)\n", hw_config->irq);
- return;
- }
-
- outb ((bits | 0x40), config_port);
-
- if (hw_config->dma2 == -1 || hw_config->dma2 == hw_config->dma)
- {
- bits |= dma_bits[dma1];
- dma2 = dma1;
- }
- else
- {
- unsigned char tmp;
-
- tmp = trix_read (0x13) & ~30;
- trix_write (0x13, tmp | 0x80 | (dma1 << 4));
-
- tmp = trix_read (0x14) & ~30;
- trix_write (0x14, tmp | 0x80 | (dma2 << 4));
- }
-
- outb ((bits), config_port); /* Write IRQ+DMA setup */
-
- ad1848_init ("AudioTrix Pro", hw_config->io_base + 4,
- hw_config->irq,
- dma1,
- dma2,
- 0,
- hw_config->osp);
- request_region (hw_config->io_base, 4, "MSS config");
-
- if (num_mixers > old_num_mixers) /* Mixer got installed */
- {
- AD1848_REROUTE (SOUND_MIXER_LINE1, SOUND_MIXER_LINE); /* Line in */
- AD1848_REROUTE (SOUND_MIXER_LINE2, SOUND_MIXER_CD);
- AD1848_REROUTE (SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* OPL4 */
- AD1848_REROUTE (SOUND_MIXER_SPEAKER, SOUND_MIXER_ALTPCM); /* SB */
- }
+ static unsigned char interrupt_bits[12] =
+ {0, 0, 0, 0, 0, 0, 0, 0x08, 0, 0x10, 0x18, 0x20};
+ char bits;
+
+ static unsigned char dma_bits[4] =
+ {1, 2, 0, 3};
+
+ int config_port = hw_config->io_base + 0;
+ int dma1 = hw_config->dma, dma2 = hw_config->dma2;
+ int old_num_mixers = num_mixers;
+
+ trix_osp = hw_config->osp;
+
+ if (!kilroy_was_here)
+ {
+ DDB(printk("AudioTrix: Attach called but not probed yet???\n"));
+ return;
+ }
+ /*
+ * Set the IRQ and DMA addresses.
+ */
+
+ bits = interrupt_bits[hw_config->irq];
+ if (bits == 0)
+ {
+ printk("AudioTrix: Bad IRQ (%d)\n", hw_config->irq);
+ return;
+ }
+ outb((bits | 0x40), config_port);
+
+ if (hw_config->dma2 == -1 || hw_config->dma2 == hw_config->dma)
+ {
+ bits |= dma_bits[dma1];
+ dma2 = dma1;
+ } else
+ {
+ unsigned char tmp;
+
+ tmp = trix_read(0x13) & ~30;
+ trix_write(0x13, tmp | 0x80 | (dma1 << 4));
+
+ tmp = trix_read(0x14) & ~30;
+ trix_write(0x14, tmp | 0x80 | (dma2 << 4));
+ }
+
+ outb((bits), config_port); /* Write IRQ+DMA setup */
+
+ hw_config->slots[0] = ad1848_init("AudioTrix Pro", hw_config->io_base + 4,
+ hw_config->irq,
+ dma1,
+ dma2,
+ 0,
+ hw_config->osp);
+ request_region(hw_config->io_base, 4, "MSS config");
+
+ if (num_mixers > old_num_mixers) /* Mixer got installed */
+ {
+ AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); /* Line in */
+ AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD);
+ AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* OPL4 */
+ AD1848_REROUTE(SOUND_MIXER_SPEAKER, SOUND_MIXER_ALTPCM); /* SB */
+ }
}
int
-probe_trix_sb (struct address_info *hw_config)
+probe_trix_sb(struct address_info *hw_config)
{
- int tmp;
- unsigned char conf;
- static char irq_translate[] =
- {-1, -1, -1, 0, 1, 2, -1, 3};
-
- if (trix_boot_len == 0)
- return 0; /* No boot code -> no fun */
-
- if (!kilroy_was_here)
- return 0; /* AudioTrix Pro has not been detected earlier */
-
- if (sb_initialized)
- return 0;
-
- if (check_region (hw_config->io_base, 16))
- {
- printk ("AudioTrix: SB I/O port conflict (%x)\n", hw_config->io_base);
- return 0;
- }
-
- if ((hw_config->io_base & 0xffffff8f) != 0x200)
- return 0;
-
- tmp = hw_config->irq;
- if (tmp > 7)
- return 0;
- if (irq_translate[tmp] == -1)
- return 0;
-
- tmp = hw_config->dma;
- if (tmp != 1 && tmp != 3)
- return 0;
-
- conf = 0x84; /* DMA and IRQ enable */
- conf |= hw_config->io_base & 0x70; /* I/O address bits */
- conf |= irq_translate[hw_config->irq];
- if (hw_config->dma == 3)
- conf |= 0x08;
- trix_write (0x1b, conf);
-
- download_boot (hw_config->io_base);
- sb_initialized = 1;
-
- hw_config->name = "AudioTrix SB";
+ int tmp;
+ unsigned char conf;
+ static char irq_translate[] =
+ {-1, -1, -1, 0, 1, 2, -1, 3};
+
+ if (trix_boot_len == 0)
+ return 0; /* No boot code -> no fun */
+
+ if (!kilroy_was_here)
+ return 0; /* AudioTrix Pro has not been detected earlier */
+
+ if (sb_initialized)
+ return 0;
+
+ if (check_region(hw_config->io_base, 16))
+ {
+ printk("AudioTrix: SB I/O port conflict (%x)\n", hw_config->io_base);
+ return 0;
+ }
+ if ((hw_config->io_base & 0xffffff8f) != 0x200)
+ return 0;
+
+ tmp = hw_config->irq;
+ if (tmp > 7)
+ return 0;
+ if (irq_translate[tmp] == -1)
+ return 0;
+
+ tmp = hw_config->dma;
+ if (tmp != 1 && tmp != 3)
+ return 0;
+
+ conf = 0x84; /* DMA and IRQ enable */
+ conf |= hw_config->io_base & 0x70; /* I/O address bits */
+ conf |= irq_translate[hw_config->irq];
+ if (hw_config->dma == 3)
+ conf |= 0x08;
+ trix_write(0x1b, conf);
+
+ download_boot(hw_config->io_base);
+ sb_initialized = 1;
+
+ hw_config->name = "AudioTrix SB";
#ifdef CONFIG_SBDSP
- return sb_dsp_detect (hw_config);
+ return sb_dsp_detect(hw_config);
#else
- return 0;
+ return 0;
#endif
}
void
-attach_trix_sb (struct address_info *hw_config)
+attach_trix_sb(struct address_info *hw_config)
{
- extern int sb_be_quiet;
- int old_quiet;
+ extern int sb_be_quiet;
+ int old_quiet;
#ifdef CONFIG_SBDSP
- hw_config->driver_use_1 = SB_NO_MIDI | SB_NO_MIXER | SB_NO_RECORDING;
+ hw_config->driver_use_1 = SB_NO_MIDI | SB_NO_MIXER | SB_NO_RECORDING;
- /* Prevent false alarms */
- old_quiet = sb_be_quiet;
- sb_be_quiet = 1;
+ /* Prevent false alarms */
+ old_quiet = sb_be_quiet;
+ sb_be_quiet = 1;
- sb_dsp_init (hw_config);
+ sb_dsp_init(hw_config);
- sb_be_quiet = old_quiet;
+ sb_be_quiet = old_quiet;
#endif
}
void
-attach_trix_mpu (struct address_info *hw_config)
+attach_trix_mpu(struct address_info *hw_config)
{
#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
- hw_config->name = "AudioTrix Pro";
- attach_uart401 (hw_config);
+ hw_config->name = "AudioTrix Pro";
+ attach_uart401(hw_config);
#endif
}
int
-probe_trix_mpu (struct address_info *hw_config)
+probe_trix_mpu(struct address_info *hw_config)
{
-#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
- unsigned char conf;
- static char irq_bits[] =
- {-1, -1, -1, 1, 2, 3, -1, 4, -1, 5};
-
- if (!kilroy_was_here)
- {
- DDB (printk ("Trix: WSS and SB modes must be initialized before MPU\n"));
- return 0; /* AudioTrix Pro has not been detected earlier */
- }
-
- if (!sb_initialized)
- {
- DDB (printk ("Trix: SB mode must be initialized before MPU\n"));
- return 0;
- }
-
- if (mpu_initialized)
- {
- DDB (printk ("Trix: MPU mode already initialized\n"));
- return 0;
- }
-
- if (check_region (hw_config->io_base, 4))
- {
- printk ("AudioTrix: MPU I/O port conflict (%x)\n", hw_config->io_base);
- return 0;
- }
-
- if (hw_config->irq > 9)
- {
- printk ("AudioTrix: Bad MPU IRQ %d\n", hw_config->irq);
- return 0;
- }
-
- if (irq_bits[hw_config->irq] == -1)
- {
- printk ("AudioTrix: Bad MPU IRQ %d\n", hw_config->irq);
- return 0;
- }
-
- switch (hw_config->io_base)
- {
- case 0x330:
- conf = 0x00;
- break;
- case 0x370:
- conf = 0x04;
- break;
- case 0x3b0:
- conf = 0x08;
- break;
- case 0x3f0:
- conf = 0x0c;
- break;
- default:
- return 0; /* Invalid port */
- }
-
- conf |= irq_bits[hw_config->irq] << 4;
-
- trix_write (0x19, (trix_read (0x19) & 0x83) | conf);
-
- mpu_initialized = 1;
-
- return probe_uart401 (hw_config);
+#ifdef DO_MIDI
+ unsigned char conf;
+ static char irq_bits[] =
+ {-1, -1, -1, 1, 2, 3, -1, 4, -1, 5};
+
+ if (!kilroy_was_here)
+ {
+ DDB(printk("Trix: WSS and SB modes must be initialized before MPU\n"));
+ return 0; /* AudioTrix Pro has not been detected earlier */
+ }
+ if (!sb_initialized)
+ {
+ DDB(printk("Trix: SB mode must be initialized before MPU\n"));
+ return 0;
+ }
+ if (mpu_initialized)
+ {
+ DDB(printk("Trix: MPU mode already initialized\n"));
+ return 0;
+ }
+ if (check_region(hw_config->io_base, 4))
+ {
+ printk("AudioTrix: MPU I/O port conflict (%x)\n", hw_config->io_base);
+ return 0;
+ }
+ if (hw_config->irq > 9)
+ {
+ printk("AudioTrix: Bad MPU IRQ %d\n", hw_config->irq);
+ return 0;
+ }
+ if (irq_bits[hw_config->irq] == -1)
+ {
+ printk("AudioTrix: Bad MPU IRQ %d\n", hw_config->irq);
+ return 0;
+ }
+ switch (hw_config->io_base)
+ {
+ case 0x330:
+ conf = 0x00;
+ break;
+ case 0x370:
+ conf = 0x04;
+ break;
+ case 0x3b0:
+ conf = 0x08;
+ break;
+ case 0x3f0:
+ conf = 0x0c;
+ break;
+ default:
+ return 0; /* Invalid port */
+ }
+
+ conf |= irq_bits[hw_config->irq] << 4;
+
+ trix_write(0x19, (trix_read(0x19) & 0x83) | conf);
+
+ mpu_initialized = 1;
+
+ return probe_uart401(hw_config);
#else
- return 0;
+ return 0;
#endif
}
void
-unload_trix_wss (struct address_info *hw_config)
+unload_trix_wss(struct address_info *hw_config)
{
- int dma2 = hw_config->dma2;
+ int dma2 = hw_config->dma2;
- if (dma2 == -1)
- dma2 = hw_config->dma;
+ if (dma2 == -1)
+ dma2 = hw_config->dma;
- release_region (0x390, 2);
- release_region (hw_config->io_base, 4);
+ release_region(0x390, 2);
+ release_region(hw_config->io_base, 4);
- ad1848_unload (hw_config->io_base + 4,
- hw_config->irq,
- hw_config->dma,
- dma2,
- 0);
+ ad1848_unload(hw_config->io_base + 4,
+ hw_config->irq,
+ hw_config->dma,
+ dma2,
+ 0);
+ sound_unload_audiodev(hw_config->slots[0]);
}
void
-unload_trix_mpu (struct address_info *hw_config)
+unload_trix_mpu(struct address_info *hw_config)
{
-#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
- unload_uart401 (hw_config);
+#ifdef DO_MIDI
+ unload_uart401(hw_config);
#endif
}
void
-unload_trix_sb (struct address_info *hw_config)
+unload_trix_sb(struct address_info *hw_config)
{
#ifdef CONFIG_SBDSP
- sb_dsp_unload (hw_config);
+ sb_dsp_unload(hw_config);
#endif
}
+#ifdef MODULE
+
+int io = -1;
+int irq = -1;
+int dma = -1;
+int dma2 = -1; /* Set this for modules that need it */
+
+int sb_io = -1;
+int sb_dma = -1;
+int sb_irq = -1;
+
+int mpu_io = -1;
+int mpu_irq = -1;
+
+struct address_info config;
+struct address_info sb_config;
+struct address_info mpu_config;
+
+static int mpu = 0;
+static int sb = 0;
+static int fw_load;
+
+int
+init_module(void)
+{
+ printk("MediaTrix audio driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+
+ if (io == -1 || dma == -1 || irq == -1)
+ {
+ printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n");
+ return -EINVAL;
+ }
+ config.io_base = io;
+ config.irq = irq;
+ config.dma = dma;
+ config.dma2 = dma2;
+
+ sb_config.io_base = sb_io;
+ sb_config.irq = sb_irq;
+ sb_config.dma = sb_dma;
+
+ mpu_config.io_base = mpu_io;
+ mpu_config.irq = mpu_irq;
+
+ if (sb_io != -1 && (sb_irq == -1 || sb_dma == -1))
+ {
+ printk(KERN_INFO "SB_IRQ and SB_DMA must be specified if SB_IO is set.\n");
+ return -EINVAL;
+ }
+ if (mpu_io != -1 && mpu_irq == -1)
+ {
+ printk(KERN_INFO "MPU_IRQ must be specified if MPU_IO is set.\n");
+ return -EINVAL;
+ }
+ if (!trix_boot)
+ {
+ fw_load = 1;
+ trix_boot_len = mod_firmware_load("/etc/sound/trxpro.bin",
+ (char **) &trix_boot);
+ }
+ if (!probe_trix_wss(&config))
+ return -ENODEV;
+ attach_trix_wss(&config);
+
+ /*
+ * We must attach in the right order to get the firmware
+ * loaded up in time.
+ */
+
+ if (sb_io != -1)
+ {
+ sb = probe_trix_sb(&sb_config);
+ if (sb)
+ attach_trix_sb(&sb_config);
+ }
+ if (mpu_io != -1)
+ {
+ mpu = probe_trix_mpu(&mpu_config);
+ if (mpu)
+ attach_trix_mpu(&mpu_config);
+ }
+ SOUND_LOCK;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ if (fw_load && trix_boot)
+ kfree(trix_boot);
+ if (sb)
+ unload_trix_sb(&sb_config);
+ if (mpu)
+ unload_trix_mpu(&mpu_config);
+ unload_trix_wss(&config);
+ SOUND_LOCK_END;
+}
+
+#endif
#endif
diff --git a/drivers/sound/uart401.c b/drivers/sound/uart401.c
index e8a399cb6..22e4fe8f9 100644
--- a/drivers/sound/uart401.c
+++ b/drivers/sound/uart401.c
@@ -11,22 +11,23 @@
* for more info.
*/
#include <linux/config.h>
-
+#include <linux/module.h>
#include "sound_config.h"
+#include "soundmodule.h"
-#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
+#if (defined(CONFIG_UART401)||defined(CONFIG_MIDI)) || defined(MODULE)
typedef struct uart401_devc
{
- int base;
- int irq;
- int *osp;
- void (*midi_input_intr) (int dev, unsigned char data);
- int opened, disabled;
- volatile unsigned char input_byte;
- int my_dev;
- int share_irq;
+ int base;
+ int irq;
+ int *osp;
+ void (*midi_input_intr) (int dev, unsigned char data);
+ int opened, disabled;
+ volatile unsigned char input_byte;
+ int my_dev;
+ int share_irq;
}
uart401_devc;
@@ -38,27 +39,27 @@ static uart401_devc *irq2devc[16] =
#define COMDPORT (devc->base+1)
#define STATPORT (devc->base+1)
-static int
-uart401_status (uart401_devc * devc)
+static int
+uart401_status(uart401_devc * devc)
{
- return inb (STATPORT);
+ return inb(STATPORT);
}
#define input_avail(devc) (!(uart401_status(devc)&INPUT_AVAIL))
#define output_ready(devc) (!(uart401_status(devc)&OUTPUT_READY))
-static void
-uart401_cmd (uart401_devc * devc, unsigned char cmd)
+static void
+uart401_cmd(uart401_devc * devc, unsigned char cmd)
{
- outb ((cmd), COMDPORT);
+ outb((cmd), COMDPORT);
}
-static int
-uart401_read (uart401_devc * devc)
+static int
+uart401_read(uart401_devc * devc)
{
- return inb (DATAPORT);
+ return inb(DATAPORT);
}
-static void
-uart401_write (uart401_devc * devc, unsigned char byte)
+static void
+uart401_write(uart401_devc * devc, unsigned char byte)
{
- outb ((byte), DATAPORT);
+ outb((byte), DATAPORT);
}
#define OUTPUT_READY 0x40
@@ -67,141 +68,139 @@ uart401_write (uart401_devc * devc, unsigned char byte)
#define MPU_RESET 0xFF
#define UART_MODE_ON 0x3F
-static int reset_uart401 (uart401_devc * devc);
-static void enter_uart_mode (uart401_devc * devc);
+static int reset_uart401(uart401_devc * devc);
+static void enter_uart_mode(uart401_devc * devc);
static void
-uart401_input_loop (uart401_devc * devc)
+uart401_input_loop(uart401_devc * devc)
{
- while (input_avail (devc))
- {
- unsigned char c = uart401_read (devc);
-
- if (c == MPU_ACK)
- devc->input_byte = c;
- else if (devc->opened & OPEN_READ && devc->midi_input_intr)
- devc->midi_input_intr (devc->my_dev, c);
- }
+ while (input_avail(devc))
+ {
+ unsigned char c = uart401_read(devc);
+
+ if (c == MPU_ACK)
+ devc->input_byte = c;
+ else if (devc->opened & OPEN_READ && devc->midi_input_intr)
+ devc->midi_input_intr(devc->my_dev, c);
+ }
}
void
-uart401intr (int irq, void *dev_id, struct pt_regs *dummy)
+uart401intr(int irq, void *dev_id, struct pt_regs *dummy)
{
- uart401_devc *devc;
+ uart401_devc *devc;
- if (irq < 1 || irq > 15)
- return;
+ if (irq < 1 || irq > 15)
+ return;
- devc = irq2devc[irq];
+ devc = irq2devc[irq];
- if (devc == NULL)
- return;
+ if (devc == NULL)
+ return;
- if (input_avail (devc))
- uart401_input_loop (devc);
+ if (input_avail(devc))
+ uart401_input_loop(devc);
}
static int
-uart401_open (int dev, int mode,
- void (*input) (int dev, unsigned char data),
- void (*output) (int dev)
+uart401_open(int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
)
{
- uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
+ uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
- if (devc->opened)
- {
- return -EBUSY;
- }
+ if (devc->opened)
+ {
+ return -EBUSY;
+ }
+ while (input_avail(devc))
+ uart401_read(devc);
- while (input_avail (devc))
- uart401_read (devc);
+ devc->midi_input_intr = input;
+ devc->opened = mode;
+ enter_uart_mode(devc);
+ devc->disabled = 0;
- devc->midi_input_intr = input;
- devc->opened = mode;
- enter_uart_mode (devc);
- devc->disabled = 0;
-
- return 0;
+ return 0;
}
static void
-uart401_close (int dev)
+uart401_close(int dev)
{
- uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
+ uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
- reset_uart401 (devc);
- devc->opened = 0;
+ reset_uart401(devc);
+ devc->opened = 0;
}
static int
-uart401_out (int dev, unsigned char midi_byte)
+uart401_out(int dev, unsigned char midi_byte)
{
- int timeout;
- unsigned long flags;
- uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
-
- if (devc->disabled)
- return 1;
- /*
- * Test for input since pending input seems to block the output.
- */
-
- save_flags (flags);
- cli ();
-
- if (input_avail (devc))
- uart401_input_loop (devc);
-
- restore_flags (flags);
-
- /*
- * Sometimes it takes about 13000 loops before the output becomes ready
- * (After reset). Normally it takes just about 10 loops.
- */
-
- for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--);
-
- if (!output_ready (devc))
- {
- printk ("MPU-401: Timeout - Device not responding\n");
- devc->disabled = 1;
- reset_uart401 (devc);
- enter_uart_mode (devc);
- return 1;
- }
-
- uart401_write (devc, midi_byte);
- return 1;
+ int timeout;
+ unsigned long flags;
+ uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
+
+ if (devc->disabled)
+ return 1;
+ /*
+ * Test for input since pending input seems to block the output.
+ */
+
+ save_flags(flags);
+ cli();
+
+ if (input_avail(devc))
+ uart401_input_loop(devc);
+
+ restore_flags(flags);
+
+ /*
+ * Sometimes it takes about 13000 loops before the output becomes ready
+ * (After reset). Normally it takes just about 10 loops.
+ */
+
+ for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
+
+ if (!output_ready(devc))
+ {
+ printk("MPU-401: Timeout - Device not responding\n");
+ devc->disabled = 1;
+ reset_uart401(devc);
+ enter_uart_mode(devc);
+ return 1;
+ }
+ uart401_write(devc, midi_byte);
+ return 1;
}
static int
-uart401_start_read (int dev)
+uart401_start_read(int dev)
{
- return 0;
+ return 0;
}
static int
-uart401_end_read (int dev)
+uart401_end_read(int dev)
{
- return 0;
+ return 0;
}
static int
-uart401_ioctl (int dev, unsigned cmd, caddr_t arg)
+uart401_ioctl(int dev, unsigned cmd, caddr_t arg)
{
- return -EINVAL;
+ return -EINVAL;
}
static void
-uart401_kick (int dev)
+uart401_kick(int dev)
{
}
static int
-uart401_buffer_status (int dev)
+uart401_buffer_status(int dev)
{
- return 0;
+ return 0;
}
#define MIDI_SYNTH_NAME "MPU-401 UART"
@@ -210,253 +209,300 @@ uart401_buffer_status (int dev)
static struct midi_operations uart401_operations =
{
- {"MPU-401 (UART) MIDI", 0, 0, SNDCARD_MPU401},
- &std_midi_synth,
- {0},
- uart401_open,
- uart401_close,
- uart401_ioctl,
- uart401_out,
- uart401_start_read,
- uart401_end_read,
- uart401_kick,
- NULL,
- uart401_buffer_status,
- NULL
+ {"MPU-401 (UART) MIDI", 0, 0, SNDCARD_MPU401},
+ &std_midi_synth,
+ {0},
+ uart401_open,
+ uart401_close,
+ uart401_ioctl,
+ uart401_out,
+ uart401_start_read,
+ uart401_end_read,
+ uart401_kick,
+ NULL,
+ uart401_buffer_status,
+ NULL
};
static void
-enter_uart_mode (uart401_devc * devc)
+enter_uart_mode(uart401_devc * devc)
{
- int ok, timeout;
- unsigned long flags;
+ int ok, timeout;
+ unsigned long flags;
- save_flags (flags);
- cli ();
- for (timeout = 30000; timeout < 0 && !output_ready (devc); timeout--);
+ save_flags(flags);
+ cli();
+ for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
- devc->input_byte = 0;
- uart401_cmd (devc, UART_MODE_ON);
+ devc->input_byte = 0;
+ uart401_cmd(devc, UART_MODE_ON);
- ok = 0;
- for (timeout = 50000; timeout > 0 && !ok; timeout--)
- if (devc->input_byte == MPU_ACK)
- ok = 1;
- else if (input_avail (devc))
- if (uart401_read (devc) == MPU_ACK)
- ok = 1;
+ ok = 0;
+ for (timeout = 50000; timeout > 0 && !ok; timeout--)
+ if (devc->input_byte == MPU_ACK)
+ ok = 1;
+ else if (input_avail(devc))
+ if (uart401_read(devc) == MPU_ACK)
+ ok = 1;
- restore_flags (flags);
+ restore_flags(flags);
}
void
-attach_uart401 (struct address_info *hw_config)
+attach_uart401(struct address_info *hw_config)
{
- uart401_devc *devc;
- char *name = "MPU-401 (UART) MIDI";
-
- if (hw_config->name)
- name = hw_config->name;
-
- if (detected_devc == NULL)
- return;
-
-
- devc = (uart401_devc *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (uart401_devc)));
- sound_mem_sizes[sound_nblocks] = sizeof (uart401_devc);
- if (sound_nblocks < 1024)
- sound_nblocks++;;
- if (devc == NULL)
- {
- printk ("uart401: Can't allocate memory\n");
- return;
- }
-
- memcpy ((char *) devc, (char *) detected_devc, sizeof (uart401_devc));
- detected_devc = NULL;
-
- devc->irq = hw_config->irq;
- if (devc->irq < 0)
- {
- devc->share_irq = 1;
- devc->irq *= -1;
- }
- else
- devc->share_irq = 0;
-
- if (devc->irq < 1 || devc->irq > 15)
- return;
-
- if (!devc->share_irq)
- if (snd_set_irq_handler (devc->irq, uart401intr, "uart401", devc->osp) < 0)
- {
- printk ("uart401: Failed to allocate IRQ%d\n", devc->irq);
- devc->share_irq = 1;
- }
-
- irq2devc[devc->irq] = devc;
- devc->my_dev = num_midis;
-
- request_region (hw_config->io_base, 4, "SB MIDI");
- enter_uart_mode (devc);
-
- if (num_midis >= MAX_MIDI_DEV)
- {
- printk ("Sound: Too many midi devices detected\n");
- return;
- }
-
- conf_printf (name, hw_config);
+ uart401_devc *devc;
+ char *name = "MPU-401 (UART) MIDI";
+
+ if (hw_config->name)
+ name = hw_config->name;
+
+ if (detected_devc == NULL)
+ return;
+
+
+ devc = (uart401_devc *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(uart401_devc)));
+ sound_mem_sizes[sound_nblocks] = sizeof(uart401_devc);
+ if (sound_nblocks < 1024)
+ sound_nblocks++;;
+ if (devc == NULL)
+ {
+ printk(KERN_WARNING "uart401: Can't allocate memory\n");
+ return;
+ }
+ memcpy((char *) devc, (char *) detected_devc, sizeof(uart401_devc));
+ detected_devc = NULL;
+
+ devc->irq = hw_config->irq;
+ if (devc->irq < 0)
+ {
+ devc->share_irq = 1;
+ devc->irq *= -1;
+ } else
+ devc->share_irq = 0;
+
+ if (devc->irq < 1 || devc->irq > 15)
+ return;
+
+ if (!devc->share_irq)
+ if (snd_set_irq_handler(devc->irq, uart401intr, "MPU-401 UART", devc->osp) < 0)
+ {
+ printk(KERN_WARNING "uart401: Failed to allocate IRQ%d\n", devc->irq);
+ devc->share_irq = 1;
+ }
+ irq2devc[devc->irq] = devc;
+ devc->my_dev = sound_alloc_mididev();
+
+ request_region(hw_config->io_base, 4, "MPU-401 UART");
+ enter_uart_mode(devc);
+
+ if (devc->my_dev == -1)
+ {
+ printk(KERN_INFO "uart401: Too many midi devices detected\n");
+ return;
+ }
+ conf_printf(name, hw_config);
+
+ std_midi_synth.midi_dev = devc->my_dev;
+
+
+ midi_devs[devc->my_dev] = (struct midi_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct midi_operations)));
+ sound_mem_sizes[sound_nblocks] = sizeof(struct midi_operations);
+
+ if (sound_nblocks < 1024)
+ sound_nblocks++;;
+ if (midi_devs[devc->my_dev] == NULL)
+ {
+ printk("uart401: Failed to allocate memory\n");
+ sound_unload_mididev(devc->my_dev);
+ return;
+ }
+ memcpy((char *) midi_devs[devc->my_dev], (char *) &uart401_operations,
+ sizeof(struct midi_operations));
+
+ midi_devs[devc->my_dev]->devc = devc;
+
+
+ midi_devs[devc->my_dev]->converter = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct synth_operations)));
+ sound_mem_sizes[sound_nblocks] = sizeof(struct synth_operations);
+
+ if (sound_nblocks < 1024)
+ sound_nblocks++;
+
+ if (midi_devs[devc->my_dev]->converter == NULL)
+ {
+ printk(KERN_WARNING "uart401: Failed to allocate memory\n");
+ sound_unload_mididev(devc->my_dev);
+ return;
+ }
+ memcpy((char *) midi_devs[devc->my_dev]->converter, (char *) &std_midi_synth,
+ sizeof(struct synth_operations));
+
+ strcpy(midi_devs[devc->my_dev]->info.name, name);
+ midi_devs[devc->my_dev]->converter->id = "UART401";
+ hw_config->slots[4] = devc->my_dev;
+ sequencer_init();
+ devc->opened = 0;
+}
- std_midi_synth.midi_dev = devc->my_dev = num_midis;
+static int
+reset_uart401(uart401_devc * devc)
+{
+ int ok, timeout, n;
+ /*
+ * Send the RESET command. Try again if no success at the first time.
+ */
- midi_devs[num_midis] = (struct midi_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct midi_operations)));
- sound_mem_sizes[sound_nblocks] = sizeof (struct midi_operations);
+ ok = 0;
- if (sound_nblocks < 1024)
- sound_nblocks++;;
- if (midi_devs[num_midis] == NULL)
- {
- printk ("uart401: Failed to allocate memory\n");
- return;
- }
+ for (n = 0; n < 2 && !ok; n++)
+ {
+ for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
- memcpy ((char *) midi_devs[num_midis], (char *) &uart401_operations,
- sizeof (struct midi_operations));
+ devc->input_byte = 0;
+ uart401_cmd(devc, MPU_RESET);
- midi_devs[num_midis]->devc = devc;
+ /*
+ * Wait at least 25 msec. This method is not accurate so let's make the
+ * loop bit longer. Cannot sleep since this is called during boot.
+ */
+ for (timeout = 50000; timeout > 0 && !ok; timeout--)
+ if (devc->input_byte == MPU_ACK) /* Interrupt */
+ ok = 1;
+ else if (input_avail(devc))
+ if (uart401_read(devc) == MPU_ACK)
+ ok = 1;
- midi_devs[num_midis]->converter = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct synth_operations)));
- sound_mem_sizes[sound_nblocks] = sizeof (struct synth_operations);
+ }
- if (sound_nblocks < 1024)
- sound_nblocks++;;
- if (midi_devs[num_midis]->converter == NULL)
- {
- printk ("uart401: Failed to allocate memory\n");
- return;
- }
+ if (ok)
+ {
+ DEB(printk("Reset UART401 OK\n"));
+ } else
+ DDB(printk("Reset UART401 failed - No hardware detected.\n"));
- memcpy ((char *) midi_devs[num_midis]->converter, (char *) &std_midi_synth,
- sizeof (struct synth_operations));
+ if (ok)
+ uart401_input_loop(devc); /*
+ * Flush input before enabling interrupts
+ */
- strcpy (midi_devs[num_midis]->info.name, name);
- midi_devs[num_midis]->converter->id = "UART401";
- num_midis++;
- sequencer_init ();
- devc->opened = 0;
+ return ok;
}
-static int
-reset_uart401 (uart401_devc * devc)
+int
+probe_uart401(struct address_info *hw_config)
{
- int ok, timeout, n;
-
- /*
- * Send the RESET command. Try again if no success at the first time.
- */
-
- ok = 0;
+ int ok = 0;
+ unsigned long flags;
- for (n = 0; n < 2 && !ok; n++)
- {
- for (timeout = 30000; timeout < 0 && !output_ready (devc); timeout--);
+ static uart401_devc hw_info;
+ uart401_devc *devc = &hw_info;
- devc->input_byte = 0;
- uart401_cmd (devc, MPU_RESET);
+ DDB(printk("Entered probe_uart401()\n"));
- /*
- * Wait at least 25 msec. This method is not accurate so let's make the
- * loop bit longer. Cannot sleep since this is called during boot.
- */
+ detected_devc = NULL;
- for (timeout = 50000; timeout > 0 && !ok; timeout--)
- if (devc->input_byte == MPU_ACK) /* Interrupt */
- ok = 1;
- else if (input_avail (devc))
- if (uart401_read (devc) == MPU_ACK)
- ok = 1;
+ if (check_region(hw_config->io_base, 4))
+ return 0;
- }
+ devc->base = hw_config->io_base;
+ devc->irq = hw_config->irq;
+ devc->osp = hw_config->osp;
+ devc->midi_input_intr = NULL;
+ devc->opened = 0;
+ devc->input_byte = 0;
+ devc->my_dev = 0;
+ devc->share_irq = 0;
+ save_flags(flags);
+ cli();
+ ok = reset_uart401(devc);
+ restore_flags(flags);
- if (ok)
- {
- DDB (printk ("Reset UART401 OK\n"));
- }
- else
- DDB (printk ("Reset UART401 failed - No hardware detected.\n"));
+ if (ok)
+ detected_devc = devc;
- if (ok)
- uart401_input_loop (devc); /*
- * Flush input before enabling interrupts
- */
-
- return ok;
+ return ok;
}
-int
-probe_uart401 (struct address_info *hw_config)
+void
+unload_uart401(struct address_info *hw_config)
{
- int ok = 0;
- unsigned long flags;
-
- static uart401_devc hw_info;
- uart401_devc *devc = &hw_info;
-
- DDB (printk ("Entered probe_uart401()\n"));
+ uart401_devc *devc;
- detected_devc = NULL;
+ int irq = hw_config->irq;
- if (check_region (hw_config->io_base, 4))
- return 0;
+ if (irq < 0)
+ {
+ irq *= -1;
+ }
+ if (irq < 1 || irq > 15)
+ return;
- devc->base = hw_config->io_base;
- devc->irq = hw_config->irq;
- devc->osp = hw_config->osp;
- devc->midi_input_intr = NULL;
- devc->opened = 0;
- devc->input_byte = 0;
- devc->my_dev = 0;
- devc->share_irq = 0;
+ devc = irq2devc[irq];
+ if (devc == NULL)
+ return;
- save_flags (flags);
- cli ();
- ok = reset_uart401 (devc);
- restore_flags (flags);
+ reset_uart401(devc);
+ release_region(hw_config->io_base, 4);
- if (ok)
- detected_devc = devc;
+ if (!devc->share_irq)
+ snd_release_irq(devc->irq);
- return ok;
+ /* Free device too !! - AC FIXME: CHECK THIS IS RIGHT */
+ if (devc)
+ vfree(devc);
+ sound_unload_mididev(hw_config->slots[4]);
}
-void
-unload_uart401 (struct address_info *hw_config)
-{
- uart401_devc *devc;
-
- int irq = hw_config->irq;
+#ifdef MODULE
- if (irq < 0)
- irq *= -1;
+int io = -1;
+int irq = -1;
- if (irq < 1 || irq > 15)
- return;
+MODULE_PARM(io, "i");
+MODULE_PARM(irq, "i");
+struct address_info hw;
- devc = irq2devc[irq];
- if (devc == NULL)
- return;
-
- reset_uart401 (devc);
- release_region (hw_config->io_base, 4);
+int
+init_module(void)
+{
+ /* Can be loaded either for module use or to provide functions
+ to others */
+ if (io != -1 && irq != -1)
+ {
+ printk("MPU-401 UART driver Copyright (C) Hannu Savolainen 1993-1997");
+ hw.irq = irq;
+ hw.io_base = io;
+ if (probe_uart401(&hw) == 0)
+ return -ENODEV;
+ attach_uart401(&hw);
+ }
+ SOUND_LOCK;
+ return 0;
+}
- if (!devc->share_irq)
- snd_release_irq (devc->irq);
+void
+cleanup_module(void)
+{
+ if (io != -1 && irq != -1)
+ {
+ unload_uart401(&hw);
+ }
+ /* FREE SYMTAB */
+ SOUND_LOCK_END;
}
+#else
#endif
+
+#endif
+
+EXPORT_SYMBOL(attach_uart401);
+EXPORT_SYMBOL(probe_uart401);
+EXPORT_SYMBOL(unload_uart401);
+EXPORT_SYMBOL(uart401intr);
diff --git a/drivers/sound/uart6850.c b/drivers/sound/uart6850.c
index 547cbba92..9285e40b1 100644
--- a/drivers/sound/uart6850.c
+++ b/drivers/sound/uart6850.c
@@ -1,3 +1,6 @@
+
+
+
/*
* sound/uart6850.c
*/
@@ -7,16 +10,20 @@
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
+ * Extended by Alan Cox for Red Hat Software. Now a loadable MIDI driver.
+ * 28/4/97 - (C) Copyright Alan Cox. Released under the GPL version 2.
+ *
*/
#include <linux/config.h>
+#include <linux/module.h>
/* Mon Nov 22 22:38:35 MET 1993 marco@driq.home.usn.nl:
* added 6850 support, used with COVOX SoundMaster II and custom cards.
*/
#include "sound_config.h"
-
-#if defined(CONFIG_UART6850) && defined(CONFIG_MIDI)
+#include "soundmodule.h"
+#if defined(CONFIG_UART6850) && defined(CONFIG_MIDI) || defined(MODULE)
static int uart6850_base = 0x330;
@@ -26,27 +33,27 @@ static int *uart6850_osp;
#define COMDPORT (uart6850_base+1)
#define STATPORT (uart6850_base+1)
-static int
-uart6850_status (void)
+static int
+uart6850_status(void)
{
- return inb (STATPORT);
+ return inb(STATPORT);
}
#define input_avail() (uart6850_status()&INPUT_AVAIL)
#define output_ready() (uart6850_status()&OUTPUT_READY)
-static void
-uart6850_cmd (unsigned char cmd)
+static void
+uart6850_cmd(unsigned char cmd)
{
- outb ((cmd), COMDPORT);
+ outb((cmd), COMDPORT);
}
-static int
-uart6850_read (void)
+static int
+uart6850_read(void)
{
- return inb (DATAPORT);
+ return inb(DATAPORT);
}
-static void
-uart6850_write (unsigned char byte)
+static void
+uart6850_write(unsigned char byte)
{
- outb ((byte), DATAPORT);
+ outb((byte), DATAPORT);
}
#define OUTPUT_READY 0x02 /* Mask for data ready Bit */
@@ -60,43 +67,42 @@ static int uart6850_irq;
static int uart6850_detected = 0;
static int my_dev;
-static int reset_uart6850 (void);
+static int reset_uart6850(void);
static void (*midi_input_intr) (int dev, unsigned char data);
-static void poll_uart6850 (unsigned long dummy);
+static void poll_uart6850(unsigned long dummy);
static struct timer_list uart6850_timer =
{NULL, NULL, 0, 0, poll_uart6850};
static void
-uart6850_input_loop (void)
+uart6850_input_loop(void)
{
- int count;
+ int count;
- count = 10;
+ count = 10;
- while (count) /*
+ while (count) /*
* Not timed out
*/
- if (input_avail ())
- {
- unsigned char c = uart6850_read ();
-
- count = 100;
-
- if (uart6850_opened & OPEN_READ)
- midi_input_intr (my_dev, c);
- }
- else
- while (!input_avail () && count)
- count--;
+ if (input_avail())
+ {
+ unsigned char c = uart6850_read();
+
+ count = 100;
+
+ if (uart6850_opened & OPEN_READ)
+ midi_input_intr(my_dev, c);
+ } else
+ while (!input_avail() && count)
+ count--;
}
void
-m6850intr (int irq, void *dev_id, struct pt_regs *dummy)
+m6850intr(int irq, void *dev_id, struct pt_regs *dummy)
{
- if (input_avail ())
- uart6850_input_loop ();
+ if (input_avail())
+ uart6850_input_loop();
}
/*
@@ -105,135 +111,136 @@ m6850intr (int irq, void *dev_id, struct pt_regs *dummy)
*/
static void
-poll_uart6850 (unsigned long dummy)
+poll_uart6850(unsigned long dummy)
{
- unsigned long flags;
+ unsigned long flags;
- if (!(uart6850_opened & OPEN_READ))
- return; /* Device has been closed */
+ if (!(uart6850_opened & OPEN_READ))
+ return; /* Device has been closed */
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- if (input_avail ())
- uart6850_input_loop ();
+ if (input_avail())
+ uart6850_input_loop();
- {
- uart6850_timer.expires = (1) + jiffies;
- add_timer (&uart6850_timer);
- }; /*
+ {
+ uart6850_timer.expires = (1) + jiffies;
+ add_timer(&uart6850_timer);
+ }; /*
* Come back later
*/
- restore_flags (flags);
+ restore_flags(flags);
}
static int
-uart6850_open (int dev, int mode,
- void (*input) (int dev, unsigned char data),
- void (*output) (int dev)
+uart6850_open(int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
)
{
- if (uart6850_opened)
- {
- printk ("Midi6850: Midi busy\n");
- return -EBUSY;
- }
+ if (uart6850_opened)
+ {
+ printk("Midi6850: Midi busy\n");
+ return -EBUSY;
+ };
- ;
- uart6850_cmd (UART_RESET);
+ MOD_INC_USE_COUNT;
+ uart6850_cmd(UART_RESET);
- uart6850_input_loop ();
+ uart6850_input_loop();
- midi_input_intr = input;
- uart6850_opened = mode;
- poll_uart6850 (0); /*
+ midi_input_intr = input;
+ uart6850_opened = mode;
+ poll_uart6850(0); /*
* Enable input polling
*/
- return 0;
+ return 0;
}
static void
-uart6850_close (int dev)
+uart6850_close(int dev)
{
- uart6850_cmd (UART_MODE_ON);
+ uart6850_cmd(UART_MODE_ON);
+
+ del_timer(&uart6850_timer);;
+ uart6850_opened = 0;
- del_timer (&uart6850_timer);;
- uart6850_opened = 0;
+ MOD_DEC_USE_COUNT;
}
static int
-uart6850_out (int dev, unsigned char midi_byte)
+uart6850_out(int dev, unsigned char midi_byte)
{
- int timeout;
- unsigned long flags;
+ int timeout;
+ unsigned long flags;
- /*
- * Test for input since pending input seems to block the output.
- */
+ /*
+ * Test for input since pending input seems to block the output.
+ */
- save_flags (flags);
- cli ();
+ save_flags(flags);
+ cli();
- if (input_avail ())
- uart6850_input_loop ();
+ if (input_avail())
+ uart6850_input_loop();
- restore_flags (flags);
+ restore_flags(flags);
- /*
- * Sometimes it takes about 13000 loops before the output becomes ready
- * (After reset). Normally it takes just about 10 loops.
- */
+ /*
+ * Sometimes it takes about 13000 loops before the output becomes ready
+ * (After reset). Normally it takes just about 10 loops.
+ */
- for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); /*
- * Wait
- */
+ for (timeout = 30000; timeout > 0 && !output_ready(); timeout--); /*
+ * Wait
+ */
- if (!output_ready ())
- {
- printk ("Midi6850: Timeout\n");
- return 0;
- }
-
- uart6850_write (midi_byte);
- return 1;
+ if (!output_ready())
+ {
+ printk("Midi6850: Timeout\n");
+ return 0;
+ }
+ uart6850_write(midi_byte);
+ return 1;
}
static int
-uart6850_command (int dev, unsigned char *midi_byte)
+uart6850_command(int dev, unsigned char *midi_byte)
{
- return 1;
+ return 1;
}
static int
-uart6850_start_read (int dev)
+uart6850_start_read(int dev)
{
- return 0;
+ return 0;
}
static int
-uart6850_end_read (int dev)
+uart6850_end_read(int dev)
{
- return 0;
+ return 0;
}
static int
-uart6850_ioctl (int dev, unsigned cmd, caddr_t arg)
+uart6850_ioctl(int dev, unsigned cmd, caddr_t arg)
{
- return -EINVAL;
+ return -EINVAL;
}
static void
-uart6850_kick (int dev)
+uart6850_kick(int dev)
{
}
static int
-uart6850_buffer_status (int dev)
+uart6850_buffer_status(int dev)
{
- return 0; /*
+ return 0; /*
* No data in buffers
*/
}
@@ -244,91 +251,127 @@ uart6850_buffer_status (int dev)
static struct midi_operations uart6850_operations =
{
- {"6850 UART", 0, 0, SNDCARD_UART6850},
- &std_midi_synth,
- {0},
- uart6850_open,
- uart6850_close,
- uart6850_ioctl,
- uart6850_out,
- uart6850_start_read,
- uart6850_end_read,
- uart6850_kick,
- uart6850_command,
- uart6850_buffer_status
+ {"6850 UART", 0, 0, SNDCARD_UART6850},
+ &std_midi_synth,
+ {0},
+ uart6850_open,
+ uart6850_close,
+ uart6850_ioctl,
+ uart6850_out,
+ uart6850_start_read,
+ uart6850_end_read,
+ uart6850_kick,
+ uart6850_command,
+ uart6850_buffer_status
};
void
-attach_uart6850 (struct address_info *hw_config)
+attach_uart6850(struct address_info *hw_config)
{
- int ok, timeout;
- unsigned long flags;
-
- if (num_midis >= MAX_MIDI_DEV)
- {
- printk ("Sound: Too many midi devices detected\n");
- return;
- }
-
- uart6850_base = hw_config->io_base;
- uart6850_osp = hw_config->osp;
- uart6850_irq = hw_config->irq;
-
- if (!uart6850_detected)
- return;
-
- save_flags (flags);
- cli ();
-
- for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /*
- * Wait
- */
- uart6850_cmd (UART_MODE_ON);
-
- ok = 1;
-
- restore_flags (flags);
-
- conf_printf ("6850 Midi Interface", hw_config);
-
- std_midi_synth.midi_dev = my_dev = num_midis;
- midi_devs[num_midis++] = &uart6850_operations;
- sequencer_init ();
+ int ok, timeout;
+ unsigned long flags;
+
+ if ((my_dev = sound_alloc_mididev()) == -1)
+ {
+ printk(KERN_INFO "uart6850: Too many midi devices detected\n");
+ return;
+ }
+ uart6850_base = hw_config->io_base;
+ uart6850_osp = hw_config->osp;
+ uart6850_irq = hw_config->irq;
+
+ if (!uart6850_detected)
+ {
+ sound_unload_mididev(my_dev);
+ return;
+ }
+ save_flags(flags);
+ cli();
+
+ for (timeout = 30000; timeout > 0 && !output_ready(); timeout--); /*
+ * Wait
+ */
+ uart6850_cmd(UART_MODE_ON);
+
+ ok = 1;
+
+ restore_flags(flags);
+
+ conf_printf("6850 Midi Interface", hw_config);
+
+ std_midi_synth.midi_dev = my_dev;
+ hw_config->slots[4] = my_dev;
+ midi_devs[my_dev] = &uart6850_operations;
+ sequencer_init();
}
static int
-reset_uart6850 (void)
+reset_uart6850(void)
{
- uart6850_read ();
- return 1; /*
+ uart6850_read();
+ return 1; /*
* OK
*/
}
int
-probe_uart6850 (struct address_info *hw_config)
+probe_uart6850(struct address_info *hw_config)
{
- int ok = 0;
+ int ok = 0;
- uart6850_osp = hw_config->osp;
- uart6850_base = hw_config->io_base;
- uart6850_irq = hw_config->irq;
+ uart6850_osp = hw_config->osp;
+ uart6850_base = hw_config->io_base;
+ uart6850_irq = hw_config->irq;
- if (snd_set_irq_handler (uart6850_irq, m6850intr, "MIDI6850", uart6850_osp) < 0)
- return 0;
+ if (snd_set_irq_handler(uart6850_irq, m6850intr, "MIDI6850", uart6850_osp) < 0)
+ return 0;
- ok = reset_uart6850 ();
+ ok = reset_uart6850();
- uart6850_detected = ok;
- return ok;
+ uart6850_detected = ok;
+ return ok;
}
void
-unload_uart6850 (struct address_info *hw_config)
+unload_uart6850(struct address_info *hw_config)
+{
+ snd_release_irq(hw_config->irq);
+ sound_unload_mididev(hw_config->slots[4]);
+}
+
+
+#ifdef MODULE
+
+int io = -1;
+int irq = -1;
+
+struct address_info cfg;
+
+int
+init_module(void)
{
- snd_release_irq (hw_config->irq);
+ if (io == -1 || irq == -1)
+ {
+ printk("uart6850: irq and io must be set.\n");
+ return -EINVAL;
+ }
+ cfg.io_base = io;
+ cfg.irq = irq;
+
+ if (probe_uart6850(&cfg))
+ return -ENODEV;
+
+ SOUND_LOCK;
+ return 0;
}
+void
+cleanup_module(void)
+{
+ unload_uart6850(&cfg);
+ SOUND_LOCK_END;
+}
+#endif
#endif
diff --git a/drivers/video/.cvsignore b/drivers/video/.cvsignore
new file mode 100644
index 000000000..4671378ae
--- /dev/null
+++ b/drivers/video/.cvsignore
@@ -0,0 +1 @@
+.depend
diff --git a/drivers/video/Config.in b/drivers/video/Config.in
new file mode 100644
index 000000000..e39e33bfc
--- /dev/null
+++ b/drivers/video/Config.in
@@ -0,0 +1,76 @@
+#
+# Video configuration
+#
+
+if [ "$CONFIG_FB" = "y" ]; then
+
+ mainmenu_option next_comment
+ comment 'Frame buffer devices'
+
+ if [ "$CONFIG_AMIGA" = "y" ]; then
+ bool 'Amiga native chipset support' CONFIG_FB_AMIGA
+ if [ "$CONFIG_FB_AMIGA" != "n" ]; then
+ bool 'Amiga OCS chipset support' CONFIG_FB_AMIGA_OCS
+ bool 'Amiga ECS chipset support' CONFIG_FB_AMIGA_ECS
+ bool 'Amiga AGA chipset support' CONFIG_FB_AMIGA_AGA
+ fi
+ tristate 'Amiga Cybervision support' CONFIG_FB_CYBER
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ tristate 'Amiga RetinaZ3 support' CONFIG_FB_RETINAZ3
+ fi
+ fi
+ if [ "$CONFIG_ATARI" = "y" ]; then
+ bool 'Atari native chipset support' CONFIG_FB_ATARI
+# tristate 'Mach64 Frame Buffer support' CONFIG_FB_MACH64
+ fi
+ if [ "$CONFIG_CHRP" = "y" -o "$CONFIG_PMAC" = "y" ]; then
+ bool 'Open Firmware frame buffer device support' CONFIG_FB_OPEN_FIRMWARE
+ fi
+ tristate 'Virtual Frame Buffer support' CONFIG_FB_VIRTUAL
+
+ bool 'Advanced low level driver options' CONFIG_FBCON_ADVANCED
+ if [ "$CONFIG_FBCON_ADVANCED" = "y" ]; then
+ tristate 'Monochrome support' CONFIG_FBCON_MFB
+ tristate 'Interleaved bitplanes support' CONFIG_FBCON_ILBM
+ tristate 'Normal bitplanes support' CONFIG_FBCON_AFB
+ tristate 'Atari interleaved bitplanes (2 planes) support' CONFIG_FBCON_IPLAN2P2
+ tristate 'Atari interleaved bitplanes (4 planes) support' CONFIG_FBCON_IPLAN2P4
+ tristate 'Atari interleaved bitplanes (8 planes) support' CONFIG_FBCON_IPLAN2P8
+ tristate '8 bpp packed pixel support' CONFIG_FBCON_CFB8
+ tristate '16 bpp packed pixel support' CONFIG_FBCON_CFB16
+ tristate 'Cybervision support (accelerated)' CONFIG_FBCON_CYBER
+ tristate 'RetinaZ3 support (accelerated)' CONFIG_FBCON_RETINAZ3
+ else
+ if [ "$CONFIG_FB_AMIGA" != "n" -o "$CONFIG_FB_ATARI" != "n" -o \
+ "$CONFIG_FB_CYBER" != "n" -o "$CONFIG_FB_RETINAZ3" != "n" -o \
+ "$CONFIG_FB_VIRTUAL" != "n" ]; then
+ define_bool CONFIG_FBCON_MFB y
+ fi
+ if [ "$CONFIG_FB_AMIGA" = "y" -o "$CONFIG_FB_AMIGA" = "m" ]; then
+ define_bool CONFIG_FBCON_ILBM y
+ define_bool CONFIG_FBCON_AFB y
+ fi
+ if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATARI" = "m" ]; then
+ define_bool CONFIG_FBCON_IPLAN2P2 y
+ define_bool CONFIG_FBCON_IPLAN2P4 y
+ define_bool CONFIG_FBCON_IPLAN2P8 y
+ fi
+ if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATARI" = "m" -o \
+ "$CONFIG_FB_OPEN_FIRMWARE" = "y" -o \
+ "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then
+ define_bool CONFIG_FBCON_CFB8 y
+ fi
+ if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATARI" = "m" -o \
+ "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then
+ define_bool CONFIG_FBCON_CFB16 y
+ fi
+ if [ "$CONFIG_FB_CYBER" = "y" -o "$CONFIG_FB_CYBER" = "m" ]; then
+ define_bool CONFIG_FBCON_CYBER y
+ fi
+ if [ "$CONFIG_FB_RETINAZ3" = "y" -o "$CONFIG_FB_RETINAZ3" = "m" ]; then
+ define_bool CONFIG_FBCON_RETINAZ3 y
+ fi
+ fi
+
+ endmenu
+fi
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
new file mode 100644
index 000000000..ebed69d68
--- /dev/null
+++ b/drivers/video/Makefile
@@ -0,0 +1,199 @@
+#
+# Makefile for the kernel video drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+#
+
+GSPA = gspa
+GSPH2C = gspahextoc
+
+L_TARGET := video.a
+L_OBJS :=
+M_OBJS :=
+LX_OBJS :=
+MX_OBJS :=
+MOD_LIST_NAME := VIDEO_MODULES
+
+# Frame Buffer Console
+
+ifeq ($(CONFIG_FB),y)
+ L_OBJS += fonts.o font_8x8.o font_8x16.o pearl_8x8.o
+ LX_OBJS += fbcon.o fbcmap.o
+endif
+
+# Frame buffer devices
+
+ifeq ($(CONFIG_APOLLO),y)
+L_OBJS += dn_fb.o
+endif
+
+ifeq ($(CONFIG_FB_AMIGA),y)
+L_OBJS += amifb.o
+else
+ ifeq ($(CONFIG_FB_AMIGA),m)
+ M_OBJS += amifb.o
+ endif
+endif
+
+ifeq ($(CONFIG_FB_ATARI),y)
+L_OBJS += atafb.o
+else
+ ifeq ($(CONFIG_FB_ATARI),m)
+ M_OBJS += atafb.o
+ endif
+endif
+
+ifeq ($(CONFIG_FB_CYBER),y)
+LX_OBJS += cyberfb.o
+else
+ ifeq ($(CONFIG_FB_CYBER),m)
+ MX_OBJS += cyberfb.o
+ endif
+endif
+
+ifeq ($(CONFIG_FB_RETINAZ3),y)
+LX_OBJS += retz3fb.o
+else
+ ifeq ($(CONFIG_FB_RETINAZ3),m)
+ MX_OBJS += retz3fb.o
+ endif
+endif
+
+ifeq ($(CONFIG_FB_VIRTUAL),y)
+L_OBJS += vfb.o
+else
+ ifeq ($(CONFIG_FB_VIRTUAL),m)
+ M_OBJS += vfb.o
+ endif
+endif
+
+ifeq ($(CONFIG_FB_OPEN_FIRMWARE),y)
+L_OBJS += offb.o
+endif
+
+ifeq ($(CONFIG_FB_MACH64),y)
+L_OBJS += mach64fb.o
+else
+ ifeq ($(CONFIG_FB_MACH64),m)
+ M_OBJS += mach64fb.o
+ endif
+endif
+
+ifeq ($(CONFIG_FB_TGA),y)
+L_OBJS += tgafb.o
+endif
+
+# Low level drivers
+
+ifeq ($(CONFIG_FBCON_AFB),y)
+L_OBJS += fbcon-afb.o
+else
+ ifeq ($(CONFIG_FBCON_AFB),m)
+ M_OBJS += fbcon-afb.o
+ endif
+endif
+
+ifeq ($(CONFIG_FBCON_CFB8),y)
+L_OBJS += fbcon-cfb8.o
+else
+ ifeq ($(CONFIG_FBCON_CFB8),m)
+ M_OBJS += fbcon-cfb8.o
+ endif
+endif
+
+ifeq ($(CONFIG_FBCON_CFB16),y)
+LX_OBJS += fbcon-cfb16.o
+else
+ ifeq ($(CONFIG_FBCON_CFB16),m)
+ MX_OBJS += fbcon-cfb16.o
+ endif
+endif
+
+ifeq ($(CONFIG_FBCON_ILBM),y)
+L_OBJS += fbcon-ilbm.o
+else
+ ifeq ($(CONFIG_FBCON_ILBM),m)
+ M_OBJS += fbcon-ilbm.o
+ endif
+endif
+
+ifeq ($(CONFIG_FBCON_IPLAN2P2),y)
+L_OBJS += fbcon-iplan2p2.o
+else
+ ifeq ($(CONFIG_FBCON_IPLAN2P2),m)
+ M_OBJS += fbcon-iplan2p2.o
+ endif
+endif
+
+ifeq ($(CONFIG_FBCON_IPLAN2P4),y)
+L_OBJS += fbcon-iplan2p4.o
+else
+ ifeq ($(CONFIG_FBCON_IPLAN2P4),m)
+ M_OBJS += fbcon-iplan2p4.o
+ endif
+endif
+
+ifeq ($(CONFIG_FBCON_IPLAN2P8),y)
+L_OBJS += fbcon-iplan2p8.o
+else
+ ifeq ($(CONFIG_FBCON_IPLAN2P8),m)
+ M_OBJS += fbcon-iplan2p8.o
+ endif
+endif
+
+ifeq ($(CONFIG_FBCON_MFB),y)
+L_OBJS += fbcon-mfb.o
+else
+ ifeq ($(CONFIG_FBCON_MFB),m)
+ M_OBJS += fbcon-mfb.o
+ endif
+endif
+
+ifeq ($(CONFIG_FBCON_CYBER),y)
+L_OBJS += fbcon-cyber.o
+else
+ ifeq ($(CONFIG_FBCON_CYBER),m)
+ M_OBJS += fbcon-cyber.o
+ endif
+endif
+
+ifeq ($(CONFIG_FBCON_RETINAZ3),y)
+L_OBJS += fbcon-retz3.o
+else
+ ifeq ($(CONFIG_FBCON_RETINAZ3),m)
+ M_OBJS += fbcon-retz3.o
+ endif
+endif
+
+ifeq ($(CONFIG_FBCON_MACH64),y)
+L_OBJS += fbcon-mach64.o
+else
+ ifeq ($(CONFIG_FBCON_MACH64),m)
+ M_OBJS += fbcon-mach64.o
+ endif
+endif
+
+# GSP Console
+
+ifdef CONFIG_AMIGA_GSP
+L_OBJS := $(L_OBJS) gspcon.o gspcore.o
+endif
+
+# VGA Console
+
+ifdef CONFIG_ABSTRACT_CONSOLE
+ifdef CONFIG_VGA_CONSOLE
+L_OBJS := $(L_OBJS) vgacon.o
+endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+gspcore.c: gspcore.gsp
+ $(GSPA) $< > $*.hex
+ $(GSPH2C) $*.hex > gspcore.c
diff --git a/drivers/video/amifb.c b/drivers/video/amifb.c
new file mode 100644
index 000000000..7aa9df61a
--- /dev/null
+++ b/drivers/video/amifb.c
@@ -0,0 +1,3520 @@
+/*
+ * linux/drivers/video/amifb.c -- Amiga builtin chipset frame buffer device
+ *
+ * Copyright (C) 1995 Geert Uytterhoeven
+ *
+ * with work by Roman Zippel
+ *
+ *
+ * This file is based on the Atari frame buffer device (atafb.c):
+ *
+ * Copyright (C) 1994 Martin Schaller
+ * Roman Hodek
+ *
+ * with work by Andreas Schwab
+ * Guenther Kelleter
+ *
+ * and on the original Amiga console driver (amicon.c):
+ *
+ * Copyright (C) 1993 Hamish Macdonald
+ * Greg Harp
+ * Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk]
+ *
+ * with work by William Rucklidge (wjr@cs.cornell.edu)
+ * Geert Uytterhoeven
+ * Jes Sorensen (jds@kom.auc.dk)
+ *
+ *
+ * History:
+ *
+ * - 24 Jul 96: Copper generates now vblank interrupt and
+ * VESA Power Saving Protocol is fully implemented
+ * - 14 Jul 96: Rework and hopefully last ECS bugs fixed
+ * - 7 Mar 96: Hardware sprite support by Roman Zippel
+ * - 18 Feb 96: OCS and ECS support by Roman Zippel
+ * Hardware functions completely rewritten
+ * - 2 Dec 95: AGA version by Geert Uytterhoeven
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/config.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/setup.h>
+
+#define DEBUG
+
+#if !defined(CONFIG_FB_AMIGA_OCS) && !defined(CONFIG_FB_AMIGA_ECS) && !defined(CONFIG_FB_AMIGA_AGA)
+#define CONFIG_FB_AMIGA_OCS /* define at least one fb driver, this will change later */
+#endif
+
+#if !defined(CONFIG_FB_AMIGA_OCS)
+# define IS_OCS (0)
+#elif defined(CONFIG_FB_AMIGA_ECS) || defined(CONFIG_FB_AMIGA_AGA)
+# define IS_OCS (chipset == TAG_OCS)
+#else
+# define CONFIG_FB_AMIGA_OCS_ONLY
+# define IS_OCS (1)
+#endif
+
+#if !defined(CONFIG_FB_AMIGA_ECS)
+# define IS_ECS (0)
+#elif defined(CONFIG_FB_AMIGA_OCS) || defined(CONFIG_FB_AMIGA_AGA)
+# define IS_ECS (chipset == TAG_ECS)
+#else
+# define CONFIG_FB_AMIGA_ECS_ONLY
+# define IS_ECS (1)
+#endif
+
+#if !defined(CONFIG_FB_AMIGA_AGA)
+# define IS_AGA (0)
+#elif defined(CONFIG_FB_AMIGA_OCS) || defined(CONFIG_FB_AMIGA_ECS)
+# define IS_AGA (chipset == TAG_AGA)
+#else
+# define CONFIG_FB_AMIGA_AGA_ONLY
+# define IS_AGA (1)
+#endif
+
+#ifdef DEBUG
+# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+#else
+# define DPRINTK(fmt, args...)
+#endif
+
+/*******************************************************************************
+
+
+ Generic video timings
+ ---------------------
+
+ Timings used by the frame buffer interface:
+
+ +----------+---------------------------------------------+----------+-------+
+ | | ^ | | |
+ | | |upper_margin | | |
+ | | ¥ | | |
+ +----------###############################################----------+-------+
+ | # ^ # | |
+ | # | # | |
+ | # | # | |
+ | # | # | |
+ | left # | # right | hsync |
+ | margin # | xres # margin | len |
+ |<-------->#<---------------+--------------------------->#<-------->|<----->|
+ | # | # | |
+ | # | # | |
+ | # | # | |
+ | # |yres # | |
+ | # | # | |
+ | # | # | |
+ | # | # | |
+ | # | # | |
+ | # | # | |
+ | # | # | |
+ | # | # | |
+ | # | # | |
+ | # ¥ # | |
+ +----------###############################################----------+-------+
+ | | ^ | | |
+ | | |lower_margin | | |
+ | | ¥ | | |
+ +----------+---------------------------------------------+----------+-------+
+ | | ^ | | |
+ | | |vsync_len | | |
+ | | ¥ | | |
+ +----------+---------------------------------------------+----------+-------+
+
+
+ Amiga video timings
+ -------------------
+
+ The Amiga native chipsets uses another timing scheme:
+
+ - hsstrt: Start of horizontal synchronization pulse
+ - hsstop: End of horizontal synchronization pulse
+ - htotal: Last value on the line (i.e. line length = htotal+1)
+ - vsstrt: Start of vertical synchronization pulse
+ - vsstop: End of vertical synchronization pulse
+ - vtotal: Last line value (i.e. number of lines = vtotal+1)
+ - hcenter: Start of vertical retrace for interlace
+
+ You can specify the blanking timings independently. Currently I just set
+ them equal to the respective synchronization values:
+
+ - hbstrt: Start of horizontal blank
+ - hbstop: End of horizontal blank
+ - vbstrt: Start of vertical blank
+ - vbstop: End of vertical blank
+
+ Horizontal values are in color clock cycles (280 ns), vertical values are in
+ scanlines.
+
+ (0, 0) is somewhere in the upper-left corner :-)
+
+
+ Amiga visible window definitions
+ --------------------------------
+
+ Currently I only have values for AGA, SHRES (28 MHz dotclock). Feel free to
+ make corrections and/or additions.
+
+ Within the above synchronization specifications, the visible window is
+ defined by the following parameters (actual register resolutions may be
+ different; all horizontal values are normalized with respect to the pixel
+ clock):
+
+ - diwstrt_h: Horizontal start of the visible window
+ - diwstop_h: Horizontal stop+1(*) of the visible window
+ - diwstrt_v: Vertical start of the visible window
+ - diwstop_v: Vertical stop of the visible window
+ - ddfstrt: Horizontal start of display DMA
+ - ddfstop: Horizontal stop of display DMA
+ - hscroll: Horizontal display output delay
+
+ Sprite positioning:
+
+ - sprstrt_h: Horizontal start-4 of sprite
+ - sprstrt_v: Vertical start of sprite
+
+ (*) Even Commodore did it wrong in the AGA monitor drivers by not adding 1.
+
+ Horizontal values are in dotclock cycles (35 ns), vertical values are in
+ scanlines.
+
+ (0, 0) is somewhere in the upper-left corner :-)
+
+
+ Dependencies (AGA, SHRES (35 ns dotclock))
+ -------------------------------------------
+
+ Since there are much more parameters for the Amiga display than for the
+ frame buffer interface, there must be some dependencies among the Amiga
+ display parameters. Here's what I found out:
+
+ - ddfstrt and ddfstop are best aligned to 64 pixels.
+ - the chipset needs 64+4 horizontal pixels after the DMA start before the
+ first pixel is output, so diwstrt_h = ddfstrt+64+4 if you want to
+ display the first pixel on the line too. Increase diwstrt_h for virtual
+ screen panning.
+ - the display DMA always fetches 64 pixels at a time (fmode = 3).
+ - ddfstop is ddfstrt+#pixels-64.
+ - diwstop_h = diwstrt_h+xres+1. Because of the additional 1 this can be 1
+ more than htotal.
+ - hscroll simply adds a delay to the display output. Smooth horizontal
+ panning needs an extra 64 pixels on the left to prefetch the pixels that
+ `fall off' on the left.
+ - if ddfstrt < 192, the sprite DMA cycles are all stolen by the bitplane
+ DMA, so it's best to make the DMA start as late as possible.
+ - you really don't want to make ddfstrt < 128, since this will steal DMA
+ cycles from the other DMA channels (audio, floppy and Chip RAM refresh).
+ - I make diwstop_h and diwstop_v as large as possible.
+
+ General dependencies
+ --------------------
+
+ - all values are SHRES pixel (35ns)
+
+ table 1:fetchstart table 2:prefetch table 3:fetchsize
+ ------------------ ---------------- -----------------
+ Pixclock # SHRES|HIRES|LORES # SHRES|HIRES|LORES # SHRES|HIRES|LORES
+ -------------#------+-----+------#------+-----+------#------+-----+------
+ Bus width 1x # 16 | 32 | 64 # 16 | 32 | 64 # 64 | 64 | 64
+ Bus width 2x # 32 | 64 | 128 # 32 | 64 | 64 # 64 | 64 | 128
+ Bus width 4x # 64 | 128 | 256 # 64 | 64 | 64 # 64 | 128 | 256
+
+ - chipset needs 4 pixels before the first pixel is output
+ - ddfstrt must be aligned to fetchstart (table 1)
+ - chipset needs also prefetch (table 2) to get first pixel data, so
+ ddfstrt = ((diwstrt_h-4) & -fetchstart) - prefetch
+ - for horizontal panning decrease diwstrt_h
+ - the length of a fetchline must be aligned to fetchsize (table 3)
+ - if fetchstart is smaller than fetchsize, then ddfstrt can a little bit
+ moved to optimize use of dma (usefull for OCS/ECS overscan displays)
+ - ddfstop is ddfstrt+ddfsize-fetchsize
+ - If C= didn't change anything for AGA, then at following positions the
+ dma bus is allready used:
+ ddfstrt < 48 -> memory refresh
+ < 96 -> disk dma
+ < 160 -> audio dma
+ < 192 -> sprite 0 dma
+ < 416 -> sprite dma (32 per sprite)
+ - in accordance with the hardware reference manual a hardware stop is at
+ 192, but AGA (ECS?) can go below this.
+
+ DMA priorities
+ --------------
+
+ Since there are limits on the earliest start value for display DMA and the
+ display of sprites, I use the following policy on horizontal panning and
+ the hardware cursor:
+
+ - if you want to start display DMA too early, you loose the ability to
+ do smooth horizontal panning (xpanstep 1 -> 64).
+ - if you want to go even further, you loose the hardware cursor too.
+
+ IMHO a hardware cursor is more important for X than horizontal scrolling,
+ so that's my motivation.
+
+
+ Implementation
+ --------------
+
+ ami_decode_var() converts the frame buffer values to the Amiga values. It's
+ just a `straightforward' implementation of the above rules.
+
+
+ Standard VGA timings
+ --------------------
+
+ xres yres left right upper lower hsync vsync
+ ---- ---- ---- ----- ----- ----- ----- -----
+ 80x25 720 400 27 45 35 12 108 2
+ 80x30 720 480 27 45 30 9 108 2
+
+ These were taken from a XFree86 configuration file, recalculated for a 28 MHz
+ dotclock (Amigas don't have a 25 MHz dotclock) and converted to frame buffer
+ generic timings.
+
+ As a comparison, graphics/monitor.h suggests the following:
+
+ xres yres left right upper lower hsync vsync
+ ---- ---- ---- ----- ----- ----- ----- -----
+
+ VGA 640 480 52 112 24 19 112 - 2 +
+ VGA70 640 400 52 112 27 21 112 - 2 -
+
+
+ Sync polarities
+ ---------------
+
+ VSYNC HSYNC Vertical size Vertical total
+ ----- ----- ------------- --------------
+ + + Reserved Reserved
+ + - 400 414
+ - + 350 362
+ - - 480 496
+
+ Source: CL-GD542X Technical Reference Manual, Cirrus Logic, Oct 1992
+
+
+ Broadcast video timings
+ -----------------------
+
+ According to the CCIR and RETMA specifications, we have the following values:
+
+ CCIR -> PAL
+ -----------
+
+ - a scanline is 64 µs long, of which 52.48 µs are visible. This is about
+ 736 visible 70 ns pixels per line.
+ - we have 625 scanlines, of which 575 are visible (interlaced); after
+ rounding this becomes 576.
+
+ RETMA -> NTSC
+ -------------
+
+ - a scanline is 63.5 µs long, of which 53.5 µs are visible. This is about
+ 736 visible 70 ns pixels per line.
+ - we have 525 scanlines, of which 485 are visible (interlaced); after
+ rounding this becomes 484.
+
+ Thus if you want a PAL compatible display, you have to do the following:
+
+ - set the FB_SYNC_BROADCAST flag to indicate that standard broadcast
+ timings are to be used.
+ - make sure upper_margin+yres+lower_margin+vsync_len = 625 for an
+ interlaced, 312 for a non-interlaced and 156 for a doublescanned
+ display.
+ - make sure left_margin+xres+right_margin+hsync_len = 1816 for a SHRES,
+ 908 for a HIRES and 454 for a LORES display.
+ - the left visible part begins at 360 (SHRES; HIRES:180, LORES:90),
+ left_margin+2*hsync_len must be greater or equal.
+ - the upper visible part begins at 48 (interlaced; non-interlaced:24,
+ doublescanned:12), upper_margin+2*vsync_len must be greater or equal.
+ - ami_encode_var() calculates margins with a hsync of 5320 ns and a vsync
+ of 4 scanlines
+
+ The settings for a NTSC compatible display are straightforward.
+
+ Note that in a strict sense the PAL and NTSC standards only define the
+ encoding of the color part (chrominance) of the video signal and don't say
+ anything about horizontal/vertical synchronization nor refresh rates.
+
+
+ -- Geert --
+
+*******************************************************************************/
+
+
+ /*
+ * Custom Chipset Definitions
+ */
+
+#define CUSTOM_OFS(fld) ((long)&((struct CUSTOM*)0)->fld)
+
+ /*
+ * BPLCON0 -- Bitplane Control Register 0
+ */
+
+#define BPC0_HIRES (0x8000)
+#define BPC0_BPU2 (0x4000) /* Bit plane used count */
+#define BPC0_BPU1 (0x2000)
+#define BPC0_BPU0 (0x1000)
+#define BPC0_HAM (0x0800) /* HAM mode */
+#define BPC0_DPF (0x0400) /* Double playfield */
+#define BPC0_COLOR (0x0200) /* Enable colorburst */
+#define BPC0_GAUD (0x0100) /* Genlock audio enable */
+#define BPC0_UHRES (0x0080) /* Ultrahi res enable */
+#define BPC0_SHRES (0x0040) /* Super hi res mode */
+#define BPC0_BYPASS (0x0020) /* Bypass LUT - AGA */
+#define BPC0_BPU3 (0x0010) /* AGA */
+#define BPC0_LPEN (0x0008) /* Light pen enable */
+#define BPC0_LACE (0x0004) /* Interlace */
+#define BPC0_ERSY (0x0002) /* External resync */
+#define BPC0_ECSENA (0x0001) /* ECS enable */
+
+ /*
+ * BPLCON2 -- Bitplane Control Register 2
+ */
+
+#define BPC2_ZDBPSEL2 (0x4000) /* Bitplane to be used for ZD - AGA */
+#define BPC2_ZDBPSEL1 (0x2000)
+#define BPC2_ZDBPSEL0 (0x1000)
+#define BPC2_ZDBPEN (0x0800) /* Enable ZD with ZDBPSELx - AGA */
+#define BPC2_ZDCTEN (0x0400) /* Enable ZD with palette bit #31 - AGA */
+#define BPC2_KILLEHB (0x0200) /* Kill EHB mode - AGA */
+#define BPC2_RDRAM (0x0100) /* Color table accesses read, not write - AGA */
+#define BPC2_SOGEN (0x0080) /* SOG output pin high - AGA */
+#define BPC2_PF2PRI (0x0040) /* PF2 priority over PF1 */
+#define BPC2_PF2P2 (0x0020) /* PF2 priority wrt sprites */
+#define BPC2_PF2P1 (0x0010)
+#define BPC2_PF2P0 (0x0008)
+#define BPC2_PF1P2 (0x0004) /* ditto PF1 */
+#define BPC2_PF1P1 (0x0002)
+#define BPC2_PF1P0 (0x0001)
+
+ /*
+ * BPLCON3 -- Bitplane Control Register 3 (AGA)
+ */
+
+#define BPC3_BANK2 (0x8000) /* Bits to select color register bank */
+#define BPC3_BANK1 (0x4000)
+#define BPC3_BANK0 (0x2000)
+#define BPC3_PF2OF2 (0x1000) /* Bits for color table offset when PF2 */
+#define BPC3_PF2OF1 (0x0800)
+#define BPC3_PF2OF0 (0x0400)
+#define BPC3_LOCT (0x0200) /* Color register writes go to low bits */
+#define BPC3_SPRES1 (0x0080) /* Sprite resolution bits */
+#define BPC3_SPRES0 (0x0040)
+#define BPC3_BRDRBLNK (0x0020) /* Border blanked? */
+#define BPC3_BRDRTRAN (0x0010) /* Border transparent? */
+#define BPC3_ZDCLKEN (0x0004) /* ZD pin is 14 MHz (HIRES) clock output */
+#define BPC3_BRDRSPRT (0x0002) /* Sprites in border? */
+#define BPC3_EXTBLKEN (0x0001) /* BLANK programmable */
+
+ /*
+ * BPLCON4 -- Bitplane Control Register 4 (AGA)
+ */
+
+#define BPC4_BPLAM7 (0x8000) /* bitplane color XOR field */
+#define BPC4_BPLAM6 (0x4000)
+#define BPC4_BPLAM5 (0x2000)
+#define BPC4_BPLAM4 (0x1000)
+#define BPC4_BPLAM3 (0x0800)
+#define BPC4_BPLAM2 (0x0400)
+#define BPC4_BPLAM1 (0x0200)
+#define BPC4_BPLAM0 (0x0100)
+#define BPC4_ESPRM7 (0x0080) /* 4 high bits for even sprite colors */
+#define BPC4_ESPRM6 (0x0040)
+#define BPC4_ESPRM5 (0x0020)
+#define BPC4_ESPRM4 (0x0010)
+#define BPC4_OSPRM7 (0x0008) /* 4 high bits for odd sprite colors */
+#define BPC4_OSPRM6 (0x0004)
+#define BPC4_OSPRM5 (0x0002)
+#define BPC4_OSPRM4 (0x0001)
+
+ /*
+ * BEAMCON0 -- Beam Control Register
+ */
+
+#define BMC0_HARDDIS (0x4000) /* Disable hardware limits */
+#define BMC0_LPENDIS (0x2000) /* Disable light pen latch */
+#define BMC0_VARVBEN (0x1000) /* Enable variable vertical blank */
+#define BMC0_LOLDIS (0x0800) /* Disable long/short line toggle */
+#define BMC0_CSCBEN (0x0400) /* Composite sync/blank */
+#define BMC0_VARVSYEN (0x0200) /* Enable variable vertical sync */
+#define BMC0_VARHSYEN (0x0100) /* Enable variable horizontal sync */
+#define BMC0_VARBEAMEN (0x0080) /* Enable variable beam counters */
+#define BMC0_DUAL (0x0040) /* Enable alternate horizontal beam counter */
+#define BMC0_PAL (0x0020) /* Set decodes for PAL */
+#define BMC0_VARCSYEN (0x0010) /* Enable variable composite sync */
+#define BMC0_BLANKEN (0x0008) /* Blank enable (no longer used on AGA) */
+#define BMC0_CSYTRUE (0x0004) /* CSY polarity */
+#define BMC0_VSYTRUE (0x0002) /* VSY polarity */
+#define BMC0_HSYTRUE (0x0001) /* HSY polarity */
+
+
+ /*
+ * FMODE -- Fetch Mode Control Register (AGA)
+ */
+
+#define FMODE_SSCAN2 (0x8000) /* Sprite scan-doubling */
+#define FMODE_BSCAN2 (0x4000) /* Use PF2 modulus every other line */
+#define FMODE_SPAGEM (0x0008) /* Sprite page mode */
+#define FMODE_SPR32 (0x0004) /* Sprite 32 bit fetch */
+#define FMODE_BPAGEM (0x0002) /* Bitplane page mode */
+#define FMODE_BPL32 (0x0001) /* Bitplane 32 bit fetch */
+
+ /*
+ * Tags used to indicate a specific Pixel Clock
+ *
+ * clk_shift is the shift value to get the timings in 35 ns units
+ */
+
+enum { TAG_SHRES, TAG_HIRES, TAG_LORES };
+
+ /*
+ * Tags used to indicate the specific chipset
+ */
+
+enum { TAG_OCS, TAG_ECS, TAG_AGA };
+
+ /*
+ * Tags used to indicate the memory bandwidth
+ */
+
+enum { TAG_FMODE_1, TAG_FMODE_2, TAG_FMODE_4 };
+
+
+ /*
+ * Clock Definitions, Maximum Display Depth
+ *
+ * These depend on the E-Clock or the Chipset, so they are filled in
+ * dynamically
+ */
+
+static u_long pixclock[3]; /* SHRES/HIRES/LORES: index = clk_shift */
+static u_short maxdepth[3]; /* SHRES/HIRES/LORES: index = clk_shift */
+static u_short maxfmode, chipset;
+
+
+ /*
+ * Broadcast Video Timings
+ *
+ * Horizontal values are in 35 ns (SHRES) units
+ * Vertical values are in interlaced scanlines
+ */
+
+#define PAL_DIWSTRT_H (360) /* PAL Window Limits */
+#define PAL_DIWSTRT_V (48)
+#define PAL_HTOTAL (1816)
+#define PAL_VTOTAL (625)
+
+#define NTSC_DIWSTRT_H (360) /* NTSC Window Limits */
+#define NTSC_DIWSTRT_V (40)
+#define NTSC_HTOTAL (1816)
+#define NTSC_VTOTAL (525)
+
+
+ /*
+ * Monitor Specifications
+ *
+ * These are typical for a `generic' Amiga monitor (e.g. A1960)
+ */
+
+static long vfmin = 50, vfmax = 90, hfmin = 15000, hfmax = 38000;
+
+
+ /*
+ * Various macros
+ */
+
+#define up2(v) (((v)+1) & -2)
+#define down2(v) ((v) & -2)
+#define div2(v) ((v)>>1)
+#define mod2(v) ((v) & 1)
+
+#define up4(v) (((v)+3) & -4)
+#define down4(v) ((v) & -4)
+#define mul4(v) ((v)<<2)
+#define div4(v) ((v)>>2)
+#define mod4(v) ((v) & 3)
+
+#define up8(v) (((v)+7) & -8)
+#define down8(v) ((v) & -8)
+#define div8(v) ((v)>>3)
+#define mod8(v) ((v) & 7)
+
+#define up16(v) (((v)+15) & -16)
+#define down16(v) ((v) & -16)
+#define div16(v) ((v)>>4)
+#define mod16(v) ((v) & 15)
+
+#define up32(v) (((v)+31) & -32)
+#define down32(v) ((v) & -32)
+#define div32(v) ((v)>>5)
+#define mod32(v) ((v) & 31)
+
+#define up64(v) (((v)+63) & -64)
+#define down64(v) ((v) & -64)
+#define div64(v) ((v)>>6)
+#define mod64(v) ((v) & 63)
+
+#define upx(x,v) (((v)+(x)-1) & -(x))
+#define downx(x,v) ((v) & -(x))
+#define modx(x,v) ((v) & ((x)-1))
+
+/* if x1 is not a constant, this macro won't make real sense :-) */
+#define DIVUL(x1, x2) ({int res; asm("divul %1,%2,%3": "=d" (res): \
+ "d" (x2), "d" ((long)((x1)/0x100000000ULL)), "0" ((long)(x1))); res;})
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#define max(a, b) ((a) > (b) ? (a) : (b))
+
+#define highw(x) ((u_long)(x)>>16 & 0xffff)
+#define loww(x) ((u_long)(x) & 0xffff)
+
+#define arraysize(x) (sizeof(x)/sizeof(*(x)))
+
+#define VBlankOn() custom.intena = IF_SETCLR|IF_COPER
+#define VBlankOff() custom.intena = IF_COPER
+
+
+ /*
+ * Chip RAM we reserve for the Frame Buffer
+ *
+ * This defines the Maximum Virtual Screen Size
+ * (Setable per kernel options?)
+ */
+
+#define VIDEOMEMSIZE_AGA_2M (1310720) /* AGA (2MB) : max 1280*1024*256 */
+#define VIDEOMEMSIZE_AGA_1M (786432) /* AGA (1MB) : max 1024*768*256 */
+#define VIDEOMEMSIZE_ECS_2M (655360) /* ECS (2MB) : max 1280*1024*16 */
+#define VIDEOMEMSIZE_ECS_1M (393216) /* ECS (1MB) : max 1024*768*16 */
+#define VIDEOMEMSIZE_OCS (262144) /* OCS : max ca. 800*600*16 */
+
+#define SPRITEMEMSIZE (64*64/4) /* max 64*64*4 */
+#define DUMMYSPRITEMEMSIZE (8)
+
+#define CHIPRAM_SAFETY_LIMIT (16384)
+
+static u_long videomemory, spritememory;
+static u_long videomemorysize;
+
+ /*
+ * This is the earliest allowed start of fetching display data.
+ * Only if you really want no hardware cursor and audio,
+ * set this to 128, but let it better at 192
+ */
+
+static u_long min_fstrt = 192;
+
+#define assignchunk(name, type, ptr, size) \
+{ \
+ (name) = (type)(ptr); \
+ ptr += size; \
+}
+
+
+ /*
+ * Copper Instructions
+ */
+
+#define CMOVE(val, reg) (CUSTOM_OFS(reg)<<16 | (val))
+#define CMOVE2(val, reg) ((CUSTOM_OFS(reg)+2)<<16 | (val))
+#define CWAIT(x, y) (((y) & 0x1fe)<<23 | ((x) & 0x7f0)<<13 | 0x0001fffe)
+#define CEND (0xfffffffe)
+
+
+typedef union {
+ u_long l;
+ u_short w[2];
+} copins;
+
+static struct copdisplay {
+ copins *init;
+ copins *wait;
+ copins *list[2][2];
+ copins *rebuild[2];
+} copdisplay;
+
+static u_short currentcop = 0;
+
+ /*
+ * Hardware Cursor
+ */
+
+static int cursorrate = 20; /* Number of frames/flash toggle */
+static u_short cursorstate = -1;
+static u_short cursormode = FB_CURSOR_OFF;
+
+static u_short *lofsprite, *shfsprite, *dummysprite;
+
+ /*
+ * Current Video Mode
+ */
+
+static struct amiga_fb_par {
+
+ /* General Values */
+
+ int xres; /* vmode */
+ int yres; /* vmode */
+ int vxres; /* vmode */
+ int vyres; /* vmode */
+ int xoffset; /* vmode */
+ int yoffset; /* vmode */
+ u_short bpp; /* vmode */
+ u_short clk_shift; /* vmode */
+ u_short line_shift; /* vmode */
+ int vmode; /* vmode */
+ u_short diwstrt_h; /* vmode */
+ u_short diwstop_h; /* vmode */
+ u_short diwstrt_v; /* vmode */
+ u_short diwstop_v; /* vmode */
+ u_long next_line; /* modulo for next line */
+ u_long next_plane; /* modulo for next plane */
+
+ /* Cursor Values */
+
+ struct {
+ short crsr_x; /* movecursor */
+ short crsr_y; /* movecursor */
+ short spot_x;
+ short spot_y;
+ u_short height;
+ u_short width;
+ u_short fmode;
+ } crsr;
+
+ /* OCS Hardware Registers */
+
+ u_long bplpt0; /* vmode, pan (Note: physical address) */
+ u_long bplpt0wrap; /* vmode, pan (Note: physical address) */
+ u_short ddfstrt;
+ u_short ddfstop;
+ u_short bpl1mod;
+ u_short bpl2mod;
+ u_short bplcon0; /* vmode */
+ u_short bplcon1; /* vmode */
+ u_short htotal; /* vmode */
+ u_short vtotal; /* vmode */
+
+ /* Additional ECS Hardware Registers */
+
+ u_short bplcon3; /* vmode */
+ u_short beamcon0; /* vmode */
+ u_short hsstrt; /* vmode */
+ u_short hsstop; /* vmode */
+ u_short hbstrt; /* vmode */
+ u_short hbstop; /* vmode */
+ u_short vsstrt; /* vmode */
+ u_short vsstop; /* vmode */
+ u_short vbstrt; /* vmode */
+ u_short vbstop; /* vmode */
+ u_short hcenter; /* vmode */
+
+ /* Additional AGA Hardware Registers */
+
+ u_short fmode; /* vmode */
+} currentpar;
+
+static int currcon = 0;
+
+static struct display disp;
+static struct fb_info fb_info;
+
+
+ /*
+ * The minimum period for audio depends on htotal (for OCS/ECS/AGA)
+ * (Imported from arch/m68k/amiga/amisound.c)
+ */
+
+extern volatile u_short amiga_audio_min_period;
+
+ /*
+ * Since we can't read the palette on OCS/ECS, and since reading one
+ * single color palette entry require 5 expensive custom chip bus accesses
+ * on AGA, we keep a copy of the current palette.
+ */
+
+#if defined(CONFIG_FB_AMIGA_AGA)
+static struct { u_char red, green, blue, pad; } palette[256];
+#else
+static struct { u_char red, green, blue, pad; } palette[32];
+#endif
+
+#if defined(CONFIG_FB_AMIGA_ECS)
+static u_short ecs_palette[32];
+#endif
+
+ /*
+ * Latches for Display Changes during VBlank
+ */
+
+static u_short do_vmode_full = 0; /* Change the Video Mode */
+static u_short do_vmode_pan = 0; /* Update the Video Mode */
+static short do_blank = 0; /* (Un)Blank the Screen (±1) */
+static u_short do_cursor = 0; /* Move the Cursor */
+
+
+ /*
+ * Various Flags
+ */
+
+static u_short is_blanked = 0; /* Screen is Blanked */
+static u_short is_lace = 0; /* Screen is laced */
+
+ /*
+ * Frame Buffer Name
+ *
+ * The rest of the name is filled in during initialization
+ */
+
+static char amiga_fb_name[16] = "Amiga ";
+
+ /*
+ * Predefined Video Mode Names
+ *
+ * The a2024-?? modes don't work yet because there's no A2024 driver.
+ */
+
+static char *amiga_fb_modenames[] = {
+
+ /*
+ * Autodetect (Default) Video Mode
+ */
+
+ "default",
+
+ /*
+ * AmigaOS Video Modes
+ */
+
+ "ntsc", /* 640x200, 15 kHz, 60 Hz (NTSC) */
+ "ntsc-lace", /* 640x400, 15 kHz, 60 Hz interlaced (NTSC) */
+ "pal", /* 640x256, 15 kHz, 50 Hz (PAL) */
+ "pal-lace", /* 640x512, 15 kHz, 50 Hz interlaced (PAL) */
+ "multiscan", /* 640x480, 29 kHz, 57 Hz */
+ "multiscan-lace", /* 640x960, 29 kHz, 57 Hz interlaced */
+ "a2024-10", /* 1024x800, 10 Hz (Not yet supported) */
+ "a2024-15", /* 1024x800, 15 Hz (Not yet supported) */
+ "euro36", /* 640x200, 15 kHz, 72 Hz */
+ "euro36-lace", /* 640x400, 15 kHz, 72 Hz interlaced */
+ "euro72", /* 640x400, 29 kHz, 68 Hz */
+ "euro72-lace", /* 640x800, 29 kHz, 68 Hz interlaced */
+ "super72", /* 800x300, 23 kHz, 70 Hz */
+ "super72-lace", /* 800x600, 23 kHz, 70 Hz interlaced */
+ "dblntsc", /* 640x200, 27 kHz, 57 Hz doublescan */
+ "dblntsc-ff", /* 640x400, 27 kHz, 57 Hz */
+ "dblntsc-lace", /* 640x800, 27 kHz, 57 Hz interlaced */
+ "dblpal", /* 640x256, 27 kHz, 47 Hz doublescan */
+ "dblpal-ff", /* 640x512, 27 kHz, 47 Hz */
+ "dblpal-lace", /* 640x1024, 27 kHz, 47 Hz interlaced */
+
+ /*
+ * VGA Video Modes
+ */
+
+ "vga", /* 640x480, 31 kHz, 60 Hz (VGA) */
+ "vga70", /* 640x400, 31 kHz, 70 Hz (VGA) */
+
+ /*
+ * User Defined Video Modes: to be set after boot up using e.g. fbset
+ */
+
+ "user0", "user1", "user2", "user3", "user4", "user5", "user6", "user7"
+};
+
+static struct fb_var_screeninfo amiga_fb_predefined[] = {
+
+ /*
+ * Autodetect (Default) Video Mode
+ */
+
+ { 0, },
+
+ /*
+ * AmigaOS Video Modes
+ */
+
+ {
+ /* ntsc */
+ 640, 200, 640, 200, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 44, 16, 76, 2,
+ FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+ }, {
+ /* ntsc-lace */
+ 640, 400, 640, 400, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 88, 33, 76, 4,
+ FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+ }, {
+ /* pal */
+ 640, 256, 640, 256, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 40, 14, 76, 2,
+ FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+ }, {
+ /* pal-lace */
+ 640, 512, 640, 512, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 80, 29, 76, 4,
+ FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+ }, {
+ /* multiscan */
+ 640, 480, 640, 480, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 96, 112, 29, 8, 72, 8,
+ 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+
+ }, {
+ /* multiscan-lace */
+ 640, 960, 640, 960, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 96, 112, 58, 16, 72, 16,
+ 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+ }, {
+ /* a2024-10 (Not yet supported) */
+ 1024, 800, 1024, 800, 0, 0, 2, 0,
+ {0, 2, 0}, {0, 2, 0}, {0, 2, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 0, 0, 0, 0, 0, 0,
+ 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+ }, {
+ /* a2024-15 (Not yet supported) */
+ 1024, 800, 1024, 800, 0, 0, 2, 0,
+ {0, 2, 0}, {0, 2, 0}, {0, 2, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 0, 0, 0, 0, 0, 0,
+ 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+ }, {
+ /* euro36 */
+ 640, 200, 640, 200, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 92, 124, 6, 6, 52, 5,
+ 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+ }, {
+ /* euro36-lace */
+ 640, 400, 640, 400, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 92, 124, 12, 12, 52, 10,
+ 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+ }, {
+ /* euro72 */
+ 640, 400, 640, 400, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 164, 92, 9, 9, 80, 8,
+ 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+ }, {
+ /* euro72-lace */
+ 640, 800, 640, 800, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 164, 92, 18, 18, 80, 16,
+ 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+ }, {
+ /* super72 */
+ 800, 300, 800, 300, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 212, 140, 10, 11, 80, 7,
+ 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+ }, {
+ /* super72-lace */
+ 800, 600, 800, 600, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 212, 140, 20, 22, 80, 14,
+ 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+ }, {
+ /* dblntsc */
+ 640, 200, 640, 200, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 18, 17, 80, 4,
+ 0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
+ }, {
+ /* dblntsc-ff */
+ 640, 400, 640, 400, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 36, 35, 80, 7,
+ 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+ }, {
+ /* dblntsc-lace */
+ 640, 800, 640, 800, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 72, 70, 80, 14,
+ 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+ }, {
+ /* dblpal */
+ 640, 256, 640, 256, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 14, 13, 80, 4,
+ 0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
+ }, {
+ /* dblpal-ff */
+ 640, 512, 640, 512, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 28, 27, 80, 7,
+ 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+ }, {
+ /* dblpal-lace */
+ 640, 1024, 640, 1024, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 56, 54, 80, 14,
+ 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+ },
+
+ /*
+ * VGA Video Modes
+ */
+
+ {
+ /* vga */
+ 640, 480, 640, 480, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 64, 96, 30, 9, 112, 2,
+ 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+ }, {
+ /* vga70 */
+ 640, 400, 640, 400, 0, 0, 4, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 64, 96, 35, 12, 112, 2,
+ FB_SYNC_VERT_HIGH_ACT | FB_SYNC_COMP_HIGH_ACT, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+ },
+
+ /*
+ * User Defined Video Modes
+ */
+
+ { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }
+};
+
+#define NUM_USER_MODES (8)
+#define NUM_TOTAL_MODES arraysize(amiga_fb_predefined)
+#define NUM_PREDEF_MODES (NUM_TOTAL_MODES-NUM_USER_MODES)
+
+static int amifb_ilbm = 0; /* interleaved or normal bitplanes */
+
+static int amifb_inverse = 0;
+static int amifb_usermode = 0;
+
+ /*
+ * Some default modes
+ */
+
+#define DEFMODE_PAL "pal" /* for PAL OCS/ECS */
+#define DEFMODE_NTSC "ntsc" /* for NTSC OCS/ECS */
+#define DEFMODE_AMBER_PAL "pal-lace" /* for flicker fixed PAL (A3000) */
+#define DEFMODE_AMBER_NTSC "ntsc-lace" /* for flicker fixed NTSC (A3000) */
+#define DEFMODE_AGA "vga70" /* for AGA */
+
+ /*
+ * Macros for the conversion from real world values to hardware register
+ * values
+ *
+ * This helps us to keep our attention on the real stuff...
+ *
+ * Hardware limits for AGA:
+ *
+ * parameter min max step
+ * --------- --- ---- ----
+ * diwstrt_h 0 2047 1
+ * diwstrt_v 0 2047 1
+ * diwstop_h 0 4095 1
+ * diwstop_v 0 4095 1
+ *
+ * ddfstrt 0 2032 16
+ * ddfstop 0 2032 16
+ *
+ * htotal 8 2048 8
+ * hsstrt 0 2040 8
+ * hsstop 0 2040 8
+ * vtotal 1 4096 1
+ * vsstrt 0 4095 1
+ * vsstop 0 4095 1
+ * hcenter 0 2040 8
+ *
+ * hbstrt 0 2047 1
+ * hbstop 0 2047 1
+ * vbstrt 0 4095 1
+ * vbstop 0 4095 1
+ *
+ * Horizontal values are in 35 ns (SHRES) pixels
+ * Vertical values are in half scanlines
+ */
+
+/* bplcon1 (smooth scrolling) */
+
+#define hscroll2hw(hscroll) \
+ (((hscroll)<<12 & 0x3000) | ((hscroll)<<8 & 0xc300) | \
+ ((hscroll)<<4 & 0x0c00) | ((hscroll)<<2 & 0x00f0) | ((hscroll)>>2 & 0x000f))
+
+/* diwstrt/diwstop/diwhigh (visible display window) */
+
+#define diwstrt2hw(diwstrt_h, diwstrt_v) \
+ (((diwstrt_v)<<7 & 0xff00) | ((diwstrt_h)>>2 & 0x00ff))
+#define diwstop2hw(diwstop_h, diwstop_v) \
+ (((diwstop_v)<<7 & 0xff00) | ((diwstop_h)>>2 & 0x00ff))
+#define diwhigh2hw(diwstrt_h, diwstrt_v, diwstop_h, diwstop_v) \
+ (((diwstop_h)<<3 & 0x2000) | ((diwstop_h)<<11 & 0x1800) | \
+ ((diwstop_v)>>1 & 0x0700) | ((diwstrt_h)>>5 & 0x0020) | \
+ ((diwstrt_h)<<3 & 0x0018) | ((diwstrt_v)>>9 & 0x0007))
+
+/* ddfstrt/ddfstop (display DMA) */
+
+#define ddfstrt2hw(ddfstrt) div8(ddfstrt)
+#define ddfstop2hw(ddfstop) div8(ddfstop)
+
+/* hsstrt/hsstop/htotal/vsstrt/vsstop/vtotal/hcenter (sync timings) */
+
+#define hsstrt2hw(hsstrt) (div8(hsstrt))
+#define hsstop2hw(hsstop) (div8(hsstop))
+#define htotal2hw(htotal) (div8(htotal)-1)
+#define vsstrt2hw(vsstrt) (div2(vsstrt))
+#define vsstop2hw(vsstop) (div2(vsstop))
+#define vtotal2hw(vtotal) (div2(vtotal)-1)
+#define hcenter2hw(htotal) (div8(htotal))
+
+/* hbstrt/hbstop/vbstrt/vbstop (blanking timings) */
+
+#define hbstrt2hw(hbstrt) (((hbstrt)<<8 & 0x0700) | ((hbstrt)>>3 & 0x00ff))
+#define hbstop2hw(hbstop) (((hbstop)<<8 & 0x0700) | ((hbstop)>>3 & 0x00ff))
+#define vbstrt2hw(vbstrt) (div2(vbstrt))
+#define vbstop2hw(vbstop) (div2(vbstop))
+
+/* colour */
+
+#define rgb2hw8_high(red, green, blue) \
+ (((red)<<4 & 0xf00) | ((green) & 0x0f0) | ((blue)>>4 & 0x00f))
+#define rgb2hw8_low(red, green, blue) \
+ (((red)<<8 & 0xf00) | ((green)<<4 & 0x0f0) | ((blue) & 0x00f))
+#define rgb2hw4(red, green, blue) \
+ (((red)<<8 & 0xf00) | ((green)<<4 & 0x0f0) | ((blue) & 0x00f))
+#define rgb2hw2(red, green, blue) \
+ (((red)<<10 & 0xc00) | ((green)<<6 & 0x0c0) | ((blue)<<2 & 0x00c))
+
+/* sprpos/sprctl (sprite positioning) */
+
+#define spr2hw_pos(start_v, start_h) \
+ (((start_v)<<7&0xff00) | ((start_h)>>3&0x00ff))
+#define spr2hw_ctl(start_v, start_h, stop_v) \
+ (((stop_v)<<7&0xff00) | ((start_v)>>4&0x0040) | ((stop_v)>>5&0x0020) | \
+ ((start_h)<<3&0x0018) | ((start_v)>>7&0x0004) | ((stop_v)>>8&0x0002) | \
+ ((start_h)>>2&0x0001))
+
+/* get current vertical position of beam */
+#define get_vbpos() ((u_short)((*(u_long volatile *)&custom.vposr >> 7) & 0xffe))
+
+ /*
+ * Copper Initialisation List
+ */
+
+#define COPINITSIZE (sizeof(copins)*40)
+
+enum {
+ cip_bplcon0
+};
+
+ /*
+ * Long Frame/Short Frame Copper List
+ * Don't change the order, build_copper()/rebuild_copper() rely on this
+ */
+
+#define COPLISTSIZE (sizeof(copins)*64)
+
+enum {
+ cop_wait, cop_bplcon0,
+ cop_spr0ptrh, cop_spr0ptrl,
+ cop_diwstrt, cop_diwstop,
+ cop_diwhigh,
+};
+
+ /*
+ * Pixel modes for Bitplanes and Sprites
+ */
+
+static u_short bplpixmode[3] = {
+ BPC0_SHRES, /* 35 ns */
+ BPC0_HIRES, /* 70 ns */
+ 0 /* 140 ns */
+};
+
+static u_short sprpixmode[3] = {
+ BPC3_SPRES1 | BPC3_SPRES0, /* 35 ns */
+ BPC3_SPRES1, /* 70 ns */
+ BPC3_SPRES0 /* 140 ns */
+};
+
+ /*
+ * Fetch modes for Bitplanes and Sprites
+ */
+
+static u_short bplfetchmode[3] = {
+ 0, /* 1x */
+ FMODE_BPL32, /* 2x */
+ FMODE_BPAGEM | FMODE_BPL32 /* 4x */
+};
+
+static u_short sprfetchmode[3] = {
+ 0, /* 1x */
+ FMODE_SPR32, /* 2x */
+ FMODE_SPAGEM | FMODE_SPR32 /* 4x */
+};
+
+
+ /*
+ * Interface used by the world
+ */
+
+void amiga_video_setup(char *options, int *ints);
+
+static int amiga_fb_open(int fbidx);
+static int amiga_fb_release(int fbidx);
+static int amiga_fb_get_fix(struct fb_fix_screeninfo *fix, int con);
+static int amiga_fb_get_var(struct fb_var_screeninfo *var, int con);
+static int amiga_fb_set_var(struct fb_var_screeninfo *var, int con);
+static int amiga_fb_pan_display(struct fb_var_screeninfo *var, int con);
+static int amiga_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con);
+static int amiga_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con);
+static int amiga_fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+ u_long arg, int con);
+
+static int amiga_fb_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con);
+static int amiga_fb_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con);
+static int amiga_fb_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con);
+static int amiga_fb_get_cursorstate(struct fb_cursorstate *state, int con);
+static int amiga_fb_set_cursorstate(struct fb_cursorstate *state, int con);
+
+ /*
+ * Interface to the low level console driver
+ */
+
+unsigned long amiga_fb_init(unsigned long mem_start);
+static int amifbcon_switch(int con);
+static int amifbcon_updatevar(int con);
+static void amifbcon_blank(int blank);
+static int amifbcon_setcmap(struct fb_cmap *cmap, int con);
+
+ /*
+ * Internal routines
+ */
+
+static void do_install_cmap(int con);
+static int flash_cursor(void);
+static void amifb_interrupt(int irq, void *dev_id, struct pt_regs *fp);
+static void get_video_mode(const char *name);
+static void check_default_mode(void);
+static u_long chipalloc(u_long size);
+static char *strtoke(char *s,const char *ct);
+
+ /*
+ * Hardware routines
+ */
+
+static int ami_encode_fix(struct fb_fix_screeninfo *fix,
+ struct amiga_fb_par *par);
+static int ami_decode_var(struct fb_var_screeninfo *var,
+ struct amiga_fb_par *par);
+static int ami_encode_var(struct fb_var_screeninfo *var,
+ struct amiga_fb_par *par);
+static void ami_get_par(struct amiga_fb_par *par);
+static void ami_set_var(struct fb_var_screeninfo *var);
+#ifdef DEBUG
+static void ami_set_par(struct amiga_fb_par *par);
+#endif
+static void ami_pan_var(struct fb_var_screeninfo *var);
+static int ami_update_par(void);
+static int ami_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+ u_int *transp);
+static int ami_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp);
+static void ami_update_display(void);
+static void ami_init_display(void);
+static void ami_do_blank(void);
+static int ami_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con);
+static int ami_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con);
+static int ami_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con);
+static int ami_get_cursorstate(struct fb_cursorstate *state, int con);
+static int ami_set_cursorstate(struct fb_cursorstate *state, int con);
+static void ami_set_sprite(void);
+static void ami_init_copper(void);
+static void ami_reinit_copper(void);
+static void ami_build_copper(void);
+static void ami_rebuild_copper(void);
+
+
+ /*
+ * External references
+ */
+
+extern unsigned short ami_intena_vals[];
+
+
+static struct fb_ops amiga_fb_ops = {
+ amiga_fb_open, amiga_fb_release, amiga_fb_get_fix, amiga_fb_get_var,
+ amiga_fb_set_var, amiga_fb_get_cmap, amiga_fb_set_cmap,
+ amiga_fb_pan_display, amiga_fb_ioctl
+};
+
+struct useropts {
+ long xres;
+ long yres;
+ long xres_virtual;
+ long yres_virtual;
+ long bits_per_pixel;
+ long left_margin;
+ long right_margin;
+ long upper_margin;
+ long lower_margin;
+ long hsync_len;
+ long vsync_len;
+} useropts __initdata = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+__initfunc(void amiga_video_setup(char *options, int *ints))
+{
+ char *this_opt;
+ char mcap_spec[80];
+
+ mcap_spec[0] = '\0';
+ fb_info.fontname[0] = '\0';
+
+ if (!options || !*options)
+ return;
+
+ for (this_opt = strtok(options, ","); this_opt; this_opt = strtok(NULL, ",")) {
+ char *p;
+
+ if (!strcmp(this_opt, "inverse")) {
+ amifb_inverse = 1;
+ fb_invert_cmaps();
+ } else if (!strcmp(this_opt, "ilbm"))
+ amifb_ilbm = 1;
+ else if (!strncmp(this_opt, "monitorcap:", 11))
+ strcpy(mcap_spec, this_opt+11);
+ else if (!strncmp(this_opt, "font:", 5))
+ strcpy(fb_info.fontname, this_opt+5);
+ else if (!strncmp(this_opt, "fstart:", 7))
+ min_fstrt = simple_strtoul(this_opt+7, NULL, 0);
+ else if (!strncmp(this_opt, "depth:", 6))
+ useropts.bits_per_pixel = simple_strtoul(this_opt+6, NULL, 0);
+ else if (!strncmp(this_opt, "size:", 5)) {
+ p = this_opt + 5;
+ if (*p != ';')
+ useropts.xres = simple_strtoul(p, NULL, 0);
+ if (!(p = strchr(p, ';')))
+ continue;
+ if (*++p != ';')
+ useropts.yres = simple_strtoul(p, NULL, 0);
+ if (!(p = strchr(p, ';')))
+ continue;
+ if (*++p != ';')
+ useropts.xres_virtual = simple_strtoul(p, NULL, 0);
+ if (!(p = strchr(p, ';')))
+ continue;
+ if (*++p != ';')
+ useropts.yres_virtual = simple_strtoul(p, NULL, 0);
+ if (!(p = strchr(p, ';')))
+ continue;
+ if (*++p)
+ useropts.bits_per_pixel = simple_strtoul(p, NULL, 0);
+ } else if (!strncmp(this_opt, "timing:", 7)) {
+ p = this_opt + 7;
+ if (*p != ';')
+ useropts.left_margin = simple_strtoul(p, NULL, 0);
+ if (!(p = strchr(p, ';')))
+ continue;
+ if (*++p != ';')
+ useropts.right_margin = simple_strtoul(p, NULL, 0);
+ if (!(p = strchr(p, ';')))
+ continue;
+ if (*++p != ';')
+ useropts.upper_margin = simple_strtoul(p, NULL, 0);
+ if (!(p = strchr(p, ';')))
+ continue;
+ if (*++p)
+ useropts.lower_margin = simple_strtoul(p, NULL, 0);
+ } else if (!strncmp(this_opt, "sync:", 5)) {
+ p = this_opt + 5;
+ if (*p != ';')
+ useropts.hsync_len = simple_strtoul(p, NULL, 0);
+ if (!(p = strchr(p, ';')))
+ continue;
+ if (*++p)
+ useropts.vsync_len = simple_strtoul(p, NULL, 0);
+ } else
+ get_video_mode(this_opt);
+ }
+
+ if (min_fstrt < 48)
+ min_fstrt = 48;
+
+ if (*mcap_spec) {
+ char *p;
+ int vmin, vmax, hmin, hmax;
+
+ /* Format for monitor capabilities is: <Vmin>;<Vmax>;<Hmin>;<Hmax>
+ * <V*> vertical freq. in Hz
+ * <H*> horizontal freq. in kHz
+ */
+
+ if (!(p = strtoke(mcap_spec, ";")) || !*p)
+ goto cap_invalid;
+ vmin = simple_strtoul(p, NULL, 10);
+ if (vmin <= 0)
+ goto cap_invalid;
+ if (!(p = strtoke(NULL, ";")) || !*p)
+ goto cap_invalid;
+ vmax = simple_strtoul(p, NULL, 10);
+ if (vmax <= 0 || vmax <= vmin)
+ goto cap_invalid;
+ if (!(p = strtoke(NULL, ";")) || !*p)
+ goto cap_invalid;
+ hmin = 1000 * simple_strtoul(p, NULL, 10);
+ if (hmin <= 0)
+ goto cap_invalid;
+ if (!(p = strtoke(NULL, "")) || !*p)
+ goto cap_invalid;
+ hmax = 1000 * simple_strtoul(p, NULL, 10);
+ if (hmax <= 0 || hmax <= hmin)
+ goto cap_invalid;
+
+ vfmin = vmin;
+ vfmax = vmax;
+ hfmin = hmin;
+ hfmax = hmax;
+cap_invalid:
+ ;
+ }
+}
+
+ /*
+ * Open/Release the frame buffer device
+ */
+
+static int amiga_fb_open(int fbidx)
+{
+ /*
+ * Nothing, only a usage count for the moment
+ */
+
+ MOD_INC_USE_COUNT;
+ return(0);
+}
+
+static int amiga_fb_release(int fbidx)
+{
+ MOD_DEC_USE_COUNT;
+ return(0);
+}
+
+
+ /*
+ * Get the Fixed Part of the Display
+ */
+
+static int amiga_fb_get_fix(struct fb_fix_screeninfo *fix, int con)
+{
+ struct amiga_fb_par par;
+
+ if (con == -1)
+ ami_get_par(&par);
+ else {
+ int err;
+
+ if ((err = ami_decode_var(&fb_display[con].var, &par)))
+ return err;
+ }
+ return ami_encode_fix(fix, &par);
+}
+
+ /*
+ * Get the User Defined Part of the Display
+ */
+
+static int amiga_fb_get_var(struct fb_var_screeninfo *var, int con)
+{
+ int err = 0;
+
+ if (con == -1) {
+ struct amiga_fb_par par;
+
+ ami_get_par(&par);
+ err = ami_encode_var(var, &par);
+ } else
+ *var = fb_display[con].var;
+ return err;
+}
+
+ /*
+ * Set the User Defined Part of the Display
+ */
+
+static int amiga_fb_set_var(struct fb_var_screeninfo *var, int con)
+{
+ int err, activate = var->activate;
+ int oldxres, oldyres, oldvxres, oldvyres, oldbpp;
+ struct amiga_fb_par par;
+
+ struct display *display;
+ if (con >= 0)
+ display = &fb_display[con];
+ else
+ display = &disp; /* used during initialization */
+
+ /*
+ * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
+ * as FB_VMODE_SMOOTH_XPAN is only used internally
+ */
+
+ if (var->vmode & FB_VMODE_CONUPDATE) {
+ var->vmode |= FB_VMODE_YWRAP;
+ var->xoffset = display->var.xoffset;
+ var->yoffset = display->var.yoffset;
+ }
+ if ((err = ami_decode_var(var, &par)))
+ return err;
+ ami_encode_var(var, &par);
+ if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+ oldxres = display->var.xres;
+ oldyres = display->var.yres;
+ oldvxres = display->var.xres_virtual;
+ oldvyres = display->var.yres_virtual;
+ oldbpp = display->var.bits_per_pixel;
+ display->var = *var;
+ if (oldxres != var->xres || oldyres != var->yres ||
+ oldvxres != var->xres_virtual || oldvyres != var->yres_virtual ||
+ oldbpp != var->bits_per_pixel) {
+ struct fb_fix_screeninfo fix;
+
+ ami_encode_fix(&fix, &par);
+ display->screen_base = fix.smem_start;
+ display->visual = fix.visual;
+ display->type = fix.type;
+ display->type_aux = fix.type_aux;
+ display->ypanstep = fix.ypanstep;
+ display->ywrapstep = fix.ywrapstep;
+ display->line_length = fix.line_length;
+ display->can_soft_blank = 1;
+ display->inverse = amifb_inverse;
+ if (fb_info.changevar)
+ (*fb_info.changevar)(con);
+ }
+ if (oldbpp != var->bits_per_pixel) {
+ if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
+ return err;
+ do_install_cmap(con);
+ }
+ if (con == currcon)
+ ami_set_var(&display->var);
+ }
+ return 0;
+}
+
+ /*
+ * Pan or Wrap the Display
+ *
+ * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+ */
+
+static int amiga_fb_pan_display(struct fb_var_screeninfo *var, int con)
+{
+ if (var->vmode & FB_VMODE_YWRAP) {
+ if (var->yoffset<0 || var->yoffset >= fb_display[con].var.yres_virtual || var->xoffset)
+ return -EINVAL;
+ } else {
+ /*
+ * TODO: There will be problems when xpan!=1, so some columns
+ * on the right side will never be seen
+ */
+ if (var->xoffset+fb_display[con].var.xres > upx(16<<maxfmode, fb_display[con].var.xres_virtual) ||
+ var->yoffset+fb_display[con].var.yres > fb_display[con].var.yres_virtual)
+ return -EINVAL;
+ }
+ if (con == currcon)
+ ami_pan_var(var);
+ fb_display[con].var.xoffset = var->xoffset;
+ fb_display[con].var.yoffset = var->yoffset;
+ if (var->vmode & FB_VMODE_YWRAP)
+ fb_display[con].var.vmode |= FB_VMODE_YWRAP;
+ else
+ fb_display[con].var.vmode &= ~FB_VMODE_YWRAP;
+ return 0;
+}
+
+ /*
+ * Get the Colormap
+ */
+
+static int amiga_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con)
+{
+ if (con == currcon) /* current console? */
+ return fb_get_cmap(cmap, &fb_display[con].var, kspc,
+ ami_getcolreg);
+ else if (fb_display[con].cmap.len) /* non default colormap? */
+ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+ else
+ fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+ cmap, kspc ? 0 : 2);
+ return 0;
+}
+
+ /*
+ * Set the Colormap
+ */
+
+static int amiga_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con)
+{
+ int err;
+
+ if (!fb_display[con].cmap.len) { /* no colormap allocated? */
+ if ((err = fb_alloc_cmap(&fb_display[con].cmap,
+ 1<<fb_display[con].var.bits_per_pixel,
+ 0)))
+ return err;
+ }
+ if (con == currcon) /* current console? */
+ return fb_set_cmap(cmap, &fb_display[con].var, kspc,
+ ami_setcolreg);
+ else
+ fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+ return 0;
+}
+
+ /*
+ * Amiga Frame Buffer Specific ioctls
+ */
+
+static int amiga_fb_ioctl(struct inode *inode, struct file *file,
+ u_int cmd, u_long arg, int con)
+{
+ int i;
+
+ switch (cmd) {
+ case FBIOGET_FCURSORINFO : {
+ struct fb_fix_cursorinfo crsrfix;
+
+ i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(crsrfix));
+ if (!i) {
+ i = amiga_fb_get_fix_cursorinfo(&crsrfix, con);
+ copy_to_user((void *)arg, &crsrfix, sizeof(crsrfix));
+ }
+ return i;
+ }
+ case FBIOGET_VCURSORINFO : {
+ struct fb_var_cursorinfo crsrvar;
+
+ i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(crsrvar));
+ if (!i) {
+ i = amiga_fb_get_var_cursorinfo(&crsrvar,
+ ((struct fb_var_cursorinfo *)arg)->data, con);
+ copy_to_user((void *)arg, &crsrvar, sizeof(crsrvar));
+ }
+ return i;
+ }
+ case FBIOPUT_VCURSORINFO : {
+ struct fb_var_cursorinfo crsrvar;
+
+ i = verify_area(VERIFY_READ, (void *)arg, sizeof(crsrvar));
+ if (!i) {
+ copy_from_user(&crsrvar, (void *)arg, sizeof(crsrvar));
+ i = amiga_fb_set_var_cursorinfo(&crsrvar,
+ ((struct fb_var_cursorinfo *)arg)->data, con);
+ }
+ return i;
+ }
+ case FBIOGET_CURSORSTATE : {
+ struct fb_cursorstate crsrstate;
+
+ i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(crsrstate));
+ if (!i) {
+ i = amiga_fb_get_cursorstate(&crsrstate, con);
+ copy_to_user((void *)arg, &crsrstate, sizeof(crsrstate));
+ }
+ return i;
+ }
+ case FBIOPUT_CURSORSTATE : {
+ struct fb_cursorstate crsrstate;
+
+ i = verify_area(VERIFY_READ, (void *)arg, sizeof(crsrstate));
+ if (!i) {
+ copy_from_user(&crsrstate, (void *)arg, sizeof(crsrstate));
+ i = amiga_fb_set_cursorstate(&crsrstate, con);
+ }
+ return i;
+ }
+#ifdef DEBUG
+ case FBCMD_GET_CURRENTPAR : {
+ struct amiga_fb_par par;
+
+ i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct amiga_fb_par));
+ if (!i) {
+ ami_get_par(&par);
+ copy_to_user((void *)arg, &par, sizeof(struct amiga_fb_par));
+ }
+ return i;
+ }
+ case FBCMD_SET_CURRENTPAR : {
+ struct amiga_fb_par par;
+
+ i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct amiga_fb_par));
+ if (!i) {
+ copy_from_user(&par, (void *)arg, sizeof(struct amiga_fb_par));
+ ami_set_par(&par);
+ }
+ return i;
+ }
+#endif */ DEBUG */
+ }
+ return -EINVAL;
+}
+
+ /*
+ * Hardware Cursor
+ */
+
+static int amiga_fb_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con)
+{
+ return ami_get_fix_cursorinfo(fix, con);
+}
+
+static int amiga_fb_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con)
+{
+ return ami_get_var_cursorinfo(var, data, con);
+}
+
+static int amiga_fb_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con)
+{
+ return ami_set_var_cursorinfo(var, data, con);
+}
+
+static int amiga_fb_get_cursorstate(struct fb_cursorstate *state, int con)
+{
+ return ami_get_cursorstate(state, con);
+}
+
+static int amiga_fb_set_cursorstate(struct fb_cursorstate *state, int con)
+{
+ return ami_set_cursorstate(state, con);
+}
+
+
+ /*
+ * Initialisation
+ */
+
+__initfunc(unsigned long amiga_fb_init(unsigned long mem_start))
+{
+ int err, tag, i;
+ u_long chipptr;
+
+ if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_VIDEO))
+ return mem_start;
+
+ /*
+ * TODO: where should we put this? The DMI Resolver doesn't have a
+ * frame buffer accessible by the CPU
+ */
+
+#ifdef CONFIG_GSP_RESOLVER
+ if (amifb_resolver){
+ custom.dmacon = DMAF_MASTER | DMAF_RASTER | DMAF_COPPER |
+ DMAF_BLITTER | DMAF_SPRITE;
+ return mem_start;
+ }
+#endif
+
+ custom.dmacon = DMAF_ALL | DMAF_MASTER;
+
+ switch (amiga_chipset) {
+#ifdef CONFIG_FB_AMIGA_OCS
+ case CS_OCS:
+ strcat(amiga_fb_name, "OCS");
+default_chipset:
+ chipset = TAG_OCS;
+ maxdepth[TAG_SHRES] = 0; /* OCS means no SHRES */
+ maxdepth[TAG_HIRES] = 4;
+ maxdepth[TAG_LORES] = 6;
+ maxfmode = TAG_FMODE_1;
+ if (!amifb_usermode) /* Set the Default Video Mode */
+ get_video_mode(amiga_vblank == 50 ?
+ DEFMODE_PAL : DEFMODE_NTSC);
+ videomemorysize = VIDEOMEMSIZE_OCS;
+ break;
+#endif /* CONFIG_FB_AMIGA_OCS */
+
+#ifdef CONFIG_FB_AMIGA_ECS
+ case CS_ECS:
+ strcat(amiga_fb_name, "ECS");
+ chipset = TAG_ECS;
+ maxdepth[TAG_SHRES] = 2;
+ maxdepth[TAG_HIRES] = 4;
+ maxdepth[TAG_LORES] = 6;
+ maxfmode = TAG_FMODE_1;
+ if (!amifb_usermode) { /* Set the Default Video Mode */
+ if (AMIGAHW_PRESENT(AMBER_FF))
+ get_video_mode(amiga_vblank == 50 ?
+ DEFMODE_AMBER_PAL : DEFMODE_AMBER_NTSC);
+ else
+ get_video_mode(amiga_vblank == 50 ?
+ DEFMODE_PAL : DEFMODE_NTSC);
+ }
+ if (amiga_chip_avail()-CHIPRAM_SAFETY_LIMIT >
+ VIDEOMEMSIZE_ECS_1M)
+ videomemorysize = VIDEOMEMSIZE_ECS_2M;
+ else
+ videomemorysize = VIDEOMEMSIZE_ECS_1M;
+ break;
+#endif /* CONFIG_FB_AMIGA_ECS */
+
+#ifdef CONFIG_FB_AMIGA_AGA
+ case CS_AGA:
+ strcat(amiga_fb_name, "AGA");
+ chipset = TAG_AGA;
+ maxdepth[TAG_SHRES] = 8;
+ maxdepth[TAG_HIRES] = 8;
+ maxdepth[TAG_LORES] = 8;
+ maxfmode = TAG_FMODE_4;
+ if (!amifb_usermode) /* Set the Default Video Mode */
+ get_video_mode(DEFMODE_AGA);
+ if (amiga_chip_avail()-CHIPRAM_SAFETY_LIMIT >
+ VIDEOMEMSIZE_AGA_1M)
+ videomemorysize = VIDEOMEMSIZE_AGA_2M;
+ else
+ videomemorysize = VIDEOMEMSIZE_AGA_1M;
+ break;
+#endif /* CONFIG_FB_AMIGA_AGA */
+
+ default:
+#ifdef CONFIG_FB_AMIGA_OCS
+ printk("Unknown graphics chipset, defaulting to OCS\n");
+ strcat(amiga_fb_name, "Unknown");
+ goto default_chipset;
+#else /* CONFIG_FB_AMIGA_OCS */
+ return mem_start;
+#endif /* CONFIG_FB_AMIGA_OCS */
+ break;
+ }
+
+ /*
+ * Calculate the Pixel Clock Values for this Machine
+ */
+
+ pixclock[TAG_SHRES] = DIVUL(25E9, amiga_eclock); /* SHRES: 35 ns / 28 MHz */
+ pixclock[TAG_HIRES] = DIVUL(50E9, amiga_eclock); /* HIRES: 70 ns / 14 MHz */
+ pixclock[TAG_LORES] = DIVUL(100E9, amiga_eclock); /* LORES: 140 ns / 7 MHz */
+
+ /*
+ * Replace the Tag Values with the Real Pixel Clock Values
+ */
+
+ for (i = 0; i < NUM_PREDEF_MODES; i++) {
+ tag = amiga_fb_predefined[i].pixclock;
+ if (tag == TAG_SHRES || tag == TAG_HIRES || tag == TAG_LORES) {
+ amiga_fb_predefined[i].pixclock = pixclock[tag];
+ if (amiga_fb_predefined[i].bits_per_pixel > maxdepth[tag])
+ amiga_fb_predefined[i].bits_per_pixel = maxdepth[tag];
+ }
+ }
+
+ strcpy(fb_info.modename, amiga_fb_name);
+ fb_info.changevar = NULL;
+ fb_info.node = -1;
+ fb_info.fbops = &amiga_fb_ops;
+ fb_info.fbvar_num = NUM_TOTAL_MODES;
+ fb_info.fbvar = amiga_fb_predefined;
+ fb_info.disp = &disp;
+ fb_info.switch_con = &amifbcon_switch;
+ fb_info.updatevar = &amifbcon_updatevar;
+ fb_info.blank = &amifbcon_blank;
+ fb_info.setcmap = &amifbcon_setcmap;
+
+ err = register_framebuffer(&fb_info);
+ if (err < 0)
+ return mem_start;
+
+ chipptr = chipalloc(videomemorysize+
+ SPRITEMEMSIZE+
+ DUMMYSPRITEMEMSIZE+
+ COPINITSIZE+
+ 4*COPLISTSIZE);
+
+ assignchunk(videomemory, u_long, chipptr, videomemorysize);
+ assignchunk(spritememory, u_long, chipptr, SPRITEMEMSIZE);
+ assignchunk(dummysprite, u_short *, chipptr, DUMMYSPRITEMEMSIZE);
+ assignchunk(copdisplay.init, copins *, chipptr, COPINITSIZE);
+ assignchunk(copdisplay.list[0][0], copins *, chipptr, COPLISTSIZE);
+ assignchunk(copdisplay.list[0][1], copins *, chipptr, COPLISTSIZE);
+ assignchunk(copdisplay.list[1][0], copins *, chipptr, COPLISTSIZE);
+ assignchunk(copdisplay.list[1][1], copins *, chipptr, COPLISTSIZE);
+
+ memset(dummysprite, 0, DUMMYSPRITEMEMSIZE);
+
+ /*
+ * Enable Display DMA
+ */
+
+ custom.dmacon = DMAF_SETCLR | DMAF_MASTER | DMAF_RASTER | DMAF_COPPER |
+ DMAF_BLITTER | DMAF_SPRITE;
+
+ /*
+ * Make sure the Copper has something to do
+ */
+
+ ami_init_copper();
+
+ check_default_mode();
+
+ if (request_irq(IRQ_AMIGA_AUTO_3, amifb_interrupt, IRQ_FLG_LOCK,
+ "fb vertb handler", NULL))
+ panic("Couldn't add vblank interrupt\n");
+ ami_intena_vals[IRQ_AMIGA_VERTB] = IF_COPER;
+ ami_intena_vals[IRQ_AMIGA_COPPER] = 0;
+ custom.intena = IF_VERTB;
+ custom.intena = IF_SETCLR | IF_COPER;
+
+ amiga_fb_set_var(&amiga_fb_predefined[0], -1);
+
+ printk("%s frame buffer device, using %ldK of video memory\n",
+ fb_info.modename, videomemorysize>>10);
+
+ /* TODO: This driver cannot be unloaded yet */
+ MOD_INC_USE_COUNT;
+
+ return mem_start;
+}
+
+static int amifbcon_switch(int con)
+{
+ /* Do we have to save the colormap? */
+ if (fb_display[currcon].cmap.len)
+ fb_get_cmap(&fb_display[currcon].cmap,
+ &fb_display[currcon].var, 1, ami_getcolreg);
+
+ currcon = con;
+ ami_set_var(&fb_display[con].var);
+ /* Install new colormap */
+ do_install_cmap(con);
+ return 0;
+}
+
+ /*
+ * Update the `var' structure (called by fbcon.c)
+ */
+
+static int amifbcon_updatevar(int con)
+{
+ ami_pan_var(&fb_display[con].var);
+ return 0;
+}
+
+ /*
+ * Blank the display.
+ */
+
+static void amifbcon_blank(int blank)
+{
+ do_blank = blank ? blank : -1;
+}
+
+ /*
+ * Set the colormap
+ */
+
+static int amifbcon_setcmap(struct fb_cmap *cmap, int con)
+{
+ return(amiga_fb_set_cmap(cmap, 1, con));
+}
+
+
+static void do_install_cmap(int con)
+{
+ if (con != currcon)
+ return;
+ if (fb_display[con].cmap.len)
+ fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
+ ami_setcolreg);
+ else
+ fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+ &fb_display[con].var, 1,
+ ami_setcolreg);
+}
+
+static int flash_cursor(void)
+{
+ static int cursorcount = 1;
+
+ if (cursormode == FB_CURSOR_FLASH) {
+ if (!--cursorcount) {
+ cursorstate = -cursorstate;
+ cursorcount = cursorrate;
+ if (!is_blanked)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+ /*
+ * VBlank Display Interrupt
+ */
+
+static void amifb_interrupt(int irq, void *dev_id, struct pt_regs *fp)
+{
+ u_short ints = custom.intreqr & custom.intenar;
+ static struct irq_server server = {0, 0};
+ unsigned long flags;
+
+ if (ints & IF_BLIT) {
+ custom.intreq = IF_BLIT;
+ amiga_do_irq(IRQ_AMIGA_BLIT, fp);
+ }
+
+ if (ints & IF_COPER) {
+ custom.intreq = IF_COPER;
+ if (do_vmode_pan || do_vmode_full)
+ ami_update_display();
+
+ if (do_vmode_full)
+ ami_init_display();
+
+ if (do_vmode_pan) {
+ flash_cursor();
+ ami_rebuild_copper();
+ do_cursor = do_vmode_pan = 0;
+ } else if (do_cursor) {
+ flash_cursor();
+ ami_set_sprite();
+ do_cursor = 0;
+ } else {
+ if (flash_cursor())
+ ami_set_sprite();
+ }
+
+ save_flags(flags);
+ cli();
+ if (get_vbpos() < down2(currentpar.diwstrt_v - 6))
+ custom.copjmp2 = 0;
+ restore_flags(flags);
+
+ if (do_blank) {
+ ami_do_blank();
+ do_blank = 0;
+ }
+
+ if (do_vmode_full) {
+ ami_reinit_copper();
+ do_vmode_full = 0;
+ }
+ amiga_do_irq_list(IRQ_AMIGA_VERTB, fp, &server);
+ }
+
+ if (ints & IF_VERTB) {
+ printk("%s: Warning: IF_VERTB was enabled\n", __FUNCTION__);
+ custom.intena = IF_VERTB;
+ }
+}
+
+ /*
+ * Get a Video Mode
+ */
+
+__initfunc(static void get_video_mode(const char *name))
+{
+ int i;
+
+ for (i = 1; i < NUM_PREDEF_MODES; i++) {
+ if (!strcmp(name, amiga_fb_modenames[i])) {
+ amiga_fb_predefined[0] = amiga_fb_predefined[i];
+
+ if (useropts.xres != -1)
+ amiga_fb_predefined[0].xres = useropts.xres;
+ if (useropts.yres != -1)
+ amiga_fb_predefined[0].yres = useropts.yres;
+ if (useropts.xres_virtual != -1)
+ amiga_fb_predefined[0].xres_virtual = useropts.xres_virtual;
+ if (useropts.yres_virtual != -1)
+ amiga_fb_predefined[0].yres_virtual = useropts.yres_virtual;
+ if (useropts.bits_per_pixel != -1)
+ amiga_fb_predefined[0].bits_per_pixel = useropts.bits_per_pixel;
+ if (useropts.left_margin != -1)
+ amiga_fb_predefined[0].left_margin = useropts.left_margin;
+ if (useropts.right_margin != -1)
+ amiga_fb_predefined[0].right_margin = useropts.right_margin;
+ if (useropts.upper_margin != -1)
+ amiga_fb_predefined[0].upper_margin = useropts.upper_margin;
+ if (useropts.lower_margin != -1)
+ amiga_fb_predefined[0].lower_margin = useropts.lower_margin;
+ if (useropts.hsync_len != -1)
+ amiga_fb_predefined[0].hsync_len = useropts.hsync_len;
+ if (useropts.vsync_len != -1)
+ amiga_fb_predefined[0].vsync_len = useropts.vsync_len;
+
+ amifb_usermode = i;
+ return;
+ }
+ }
+}
+
+ /*
+ * Probe the Video Modes
+ */
+
+__initfunc(static void check_default_mode(void))
+{
+ struct amiga_fb_par par;
+ int mode;
+
+ for (mode = 0; mode < NUM_PREDEF_MODES; mode++) {
+ if (!ami_decode_var(&amiga_fb_predefined[mode], &par)) {
+ if (mode)
+ amiga_fb_predefined[0] = amiga_fb_predefined[mode];
+ return;
+ }
+ if (!mode)
+ printk("Can't use default video mode. Probing video modes...\n");
+ }
+ panic("Can't find any usable video mode");
+}
+
+ /*
+ * Allocate, Clear and Align a Block of Chip Memory
+ */
+
+__initfunc(static u_long chipalloc(u_long size))
+{
+ u_long ptr;
+
+ size += PAGE_SIZE-1;
+ if (!(ptr = (u_long)amiga_chip_alloc(size)))
+ panic("No Chip RAM for frame buffer");
+ memset((void *)ptr, 0, size);
+ ptr = PAGE_ALIGN(ptr);
+
+ return ptr;
+}
+
+ /*
+ * A strtok which returns empty strings, too
+ */
+
+__initfunc(static char *strtoke(char *s,const char *ct))
+{
+ char *sbegin, *send;
+ static char *ssave = NULL;
+
+ sbegin = s ? s : ssave;
+ if (!sbegin)
+ return NULL;
+ if (*sbegin == '\0') {
+ ssave = NULL;
+ return NULL;
+ }
+ send = strpbrk(sbegin, ct);
+ if (send && *send != '\0')
+ *send++ = '\0';
+ ssave = send;
+ return sbegin;
+}
+
+/* --------------------------- Hardware routines --------------------------- */
+
+ /*
+ * This function should fill in the `fix' structure based on the
+ * values in the `par' structure.
+ */
+
+static int ami_encode_fix(struct fb_fix_screeninfo *fix,
+ struct amiga_fb_par *par)
+{
+ memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+ strcpy(fix->id, amiga_fb_name);
+ fix->smem_start = (char *)videomemory;
+ fix->smem_len = videomemorysize;
+
+ if (amifb_ilbm) {
+ fix->type = FB_TYPE_INTERLEAVED_PLANES;
+ fix->type_aux = par->next_line;
+ } else {
+ fix->type = FB_TYPE_PLANES;
+ fix->type_aux = 0;
+ }
+ fix->line_length = div8(upx(16<<maxfmode, par->vxres));
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
+
+ if (par->vmode & FB_VMODE_YWRAP) {
+ fix->ywrapstep = 1;
+ fix->xpanstep = fix->ypanstep = 0;
+ } else {
+ fix->ywrapstep = 0;
+ if (par->vmode &= FB_VMODE_SMOOTH_XPAN)
+ fix->xpanstep = 1;
+ else
+ fix->xpanstep = 16<<maxfmode;
+ fix->ypanstep = 1;
+ }
+ return 0;
+}
+
+ /*
+ * Get the video params out of `var'. If a value doesn't fit, round
+ * it up, if it's too big, return -EINVAL.
+ */
+
+static int ami_decode_var(struct fb_var_screeninfo *var,
+ struct amiga_fb_par *par)
+{
+ u_short clk_shift, line_shift;
+ u_long maxfetchstop, fstrt, fsize, fconst, xres_n, yres_n;
+ u_long hrate = 0, vrate = 0;
+
+ /*
+ * Find a matching Pixel Clock
+ */
+
+ for (clk_shift = TAG_SHRES; clk_shift <= TAG_LORES; clk_shift++)
+ if (var->pixclock <= pixclock[clk_shift])
+ break;
+ if (clk_shift > TAG_LORES) {
+ DPRINTK("pixclock too high\n");
+ return -EINVAL;
+ }
+ par->clk_shift = clk_shift;
+
+ /*
+ * Check the Geometry Values
+ */
+
+ if ((par->xres = var->xres) < 64)
+ par->xres = 64;
+ if ((par->yres = var->yres) < 64)
+ par->yres = 64;
+ if ((par->vxres = var->xres_virtual) < par->xres)
+ par->vxres = par->xres;
+ if ((par->vyres = var->yres_virtual) < par->yres)
+ par->vyres = par->yres;
+
+ par->bpp = var->bits_per_pixel;
+ if (!var->nonstd) {
+ if (par->bpp < 1)
+ par->bpp = 1;
+ if (par->bpp > maxdepth[clk_shift]) {
+ DPRINTK("invalid bpp\n");
+ return -EINVAL;
+ }
+ } else if (var->nonstd == FB_NONSTD_HAM) {
+ if (par->bpp < 6)
+ par->bpp = 6;
+ if (par->bpp != 6) {
+ if (par->bpp < 8)
+ par->bpp = 8;
+ if (par->bpp != 8 || !IS_AGA) {
+ DPRINTK("invalid bpp for ham mode\n");
+ return -EINVAL;
+ }
+ }
+ } else {
+ DPRINTK("unknown nonstd mode\n");
+ return -EINVAL;
+ }
+
+ /*
+ * FB_VMODE_SMOOTH_XPAN will be cleared, if one of the folloing
+ * checks failed and smooth scrolling is not possible
+ */
+
+ par->vmode = var->vmode | FB_VMODE_SMOOTH_XPAN;
+ switch (par->vmode & FB_VMODE_MASK) {
+ case FB_VMODE_INTERLACED:
+ line_shift = 0;
+ break;
+ case FB_VMODE_NONINTERLACED:
+ line_shift = 1;
+ break;
+ case FB_VMODE_DOUBLE:
+ if (!IS_AGA) {
+ DPRINTK("double mode only possible with aga\n");
+ return -EINVAL;
+ }
+ line_shift = 2;
+ break;
+ default:
+ DPRINTK("unknown video mode\n");
+ return -EINVAL;
+ break;
+ }
+ par->line_shift = line_shift;
+
+ /*
+ * Vertical and Horizontal Timings
+ */
+
+ xres_n = par->xres<<clk_shift;
+ yres_n = par->yres<<line_shift;
+ par->htotal = down8((var->left_margin+par->xres+var->right_margin+var->hsync_len)<<clk_shift);
+ par->vtotal = down2(((var->upper_margin+par->yres+var->lower_margin+var->vsync_len)<<line_shift)+1);
+
+ if (IS_AGA)
+ par->bplcon3 = sprpixmode[clk_shift];
+ else
+ par->bplcon3 = 0;
+ if (var->sync & FB_SYNC_BROADCAST) {
+ par->diwstop_h = par->htotal-((var->right_margin-var->hsync_len)<<clk_shift);
+ if (IS_AGA)
+ par->diwstop_h += mod4(var->hsync_len);
+ else
+ par->diwstop_h = down4(par->diwstop_h);
+
+ par->diwstrt_h = par->diwstop_h - xres_n;
+ par->diwstop_v = par->vtotal-((var->lower_margin-var->vsync_len)<<line_shift);
+ par->diwstrt_v = par->diwstop_v - yres_n;
+ if (par->diwstop_h >= par->htotal+8) {
+ DPRINTK("invalid diwstop_h\n");
+ return -EINVAL;
+ }
+ if (par->diwstop_v > par->vtotal) {
+ DPRINTK("invaild diwstop_v\n");
+ return -EINVAL;
+ }
+
+ if (!IS_OCS) {
+ /* Initialize sync with some reasonable values for pwrsave */
+ par->hsstrt = 160;
+ par->hsstop = 320;
+ par->vsstrt = 30;
+ par->vsstop = 34;
+ } else {
+ par->hsstrt = 0;
+ par->hsstop = 0;
+ par->vsstrt = 0;
+ par->vsstop = 0;
+ }
+ if (par->vtotal > (PAL_VTOTAL+NTSC_VTOTAL)/2) {
+ /* PAL video mode */
+ if (par->htotal != PAL_HTOTAL) {
+ DPRINTK("htotal invalid for pal\n");
+ return -EINVAL;
+ }
+ if (par->diwstrt_h < PAL_DIWSTRT_H) {
+ DPRINTK("diwstrt_h too low for pal\n");
+ return -EINVAL;
+ }
+ if (par->diwstrt_v < PAL_DIWSTRT_V) {
+ DPRINTK("diwstrt_v too low for pal\n");
+ return -EINVAL;
+ }
+ hrate = 15625;
+ vrate = 50;
+ if (!IS_OCS) {
+ par->beamcon0 = BMC0_PAL;
+ par->bplcon3 |= BPC3_BRDRBLNK;
+ } else if (AMIGAHW_PRESENT(AGNUS_HR_PAL) ||
+ AMIGAHW_PRESENT(AGNUS_HR_NTSC)) {
+ par->beamcon0 = BMC0_PAL;
+ par->hsstop = 1;
+ } else if (amiga_vblank != 50) {
+ DPRINTK("pal not supported by this chipset\n");
+ return -EINVAL;
+ }
+ } else {
+ /* NTSC video mode
+ * In the AGA chipset seems to be hardware bug with BPC3_BRDRBLNK
+ * and NTSC activated, so than better let diwstop_h <= 1812
+ */
+ if (par->htotal != NTSC_HTOTAL) {
+ DPRINTK("htotal invalid for ntsc\n");
+ return -EINVAL;
+ }
+ if (par->diwstrt_h < NTSC_DIWSTRT_H) {
+ DPRINTK("diwstrt_h too low for ntsc\n");
+ return -EINVAL;
+ }
+ if (par->diwstrt_v < NTSC_DIWSTRT_V) {
+ DPRINTK("diwstrt_v too low for ntsc\n");
+ return -EINVAL;
+ }
+ hrate = 15750;
+ vrate = 60;
+ if (!IS_OCS) {
+ par->beamcon0 = 0;
+ par->bplcon3 |= BPC3_BRDRBLNK;
+ } else if (AMIGAHW_PRESENT(AGNUS_HR_PAL) ||
+ AMIGAHW_PRESENT(AGNUS_HR_NTSC)) {
+ par->beamcon0 = 0;
+ par->hsstop = 1;
+ } else if (amiga_vblank != 60) {
+ DPRINTK("ntsc not supported by this chipset\n");
+ return -EINVAL;
+ }
+ }
+ if (IS_OCS) {
+ if (par->diwstrt_h >= 1024 || par->diwstop_h < 1024 ||
+ par->diwstrt_v >= 512 || par->diwstop_v < 256) {
+ DPRINTK("invalid position for display on ocs\n");
+ return -EINVAL;
+ }
+ }
+ } else if (!IS_OCS) {
+ /* Programmable video mode */
+ par->hsstrt = var->right_margin<<clk_shift;
+ par->hsstop = (var->right_margin+var->hsync_len)<<clk_shift;
+ par->diwstop_h = par->htotal - mod8(par->hsstrt) + 8 - (1 << clk_shift);
+ if (!IS_AGA)
+ par->diwstop_h = down4(par->diwstop_h) - 16;
+ par->diwstrt_h = par->diwstop_h - xres_n;
+ par->hbstop = par->diwstrt_h + 4;
+ par->hbstrt = par->diwstop_h + 4;
+ if (par->hbstrt >= par->htotal + 8)
+ par->hbstrt -= par->htotal;
+ par->hcenter = par->hsstrt + (par->htotal >> 1);
+ par->vsstrt = var->lower_margin<<line_shift;
+ par->vsstop = (var->lower_margin+var->vsync_len)<<line_shift;
+ par->diwstop_v = par->vtotal;
+ if ((par->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED)
+ par->diwstop_v -= 2;
+ par->diwstrt_v = par->diwstop_v - yres_n;
+ par->vbstop = par->diwstrt_v - 2;
+ par->vbstrt = par->diwstop_v - 2;
+ if (par->vtotal > 2048) {
+ DPRINTK("vtotal too high\n");
+ return -EINVAL;
+ }
+ if (par->htotal > 2048) {
+ DPRINTK("htotal too high\n");
+ return -EINVAL;
+ }
+ par->bplcon3 |= BPC3_EXTBLKEN;
+ par->beamcon0 = BMC0_HARDDIS | BMC0_VARVBEN | BMC0_LOLDIS |
+ BMC0_VARVSYEN | BMC0_VARHSYEN | BMC0_VARBEAMEN |
+ BMC0_PAL | BMC0_VARCSYEN;
+ if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+ par->beamcon0 |= BMC0_HSYTRUE;
+ if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+ par->beamcon0 |= BMC0_VSYTRUE;
+ if (var->sync & FB_SYNC_COMP_HIGH_ACT)
+ par->beamcon0 |= BMC0_CSYTRUE;
+ hrate = (amiga_masterclock+par->htotal/2)/par->htotal;
+ vrate = div2(par->vtotal) * par->htotal;
+ vrate = (amiga_masterclock+vrate/2)/vrate;
+ } else {
+ DPRINTK("only broadcast modes possible for ocs\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Checking the DMA timing
+ */
+
+ fconst = 16<<maxfmode<<clk_shift;
+
+ /*
+ * smallest window start value without turn off other dma cycles
+ * than sprite1-7, unless you change min_fstrt
+ */
+
+
+ fsize = ((maxfmode+clk_shift <= 1) ? fconst : 64);
+ fstrt = downx(fconst, par->diwstrt_h-4) - fsize;
+ if (fstrt < min_fstrt) {
+ DPRINTK("fetch start too low\n");
+ return -EINVAL;
+ }
+
+ /*
+ * smallest window start value where smooth scrolling is possible
+ */
+
+ fstrt = downx(fconst, par->diwstrt_h-fconst+(1<<clk_shift)-4) - fsize;
+ if (fstrt < min_fstrt)
+ par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
+
+ maxfetchstop = down16(par->htotal - 80);
+
+ fstrt = downx(fconst, par->diwstrt_h-4) - 64 - fconst;
+ fsize = upx(fconst, xres_n + modx(fconst, downx(1<<clk_shift, par->diwstrt_h-4)));
+ if (fstrt + fsize > maxfetchstop)
+ par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
+
+ fsize = upx(fconst, xres_n);
+ if (fstrt + fsize > maxfetchstop) {
+ DPRINTK("fetch stop too high\n");
+ return -EINVAL;
+ }
+
+ if (maxfmode + clk_shift <= 1) {
+ fsize = up64(xres_n + fconst - 1);
+ if (min_fstrt + fsize - 64 > maxfetchstop)
+ par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
+
+ fsize = up64(xres_n);
+ if (min_fstrt + fsize - 64 > maxfetchstop) {
+ DPRINTK("fetch size too high\n");
+ return -EINVAL;
+ }
+
+ fsize -= 64;
+ } else
+ fsize -= fconst;
+
+ /*
+ * Check if there is enough time to update the bitplane pointers for ywrap
+ */
+
+ if (par->htotal-fsize-64 < par->bpp*64)
+ par->vmode &= ~FB_VMODE_YWRAP;
+
+ /*
+ * Bitplane calculations and check the Memory Requirements
+ */
+
+ if (amifb_ilbm) {
+ par->next_plane = div8(upx(16<<maxfmode, par->vxres));
+ par->next_line = par->bpp*par->next_plane;
+ if (par->next_line * par->vyres > videomemorysize) {
+ DPRINTK("too few video mem\n");
+ return -EINVAL;
+ }
+ } else {
+ par->next_line = div8(upx(16<<maxfmode, par->vxres));
+ par->next_plane = par->vyres*par->next_line;
+ if (par->next_plane * par->bpp > videomemorysize) {
+ DPRINTK("too few video mem\n");
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * Hardware Register Values
+ */
+
+ par->bplcon0 = BPC0_COLOR | bplpixmode[clk_shift];
+ if (!IS_OCS)
+ par->bplcon0 |= BPC0_ECSENA;
+ if (par->bpp == 8)
+ par->bplcon0 |= BPC0_BPU3;
+ else
+ par->bplcon0 |= par->bpp<<12;
+ if (var->nonstd == FB_NONSTD_HAM)
+ par->bplcon0 |= BPC0_HAM;
+ if (var->sync & FB_SYNC_EXT)
+ par->bplcon0 |= BPC0_ERSY;
+
+ if (IS_AGA)
+ par->fmode = bplfetchmode[maxfmode];
+
+ switch (par->vmode & FB_VMODE_MASK) {
+ case FB_VMODE_INTERLACED:
+ par->bplcon0 |= BPC0_LACE;
+ break;
+ case FB_VMODE_DOUBLE:
+ if (IS_AGA)
+ par->fmode |= FMODE_SSCAN2 | FMODE_BSCAN2;
+ break;
+ }
+
+ if (!((par->vmode ^ var->vmode) & FB_VMODE_YWRAP)) {
+ par->xoffset = var->xoffset;
+ par->yoffset = var->yoffset;
+ if (par->vmode & FB_VMODE_YWRAP) {
+ if (par->xoffset || par->yoffset < 0 || par->yoffset >= par->vyres)
+ par->xoffset = par->yoffset = 0;
+ } else {
+ if (par->xoffset < 0 || par->xoffset > upx(16<<maxfmode, par->vxres-par->xres) ||
+ par->yoffset < 0 || par->yoffset > par->vyres-par->yres)
+ par->xoffset = par->yoffset = 0;
+ }
+ } else
+ par->xoffset = par->yoffset = 0;
+
+ par->crsr.crsr_x = par->crsr.crsr_y = 0;
+ par->crsr.spot_x = par->crsr.spot_y = 0;
+ par->crsr.height = par->crsr.width = 0;
+
+ if (hrate < hfmin || hrate > hfmax || vrate < vfmin || vrate > vfmax) {
+ DPRINTK("mode doesn't fit for monitor\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+ /*
+ * Fill the `var' structure based on the values in `par' and maybe
+ * other values read out of the hardware.
+ */
+
+static int ami_encode_var(struct fb_var_screeninfo *var,
+ struct amiga_fb_par *par)
+{
+ u_short clk_shift, line_shift;
+ int i;
+
+ clk_shift = par->clk_shift;
+ line_shift = par->line_shift;
+
+ var->xres = par->xres;
+ var->yres = par->yres;
+ var->xres_virtual = par->vxres;
+ var->yres_virtual = par->vyres;
+ var->xoffset = par->xoffset;
+ var->yoffset = par->yoffset;
+
+ var->bits_per_pixel = par->bpp;
+ var->grayscale = 0;
+
+ if (IS_AGA) {
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->red.msb_right = 0;
+ } else {
+ if (clk_shift == TAG_SHRES) {
+ var->red.offset = 0;
+ var->red.length = 2;
+ var->red.msb_right = 0;
+ } else {
+ var->red.offset = 0;
+ var->red.length = 4;
+ var->red.msb_right = 0;
+ }
+ }
+ var->blue = var->green = var->red;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ var->transp.msb_right = 0;
+
+ if (par->bplcon0 & BPC0_HAM)
+ var->nonstd = FB_NONSTD_HAM;
+ else
+ var->nonstd = 0;
+ var->activate = 0;
+
+ var->height = -1;
+ var->width = -1;
+ var->accel = 0;
+
+ var->pixclock = pixclock[clk_shift];
+
+ if (IS_AGA && par->fmode & FMODE_BSCAN2)
+ var->vmode = FB_VMODE_DOUBLE;
+ else if (par->bplcon0 & BPC0_LACE)
+ var->vmode = FB_VMODE_INTERLACED;
+ else
+ var->vmode = FB_VMODE_NONINTERLACED;
+
+ if (!IS_OCS && par->beamcon0 & BMC0_VARBEAMEN) {
+ var->hsync_len = (par->hsstop-par->hsstrt)>>clk_shift;
+ var->right_margin = par->hsstrt>>clk_shift;
+ var->left_margin = (par->htotal>>clk_shift) - var->xres - var->right_margin - var->hsync_len;
+ var->vsync_len = (par->vsstop-par->vsstrt)>>line_shift;
+ var->lower_margin = par->vsstrt>>line_shift;
+ var->upper_margin = (par->vtotal>>line_shift) - var->yres - var->lower_margin - var->vsync_len;
+ var->sync = 0;
+ if (par->beamcon0 & BMC0_HSYTRUE)
+ var->sync |= FB_SYNC_HOR_HIGH_ACT;
+ if (par->beamcon0 & BMC0_VSYTRUE)
+ var->sync |= FB_SYNC_VERT_HIGH_ACT;
+ if (par->beamcon0 & BMC0_CSYTRUE)
+ var->sync |= FB_SYNC_COMP_HIGH_ACT;
+ } else {
+ var->sync = FB_SYNC_BROADCAST;
+ var->hsync_len = (152>>clk_shift) + mod4(par->diwstop_h);
+ var->right_margin = ((par->htotal - down4(par->diwstop_h))>>clk_shift) + var->hsync_len;
+ var->left_margin = (par->htotal>>clk_shift) - var->xres - var->right_margin - var->hsync_len;
+ var->vsync_len = 4>>line_shift;
+ var->lower_margin = ((par->vtotal - par->diwstop_v)>>line_shift) + var->vsync_len;
+ var->upper_margin = (((par->vtotal - 2)>>line_shift) + 1) - var->yres -
+ var->lower_margin - var->vsync_len;
+ }
+
+ if (par->bplcon0 & BPC0_ERSY)
+ var->sync |= FB_SYNC_EXT;
+ if (par->vmode & FB_VMODE_YWRAP)
+ var->vmode |= FB_VMODE_YWRAP;
+
+ for (i = 0; i < arraysize(var->reserved); i++)
+ var->reserved[i] = 0;
+
+ return 0;
+}
+
+ /*
+ * Get current hardware setting
+ */
+
+static void ami_get_par(struct amiga_fb_par *par)
+{
+ *par = currentpar;
+}
+
+ /*
+ * Set new videomode
+ */
+
+static void ami_set_var(struct fb_var_screeninfo *var)
+{
+ do_vmode_pan = 0;
+ do_vmode_full = 0;
+ ami_decode_var(var, &currentpar);
+ ami_build_copper();
+ do_vmode_full = 1;
+}
+
+#ifdef DEBUG
+static void ami_set_par(struct amiga_fb_par *par)
+{
+ do_vmode_pan = 0;
+ do_vmode_full = 0;
+ currentpar = *par;
+ ami_build_copper();
+ do_vmode_full = 1;
+}
+#endif
+
+ /*
+ * Pan or Wrap the Display
+ *
+ * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+ * in `var'.
+ */
+
+static void ami_pan_var(struct fb_var_screeninfo *var)
+{
+ struct amiga_fb_par *par = &currentpar;
+
+ par->xoffset = var->xoffset;
+ par->yoffset = var->yoffset;
+ if (var->vmode & FB_VMODE_YWRAP)
+ par->vmode |= FB_VMODE_YWRAP;
+ else
+ par->vmode &= ~FB_VMODE_YWRAP;
+
+ do_vmode_pan = 0;
+ ami_update_par();
+ do_vmode_pan = 1;
+}
+
+ /*
+ * Update hardware
+ */
+
+static int ami_update_par(void)
+{
+ struct amiga_fb_par *par = &currentpar;
+ short clk_shift, vshift, fstrt, fsize, fstop, fconst, shift, move, mod;
+
+ clk_shift = par->clk_shift;
+
+ if (!(par->vmode & FB_VMODE_SMOOTH_XPAN))
+ par->xoffset = upx(16<<maxfmode, par->xoffset);
+
+ fconst = 16<<maxfmode<<clk_shift;
+ vshift = modx(16<<maxfmode, par->xoffset);
+ fstrt = par->diwstrt_h - (vshift<<clk_shift) - 4;
+ fsize = (par->xres+vshift)<<clk_shift;
+ shift = modx(fconst, fstrt);
+ move = downx(2<<maxfmode, div8(par->xoffset));
+ if (maxfmode + clk_shift > 1) {
+ fstrt = downx(fconst, fstrt) - 64;
+ fsize = upx(fconst, fsize);
+ fstop = fstrt + fsize - fconst;
+ } else {
+ mod = fstrt = downx(fconst, fstrt) - fconst;
+ fstop = fstrt + upx(fconst, fsize) - 64;
+ fsize = up64(fsize);
+ fstrt = fstop - fsize + 64;
+ if (fstrt < min_fstrt) {
+ fstop += min_fstrt - fstrt;
+ fstrt = min_fstrt;
+ }
+ move = move - div8((mod-fstrt)>>clk_shift);
+ }
+ mod = par->next_line - div8(fsize>>clk_shift);
+ par->ddfstrt = fstrt;
+ par->ddfstop = fstop;
+ par->bplcon1 = hscroll2hw(shift);
+ par->bpl2mod = mod;
+ if (par->bplcon0 & BPC0_LACE)
+ par->bpl2mod += par->next_line;
+ if (IS_AGA && (par->fmode & FMODE_BSCAN2))
+ par->bpl1mod = -div8(fsize>>clk_shift);
+ else
+ par->bpl1mod = par->bpl2mod;
+
+ if (par->yoffset) {
+ par->bplpt0 = ZTWO_PADDR((u_long)videomemory + par->next_line*par->yoffset + move);
+ if (par->vmode & FB_VMODE_YWRAP) {
+ if (par->yoffset > par->vyres-par->yres) {
+ par->bplpt0wrap = ZTWO_PADDR((u_long)videomemory + move);
+ if (par->bplcon0 & BPC0_LACE && mod2(par->diwstrt_v+par->vyres-par->yoffset))
+ par->bplpt0wrap += par->next_line;
+ }
+ }
+ } else
+ par->bplpt0 = ZTWO_PADDR((u_long)videomemory + move);
+
+ if (par->bplcon0 & BPC0_LACE && mod2(par->diwstrt_v))
+ par->bplpt0 += par->next_line;
+
+ return 0;
+}
+
+ /*
+ * Read a single color register and split it into
+ * colors/transparent. Return != 0 for invalid regno.
+ */
+
+static int ami_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+ u_int *transp)
+{
+ if (IS_AGA) {
+ if (regno > 255)
+ return 1;
+ } else {
+ if (regno > 31)
+ return 1;
+ }
+
+ *red = palette[regno].red;
+ *green = palette[regno].green;
+ *blue = palette[regno].blue;
+ return 0;
+}
+
+
+ /*
+ * Set a single color register. The values supplied are already
+ * rounded down to the hardware's capabilities (according to the
+ * entries in the var structure). Return != 0 for invalid regno.
+ */
+
+static int ami_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp)
+{
+#if defined(CONFIG_FB_AMIGA_AGA)
+ u_short bplcon3 = currentpar.bplcon3;
+
+ if (IS_AGA) {
+ if (regno > 255)
+ return 1;
+ } else
+#endif
+ if (regno > 31)
+ return 1;
+
+ /*
+ * Update the corresponding Hardware Color Register, unless it's Color
+ * Register 0 and the screen is blanked.
+ *
+ * VBlank is switched off to protect bplcon3 or ecs_palette[] from
+ * being changed by ami_do_blank() during the VBlank.
+ */
+
+ palette[regno].red = red;
+ palette[regno].green = green;
+ palette[regno].blue = blue;
+
+ if (regno || !is_blanked) {
+#if defined(CONFIG_FB_AMIGA_AGA)
+ if (IS_AGA) {
+ VBlankOff();
+ custom.bplcon3 = bplcon3 | (regno<<8 & 0xe000);
+ custom.color[regno&31] = rgb2hw8_high(red, green, blue);
+ custom.bplcon3 = bplcon3 | (regno<<8 & 0xe000) | BPC3_LOCT;
+ custom.color[regno&31] = rgb2hw8_low(red, green, blue);
+ custom.bplcon3 = bplcon3;
+ VBlankOn();
+ } else
+#endif
+ {
+#if defined(CONFIG_FB_AMIGA_ECS)
+ if (currentpar.bplcon0 & BPC0_SHRES) {
+ u_short color, mask;
+ int i;
+
+ mask = 0x3333;
+ color = rgb2hw2(red, green, blue);
+ VBlankOff();
+ for (i = regno+12; i >= (int)regno; i -= 4)
+ custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
+ mask <<=2; color >>= 2;
+ regno = down16(regno)+mul4(mod4(regno));
+ for (i = regno+3; i >= (int)regno; i--)
+ custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
+ VBlankOn();
+ } else
+#endif
+ custom.color[regno] = rgb2hw4(red, green, blue);
+ }
+ }
+ return 0;
+}
+
+static void ami_update_display(void)
+{
+ struct amiga_fb_par *par = &currentpar;
+
+ custom.bplcon1 = par->bplcon1;
+ custom.bpl1mod = par->bpl1mod;
+ custom.bpl2mod = par->bpl2mod;
+ custom.ddfstrt = ddfstrt2hw(par->ddfstrt);
+ custom.ddfstop = ddfstop2hw(par->ddfstop);
+}
+
+ /*
+ * Change the video mode (called by VBlank interrupt)
+ */
+
+static void ami_init_display(void)
+{
+ struct amiga_fb_par *par = &currentpar;
+
+ custom.bplcon0 = par->bplcon0 & ~BPC0_LACE;
+ custom.bplcon2 = (IS_OCS ? 0 : BPC2_KILLEHB) | BPC2_PF2P2 | BPC2_PF1P2;
+ if (!IS_OCS) {
+ custom.bplcon3 = par->bplcon3;
+ if (IS_AGA)
+ custom.bplcon4 = BPC4_ESPRM4 | BPC4_OSPRM4;
+ if (par->beamcon0 & BMC0_VARBEAMEN) {
+ custom.htotal = htotal2hw(par->htotal);
+ custom.hbstrt = hbstrt2hw(par->hbstrt);
+ custom.hbstop = hbstop2hw(par->hbstop);
+ custom.hsstrt = hsstrt2hw(par->hsstrt);
+ custom.hsstop = hsstop2hw(par->hsstop);
+ custom.hcenter = hcenter2hw(par->hcenter);
+ custom.vtotal = vtotal2hw(par->vtotal);
+ custom.vbstrt = vbstrt2hw(par->vbstrt);
+ custom.vbstop = vbstop2hw(par->vbstop);
+ custom.vsstrt = vsstrt2hw(par->vsstrt);
+ custom.vsstop = vsstop2hw(par->vsstop);
+ }
+ }
+ if (!IS_OCS || par->hsstop)
+ custom.beamcon0 = par->beamcon0;
+ if (IS_AGA)
+ custom.fmode = par->fmode;
+
+ /*
+ * The minimum period for audio depends on htotal
+ */
+
+ amiga_audio_min_period = div16(par->htotal);
+
+ is_lace = par->bplcon0 & BPC0_LACE ? 1 : 0;
+#if 1
+ if (is_lace) {
+ if (custom.vposr & 0x8000)
+ custom.cop2lc = (u_short *)ZTWO_PADDR(copdisplay.list[currentcop][1]);
+ else
+ custom.cop2lc = (u_short *)ZTWO_PADDR(copdisplay.list[currentcop][0]);
+ } else {
+ custom.vposw = custom.vposr | 0x8000;
+ custom.cop2lc = (u_short *)ZTWO_PADDR(copdisplay.list[currentcop][1]);
+ }
+#else
+ custom.vposw = custom.vposr | 0x8000;
+ custom.cop2lc = (u_short *)ZTWO_PADDR(copdisplay.list[currentcop][1]);
+#endif
+}
+
+ /*
+ * (Un)Blank the screen (called by VBlank interrupt)
+ */
+
+static void ami_do_blank(void)
+{
+ struct amiga_fb_par *par = &currentpar;
+#if defined(CONFIG_FB_AMIGA_AGA)
+ u_short bplcon3 = par->bplcon3;
+#endif
+ u_char red, green, blue;
+
+ if (do_blank > 0) {
+ custom.dmacon = DMAF_RASTER | DMAF_SPRITE;
+ red = green = blue = 0;
+ if (!IS_OCS && do_blank > 1) {
+ switch (do_blank) {
+ case 2 : /* suspend vsync */
+ custom.hsstrt = hsstrt2hw(par->hsstrt);
+ custom.hsstop = hsstop2hw(par->hsstop);
+ custom.vsstrt = vsstrt2hw(par->vtotal+4);
+ custom.vsstop = vsstop2hw(par->vtotal+4);
+ break;
+ case 3 : /* suspend hsync */
+ custom.hsstrt = hsstrt2hw(par->htotal+16);
+ custom.hsstop = hsstop2hw(par->htotal+16);
+ custom.vsstrt = vsstrt2hw(par->vsstrt);
+ custom.vsstop = vsstrt2hw(par->vsstop);
+ break;
+ case 4 : /* powerdown */
+ custom.hsstrt = hsstrt2hw(par->htotal+16);
+ custom.hsstop = hsstop2hw(par->htotal+16);
+ custom.vsstrt = vsstrt2hw(par->vtotal+4);
+ custom.vsstop = vsstop2hw(par->vtotal+4);
+ break;
+ }
+ if (!(par->beamcon0 & BMC0_VARBEAMEN)) {
+ custom.htotal = htotal2hw(par->htotal);
+ custom.vtotal = vtotal2hw(par->vtotal);
+ custom.beamcon0 = BMC0_HARDDIS | BMC0_VARBEAMEN |
+ BMC0_VARVSYEN | BMC0_VARHSYEN | BMC0_VARCSYEN;
+ }
+ }
+ } else {
+ custom.dmacon = DMAF_SETCLR | DMAF_RASTER | DMAF_SPRITE;
+ red = palette[0].red;
+ green = palette[0].green;
+ blue = palette[0].blue;
+ if (!IS_OCS) {
+ custom.hsstrt = hsstrt2hw(par->hsstrt);
+ custom.hsstop = hsstop2hw(par->hsstop);
+ custom.vsstrt = vsstrt2hw(par->vsstrt);
+ custom.vsstop = vsstop2hw(par->vsstop);
+ custom.beamcon0 = par->beamcon0;
+ }
+ }
+#if defined(CONFIG_FB_AMIGA_AGA)
+ if (IS_AGA) {
+ custom.bplcon3 = bplcon3;
+ custom.color[0] = rgb2hw8_high(red, green, blue);
+ custom.bplcon3 = bplcon3 | BPC3_LOCT;
+ custom.color[0] = rgb2hw8_low(red, green, blue);
+ custom.bplcon3 = bplcon3;
+ } else
+#endif
+ {
+#if defined(CONFIG_FB_AMIGA_ECS)
+ if (par->bplcon0 & BPC0_SHRES) {
+ u_short color, mask;
+ int i;
+
+ mask = 0x3333;
+ color = rgb2hw2(red, green, blue);
+ for (i = 12; i >= 0; i -= 4)
+ custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
+ mask <<=2; color >>= 2;
+ for (i = 3; i >= 0; i--)
+ custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
+ } else
+#endif
+ custom.color[0] = rgb2hw4(red, green, blue);
+ }
+ is_blanked = do_blank > 0 ? do_blank : 0;
+}
+
+ /*
+ * Flash the cursor (called by VBlank interrupt)
+ */
+
+static int ami_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con)
+{
+ struct amiga_fb_par *par = &currentpar;
+
+ fix->crsr_width = fix->crsr_xsize = par->crsr.width;
+ fix->crsr_height = fix->crsr_ysize = par->crsr.height;
+ fix->crsr_color1 = 17;
+ fix->crsr_color2 = 18;
+ return 0;
+}
+
+static int ami_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con)
+{
+ struct amiga_fb_par *par = &currentpar;
+ register u_short *lspr, *sspr;
+#ifdef __mc68000__
+ register u_long datawords asm ("d2");
+#else
+ register u_long datawords;
+#endif
+ register short delta;
+ register u_char color;
+ short height, width, bits, words;
+ int i, size, alloc;
+
+ size = par->crsr.height*par->crsr.width;
+ alloc = var->height*var->width;
+ var->height = par->crsr.height;
+ var->width = par->crsr.width;
+ var->xspot = par->crsr.spot_x;
+ var->yspot = par->crsr.spot_y;
+ if (size > var->height*var->width)
+ return -ENAMETOOLONG;
+ if ((i = verify_area(VERIFY_WRITE, (void *)data, size)))
+ return i;
+ delta = 1<<par->crsr.fmode;
+ lspr = lofsprite + (delta<<1);
+ if (par->bplcon0 & BPC0_LACE)
+ sspr = shfsprite + (delta<<1);
+ else
+ sspr = 0;
+ for (height = (short)var->height-1; height >= 0; height--) {
+ bits = 0; words = delta; datawords = 0;
+ for (width = (short)var->width-1; width >= 0; width--) {
+ if (bits == 0) {
+ bits = 16; --words;
+#ifdef __mc68000__
+ asm volatile ("movew %1@(%3:w:2),%0 ; swap %0 ; movew %1@+,%0"
+ : "=d" (datawords), "=a" (lspr) : "1" (lspr), "d" (delta));
+#else
+ datawords = (*(lspr+delta) << 16) | (*lspr++);
+#endif
+ }
+ --bits;
+#ifdef __mc68000__
+ asm volatile (
+ "clrb %0 ; swap %1 ; lslw #1,%1 ; roxlb #1,%0 ; "
+ "swap %1 ; lslw #1,%1 ; roxlb #1,%0"
+ : "=d" (color), "=d" (datawords) : "1" (datawords));
+#else
+ color = (((datawords >> 30) & 2)
+ | ((datawords >> 15) & 1));
+ datawords <<= 1;
+#endif
+ put_user(color, data++);
+ }
+ if (bits > 0) {
+ --words; ++lspr;
+ }
+ while (--words >= 0)
+ ++lspr;
+#ifdef __mc68000__
+ asm volatile ("lea %0@(%4:w:2),%0 ; tstl %1 ; jeq 1f ; exg %0,%1\n1:"
+ : "=a" (lspr), "=a" (sspr) : "0" (lspr), "1" (sspr), "d" (delta));
+#else
+ lspr += delta;
+ if (sspr) {
+ u_short *tmp = lspr;
+ lspr = sspr;
+ sspr = tmp;
+ }
+#endif
+ }
+ return 0;
+}
+
+static int ami_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con)
+{
+ struct amiga_fb_par *par = &currentpar;
+ register u_short *lspr, *sspr;
+#ifdef __mc68000__
+ register u_long datawords asm ("d2");
+#else
+ register u_long datawords;
+#endif
+ register short delta;
+ u_short fmode;
+ short height, width, bits, words;
+ int i;
+
+ if (!var->width)
+ return -EINVAL;
+ else if (var->width <= 16)
+ fmode = TAG_FMODE_1;
+ else if (var->width <= 32)
+ fmode = TAG_FMODE_2;
+ else if (var->width <= 64)
+ fmode = TAG_FMODE_4;
+ else
+ return -EINVAL;
+ if (fmode > maxfmode)
+ return -EINVAL;
+ if (!var->height)
+ return -EINVAL;
+ if ((i = verify_area(VERIFY_READ, (void *)data, var->width*var->height)))
+ return i;
+ delta = 1<<fmode;
+ lofsprite = shfsprite = (u_short *)spritememory;
+ lspr = lofsprite + (delta<<1);
+ if (par->bplcon0 & BPC0_LACE) {
+ if (((var->height+4)<<fmode<<2) > SPRITEMEMSIZE)
+ return -EINVAL;
+ memset(lspr, 0, (var->height+4)<<fmode<<2);
+ shfsprite += ((var->height+5)&-2)<<fmode;
+ sspr = shfsprite + (delta<<1);
+ } else {
+ if (((var->height+2)<<fmode<<2) > SPRITEMEMSIZE)
+ return -EINVAL;
+ memset(lspr, 0, (var->height+2)<<fmode<<2);
+ sspr = 0;
+ }
+ for (height = (short)var->height-1; height >= 0; height--) {
+ bits = 16; words = delta; datawords = 0;
+ for (width = (short)var->width-1; width >= 0; width--) {
+ unsigned long tdata = 0;
+ get_user(tdata, (char *)data);
+ data++;
+#ifdef __mc68000__
+ asm volatile (
+ "lsrb #1,%2 ; roxlw #1,%0 ; swap %0 ; "
+ "lsrb #1,%2 ; roxlw #1,%0 ; swap %0"
+ : "=d" (datawords)
+ : "0" (datawords), "d" (tdata));
+#else
+ datawords = ((datawords << 1) & 0xfffefffe);
+ datawords |= tdata & 1;
+ datawords |= (tdata & 2) << (16-1);
+#endif
+ if (--bits == 0) {
+ bits = 16; --words;
+#ifdef __mc68000__
+ asm volatile ("swap %2 ; movew %2,%0@(%3:w:2) ; swap %2 ; movew %2,%0@+"
+ : "=a" (lspr) : "0" (lspr), "d" (datawords), "d" (delta));
+#else
+ *(lspr+delta) = (u_short) (datawords >> 16);
+ *lspr++ = (u_short) (datawords & 0xffff);
+#endif
+ }
+ }
+ if (bits < 16) {
+ --words;
+#ifdef __mc68000__
+ asm volatile (
+ "swap %2 ; lslw %4,%2 ; movew %2,%0@(%3:w:2) ; "
+ "swap %2 ; lslw %4,%2 ; movew %2,%0@+"
+ : "=a" (lspr) : "0" (lspr), "d" (datawords), "d" (delta), "d" (bits));
+#else
+ *(lspr+delta) = (u_short) (datawords >> (16+bits));
+ *lspr++ = (u_short) ((datawords & 0x0000ffff) >> bits);
+#endif
+ }
+ while (--words >= 0) {
+#ifdef __mc68000__
+ asm volatile ("moveql #0,%%d0 ; movew %%d0,%0@(%2:w:2) ; movew %%d0,%0@+"
+ : "=a" (lspr) : "0" (lspr), "d" (delta) : "d0");
+#else
+ *(lspr+delta) = 0;
+ *lspr++ = 0;
+#endif
+ }
+#ifdef __mc68000__
+ asm volatile ("lea %0@(%4:w:2),%0 ; tstl %1 ; jeq 1f ; exg %0,%1\n1:"
+ : "=a" (lspr), "=a" (sspr) : "0" (lspr), "1" (sspr), "d" (delta));
+#else
+ lspr += delta;
+ if (sspr) {
+ u_short *tmp = lspr;
+ lspr = sspr;
+ sspr = tmp;
+ }
+#endif
+ }
+ par->crsr.height = var->height;
+ par->crsr.width = var->width;
+ par->crsr.spot_x = var->xspot;
+ par->crsr.spot_y = var->yspot;
+ par->crsr.fmode = fmode;
+ if (IS_AGA) {
+ par->fmode &= ~(FMODE_SPAGEM | FMODE_SPR32);
+ par->fmode |= sprfetchmode[fmode];
+ custom.fmode = par->fmode;
+ }
+ return 0;
+}
+
+static int ami_get_cursorstate(struct fb_cursorstate *state, int con)
+{
+ struct amiga_fb_par *par = &currentpar;
+
+ state->xoffset = par->crsr.crsr_x;
+ state->yoffset = par->crsr.crsr_y;
+ state->mode = cursormode;
+ return 0;
+}
+
+static int ami_set_cursorstate(struct fb_cursorstate *state, int con)
+{
+ struct amiga_fb_par *par = &currentpar;
+
+ par->crsr.crsr_x = state->xoffset;
+ par->crsr.crsr_y = state->yoffset;
+ if ((cursormode = state->mode) == FB_CURSOR_OFF)
+ cursorstate = -1;
+ do_cursor = 1;
+ return 0;
+}
+
+static void ami_set_sprite(void)
+{
+ struct amiga_fb_par *par = &currentpar;
+ copins *copl, *cops;
+ u_short hs, vs, ve;
+ u_long pl, ps, pt;
+ short mx, my;
+
+ cops = copdisplay.list[currentcop][0];
+ copl = copdisplay.list[currentcop][1];
+ ps = pl = ZTWO_PADDR(dummysprite);
+ mx = par->crsr.crsr_x-par->crsr.spot_x;
+ my = par->crsr.crsr_y-par->crsr.spot_y;
+ if (!(par->vmode & FB_VMODE_YWRAP)) {
+ mx -= par->xoffset;
+ my -= par->yoffset;
+ }
+ if (!is_blanked && cursorstate > 0 && par->crsr.height > 0 &&
+ mx > -(short)par->crsr.width && mx < par->xres &&
+ my > -(short)par->crsr.height && my < par->yres) {
+ pl = ZTWO_PADDR(lofsprite);
+ hs = par->diwstrt_h + (mx<<par->clk_shift) - 4;
+ vs = par->diwstrt_v + (my<<par->line_shift);
+ ve = vs + (par->crsr.height<<par->line_shift);
+ if (par->bplcon0 & BPC0_LACE) {
+ ps = ZTWO_PADDR(shfsprite);
+ lofsprite[0] = spr2hw_pos(vs, hs);
+ shfsprite[0] = spr2hw_pos(vs+1, hs);
+ if (mod2(vs)) {
+ lofsprite[1<<par->crsr.fmode] = spr2hw_ctl(vs, hs, ve);
+ shfsprite[1<<par->crsr.fmode] = spr2hw_ctl(vs+1, hs, ve+1);
+ pt = pl; pl = ps; ps = pt;
+ } else {
+ lofsprite[1<<par->crsr.fmode] = spr2hw_ctl(vs, hs, ve+1);
+ shfsprite[1<<par->crsr.fmode] = spr2hw_ctl(vs+1, hs, ve);
+ }
+ } else {
+ lofsprite[0] = spr2hw_pos(vs, hs) | (IS_AGA && (par->fmode & FMODE_BSCAN2) ? 0x80 : 0);
+ lofsprite[1<<par->crsr.fmode] = spr2hw_ctl(vs, hs, ve);
+ }
+ }
+ copl[cop_spr0ptrh].w[1] = highw(pl);
+ copl[cop_spr0ptrl].w[1] = loww(pl);
+ if (par->bplcon0 & BPC0_LACE) {
+ cops[cop_spr0ptrh].w[1] = highw(ps);
+ cops[cop_spr0ptrl].w[1] = loww(ps);
+ }
+}
+
+ /*
+ * Initialise the Copper Initialisation List
+ */
+
+__initfunc(static void ami_init_copper(void))
+{
+ copins *cop = copdisplay.init;
+ u_long p;
+ int i;
+
+ if (!IS_OCS) {
+ (cop++)->l = CMOVE(BPC0_COLOR | BPC0_SHRES | BPC0_ECSENA, bplcon0);
+ (cop++)->l = CMOVE(0x0181, diwstrt);
+ (cop++)->l = CMOVE(0x0281, diwstop);
+ (cop++)->l = CMOVE(0x0000, diwhigh);
+ } else
+ (cop++)->l = CMOVE(BPC0_COLOR, bplcon0);
+ p = ZTWO_PADDR(dummysprite);
+ for (i = 0; i < 8; i++) {
+ (cop++)->l = CMOVE(0, spr[i].pos);
+ (cop++)->l = CMOVE(highw(p), sprpt[i]);
+ (cop++)->l = CMOVE2(loww(p), sprpt[i]);
+ }
+
+ (cop++)->l = CMOVE(IF_SETCLR | IF_COPER, intreq);
+ copdisplay.wait = cop;
+ (cop++)->l = CEND;
+ (cop++)->l = CMOVE(0, copjmp2);
+ cop->l = CEND;
+
+ custom.cop1lc = (u_short *)ZTWO_PADDR(copdisplay.init);
+ custom.copjmp1 = 0;
+}
+
+static void ami_reinit_copper(void)
+{
+ struct amiga_fb_par *par = &currentpar;
+
+ copdisplay.init[cip_bplcon0].w[1] = ~(BPC0_BPU3 | BPC0_BPU2 | BPC0_BPU1 | BPC0_BPU0) & par->bplcon0;
+ copdisplay.wait->l = CWAIT(32, par->diwstrt_v-4);
+}
+
+ /*
+ * Build the Copper List
+ */
+
+static void ami_build_copper(void)
+{
+ struct amiga_fb_par *par = &currentpar;
+ copins *copl, *cops;
+ u_long p;
+
+ currentcop = 1 - currentcop;
+
+ copl = copdisplay.list[currentcop][1];
+
+ (copl++)->l = CWAIT(0, 10);
+ (copl++)->l = CMOVE(par->bplcon0, bplcon0);
+ (copl++)->l = CMOVE(0, sprpt[0]);
+ (copl++)->l = CMOVE2(0, sprpt[0]);
+
+ if (par->bplcon0 & BPC0_LACE) {
+ cops = copdisplay.list[currentcop][0];
+
+ (cops++)->l = CWAIT(0, 10);
+ (cops++)->l = CMOVE(par->bplcon0, bplcon0);
+ (cops++)->l = CMOVE(0, sprpt[0]);
+ (cops++)->l = CMOVE2(0, sprpt[0]);
+
+ (copl++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v+1), diwstrt);
+ (copl++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v+1), diwstop);
+ (cops++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v), diwstrt);
+ (cops++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v), diwstop);
+ if (!IS_OCS) {
+ (copl++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v+1,
+ par->diwstop_h, par->diwstop_v+1), diwhigh);
+ (cops++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v,
+ par->diwstop_h, par->diwstop_v), diwhigh);
+#if 0
+ if (par->beamcon0 & BMC0_VARBEAMEN) {
+ (copl++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal);
+ (copl++)->l = CMOVE(vbstrt2hw(par->vbstrt+1), vbstrt);
+ (copl++)->l = CMOVE(vbstop2hw(par->vbstop+1), vbstop);
+ (cops++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal);
+ (cops++)->l = CMOVE(vbstrt2hw(par->vbstrt), vbstrt);
+ (cops++)->l = CMOVE(vbstop2hw(par->vbstop), vbstop);
+ }
+#endif
+ }
+ p = ZTWO_PADDR(copdisplay.list[currentcop][0]);
+ (copl++)->l = CMOVE(highw(p), cop2lc);
+ (copl++)->l = CMOVE2(loww(p), cop2lc);
+ p = ZTWO_PADDR(copdisplay.list[currentcop][1]);
+ (cops++)->l = CMOVE(highw(p), cop2lc);
+ (cops++)->l = CMOVE2(loww(p), cop2lc);
+ copdisplay.rebuild[0] = cops;
+ } else {
+ (copl++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v), diwstrt);
+ (copl++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v), diwstop);
+ if (!IS_OCS) {
+ (copl++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v,
+ par->diwstop_h, par->diwstop_v), diwhigh);
+#if 0
+ if (par->beamcon0 & BMC0_VARBEAMEN) {
+ (copl++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal);
+ (copl++)->l = CMOVE(vbstrt2hw(par->vbstrt), vbstrt);
+ (copl++)->l = CMOVE(vbstop2hw(par->vbstop), vbstop);
+ }
+#endif
+ }
+ }
+ copdisplay.rebuild[1] = copl;
+
+ ami_update_par();
+ ami_rebuild_copper();
+}
+
+ /*
+ * Rebuild the Copper List
+ *
+ * We only change the things that are not static
+ */
+
+static void ami_rebuild_copper(void)
+{
+ struct amiga_fb_par *par = &currentpar;
+ copins *copl, *cops;
+ u_short line, h_end1, h_end2;
+ short i;
+ u_long p;
+
+ if (IS_AGA && maxfmode + par->clk_shift == 0)
+ h_end1 = par->diwstrt_h-64;
+ else
+ h_end1 = par->htotal-32;
+ h_end2 = par->ddfstop+64;
+
+ ami_set_sprite();
+
+ copl = copdisplay.rebuild[1];
+ p = par->bplpt0;
+ if (par->vmode & FB_VMODE_YWRAP) {
+ if ((par->vyres-par->yoffset) != 1 || !mod2(par->diwstrt_v)) {
+ if (par->yoffset > par->vyres-par->yres) {
+ for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
+ (copl++)->l = CMOVE(highw(p), bplpt[i]);
+ (copl++)->l = CMOVE2(loww(p), bplpt[i]);
+ }
+ line = par->diwstrt_v + ((par->vyres-par->yoffset)<<par->line_shift) - 1;
+ while (line >= 512) {
+ (copl++)->l = CWAIT(h_end1, 510);
+ line -= 512;
+ }
+ if (line >= 510 && IS_AGA && maxfmode + par->clk_shift == 0)
+ (copl++)->l = CWAIT(h_end1, line);
+ else
+ (copl++)->l = CWAIT(h_end2, line);
+ p = par->bplpt0wrap;
+ }
+ } else p = par->bplpt0wrap;
+ }
+ for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
+ (copl++)->l = CMOVE(highw(p), bplpt[i]);
+ (copl++)->l = CMOVE2(loww(p), bplpt[i]);
+ }
+ copl->l = CEND;
+
+ if (par->bplcon0 & BPC0_LACE) {
+ cops = copdisplay.rebuild[0];
+ p = par->bplpt0;
+ if (mod2(par->diwstrt_v))
+ p -= par->next_line;
+ else
+ p += par->next_line;
+ if (par->vmode & FB_VMODE_YWRAP) {
+ if ((par->vyres-par->yoffset) != 1 || mod2(par->diwstrt_v)) {
+ if (par->yoffset > par->vyres-par->yres+1) {
+ for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
+ (cops++)->l = CMOVE(highw(p), bplpt[i]);
+ (cops++)->l = CMOVE2(loww(p), bplpt[i]);
+ }
+ line = par->diwstrt_v + ((par->vyres-par->yoffset)<<par->line_shift) - 2;
+ while (line >= 512) {
+ (cops++)->l = CWAIT(h_end1, 510);
+ line -= 512;
+ }
+ if (line > 510 && IS_AGA && maxfmode + par->clk_shift == 0)
+ (cops++)->l = CWAIT(h_end1, line);
+ else
+ (cops++)->l = CWAIT(h_end2, line);
+ p = par->bplpt0wrap;
+ if (mod2(par->diwstrt_v+par->vyres-par->yoffset))
+ p -= par->next_line;
+ else
+ p += par->next_line;
+ }
+ } else p = par->bplpt0wrap - par->next_line;
+ }
+ for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
+ (cops++)->l = CMOVE(highw(p), bplpt[i]);
+ (cops++)->l = CMOVE2(loww(p), bplpt[i]);
+ }
+ cops->l = CEND;
+ }
+}
+
+
+#ifdef MODULE
+int init_module(void)
+{
+ return(amiga_fb_init(NULL));
+}
+
+void cleanup_module(void)
+{
+ /* Not reached because the usecount will never
+ be decremented to zero */
+ unregister_framebuffer(&fb_info);
+ /* TODO: clean up ... */
+}
+#endif /* MODULE */
diff --git a/drivers/video/atafb.c b/drivers/video/atafb.c
new file mode 100644
index 000000000..ada071f2b
--- /dev/null
+++ b/drivers/video/atafb.c
@@ -0,0 +1,3152 @@
+/*
+ * linux/drivers/video/atafb.c -- Atari builtin chipset frame buffer device
+ *
+ * Copyright (C) 1994 Martin Schaller & Roman Hodek
+ *
+ * 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.
+ *
+ * History:
+ * - 03 Jan 95: Original version by Martin Schaller: The TT driver and
+ * all the device independent stuff
+ * - 09 Jan 95: Roman: I've added the hardware abstraction (hw_switch)
+ * and wrote the Falcon, ST(E), and External drivers
+ * based on the original TT driver.
+ * - 07 May 95: Martin: Added colormap operations for the external driver
+ * - 21 May 95: Martin: Added support for overscan
+ * Andreas: some bug fixes for this
+ * - Jul 95: Guenther Kelleter <guenther@pool.informatik.rwth-aachen.de>:
+ * Programmable Falcon video modes
+ * (thanks to Christian Cartus for documentation
+ * of VIDEL registers).
+ * - 27 Dec 95: Guenther: Implemented user definable video modes "user[0-7]"
+ * on minor 24...31. "user0" may be set on commandline by
+ * "R<x>;<y>;<depth>". (Makes sense only on Falcon)
+ * Video mode switch on Falcon now done at next VBL interrupt
+ * to avoid the annoying right shift of the screen.
+ * - 23 Sep 97: Juergen: added xres_virtual for cards like ProMST
+ * The external-part is legacy, therefore hardware-specific
+ * functions like panning/hardwarescrolling/blanking isn't
+ * supported.
+ * - 29 Sep 97: Juergen: added Romans suggestion for pan_display
+ * (var->xoffset was changed even if no set_screen_base avail.)
+ * - 05 Oct 97: Juergen: extfb (PACKED_PIXEL) is FB_PSEUDOCOLOR 'cause
+ * we know how to set the colors
+ * ext_*palette: read from ext_colors (former MV300_colors)
+ * write to ext_colors and RAMDAC
+ *
+ * To do:
+ * - For the Falcon it is not possible to set random video modes on
+ * SM124 and SC/TV, only the bootup resolution is supported.
+ *
+ */
+
+#define ATAFB_TT
+#define ATAFB_STE
+#define ATAFB_EXT
+#define ATAFB_FALCON
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include <asm/setup.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+
+#include <linux/fb.h>
+#include <asm/atarikb.h>
+
+#define SWITCH_ACIA 0x01 /* modes for switch on OverScan */
+#define SWITCH_SND6 0x40
+#define SWITCH_SND7 0x80
+#define SWITCH_NONE 0x00
+
+
+#define arraysize(x) (sizeof(x)/sizeof(*(x)))
+
+#define up(x, r) (((x) + (r) - 1) & ~((r)-1))
+
+
+static int default_par=0; /* default resolution (0=none) */
+
+static unsigned long default_mem_req=0;
+
+static int hwscroll=-1;
+
+static int use_hwscroll = 1;
+
+static int sttt_xres=640,st_yres=400,tt_yres=480;
+static int sttt_xres_virtual=640,sttt_yres_virtual=400;
+static int ovsc_offset=0, ovsc_addlen=0;
+int ovsc_switchmode=0;
+
+static struct atari_fb_par {
+ unsigned long screen_base;
+ int yres_virtual;
+ union {
+ struct {
+ int mode;
+ int sync;
+ } tt, st;
+ struct falcon_hw {
+ /* Here are fields for storing a video mode, as direct
+ * parameters for the hardware.
+ */
+ short sync;
+ short line_width;
+ short line_offset;
+ short st_shift;
+ short f_shift;
+ short vid_control;
+ short vid_mode;
+ short xoffset;
+ short hht, hbb, hbe, hdb, hde, hss;
+ short vft, vbb, vbe, vdb, vde, vss;
+ /* auxiliary information */
+ short mono;
+ short ste_mode;
+ short bpp;
+ } falcon;
+ /* Nothing needed for external mode */
+ } hw;
+} current_par;
+
+/* Don't calculate an own resolution, and thus don't change the one found when
+ * booting (currently used for the Falcon to keep settings for internal video
+ * hardware extensions (e.g. ScreenBlaster) */
+static int DontCalcRes = 0;
+
+#define HHT hw.falcon.hht
+#define HBB hw.falcon.hbb
+#define HBE hw.falcon.hbe
+#define HDB hw.falcon.hdb
+#define HDE hw.falcon.hde
+#define HSS hw.falcon.hss
+#define VFT hw.falcon.vft
+#define VBB hw.falcon.vbb
+#define VBE hw.falcon.vbe
+#define VDB hw.falcon.vdb
+#define VDE hw.falcon.vde
+#define VSS hw.falcon.vss
+#define VCO_CLOCK25 0x04
+#define VCO_CSYPOS 0x10
+#define VCO_VSYPOS 0x20
+#define VCO_HSYPOS 0x40
+#define VCO_SHORTOFFS 0x100
+#define VMO_DOUBLE 0x01
+#define VMO_INTER 0x02
+#define VMO_PREMASK 0x0c
+
+static struct fb_info fb_info;
+
+static unsigned long screen_base; /* base address of screen */
+static unsigned long real_screen_base; /* (only for Overscan) */
+
+static int screen_len;
+
+static int current_par_valid=0;
+
+static int currcon=0;
+
+static int mono_moni=0;
+
+static struct display disp;
+
+
+#ifdef ATAFB_EXT
+/* external video handling */
+
+static unsigned external_xres;
+static unsigned external_xres_virtual;
+static unsigned external_yres;
+/* not needed - atafb will never support panning/hardwarescroll with external
+ * static unsigned external_yres_virtual;
+*/
+
+static unsigned external_depth;
+static int external_pmode;
+static unsigned long external_addr = 0;
+static unsigned long external_len;
+static unsigned long external_vgaiobase = 0;
+static unsigned int external_bitspercol = 6;
+
+/*
+JOE <joe@amber.dinoco.de>:
+added card type for external driver, is only needed for
+colormap handling.
+*/
+
+enum cardtype { IS_VGA, IS_MV300 };
+static enum cardtype external_card_type = IS_VGA;
+
+/*
+The MV300 mixes the color registers. So we need an array of munged
+indices in order to acces the correct reg.
+*/
+static int MV300_reg_1bit[2]={0,1};
+static int MV300_reg_4bit[16]={
+0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
+static int MV300_reg_8bit[256]={
+0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240,
+8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248,
+4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244,
+12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252,
+2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242,
+10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250,
+6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246,
+14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254,
+1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241,
+9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249,
+5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245,
+13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253,
+3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243,
+11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251,
+7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247,
+15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255 };
+
+static int *MV300_reg = MV300_reg_8bit;
+
+/*
+And on the MV300 it's difficult to read out the hardware palette. So we
+just keep track of the set colors in our own array here, and use that!
+*/
+
+static struct { unsigned char red,green,blue,pad; } ext_color[256];
+#endif /* ATAFB_EXT */
+
+
+static int inverse=0;
+
+extern int fontheight_8x8;
+extern int fontwidth_8x8;
+extern unsigned char fontdata_8x8[];
+
+extern int fontheight_8x16;
+extern int fontwidth_8x16;
+extern unsigned char fontdata_8x16[];
+
+/* import first 16 colors from fbcon.c */
+extern unsigned short packed16_cmap[16];
+
+
+/* ++roman: This structure abstracts from the underlying hardware (ST(e),
+ * TT, or Falcon.
+ *
+ * int (*detect)( void )
+ * This function should detect the current video mode settings and
+ * store them in atari_fb_predefined[0] for later reference by the
+ * user. Return the index+1 of an equivalent predefined mode or 0
+ * if there is no such.
+ *
+ * int (*encode_fix)( struct fb_fix_screeninfo *fix,
+ * struct atari_fb_par *par )
+ * This function should fill in the 'fix' structure based on the
+ * values in the 'par' structure.
+ *
+ * int (*decode_var)( struct fb_var_screeninfo *var,
+ * struct atari_fb_par *par )
+ * Get the video params out of 'var'. If a value doesn't fit, round
+ * it up, if it's too big, return EINVAL.
+ * Round up in the following order: bits_per_pixel, xres, yres,
+ * xres_virtual, yres_virtual, xoffset, yoffset, grayscale, bitfields,
+ * horizontal timing, vertical timing.
+ *
+ * int (*encode_var)( struct fb_var_screeninfo *var,
+ * struct atari_fb_par *par );
+ * Fill the 'var' structure based on the values in 'par' and maybe
+ * other values read out of the hardware.
+ *
+ * void (*get_par)( struct atari_fb_par *par )
+ * Fill the hardware's 'par' structure.
+ *
+ * void (*set_par)( struct atari_fb_par *par )
+ * Set the hardware according to 'par'.
+ *
+ * int (*setcolreg)( unsigned regno, unsigned red,
+ * unsigned green, unsigned blue,
+ * unsigned transp )
+ * Set a single color register. The values supplied are already
+ * rounded down to the hardware's capabilities (according to the
+ * entries in the var structure). Return != 0 for invalid regno.
+ *
+ * int (*getcolreg)( unsigned regno, unsigned *red,
+ * unsigned *green, unsigned *blue,
+ * unsigned *transp )
+ * Read a single color register and split it into
+ * colors/transparent. Return != 0 for invalid regno.
+ *
+ * void (*set_screen_base)( unsigned long s_base )
+ * Set the base address of the displayed frame buffer. Only called
+ * if yres_virtual > yres or xres_virtual > xres.
+ *
+ * int (*blank)( int blank_mode )
+ * Blank the screen if blank_mode!=0, else unblank. If blank==NULL then
+ * the caller blanks by setting the CLUT to all black. Return 0 if blanking
+ * succeeded, !=0 if un-/blanking failed due to e.g. a video mode which
+ * doesn't support it. Implements VESA suspend and powerdown modes on
+ * hardware that supports disabling hsync/vsync:
+ * blank_mode==2: suspend vsync, 3:suspend hsync, 4: powerdown.
+ */
+
+static struct fb_hwswitch {
+ int (*detect)( void );
+ int (*encode_fix)( struct fb_fix_screeninfo *fix,
+ struct atari_fb_par *par );
+ int (*decode_var)( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par );
+ int (*encode_var)( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par );
+ void (*get_par)( struct atari_fb_par *par );
+ void (*set_par)( struct atari_fb_par *par );
+ int (*getcolreg)( unsigned regno, unsigned *red,
+ unsigned *green, unsigned *blue,
+ unsigned *transp );
+ int (*setcolreg)( unsigned regno, unsigned red,
+ unsigned green, unsigned blue,
+ unsigned transp );
+ void (*set_screen_base)( unsigned long s_base );
+ int (*blank)( int blank_mode );
+ int (*pan_display)( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par);
+} *fbhw;
+
+static char *autodetect_names[] = {"autodetect", NULL};
+static char *stlow_names[] = {"stlow", NULL};
+static char *stmid_names[] = {"stmid", "default5", NULL};
+static char *sthigh_names[] = {"sthigh", "default4", NULL};
+static char *ttlow_names[] = {"ttlow", NULL};
+static char *ttmid_names[]= {"ttmid", "default1", NULL};
+static char *tthigh_names[]= {"tthigh", "default2", NULL};
+static char *vga2_names[] = {"vga2", NULL};
+static char *vga4_names[] = {"vga4", NULL};
+static char *vga16_names[] = {"vga16", "default3", NULL};
+static char *vga256_names[] = {"vga256", NULL};
+static char *falh2_names[] = {"falh2", NULL};
+static char *falh16_names[] = {"falh16", NULL};
+static char *user0_names[] = {"user0", NULL};
+static char *user1_names[] = {"user1", NULL};
+static char *user2_names[] = {"user2", NULL};
+static char *user3_names[] = {"user3", NULL};
+static char *user4_names[] = {"user4", NULL};
+static char *user5_names[] = {"user5", NULL};
+static char *user6_names[] = {"user6", NULL};
+static char *user7_names[] = {"user7", NULL};
+static char *dummy_names[] = {"dummy", NULL};
+
+static char **fb_var_names[] = {
+ /* Writing the name arrays directly in this array (via "(char *[]){...}")
+ * crashes gcc 2.5.8 (sigsegv) if the inner array
+ * contains more than two items. I've also seen that all elements
+ * were identical to the last (my cross-gcc) :-(*/
+ autodetect_names,
+ stlow_names,
+ stmid_names,
+ sthigh_names,
+ ttlow_names,
+ ttmid_names,
+ tthigh_names,
+ vga2_names,
+ vga4_names,
+ vga16_names,
+ vga256_names,
+ falh2_names,
+ falh16_names,
+ dummy_names, dummy_names, dummy_names, dummy_names,
+ dummy_names, dummy_names, dummy_names, dummy_names,
+ dummy_names, dummy_names,
+ user0_names,
+ user1_names,
+ user2_names,
+ user3_names,
+ user4_names,
+ user5_names,
+ user6_names,
+ user7_names,
+ NULL
+ /* ,NULL */ /* this causes a sigsegv on my gcc-2.5.8 */
+};
+
+static struct fb_var_screeninfo atari_fb_predefined[] = {
+ /*
+ * yres_virtual==0 means use hw-scrolling if possible, else yres
+ */
+ { /* autodetect */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* xres-grayscale */
+ {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, /* red green blue tran*/
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* st low */
+ 320, 200, 320, 0, 0, 0, 4, 0,
+ {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* st mid */
+ 640, 200, 640, 0, 0, 0, 2, 0,
+ {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* st high */
+ 640, 400, 640, 0, 0, 0, 1, 0,
+ {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* tt low */
+ 320, 480, 320, 0, 0, 0, 8, 0,
+ {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* tt mid */
+ 640, 480, 640, 0, 0, 0, 4, 0,
+ {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* tt high */
+ 1280, 960, 1280, 0, 0, 0, 1, 0,
+ {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* vga2 */
+ 640, 480, 640, 0, 0, 0, 1, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* vga4 */
+ 640, 480, 640, 0, 0, 0, 2, 0,
+ {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* vga16 */
+ 640, 480, 640, 0, 0, 0, 4, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* vga256 */
+ 640, 480, 640, 0, 0, 0, 8, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* falh2 */
+ 896, 608, 896, 0, 0, 0, 1, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* falh16 */
+ 896, 608, 896, 0, 0, 0, 4, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* Minor 14..23 free for more standard video modes */
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ { 0, },
+ /* Minor 24..31 reserved for user defined video modes */
+ { /* user0, initialized to Rx;y;d from commandline, if supplied */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* user1 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* user2 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* user3 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* user4 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* user5 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* user6 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { /* user7 */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+static int num_atari_fb_predefined=arraysize(atari_fb_predefined);
+
+
+static int
+get_video_mode(char *vname)
+{
+ char ***name_list;
+ char **name;
+ int i;
+ name_list=fb_var_names;
+ for (i = 0 ; i < num_atari_fb_predefined ; i++) {
+ name=*(name_list++);
+ if (! name || ! *name)
+ break;
+ while (*name) {
+ if (! strcmp(vname, *name))
+ return i+1;
+ name++;
+ }
+ }
+ return 0;
+}
+
+
+
+/* ------------------- TT specific functions ---------------------- */
+
+#ifdef ATAFB_TT
+
+static int tt_encode_fix( struct fb_fix_screeninfo *fix,
+ struct atari_fb_par *par )
+
+{
+ int mode;
+
+ strcpy(fix->id,"Atari Builtin");
+ fix->smem_start = (char *)real_screen_base;
+ fix->smem_len = screen_len;
+ fix->type=FB_TYPE_INTERLEAVED_PLANES;
+ fix->type_aux=2;
+ fix->visual=FB_VISUAL_PSEUDOCOLOR;
+ mode = par->hw.tt.mode & TT_SHIFTER_MODEMASK;
+ if (mode == TT_SHIFTER_TTHIGH || mode == TT_SHIFTER_STHIGH) {
+ fix->type=FB_TYPE_PACKED_PIXELS;
+ fix->type_aux=0;
+ if (mode == TT_SHIFTER_TTHIGH)
+ fix->visual=FB_VISUAL_MONO01;
+ }
+ fix->xpanstep=0;
+ fix->ypanstep=1;
+ fix->ywrapstep=0;
+ fix->line_length = 0;
+ return 0;
+}
+
+
+static int tt_decode_var( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+ int xres=var->xres;
+ int yres=var->yres;
+ int bpp=var->bits_per_pixel;
+ int linelen;
+ int yres_virtual = var->yres_virtual;
+
+ if (mono_moni) {
+ if (bpp > 1 || xres > sttt_xres*2 || yres >tt_yres*2)
+ return -EINVAL;
+ par->hw.tt.mode=TT_SHIFTER_TTHIGH;
+ xres=sttt_xres*2;
+ yres=tt_yres*2;
+ bpp=1;
+ } else {
+ if (bpp > 8 || xres > sttt_xres || yres > tt_yres)
+ return -EINVAL;
+ if (bpp > 4) {
+ if (xres > sttt_xres/2 || yres > tt_yres)
+ return -EINVAL;
+ par->hw.tt.mode=TT_SHIFTER_TTLOW;
+ xres=sttt_xres/2;
+ yres=tt_yres;
+ bpp=8;
+ }
+ else if (bpp > 2) {
+ if (xres > sttt_xres || yres > tt_yres)
+ return -EINVAL;
+ if (xres > sttt_xres/2 || yres > st_yres/2) {
+ par->hw.tt.mode=TT_SHIFTER_TTMID;
+ xres=sttt_xres;
+ yres=tt_yres;
+ bpp=4;
+ }
+ else {
+ par->hw.tt.mode=TT_SHIFTER_STLOW;
+ xres=sttt_xres/2;
+ yres=st_yres/2;
+ bpp=4;
+ }
+ }
+ else if (bpp > 1) {
+ if (xres > sttt_xres || yres > st_yres/2)
+ return -EINVAL;
+ par->hw.tt.mode=TT_SHIFTER_STMID;
+ xres=sttt_xres;
+ yres=st_yres/2;
+ bpp=2;
+ }
+ else if (var->xres > sttt_xres || var->yres > st_yres) {
+ return -EINVAL;
+ }
+ else {
+ par->hw.tt.mode=TT_SHIFTER_STHIGH;
+ xres=sttt_xres;
+ yres=st_yres;
+ bpp=1;
+ }
+ }
+ if (yres_virtual <= 0)
+ yres_virtual = 0;
+ else if (yres_virtual < yres)
+ yres_virtual = yres;
+ if (var->sync & FB_SYNC_EXT)
+ par->hw.tt.sync=0;
+ else
+ par->hw.tt.sync=1;
+ linelen=xres*bpp/8;
+ if (yres_virtual * linelen > screen_len && screen_len)
+ return -EINVAL;
+ if (yres * linelen > screen_len && screen_len)
+ return -EINVAL;
+ if (var->yoffset + yres > yres_virtual && yres_virtual)
+ return -EINVAL;
+ par->yres_virtual = yres_virtual;
+ par->screen_base = screen_base + var->yoffset * linelen;
+ return 0;
+}
+
+static int tt_encode_var( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+ int linelen, i;
+ var->red.offset=0;
+ var->red.length=4;
+ var->red.msb_right=0;
+ var->grayscale=0;
+
+ var->pixclock=31041;
+ var->left_margin=120; /* these may be incorrect */
+ var->right_margin=100;
+ var->upper_margin=8;
+ var->lower_margin=16;
+ var->hsync_len=140;
+ var->vsync_len=30;
+
+ var->height=-1;
+ var->width=-1;
+
+ if (par->hw.tt.sync & 1)
+ var->sync=0;
+ else
+ var->sync=FB_SYNC_EXT;
+
+ switch (par->hw.tt.mode & TT_SHIFTER_MODEMASK) {
+ case TT_SHIFTER_STLOW:
+ var->xres=sttt_xres/2;
+ var->xres_virtual=sttt_xres_virtual/2;
+ var->yres=st_yres/2;
+ var->bits_per_pixel=4;
+ break;
+ case TT_SHIFTER_STMID:
+ var->xres=sttt_xres;
+ var->xres_virtual=sttt_xres_virtual;
+ var->yres=st_yres/2;
+ var->bits_per_pixel=2;
+ break;
+ case TT_SHIFTER_STHIGH:
+ var->xres=sttt_xres;
+ var->xres_virtual=sttt_xres_virtual;
+ var->yres=st_yres;
+ var->bits_per_pixel=1;
+ break;
+ case TT_SHIFTER_TTLOW:
+ var->xres=sttt_xres/2;
+ var->xres_virtual=sttt_xres_virtual/2;
+ var->yres=tt_yres;
+ var->bits_per_pixel=8;
+ break;
+ case TT_SHIFTER_TTMID:
+ var->xres=sttt_xres;
+ var->xres_virtual=sttt_xres_virtual;
+ var->yres=tt_yres;
+ var->bits_per_pixel=4;
+ break;
+ case TT_SHIFTER_TTHIGH:
+ var->red.length=0;
+ var->xres=sttt_xres*2;
+ var->xres_virtual=sttt_xres_virtual*2;
+ var->yres=tt_yres*2;
+ var->bits_per_pixel=1;
+ break;
+ }
+ var->blue=var->green=var->red;
+ var->transp.offset=0;
+ var->transp.length=0;
+ var->transp.msb_right=0;
+ linelen=var->xres_virtual * var->bits_per_pixel / 8;
+ if (! use_hwscroll)
+ var->yres_virtual=var->yres;
+ else if (screen_len) {
+ if (par->yres_virtual)
+ var->yres_virtual = par->yres_virtual;
+ else
+ /* yres_virtual==0 means use maximum */
+ var->yres_virtual = screen_len / linelen;
+ } else {
+ if (hwscroll < 0)
+ var->yres_virtual = 2 * var->yres;
+ else
+ var->yres_virtual=var->yres+hwscroll * 16;
+ }
+ var->xoffset=0;
+ if (screen_base)
+ var->yoffset=(par->screen_base - screen_base)/linelen;
+ else
+ var->yoffset=0;
+ var->nonstd=0;
+ var->activate=0;
+ var->vmode=FB_VMODE_NONINTERLACED;
+ for (i=0; i<arraysize(var->reserved); i++)
+ var->reserved[i]=0;
+ return 0;
+}
+
+
+static void tt_get_par( struct atari_fb_par *par )
+{
+ unsigned long addr;
+ par->hw.tt.mode=shifter_tt.tt_shiftmode;
+ par->hw.tt.sync=shifter.syncmode;
+ addr = ((shifter.bas_hi & 0xff) << 16) |
+ ((shifter.bas_md & 0xff) << 8) |
+ ((shifter.bas_lo & 0xff));
+ par->screen_base = PTOV(addr);
+}
+
+static void tt_set_par( struct atari_fb_par *par )
+{
+ shifter_tt.tt_shiftmode=par->hw.tt.mode;
+ shifter.syncmode=par->hw.tt.sync;
+ /* only set screen_base if really necessary */
+ if (current_par.screen_base != par->screen_base)
+ fbhw->set_screen_base(par->screen_base);
+}
+
+
+static int tt_getcolreg( unsigned regno, unsigned *red,
+ unsigned *green, unsigned *blue,
+ unsigned *transp )
+{
+ if ((shifter_tt.tt_shiftmode & TT_SHIFTER_MODEMASK) == TT_SHIFTER_STHIGH)
+ regno += 254;
+ if (regno > 255)
+ return 1;
+ *blue = tt_palette[regno];
+ *green = (*blue >> 4) & 0xf;
+ *red = (*blue >> 8) & 0xf;
+ *blue &= 0xf;
+ *transp = 0;
+ return 0;
+}
+
+
+static int tt_setcolreg( unsigned regno, unsigned red,
+ unsigned green, unsigned blue,
+ unsigned transp )
+{
+ if ((shifter_tt.tt_shiftmode & TT_SHIFTER_MODEMASK) == TT_SHIFTER_STHIGH)
+ regno += 254;
+ if (regno > 255)
+ return 1;
+ tt_palette[regno] = (red << 8) | (green << 4) | blue;
+ if ((shifter_tt.tt_shiftmode & TT_SHIFTER_MODEMASK) ==
+ TT_SHIFTER_STHIGH && regno == 254)
+ tt_palette[0] = 0;
+ return 0;
+}
+
+
+static int tt_detect( void )
+
+{ struct atari_fb_par par;
+
+ /* Determine the connected monitor: The DMA sound must be
+ * disabled before reading the MFP GPIP, because the Sound
+ * Done Signal and the Monochrome Detect are XORed together!
+ *
+ * Even on a TT, we should look if there is a DMA sound. It was
+ * announced that the Eagle is TT compatible, but only the PCM is
+ * missing...
+ */
+ if (ATARIHW_PRESENT(PCM_8BIT)) {
+ tt_dmasnd.ctrl = DMASND_CTRL_OFF;
+ udelay(20); /* wait a while for things to settle down */
+ }
+ mono_moni = (mfp.par_dt_reg & 0x80) == 0;
+
+ tt_get_par(&par);
+ tt_encode_var(&atari_fb_predefined[0], &par);
+
+ return 1;
+}
+
+#endif /* ATAFB_TT */
+
+/* ------------------- Falcon specific functions ---------------------- */
+
+#ifdef ATAFB_FALCON
+
+static int mon_type; /* Falcon connected monitor */
+static int f030_bus_width; /* Falcon ram bus width (for vid_control) */
+#define F_MON_SM 0
+#define F_MON_SC 1
+#define F_MON_VGA 2
+#define F_MON_TV 3
+
+/* Multisync monitor capabilities */
+/* Atari-TOS defaults if no boot option present */
+static long vfmin=58, vfmax=62, hfmin=31000, hfmax=32000;
+
+static struct pixel_clock {
+ unsigned long f; /* f/[Hz] */
+ unsigned long t; /* t/[ps] (=1/f) */
+ int right, hsync, left; /* standard timing in clock cycles, not pixel */
+ /* hsync initialized in falcon_detect() */
+ int sync_mask; /* or-mask for hw.falcon.sync to set this clock */
+ int control_mask; /* ditto, for hw.falcon.vid_control */
+}
+f25 = {25175000, 39721, 18, 0, 42, 0x0, VCO_CLOCK25},
+f32 = {32000000, 31250, 18, 0, 42, 0x0, 0},
+fext = { 0, 0, 18, 0, 42, 0x1, 0};
+
+/* VIDEL-prescale values [mon_type][pixel_length from VCO] */
+static int vdl_prescale[4][3] = {{4,2,1}, {4,2,1}, {4,2,2}, {4,2,1}};
+
+/* Default hsync timing [mon_type] in picoseconds */
+static long h_syncs[4] = {3000000, 4875000, 4000000, 4875000};
+
+
+static inline int hxx_prescale(struct falcon_hw *hw)
+{
+ return hw->ste_mode ? 16 :
+ vdl_prescale[mon_type][hw->vid_mode >> 2 & 0x3];
+}
+
+static int falcon_encode_fix( struct fb_fix_screeninfo *fix,
+ struct atari_fb_par *par )
+{
+ strcpy(fix->id, "Atari Builtin");
+ fix->smem_start = (char *)real_screen_base;
+ fix->smem_len = screen_len;
+ fix->type = FB_TYPE_INTERLEAVED_PLANES;
+ fix->type_aux = 2;
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+ fix->ywrapstep = 0;
+ if (par->hw.falcon.mono) {
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->type_aux = 0;
+ /* no smooth scrolling with longword aligned video mem */
+ fix->xpanstep = 32;
+ }
+ else if (par->hw.falcon.f_shift & 0x100) {
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->type_aux = 0;
+ /* Is this ok or should it be DIRECTCOLOR? */
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ fix->xpanstep = 2;
+ }
+ fix->line_length = 0;
+ return 0;
+}
+
+
+static int falcon_decode_var( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+ int bpp = var->bits_per_pixel;
+ int xres = var->xres;
+ int yres = var->yres;
+ int xres_virtual = var->xres_virtual;
+ int yres_virtual = var->yres_virtual;
+ int left_margin, right_margin, hsync_len;
+ int upper_margin, lower_margin, vsync_len;
+ int linelen;
+ int interlace = 0, doubleline = 0;
+ struct pixel_clock *pclock;
+ int plen; /* width of pixel in clock cycles */
+ int xstretch;
+ int prescale;
+ int longoffset = 0;
+ int hfreq, vfreq;
+
+/*
+ Get the video params out of 'var'. If a value doesn't fit, round
+ it up, if it's too big, return EINVAL.
+ Round up in the following order: bits_per_pixel, xres, yres,
+ xres_virtual, yres_virtual, xoffset, yoffset, grayscale, bitfields,
+ horizontal timing, vertical timing.
+
+ There is a maximum of screen resolution determined by pixelclock
+ and minimum frame rate -- (X+hmarg.)*(Y+vmarg.)*vfmin <= pixelclock.
+ In interlace mode this is " * " *vfmin <= pixelclock.
+ Additional constraints: hfreq.
+ Frequency range for multisync monitors is given via command line.
+ For TV and SM124 both frequencies are fixed.
+
+ X % 16 == 0 to fit 8x?? font (except 1 bitplane modes must use X%32==0)
+ Y % 16 == 0 to fit 8x16 font
+ Y % 8 == 0 if Y<400
+
+ Currently interlace and doubleline mode in var are ignored.
+ On SM124 and TV only the standard resolutions can be used.
+*/
+
+ /* Reject uninitialized mode */
+ if (!xres || !yres || !bpp)
+ return -EINVAL;
+
+ if (mon_type == F_MON_SM && bpp != 1) {
+ return -EINVAL;
+ }
+ else if (bpp <= 1) {
+ bpp = 1;
+ par->hw.falcon.f_shift = 0x400;
+ par->hw.falcon.st_shift = 0x200;
+ }
+ else if (bpp <= 2) {
+ bpp = 2;
+ par->hw.falcon.f_shift = 0x000;
+ par->hw.falcon.st_shift = 0x100;
+ }
+ else if (bpp <= 4) {
+ bpp = 4;
+ par->hw.falcon.f_shift = 0x000;
+ par->hw.falcon.st_shift = 0x000;
+ }
+ else if (bpp <= 8) {
+ bpp = 8;
+ par->hw.falcon.f_shift = 0x010;
+ }
+ else if (bpp <= 16) {
+ bpp = 16; /* packed pixel mode */
+ par->hw.falcon.f_shift = 0x100; /* hicolor, no overlay */
+ }
+ else
+ return -EINVAL;
+ par->hw.falcon.bpp = bpp;
+
+ if (mon_type == F_MON_SM || DontCalcRes) {
+ /* Skip all calculations. VGA/TV/SC1224 only supported. */
+ struct fb_var_screeninfo *myvar = &atari_fb_predefined[0];
+
+ if (bpp > myvar->bits_per_pixel ||
+ var->xres > myvar->xres ||
+ var->yres > myvar->yres)
+ return -EINVAL;
+ fbhw->get_par(par); /* Current par will be new par */
+ goto set_screen_base; /* Don't forget this */
+ }
+
+ /* Only some fixed resolutions < 640x400 */
+ if (xres <= 320)
+ xres = 320;
+ else if (xres <= 640 && bpp != 16)
+ xres = 640;
+ if (yres <= 200)
+ yres = 200;
+ else if (yres <= 240)
+ yres = 240;
+ else if (yres <= 400)
+ yres = 400;
+
+ /* 2 planes must use STE compatibility mode */
+ par->hw.falcon.ste_mode = bpp==2;
+ par->hw.falcon.mono = bpp==1;
+
+ /* Total and visible scanline length must be a multiple of one longword,
+ * this and the console fontwidth yields the alignment for xres and
+ * xres_virtual.
+ * TODO: this way "odd" fontheights are not supported
+ *
+ * Special case in STE mode: blank and graphic positions don't align,
+ * avoid trash at right margin
+ */
+ if (par->hw.falcon.ste_mode)
+ xres = (xres + 63) & ~63;
+ else if (bpp == 1)
+ xres = (xres + 31) & ~31;
+ else
+ xres = (xres + 15) & ~15;
+ if (yres >= 400)
+ yres = (yres + 15) & ~15;
+ else
+ yres = (yres + 7) & ~7;
+
+ if (xres_virtual < xres)
+ xres_virtual = xres;
+ else if (bpp == 1)
+ xres_virtual = (xres_virtual + 31) & ~31;
+ else
+ xres_virtual = (xres_virtual + 15) & ~15;
+
+ if (yres_virtual <= 0)
+ yres_virtual = 0;
+ else if (yres_virtual < yres)
+ yres_virtual = yres;
+
+ /* backward bug-compatibility */
+ if (var->pixclock > 1)
+ var->pixclock -= 1;
+
+ par->hw.falcon.line_width = bpp * xres / 16;
+ par->hw.falcon.line_offset = bpp * (xres_virtual - xres) / 16;
+
+ /* single or double pixel width */
+ xstretch = (xres < 640) ? 2 : 1;
+
+#if 0 /* SM124 supports only 640x400, this is rejected above */
+ if (mon_type == F_MON_SM) {
+ if (xres != 640 && yres != 400)
+ return -EINVAL;
+ plen = 1;
+ pclock = &f32;
+ /* SM124-mode is special */
+ par->hw.falcon.ste_mode = 1;
+ par->hw.falcon.f_shift = 0x000;
+ par->hw.falcon.st_shift = 0x200;
+ left_margin = hsync_len = 128 / plen;
+ right_margin = 0;
+ /* TODO set all margins */
+ }
+ else
+#endif
+ if (mon_type == F_MON_SC || mon_type == F_MON_TV) {
+ plen = 2 * xstretch;
+ if (var->pixclock > f32.t * plen)
+ return -EINVAL;
+ pclock = &f32;
+ if (yres > 240)
+ interlace = 1;
+ if (var->pixclock == 0) {
+ /* set some minimal margins which center the screen */
+ left_margin = 32;
+ right_margin = 18;
+ hsync_len = pclock->hsync / plen;
+ upper_margin = 31;
+ lower_margin = 14;
+ vsync_len = interlace ? 3 : 4;
+ } else {
+ left_margin = var->left_margin;
+ right_margin = var->right_margin;
+ hsync_len = var->hsync_len;
+ upper_margin = var->upper_margin;
+ lower_margin = var->lower_margin;
+ vsync_len = var->vsync_len;
+ if (var->vmode & FB_VMODE_INTERLACED) {
+ upper_margin = (upper_margin + 1) / 2;
+ lower_margin = (lower_margin + 1) / 2;
+ vsync_len = (vsync_len + 1) / 2;
+ } else if (var->vmode & FB_VMODE_DOUBLE) {
+ upper_margin *= 2;
+ lower_margin *= 2;
+ vsync_len *= 2;
+ }
+ }
+ }
+ else
+ { /* F_MON_VGA */
+ if (bpp == 16)
+ xstretch = 2; /* Double pixel width only for hicolor */
+ /* Default values are used for vert./hor. timing if no pixelclock given. */
+ if (var->pixclock == 0) {
+ int linesize;
+
+ /* Choose master pixelclock depending on hor. timing */
+ plen = 1 * xstretch;
+ if ((plen * xres + f25.right+f25.hsync+f25.left) * hfmin < f25.f)
+ pclock = &f25;
+ else if ((plen * xres + f32.right+f32.hsync+f32.left) * hfmin < f32.f)
+ pclock = &f32;
+ else if ((plen * xres + fext.right+fext.hsync+fext.left) * hfmin < fext.f
+ && fext.f)
+ pclock = &fext;
+ else
+ return -EINVAL;
+
+ left_margin = pclock->left / plen;
+ right_margin = pclock->right / plen;
+ hsync_len = pclock->hsync / plen;
+ linesize = left_margin + xres + right_margin + hsync_len;
+ upper_margin = 31;
+ lower_margin = 11;
+ vsync_len = 3;
+ }
+ else {
+ /* Choose largest pixelclock <= wanted clock */
+ int i;
+ unsigned long pcl = ULONG_MAX;
+ pclock = 0;
+ for (i=1; i <= 4; i *= 2) {
+ if (f25.t*i >= var->pixclock && f25.t*i < pcl) {
+ pcl = f25.t * i;
+ pclock = &f25;
+ }
+ if (f32.t*i >= var->pixclock && f32.t*i < pcl) {
+ pcl = f32.t * i;
+ pclock = &f32;
+ }
+ if (fext.t && fext.t*i >= var->pixclock && fext.t*i < pcl) {
+ pcl = fext.t * i;
+ pclock = &fext;
+ }
+ }
+ if (!pclock)
+ return -EINVAL;
+ plen = pcl / pclock->t;
+
+ left_margin = var->left_margin;
+ right_margin = var->right_margin;
+ hsync_len = var->hsync_len;
+ upper_margin = var->upper_margin;
+ lower_margin = var->lower_margin;
+ vsync_len = var->vsync_len;
+ /* Internal unit is [single lines per (half-)frame] */
+ if (var->vmode & FB_VMODE_INTERLACED) {
+ /* # lines in half frame */
+ /* External unit is [lines per full frame] */
+ upper_margin = (upper_margin + 1) / 2;
+ lower_margin = (lower_margin + 1) / 2;
+ vsync_len = (vsync_len + 1) / 2;
+ }
+ else if (var->vmode & FB_VMODE_DOUBLE) {
+ /* External unit is [double lines per frame] */
+ upper_margin *= 2;
+ lower_margin *= 2;
+ vsync_len *= 2;
+ }
+ }
+ if (pclock == &fext)
+ longoffset = 1; /* VIDEL doesn't synchronize on short offset */
+ }
+ /* Is video bus bandwidth (32MB/s) too low for this resolution? */
+ /* this is definitely wrong if bus clock != 32MHz */
+ if (pclock->f / plen / 8 * bpp > 32000000L)
+ return -EINVAL;
+
+ if (vsync_len < 1)
+ vsync_len = 1;
+
+ /* include sync lengths in right/lower margin for all calculations */
+ right_margin += hsync_len;
+ lower_margin += vsync_len;
+
+ /* ! In all calculations of margins we use # of lines in half frame
+ * (which is a full frame in non-interlace mode), so we can switch
+ * between interlace and non-interlace without messing around
+ * with these.
+ */
+ again:
+ /* Set base_offset 128 and video bus width */
+ par->hw.falcon.vid_control = mon_type | f030_bus_width;
+ if (!longoffset)
+ par->hw.falcon.vid_control |= VCO_SHORTOFFS; /* base_offset 64 */
+ if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+ par->hw.falcon.vid_control |= VCO_HSYPOS;
+ if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+ par->hw.falcon.vid_control |= VCO_VSYPOS;
+ /* Pixelclock */
+ par->hw.falcon.vid_control |= pclock->control_mask;
+ /* External or internal clock */
+ par->hw.falcon.sync = pclock->sync_mask | 0x2;
+ /* Pixellength and prescale */
+ par->hw.falcon.vid_mode = (2/plen) << 2;
+ if (doubleline)
+ par->hw.falcon.vid_mode |= VMO_DOUBLE;
+ if (interlace)
+ par->hw.falcon.vid_mode |= VMO_INTER;
+
+ /*********************
+ Horizontal timing: unit = [master clock cycles]
+ unit of hxx-registers: [master clock cycles * prescale]
+ Hxx-registers are 9 bit wide
+
+ 1 line = ((hht + 2) * 2 * prescale) clock cycles
+
+ graphic output = hdb & 0x200 ?
+ ((hht+2)*2 - hdb + hde) * prescale - hdboff + hdeoff:
+ ( hht + 2 - hdb + hde) * prescale - hdboff + hdeoff
+ (this must be a multiple of plen*128/bpp, on VGA pixels
+ to the right may be cut off with a bigger right margin)
+
+ start of graphics relative to start of 1st halfline = hdb & 0x200 ?
+ (hdb - hht - 2) * prescale + hdboff :
+ hdb * prescale + hdboff
+
+ end of graphics relative to start of 1st halfline =
+ (hde + hht + 2) * prescale + hdeoff
+ *********************/
+ /* Calculate VIDEL registers */
+ {
+ int hdb_off, hde_off, base_off;
+ int gstart, gend1, gend2, align;
+
+ prescale = hxx_prescale(&par->hw.falcon);
+ base_off = par->hw.falcon.vid_control & VCO_SHORTOFFS ? 64 : 128;
+
+ /* Offsets depend on video mode */
+ /* Offsets are in clock cycles, divide by prescale to
+ * calculate hd[be]-registers
+ */
+ if (par->hw.falcon.f_shift & 0x100) {
+ align = 1;
+ hde_off = 0;
+ hdb_off = (base_off + 16 * plen) + prescale;
+ }
+ else {
+ align = 128 / bpp;
+ hde_off = ((128 / bpp + 2) * plen);
+ if (par->hw.falcon.ste_mode)
+ hdb_off = (64 + base_off + (128 / bpp + 2) * plen) + prescale;
+ else
+ hdb_off = (base_off + (128 / bpp + 18) * plen) + prescale;
+ }
+
+ gstart = (prescale/2 + plen * left_margin) / prescale;
+ /* gend1 is for hde (gend-gstart multiple of align), shifter's xres */
+ gend1 = gstart + ((xres + align-1) / align)*align * plen / prescale;
+ /* gend2 is for hbb, visible xres (rest to gend1 is cut off by hblank) */
+ gend2 = gstart + xres * plen / prescale;
+ par->HHT = plen * (left_margin + xres + right_margin) /
+ (2 * prescale) - 2;
+/* par->HHT = (gend2 + plen * right_margin / prescale) / 2 - 2;*/
+
+ par->HDB = gstart - hdb_off/prescale;
+ par->HBE = gstart;
+ if (par->HDB < 0) par->HDB += par->HHT + 2 + 0x200;
+ par->HDE = gend1 - par->HHT - 2 - hde_off/prescale;
+ par->HBB = gend2 - par->HHT - 2;
+#if 0
+ /* One more Videl constraint: data fetch of two lines must not overlap */
+ if (par->HDB & 0x200 && par->HDB & ~0x200 - par->HDE <= 5) {
+ /* if this happens increase margins, decrease hfreq. */
+ }
+#endif
+ if (hde_off % prescale)
+ par->HBB++; /* compensate for non matching hde and hbb */
+ par->HSS = par->HHT + 2 - plen * hsync_len / prescale;
+ if (par->HSS < par->HBB)
+ par->HSS = par->HBB;
+ }
+
+ /* check hor. frequency */
+ hfreq = pclock->f / ((par->HHT+2)*prescale*2);
+ if (hfreq > hfmax && mon_type!=F_MON_VGA) {
+ /* ++guenther: ^^^^^^^^^^^^^^^^^^^ can't remember why I did this */
+ /* Too high -> enlarge margin */
+ left_margin += 1;
+ right_margin += 1;
+ goto again;
+ }
+ if (hfreq > hfmax || hfreq < hfmin)
+ return -EINVAL;
+
+ /* Vxx-registers */
+ /* All Vxx must be odd in non-interlace, since frame starts in the middle
+ * of the first displayed line!
+ * One frame consists of VFT+1 half lines. VFT+1 must be even in
+ * non-interlace, odd in interlace mode for synchronisation.
+ * Vxx-registers are 11 bit wide
+ */
+ par->VBE = (upper_margin * 2 + 1); /* must begin on odd halfline */
+ par->VDB = par->VBE;
+ par->VDE = yres;
+ if (!interlace) par->VDE <<= 1;
+ if (doubleline) par->VDE <<= 1; /* VDE now half lines per (half-)frame */
+ par->VDE += par->VDB;
+ par->VBB = par->VDE;
+ par->VFT = par->VBB + (lower_margin * 2 - 1) - 1;
+ par->VSS = par->VFT+1 - (vsync_len * 2 - 1);
+ /* vbb,vss,vft must be even in interlace mode */
+ if (interlace) {
+ par->VBB++;
+ par->VSS++;
+ par->VFT++;
+ }
+
+ /* V-frequency check, hope I didn't create any loop here. */
+ /* Interlace and doubleline are mutually exclusive. */
+ vfreq = (hfreq * 2) / (par->VFT + 1);
+ if (vfreq > vfmax && !doubleline && !interlace) {
+ /* Too high -> try again with doubleline */
+ doubleline = 1;
+ goto again;
+ }
+ else if (vfreq < vfmin && !interlace && !doubleline) {
+ /* Too low -> try again with interlace */
+ interlace = 1;
+ goto again;
+ }
+ else if (vfreq < vfmin && doubleline) {
+ /* Doubleline too low -> clear doubleline and enlarge margins */
+ int lines;
+ doubleline = 0;
+ for (lines=0; (hfreq*2)/(par->VFT+1+4*lines-2*yres)>vfmax; lines++)
+ ;
+ upper_margin += lines;
+ lower_margin += lines;
+ goto again;
+ }
+ else if (vfreq > vfmax && doubleline) {
+ /* Doubleline too high -> enlarge margins */
+ int lines;
+ for (lines=0; (hfreq*2)/(par->VFT+1+4*lines)>vfmax; lines+=2)
+ ;
+ upper_margin += lines;
+ lower_margin += lines;
+ goto again;
+ }
+ else if (vfreq > vfmax && interlace) {
+ /* Interlace, too high -> enlarge margins */
+ int lines;
+ for (lines=0; (hfreq*2)/(par->VFT+1+4*lines)>vfmax; lines++)
+ ;
+ upper_margin += lines;
+ lower_margin += lines;
+ goto again;
+ }
+ else if (vfreq < vfmin || vfreq > vfmax)
+ return -EINVAL;
+
+ set_screen_base:
+ linelen = xres_virtual * bpp / 8;
+ if (yres_virtual * linelen > screen_len && screen_len)
+ return -EINVAL;
+ if (yres * linelen > screen_len && screen_len)
+ return -EINVAL;
+ if (var->yoffset + yres > yres_virtual && yres_virtual)
+ return -EINVAL;
+ par->yres_virtual = yres_virtual;
+ par->screen_base = screen_base + var->yoffset * linelen;
+ par->hw.falcon.xoffset = 0;
+
+ return 0;
+}
+
+static int falcon_encode_var( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+/* !!! only for VGA !!! */
+ int linelen, i;
+ int prescale, plen;
+ int hdb_off, hde_off, base_off;
+ struct falcon_hw *hw = &par->hw.falcon;
+
+ /* possible frequencies: 25.175 or 32MHz */
+ var->pixclock = hw->sync & 0x1 ? fext.t :
+ hw->vid_control & VCO_CLOCK25 ? f25.t : f32.t;
+
+ var->height=-1;
+ var->width=-1;
+
+ var->sync=0;
+ if (hw->vid_control & VCO_HSYPOS)
+ var->sync |= FB_SYNC_HOR_HIGH_ACT;
+ if (hw->vid_control & VCO_VSYPOS)
+ var->sync |= FB_SYNC_VERT_HIGH_ACT;
+
+ var->vmode = FB_VMODE_NONINTERLACED;
+ if (hw->vid_mode & VMO_INTER)
+ var->vmode |= FB_VMODE_INTERLACED;
+ if (hw->vid_mode & VMO_DOUBLE)
+ var->vmode |= FB_VMODE_DOUBLE;
+
+ /* visible y resolution:
+ * Graphics display starts at line VDB and ends at line
+ * VDE. If interlace mode off unit of VC-registers is
+ * half lines, else lines.
+ */
+ var->yres = hw->vde - hw->vdb;
+ if (!(var->vmode & FB_VMODE_INTERLACED))
+ var->yres >>= 1;
+ if (var->vmode & FB_VMODE_DOUBLE)
+ var->yres >>= 1;
+
+ /* to get bpp, we must examine f_shift and st_shift.
+ * f_shift is valid if any of bits no. 10, 8 or 4
+ * is set. Priority in f_shift is: 10 ">" 8 ">" 4, i.e.
+ * if bit 10 set then bit 8 and bit 4 don't care...
+ * If all these bits are 0 get display depth from st_shift
+ * (as for ST and STE)
+ */
+ if (hw->f_shift & 0x400) /* 2 colors */
+ var->bits_per_pixel = 1;
+ else if (hw->f_shift & 0x100) /* hicolor */
+ var->bits_per_pixel = 16;
+ else if (hw->f_shift & 0x010) /* 8 bitplanes */
+ var->bits_per_pixel = 8;
+ else if (hw->st_shift == 0)
+ var->bits_per_pixel = 4;
+ else if (hw->st_shift == 0x100)
+ var->bits_per_pixel = 2;
+ else /* if (hw->st_shift == 0x200) */
+ var->bits_per_pixel = 1;
+
+ var->xres = hw->line_width * 16 / var->bits_per_pixel;
+ var->xres_virtual = var->xres + hw->line_offset * 16 / var->bits_per_pixel;
+ if (hw->xoffset)
+ var->xres_virtual += 16;
+
+ if (var->bits_per_pixel == 16) {
+ var->red.offset=11;
+ var->red.length=5;
+ var->red.msb_right=0;
+ var->green.offset=5;
+ var->green.length=6;
+ var->green.msb_right=0;
+ var->blue.offset=0;
+ var->blue.length=5;
+ var->blue.msb_right=0;
+ }
+ else {
+ var->red.offset=0;
+ var->red.length = hw->ste_mode ? 4 : 6;
+ var->red.msb_right=0;
+ var->grayscale=0;
+ var->blue=var->green=var->red;
+ }
+ var->transp.offset=0;
+ var->transp.length=0;
+ var->transp.msb_right=0;
+
+ linelen = var->xres_virtual * var->bits_per_pixel / 8;
+ if (screen_len)
+ if (par->yres_virtual)
+ var->yres_virtual = par->yres_virtual;
+ else
+ /* yres_virtual==0 means use maximum */
+ var->yres_virtual = screen_len / linelen;
+ else {
+ if (hwscroll < 0)
+ var->yres_virtual = 2 * var->yres;
+ else
+ var->yres_virtual=var->yres+hwscroll * 16;
+ }
+ var->xoffset=0; /* TODO change this */
+
+ /* hdX-offsets */
+ prescale = hxx_prescale(hw);
+ plen = 4 >> (hw->vid_mode >> 2 & 0x3);
+ base_off = hw->vid_control & VCO_SHORTOFFS ? 64 : 128;
+ if (hw->f_shift & 0x100) {
+ hde_off = 0;
+ hdb_off = (base_off + 16 * plen) + prescale;
+ }
+ else {
+ hde_off = ((128 / var->bits_per_pixel + 2) * plen);
+ if (hw->ste_mode)
+ hdb_off = (64 + base_off + (128 / var->bits_per_pixel + 2) * plen)
+ + prescale;
+ else
+ hdb_off = (base_off + (128 / var->bits_per_pixel + 18) * plen)
+ + prescale;
+ }
+
+ /* Right margin includes hsync */
+ var->left_margin = hdb_off + prescale * ((hw->hdb & 0x1ff) -
+ (hw->hdb & 0x200 ? 2+hw->hht : 0));
+ if (hw->ste_mode || mon_type!=F_MON_VGA)
+ var->right_margin = prescale * (hw->hht + 2 - hw->hde) - hde_off;
+ else
+ /* can't use this in ste_mode, because hbb is +1 off */
+ var->right_margin = prescale * (hw->hht + 2 - hw->hbb);
+ var->hsync_len = prescale * (hw->hht + 2 - hw->hss);
+
+ /* Lower margin includes vsync */
+ var->upper_margin = hw->vdb / 2 ; /* round down to full lines */
+ var->lower_margin = (hw->vft+1 - hw->vde + 1) / 2; /* round up */
+ var->vsync_len = (hw->vft+1 - hw->vss + 1) / 2; /* round up */
+ if (var->vmode & FB_VMODE_INTERLACED) {
+ var->upper_margin *= 2;
+ var->lower_margin *= 2;
+ var->vsync_len *= 2;
+ }
+ else if (var->vmode & FB_VMODE_DOUBLE) {
+ var->upper_margin = (var->upper_margin + 1) / 2;
+ var->lower_margin = (var->lower_margin + 1) / 2;
+ var->vsync_len = (var->vsync_len + 1) / 2;
+ }
+
+ var->pixclock *= plen;
+ var->left_margin /= plen;
+ var->right_margin /= plen;
+ var->hsync_len /= plen;
+
+ var->right_margin -= var->hsync_len;
+ var->lower_margin -= var->vsync_len;
+
+ if (screen_base)
+ var->yoffset=(par->screen_base - screen_base)/linelen;
+ else
+ var->yoffset=0;
+ var->nonstd=0; /* what is this for? */
+ var->activate=0;
+ for (i=0; i<arraysize(var->reserved); i++)
+ var->reserved[i]=0;
+ return 0;
+}
+
+
+static int f_change_mode = 0;
+static struct falcon_hw f_new_mode;
+static int f_pan_display = 0;
+
+static void falcon_get_par( struct atari_fb_par *par )
+{
+ unsigned long addr;
+ struct falcon_hw *hw = &par->hw.falcon;
+
+ hw->line_width = shifter_f030.scn_width;
+ hw->line_offset = shifter_f030.off_next;
+ hw->st_shift = videl.st_shift & 0x300;
+ hw->f_shift = videl.f_shift;
+ hw->vid_control = videl.control;
+ hw->vid_mode = videl.mode;
+ hw->sync = shifter.syncmode & 0x1;
+ hw->xoffset = videl.xoffset & 0xf;
+ hw->hht = videl.hht;
+ hw->hbb = videl.hbb;
+ hw->hbe = videl.hbe;
+ hw->hdb = videl.hdb;
+ hw->hde = videl.hde;
+ hw->hss = videl.hss;
+ hw->vft = videl.vft;
+ hw->vbb = videl.vbb;
+ hw->vbe = videl.vbe;
+ hw->vdb = videl.vdb;
+ hw->vde = videl.vde;
+ hw->vss = videl.vss;
+
+ addr = (shifter.bas_hi & 0xff) << 16 |
+ (shifter.bas_md & 0xff) << 8 |
+ (shifter.bas_lo & 0xff);
+ par->screen_base = PTOV(addr);
+
+ /* derived parameters */
+ hw->ste_mode = (hw->f_shift & 0x510)==0 && hw->st_shift==0x100;
+ hw->mono = (hw->f_shift & 0x400) ||
+ ((hw->f_shift & 0x510)==0 && hw->st_shift==0x200);
+}
+
+static void falcon_set_par( struct atari_fb_par *par )
+{
+ f_change_mode = 0;
+
+ /* only set screen_base if really necessary */
+ if (current_par.screen_base != par->screen_base)
+ fbhw->set_screen_base(par->screen_base);
+
+ /* Don't touch any other registers if we keep the default resolution */
+ if (DontCalcRes)
+ return;
+
+ /* Tell vbl-handler to change video mode.
+ * We change modes only on next VBL, to avoid desynchronisation
+ * (a shift to the right and wrap around by a random number of pixels
+ * in all monochrome modes).
+ * This seems to work on my Falcon.
+ */
+ f_new_mode = par->hw.falcon;
+ f_change_mode = 1;
+}
+
+
+static void falcon_vbl_switcher( int irq, void *dummy, struct pt_regs *fp )
+{
+ struct falcon_hw *hw = &f_new_mode;
+
+ if (f_change_mode) {
+ f_change_mode = 0;
+
+ if (hw->sync & 0x1) {
+ /* Enable external pixelclock. This code only for ScreenWonder */
+ *(volatile unsigned short*)0xffff9202 = 0xffbf;
+ }
+ else {
+ /* Turn off external clocks. Read sets all output bits to 1. */
+ *(volatile unsigned short*)0xffff9202;
+ }
+ shifter.syncmode = hw->sync;
+
+ videl.hht = hw->hht;
+ videl.hbb = hw->hbb;
+ videl.hbe = hw->hbe;
+ videl.hdb = hw->hdb;
+ videl.hde = hw->hde;
+ videl.hss = hw->hss;
+ videl.vft = hw->vft;
+ videl.vbb = hw->vbb;
+ videl.vbe = hw->vbe;
+ videl.vdb = hw->vdb;
+ videl.vde = hw->vde;
+ videl.vss = hw->vss;
+
+ videl.f_shift = 0; /* write enables Falcon palette, 0: 4 planes */
+ if (hw->ste_mode) {
+ videl.st_shift = hw->st_shift; /* write enables STE palette */
+ }
+ else {
+ /* IMPORTANT:
+ * set st_shift 0, so we can tell the screen-depth if f_shift==0.
+ * Writing 0 to f_shift enables 4 plane Falcon mode but
+ * doesn't set st_shift. st_shift!=0 (!=4planes) is impossible
+ * with Falcon palette.
+ */
+ videl.st_shift = 0;
+ /* now back to Falcon palette mode */
+ videl.f_shift = hw->f_shift;
+ }
+ /* writing to st_shift changed scn_width and vid_mode */
+ videl.xoffset = hw->xoffset;
+ shifter_f030.scn_width = hw->line_width;
+ shifter_f030.off_next = hw->line_offset;
+ videl.control = hw->vid_control;
+ videl.mode = hw->vid_mode;
+ }
+ if (f_pan_display) {
+ f_pan_display = 0;
+ videl.xoffset = current_par.hw.falcon.xoffset;
+ shifter_f030.off_next = current_par.hw.falcon.line_offset;
+ }
+}
+
+
+static int falcon_pan_display( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+ int xoffset;
+ int bpp = fb_display[currcon].var.bits_per_pixel;
+
+ if (bpp == 1)
+ var->xoffset = up(var->xoffset, 32);
+ if (bpp != 16)
+ par->hw.falcon.xoffset = var->xoffset & 15;
+ else {
+ par->hw.falcon.xoffset = 0;
+ var->xoffset = up(var->xoffset, 2);
+ }
+ par->hw.falcon.line_offset = bpp *
+ (fb_display[currcon].var.xres_virtual - fb_display[currcon].var.xres) / 16;
+ if (par->hw.falcon.xoffset)
+ par->hw.falcon.line_offset -= bpp;
+ xoffset = var->xoffset - par->hw.falcon.xoffset;
+
+ par->screen_base = screen_base +
+ (var->yoffset * fb_display[currcon].var.xres_virtual + xoffset) * bpp / 8;
+ if (fbhw->set_screen_base)
+ fbhw->set_screen_base (par->screen_base);
+ else
+ return -EINVAL; /* shouldn't happen */
+ f_pan_display = 1;
+ return 0;
+}
+
+
+static int falcon_getcolreg( unsigned regno, unsigned *red,
+ unsigned *green, unsigned *blue,
+ unsigned *transp )
+{ unsigned long col;
+
+ if (regno > 255)
+ return 1;
+ /* This works in STE-mode (with 4bit/color) since f030_col-registers
+ * hold up to 6bit/color.
+ * Even with hicolor r/g/b=5/6/5 bit!
+ */
+ col = f030_col[regno];
+ *red = (col >> 26) & 0x3f;
+ *green = (col >> 18) & 0x3f;
+ *blue = (col >> 2) & 0x3f;
+ *transp = 0;
+ return 0;
+}
+
+
+static int falcon_setcolreg( unsigned regno, unsigned red,
+ unsigned green, unsigned blue,
+ unsigned transp )
+{
+ if (regno > 255)
+ return 1;
+ f030_col[regno] = (red << 26) | (green << 18) | (blue << 2);
+ if (regno < 16) {
+ shifter_tt.color_reg[regno] =
+ (((red & 0xe) >> 1) | ((red & 1) << 3) << 8) |
+ (((green & 0xe) >> 1) | ((green & 1) << 3) << 4) |
+ ((blue & 0xe) >> 1) | ((blue & 1) << 3);
+#ifdef CONFIG_FBCON_CFB16
+ packed16_cmap[regno] = (red << 11) | (green << 5) | blue;
+#endif
+ }
+ return 0;
+}
+
+
+static int falcon_blank( int blank_mode )
+{
+/* ++guenther: we can switch off graphics by changing VDB and VDE,
+ * so VIDEL doesn't hog the bus while saving.
+ * (this may affect usleep()).
+ */
+ int vdb, vss, hbe, hss;
+
+ if (mon_type == F_MON_SM) /* this doesn't work on SM124 */
+ return 1;
+
+ vdb = current_par.VDB;
+ vss = current_par.VSS;
+ hbe = current_par.HBE;
+ hss = current_par.HSS;
+
+ if (blank_mode >= 1) {
+ /* disable graphics output (this speeds up the CPU) ... */
+ vdb = current_par.VFT + 1;
+ /* ... and blank all lines */
+ hbe = current_par.HHT + 2;
+ }
+ /* use VESA suspend modes on VGA monitors */
+ if (mon_type == F_MON_VGA) {
+ if (blank_mode == 2 || blank_mode == 4)
+ vss = current_par.VFT + 1;
+ if (blank_mode == 3 || blank_mode == 4)
+ hss = current_par.HHT + 2;
+ }
+
+ videl.vdb = vdb;
+ videl.vss = vss;
+ videl.hbe = hbe;
+ videl.hss = hss;
+
+ return 0;
+}
+
+
+static int falcon_detect( void )
+{
+ struct atari_fb_par par;
+ unsigned char fhw;
+
+ /* Determine connected monitor and set monitor parameters */
+ fhw = *(unsigned char*)0xffff8006;
+ mon_type = fhw >> 6 & 0x3;
+ /* bit 1 of fhw: 1=32 bit ram bus, 0=16 bit */
+ f030_bus_width = fhw << 6 & 0x80;
+ switch (mon_type) {
+ case F_MON_SM:
+ vfmin = 70;
+ vfmax = 72;
+ hfmin = 35713;
+ hfmax = 35715;
+ break;
+ case F_MON_SC:
+ case F_MON_TV:
+ /* PAL...NTSC */
+ vfmin = 49; /* not 50, since TOS defaults to 49.9x Hz */
+ vfmax = 60;
+ hfmin = 15620;
+ hfmax = 15755;
+ break;
+ }
+ /* initialize hsync-len */
+ f25.hsync = h_syncs[mon_type] / f25.t;
+ f32.hsync = h_syncs[mon_type] / f32.t;
+ if (fext.t)
+ fext.hsync = h_syncs[mon_type] / fext.t;
+
+ falcon_get_par(&par);
+ falcon_encode_var(&atari_fb_predefined[0], &par);
+
+ /* Detected mode is always the "autodetect" slot */
+ return 1;
+}
+
+#endif /* ATAFB_FALCON */
+
+/* ------------------- ST(E) specific functions ---------------------- */
+
+#ifdef ATAFB_STE
+
+static int stste_encode_fix( struct fb_fix_screeninfo *fix,
+ struct atari_fb_par *par )
+
+{
+ int mode;
+
+ strcpy(fix->id,"Atari Builtin");
+ fix->smem_start = (char *)real_screen_base;
+ fix->smem_len = screen_len;
+ fix->type = FB_TYPE_INTERLEAVED_PLANES;
+ fix->type_aux = 2;
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
+ mode = par->hw.st.mode & 3;
+ if (mode == ST_HIGH) {
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->type_aux = 0;
+ fix->visual = FB_VISUAL_MONO10;
+ }
+ if (ATARIHW_PRESENT(EXTD_SHIFTER)) {
+ fix->xpanstep = 16;
+ fix->ypanstep = 1;
+ } else {
+ fix->xpanstep = 0;
+ fix->ypanstep = 0;
+ }
+ fix->ywrapstep = 0;
+ fix->line_length = 0;
+ return 0;
+}
+
+
+static int stste_decode_var( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+ int xres=var->xres;
+ int yres=var->yres;
+ int bpp=var->bits_per_pixel;
+ int linelen;
+ int yres_virtual = var->yres_virtual;
+
+ if (mono_moni) {
+ if (bpp > 1 || xres > sttt_xres || yres > st_yres)
+ return -EINVAL;
+ par->hw.st.mode=ST_HIGH;
+ xres=sttt_xres;
+ yres=st_yres;
+ bpp=1;
+ } else {
+ if (bpp > 4 || xres > sttt_xres || yres > st_yres)
+ return -EINVAL;
+ if (bpp > 2) {
+ if (xres > sttt_xres/2 || yres > st_yres/2)
+ return -EINVAL;
+ par->hw.st.mode=ST_LOW;
+ xres=sttt_xres/2;
+ yres=st_yres/2;
+ bpp=4;
+ }
+ else if (bpp > 1) {
+ if (xres > sttt_xres || yres > st_yres/2)
+ return -EINVAL;
+ par->hw.st.mode=ST_MID;
+ xres=sttt_xres;
+ yres=st_yres/2;
+ bpp=2;
+ }
+ else
+ return -EINVAL;
+ }
+ if (yres_virtual <= 0)
+ yres_virtual = 0;
+ else if (yres_virtual < yres)
+ yres_virtual = yres;
+ if (var->sync & FB_SYNC_EXT)
+ par->hw.st.sync=(par->hw.st.sync & ~1) | 1;
+ else
+ par->hw.st.sync=(par->hw.st.sync & ~1);
+ linelen=xres*bpp/8;
+ if (yres_virtual * linelen > screen_len && screen_len)
+ return -EINVAL;
+ if (yres * linelen > screen_len && screen_len)
+ return -EINVAL;
+ if (var->yoffset + yres > yres_virtual && yres_virtual)
+ return -EINVAL;
+ par->yres_virtual = yres_virtual;
+ par->screen_base=screen_base+ var->yoffset*linelen;
+ return 0;
+}
+
+static int stste_encode_var( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+ int linelen, i;
+ var->red.offset=0;
+ var->red.length = ATARIHW_PRESENT(EXTD_SHIFTER) ? 4 : 3;
+ var->red.msb_right=0;
+ var->grayscale=0;
+
+ var->pixclock=31041;
+ var->left_margin=120; /* these are incorrect */
+ var->right_margin=100;
+ var->upper_margin=8;
+ var->lower_margin=16;
+ var->hsync_len=140;
+ var->vsync_len=30;
+
+ var->height=-1;
+ var->width=-1;
+
+ if (!(par->hw.st.sync & 1))
+ var->sync=0;
+ else
+ var->sync=FB_SYNC_EXT;
+
+ switch (par->hw.st.mode & 3) {
+ case ST_LOW:
+ var->xres=sttt_xres/2;
+ var->yres=st_yres/2;
+ var->bits_per_pixel=4;
+ break;
+ case ST_MID:
+ var->xres=sttt_xres;
+ var->yres=st_yres/2;
+ var->bits_per_pixel=2;
+ break;
+ case ST_HIGH:
+ var->xres=sttt_xres;
+ var->yres=st_yres;
+ var->bits_per_pixel=1;
+ break;
+ }
+ var->blue=var->green=var->red;
+ var->transp.offset=0;
+ var->transp.length=0;
+ var->transp.msb_right=0;
+ var->xres_virtual=sttt_xres_virtual;
+ linelen=var->xres_virtual * var->bits_per_pixel / 8;
+ ovsc_addlen=linelen*(sttt_yres_virtual - st_yres);
+
+ if (! use_hwscroll)
+ var->yres_virtual=var->yres;
+ else if (screen_len)
+ if (par->yres_virtual)
+ var->yres_virtual = par->yres_virtual;
+ else
+ /* yres_virtual==0 means use maximum */
+ var->yres_virtual = screen_len / linelen;
+ else {
+ if (hwscroll < 0)
+ var->yres_virtual = 2 * var->yres;
+ else
+ var->yres_virtual=var->yres+hwscroll * 16;
+ }
+ var->xoffset=0;
+ if (screen_base)
+ var->yoffset=(par->screen_base - screen_base)/linelen;
+ else
+ var->yoffset=0;
+ var->nonstd=0;
+ var->activate=0;
+ var->vmode=FB_VMODE_NONINTERLACED;
+ for (i=0; i<arraysize(var->reserved); i++)
+ var->reserved[i]=0;
+ return 0;
+}
+
+
+static void stste_get_par( struct atari_fb_par *par )
+{
+ unsigned long addr;
+ par->hw.st.mode=shifter_tt.st_shiftmode;
+ par->hw.st.sync=shifter.syncmode;
+ addr = ((shifter.bas_hi & 0xff) << 16) |
+ ((shifter.bas_md & 0xff) << 8);
+ if (ATARIHW_PRESENT(EXTD_SHIFTER))
+ addr |= (shifter.bas_lo & 0xff);
+ par->screen_base = PTOV(addr);
+}
+
+static void stste_set_par( struct atari_fb_par *par )
+{
+ shifter_tt.st_shiftmode=par->hw.st.mode;
+ shifter.syncmode=par->hw.st.sync;
+ /* only set screen_base if really necessary */
+ if (current_par.screen_base != par->screen_base)
+ fbhw->set_screen_base(par->screen_base);
+}
+
+
+static int stste_getcolreg( unsigned regno, unsigned *red,
+ unsigned *green, unsigned *blue,
+ unsigned *transp )
+{ unsigned col;
+
+ if (regno > 15)
+ return 1;
+ col = shifter_tt.color_reg[regno];
+ if (ATARIHW_PRESENT(EXTD_SHIFTER)) {
+ *red = ((col >> 7) & 0xe) | ((col >> 11) & 1);
+ *green = ((col >> 3) & 0xe) | ((col >> 7) & 1);
+ *blue = ((col << 1) & 0xe) | ((col >> 3) & 1);
+ }
+ else {
+ *red = (col >> 8) & 0x7;
+ *green = (col >> 4) & 0x7;
+ *blue = col & 0x7;
+ }
+ *transp = 0;
+ return 0;
+}
+
+
+static int stste_setcolreg( unsigned regno, unsigned red,
+ unsigned green, unsigned blue,
+ unsigned transp )
+{
+ if (regno > 15)
+ return 1;
+ if (ATARIHW_PRESENT(EXTD_SHIFTER))
+ shifter_tt.color_reg[regno] =
+ (((red & 0xe) >> 1) | ((red & 1) << 3) << 8) |
+ (((green & 0xe) >> 1) | ((green & 1) << 3) << 4) |
+ ((blue & 0xe) >> 1) | ((blue & 1) << 3);
+ else
+ shifter_tt.color_reg[regno] =
+ ((red & 0x7) << 8) |
+ ((green & 0x7) << 4) |
+ (blue & 0x7);
+ return 0;
+}
+
+
+static int stste_detect( void )
+
+{ struct atari_fb_par par;
+
+ /* Determine the connected monitor: The DMA sound must be
+ * disabled before reading the MFP GPIP, because the Sound
+ * Done Signal and the Monochrome Detect are XORed together!
+ */
+ if (ATARIHW_PRESENT(PCM_8BIT)) {
+ tt_dmasnd.ctrl = DMASND_CTRL_OFF;
+ udelay(20); /* wait a while for things to settle down */
+ }
+ mono_moni = (mfp.par_dt_reg & 0x80) == 0;
+
+ stste_get_par(&par);
+ stste_encode_var(&atari_fb_predefined[0], &par);
+
+ if (!ATARIHW_PRESENT(EXTD_SHIFTER))
+ use_hwscroll = 0;
+ return 1;
+}
+
+static void stste_set_screen_base(unsigned long s_base)
+{
+ unsigned long addr;
+ addr= VTOP(s_base);
+ /* Setup Screen Memory */
+ shifter.bas_hi=(unsigned char) ((addr & 0xff0000) >> 16);
+ shifter.bas_md=(unsigned char) ((addr & 0x00ff00) >> 8);
+ if (ATARIHW_PRESENT(EXTD_SHIFTER))
+ shifter.bas_lo=(unsigned char) (addr & 0x0000ff);
+}
+
+#endif /* ATAFB_STE */
+
+/* Switching the screen size should be done during vsync, otherwise
+ * the margins may get messed up. This is a well known problem of
+ * the ST's video system.
+ *
+ * Unfortunately there is hardly any way to find the vsync, as the
+ * vertical blank interrupt is no longer in time on machines with
+ * overscan type modifications.
+ *
+ * We can, however, use Timer B to safely detect the black shoulder,
+ * but then we've got to guess an appropriate delay to find the vsync.
+ * This might not work on every machine.
+ *
+ * martin_rogge @ ki.maus.de, 8th Aug 1995
+ */
+
+#define LINE_DELAY (mono_moni ? 30 : 70)
+#define SYNC_DELAY (mono_moni ? 1500 : 2000)
+
+/* SWITCH_ACIA may be used for Falcon (ScreenBlaster III internal!) */
+static void st_ovsc_switch(int switchmode)
+{
+ unsigned long flags;
+ register unsigned char old, new;
+
+ if ((switchmode & (SWITCH_ACIA | SWITCH_SND6 | SWITCH_SND7)) == 0)
+ return;
+ save_flags(flags);
+ cli();
+
+ mfp.tim_ct_b = 0x10;
+ mfp.active_edge |= 8;
+ mfp.tim_ct_b = 0;
+ mfp.tim_dt_b = 0xf0;
+ mfp.tim_ct_b = 8;
+ while (mfp.tim_dt_b > 1) /* TOS does it this way, don't ask why */
+ ;
+ new = mfp.tim_dt_b;
+ do {
+ udelay(LINE_DELAY);
+ old = new;
+ new = mfp.tim_dt_b;
+ } while (old != new);
+ mfp.tim_ct_b = 0x10;
+ udelay(SYNC_DELAY);
+
+ if (switchmode == SWITCH_ACIA)
+ acia.key_ctrl = (ACIA_DIV64|ACIA_D8N1S|ACIA_RHTID|ACIA_RIE);
+ else {
+ sound_ym.rd_data_reg_sel = 14;
+ sound_ym.wd_data = sound_ym.rd_data_reg_sel | switchmode;
+ }
+ restore_flags(flags);
+}
+
+/* ------------------- External Video ---------------------- */
+
+#ifdef ATAFB_EXT
+
+static int ext_encode_fix( struct fb_fix_screeninfo *fix,
+ struct atari_fb_par *par )
+
+{
+ strcpy(fix->id,"Unknown Extern");
+ fix->smem_start=(char *)external_addr;
+ fix->smem_len=(external_len + PAGE_SIZE -1) & PAGE_MASK;
+ if (external_depth == 1) {
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ /* The letters 'n' and 'i' in the "atavideo=external:" stand
+ * for "normal" and "inverted", rsp., in the monochrome case */
+ fix->visual =
+ (external_pmode == FB_TYPE_INTERLEAVED_PLANES ||
+ external_pmode == FB_TYPE_PACKED_PIXELS) ?
+ FB_VISUAL_MONO10 :
+ FB_VISUAL_MONO01;
+ }
+ else {
+ /* Use STATIC if we don't know how to access color registers */
+ int visual = external_vgaiobase ?
+ FB_VISUAL_PSEUDOCOLOR :
+ FB_VISUAL_STATIC_PSEUDOCOLOR;
+ switch (external_pmode) {
+ case -1: /* truecolor */
+ fix->type=FB_TYPE_PACKED_PIXELS;
+ fix->visual=FB_VISUAL_TRUECOLOR;
+ break;
+ case FB_TYPE_PACKED_PIXELS:
+ fix->type=FB_TYPE_PACKED_PIXELS;
+ fix->visual=visual;
+ break;
+ case FB_TYPE_PLANES:
+ fix->type=FB_TYPE_PLANES;
+ fix->visual=visual;
+ break;
+ case FB_TYPE_INTERLEAVED_PLANES:
+ fix->type=FB_TYPE_INTERLEAVED_PLANES;
+ fix->type_aux=2;
+ fix->visual=visual;
+ break;
+ }
+ }
+ fix->xpanstep = 0;
+ fix->ypanstep = 0;
+ fix->ywrapstep = 0;
+ fix->line_length = 0;
+ return 0;
+}
+
+
+static int ext_decode_var( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+ struct fb_var_screeninfo *myvar = &atari_fb_predefined[0];
+
+ if (var->bits_per_pixel > myvar->bits_per_pixel ||
+ var->xres > myvar->xres ||
+ var->xres_virtual > myvar->xres_virtual ||
+ var->yres > myvar->yres ||
+ var->xoffset > 0 ||
+ var->yoffset > 0)
+ return -EINVAL;
+ return 0;
+}
+
+
+static int ext_encode_var( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+ int i;
+
+ var->red.offset=0;
+ var->red.length=(external_pmode == -1) ? external_depth/3 :
+ (external_vgaiobase ? external_bitspercol : 0);
+ var->red.msb_right=0;
+ var->grayscale=0;
+
+ var->pixclock=31041;
+ var->left_margin=120; /* these are surely incorrect */
+ var->right_margin=100;
+ var->upper_margin=8;
+ var->lower_margin=16;
+ var->hsync_len=140;
+ var->vsync_len=30;
+
+ var->height=-1;
+ var->width=-1;
+
+ var->sync=0;
+
+ var->xres = external_xres;
+ var->yres = external_yres;
+ var->xres_virtual = external_xres_virtual;
+ var->bits_per_pixel = external_depth;
+
+ var->blue=var->green=var->red;
+ var->transp.offset=0;
+ var->transp.length=0;
+ var->transp.msb_right=0;
+ var->yres_virtual=var->yres;
+ var->xoffset=0;
+ var->yoffset=0;
+ var->nonstd=0;
+ var->activate=0;
+ var->vmode=FB_VMODE_NONINTERLACED;
+ for (i=0; i<arraysize(var->reserved); i++)
+ var->reserved[i]=0;
+ return 0;
+}
+
+
+static void ext_get_par( struct atari_fb_par *par )
+{
+ par->screen_base = external_addr;
+}
+
+static void ext_set_par( struct atari_fb_par *par )
+{
+}
+
+#define OUTB(port,val) \
+ *((unsigned volatile char *) ((port)+external_vgaiobase))=(val)
+#define INB(port) \
+ (*((unsigned volatile char *) ((port)+external_vgaiobase)))
+#define DACDelay \
+ do { \
+ unsigned char tmp=INB(0x3da); \
+ tmp=INB(0x3da); \
+ } while (0)
+
+static int ext_getcolreg( unsigned regno, unsigned *red,
+ unsigned *green, unsigned *blue,
+ unsigned *transp )
+
+{ unsigned char colmask = (1 << external_bitspercol) - 1;
+
+ if (! external_vgaiobase)
+ return 1;
+
+ *red = ext_color[regno].red;
+ *green = ext_color[regno].green;
+ *blue = ext_color[regno].blue;
+ *transp=0;
+ return 0;
+}
+
+static int ext_setcolreg( unsigned regno, unsigned red,
+ unsigned green, unsigned blue,
+ unsigned transp )
+
+{ unsigned char colmask = (1 << external_bitspercol) - 1;
+
+ if (! external_vgaiobase)
+ return 1;
+
+ ext_color[regno].red = red;
+ ext_color[regno].green = green;
+ ext_color[regno].blue = blue;
+
+ switch (external_card_type) {
+ case IS_VGA:
+ OUTB(0x3c8, regno);
+ DACDelay;
+ OUTB(0x3c9, red & colmask);
+ DACDelay;
+ OUTB(0x3c9, green & colmask);
+ DACDelay;
+ OUTB(0x3c9, blue & colmask);
+ DACDelay;
+ return 0;
+
+ case IS_MV300:
+ OUTB((MV300_reg[regno] << 2)+1, red);
+ OUTB((MV300_reg[regno] << 2)+1, green);
+ OUTB((MV300_reg[regno] << 2)+1, blue);
+ return 0;
+
+ default:
+ return 1;
+ }
+}
+
+
+static int ext_detect( void )
+
+{
+ struct fb_var_screeninfo *myvar = &atari_fb_predefined[0];
+ struct atari_fb_par dummy_par;
+
+ myvar->xres = external_xres;
+ myvar->xres_virtual = external_xres_virtual;
+ myvar->yres = external_yres;
+ myvar->bits_per_pixel = external_depth;
+ ext_encode_var(myvar, &dummy_par);
+ return 1;
+}
+
+#endif /* ATAFB_EXT */
+
+/* ------ This is the same for most hardware types -------- */
+
+static void set_screen_base(unsigned long s_base)
+{
+ unsigned long addr;
+ addr= VTOP(s_base);
+ /* Setup Screen Memory */
+ shifter.bas_hi=(unsigned char) ((addr & 0xff0000) >> 16);
+ shifter.bas_md=(unsigned char) ((addr & 0x00ff00) >> 8);
+ shifter.bas_lo=(unsigned char) (addr & 0x0000ff);
+}
+
+
+static int pan_display( struct fb_var_screeninfo *var,
+ struct atari_fb_par *par )
+{
+ if (!fbhw->set_screen_base ||
+ (!ATARIHW_PRESENT(EXTD_SHIFTER) && var->xoffset))
+ return -EINVAL;
+ var->xoffset = up(var->xoffset, 16);
+ par->screen_base = screen_base +
+ (var->yoffset * fb_display[currcon].var.xres_virtual + var->xoffset)
+ * fb_display[currcon].var.bits_per_pixel / 8;
+ fbhw->set_screen_base (par->screen_base);
+ return 0;
+}
+
+
+/* ------------ Interfaces to hardware functions ------------ */
+
+
+#ifdef ATAFB_TT
+static struct fb_hwswitch tt_switch = {
+ tt_detect, tt_encode_fix, tt_decode_var, tt_encode_var,
+ tt_get_par, tt_set_par, tt_getcolreg, tt_setcolreg,
+ set_screen_base, NULL, pan_display
+};
+#endif
+
+#ifdef ATAFB_FALCON
+static struct fb_hwswitch falcon_switch = {
+ falcon_detect, falcon_encode_fix, falcon_decode_var, falcon_encode_var,
+ falcon_get_par, falcon_set_par, falcon_getcolreg,
+ falcon_setcolreg, set_screen_base, falcon_blank, falcon_pan_display
+};
+#endif
+
+#ifdef ATAFB_STE
+static struct fb_hwswitch st_switch = {
+ stste_detect, stste_encode_fix, stste_decode_var, stste_encode_var,
+ stste_get_par, stste_set_par, stste_getcolreg, stste_setcolreg,
+ stste_set_screen_base, NULL, pan_display
+};
+#endif
+
+#ifdef ATAFB_EXT
+static struct fb_hwswitch ext_switch = {
+ ext_detect, ext_encode_fix, ext_decode_var, ext_encode_var,
+ ext_get_par, ext_set_par, ext_getcolreg, ext_setcolreg, NULL, NULL, NULL
+};
+#endif
+
+
+
+static void atari_fb_get_par( struct atari_fb_par *par )
+{
+ if (current_par_valid) {
+ *par=current_par;
+ }
+ else
+ fbhw->get_par(par);
+}
+
+
+static void atari_fb_set_par( struct atari_fb_par *par )
+{
+ fbhw->set_par(par);
+ current_par=*par;
+ current_par_valid=1;
+}
+
+
+
+/* =========================================================== */
+/* ============== Hardware Independent Functions ============= */
+/* =========================================================== */
+
+
+/* used for hardware scrolling */
+
+static int
+fb_update_var(int con)
+{
+ int off=fb_display[con].var.yoffset*fb_display[con].var.xres_virtual*
+ fb_display[con].var.bits_per_pixel>>3;
+
+ current_par.screen_base=screen_base + off;
+
+ if (fbhw->set_screen_base)
+ fbhw->set_screen_base(current_par.screen_base);
+ return 0;
+}
+
+static int
+do_fb_set_var(struct fb_var_screeninfo *var, int isactive)
+{
+ int err,activate;
+ struct atari_fb_par par;
+ if ((err=fbhw->decode_var(var, &par)))
+ return err;
+ activate=var->activate;
+ if (((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) && isactive)
+ atari_fb_set_par(&par);
+ fbhw->encode_var(var, &par);
+ var->activate=activate;
+ return 0;
+}
+
+/* Functions for handling colormap */
+
+static void
+do_install_cmap(int con)
+{
+ if (con != currcon)
+ return;
+ if (fb_display[con].cmap.len)
+ fb_set_cmap(&fb_display[con].cmap, &(fb_display[con].var), 1,
+ fbhw->setcolreg);
+ else
+ fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+ &(fb_display[con].var), 1,
+ fbhw->setcolreg);
+}
+
+
+ /*
+ * Open/Release the frame buffer device
+ */
+
+static int atari_fb_open(int fbidx)
+{
+ /*
+ * Nothing, only a usage count for the moment
+ */
+
+ MOD_INC_USE_COUNT;
+ return(0);
+}
+
+static int atari_fb_release(int fbidx)
+{
+ MOD_DEC_USE_COUNT;
+ return(0);
+}
+
+
+static int
+atari_fb_get_fix(struct fb_fix_screeninfo *fix, int con)
+{
+ struct atari_fb_par par;
+ if (con == -1)
+ atari_fb_get_par(&par);
+ else
+ fbhw->decode_var(&fb_display[con].var,&par);
+ memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+ return fbhw->encode_fix(fix, &par);
+}
+
+static int
+atari_fb_get_var(struct fb_var_screeninfo *var, int con)
+{
+ struct atari_fb_par par;
+ if (con == -1) {
+ atari_fb_get_par(&par);
+ fbhw->encode_var(var, &par);
+ }
+ else
+ *var=fb_display[con].var;
+ return 0;
+}
+
+static void
+atari_fb_set_disp(int con)
+{
+ struct fb_fix_screeninfo fix;
+ struct display *display;
+
+ if (con >= 0)
+ display = &fb_display[con];
+ else
+ display = &disp; /* used during initialization */
+
+ atari_fb_get_fix(&fix, con);
+ if (con == -1)
+ con=0;
+ display->screen_base = (u_char *)fix.smem_start;
+ display->visual = fix.visual;
+ display->type = fix.type;
+ display->type_aux = fix.type_aux;
+ display->ypanstep = fix.ypanstep;
+ display->ywrapstep = fix.ywrapstep;
+ display->line_length = fix.line_length;
+ if (fix.visual != FB_VISUAL_PSEUDOCOLOR &&
+ fix.visual != FB_VISUAL_DIRECTCOLOR)
+ display->can_soft_blank = 0;
+ else
+ display->can_soft_blank = 1;
+ display->inverse =
+ (fix.visual == FB_VISUAL_MONO01 ? !inverse : inverse);
+}
+
+static int
+atari_fb_set_var(struct fb_var_screeninfo *var, int con)
+{
+ int err,oldxres,oldyres,oldbpp,oldxres_virtual,
+ oldyres_virtual,oldyoffset;
+ if ((err=do_fb_set_var(var, con==currcon)))
+ return err;
+ if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+ oldxres=fb_display[con].var.xres;
+ oldyres=fb_display[con].var.yres;
+ oldxres_virtual=fb_display[con].var.xres_virtual;
+ oldyres_virtual=fb_display[con].var.yres_virtual;
+ oldbpp=fb_display[con].var.bits_per_pixel;
+ oldyoffset=fb_display[con].var.yoffset;
+ fb_display[con].var=*var;
+ if (oldxres != var->xres || oldyres != var->yres
+ || oldxres_virtual != var->xres_virtual
+ || oldyres_virtual != var->yres_virtual
+ || oldbpp != var->bits_per_pixel
+ || oldyoffset != var->yoffset) {
+ atari_fb_set_disp(con);
+ (*fb_info.changevar)(con);
+ fb_alloc_cmap(&fb_display[con].cmap, 0, 0);
+ do_install_cmap(con);
+ }
+ }
+ var->activate=0;
+ return 0;
+}
+
+
+
+static int
+atari_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con)
+{
+ if (con == currcon) /* current console ? */
+ return fb_get_cmap(cmap, &(fb_display[con].var), kspc,
+ fbhw->getcolreg);
+ else
+ if (fb_display[con].cmap.len) /* non default colormap ? */
+ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+ else
+ fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+ cmap, kspc ? 0 : 2);
+ return 0;
+}
+
+static int
+atari_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con)
+{
+ int err;
+ if (! fb_display[con].cmap.len) { /* no colormap allocated ? */
+ if ((err = fb_alloc_cmap(&fb_display[con].cmap,
+ 1 << fb_display[con].var.bits_per_pixel,
+ 0)))
+ return err;
+ }
+ if (con == currcon) /* current console ? */
+ return fb_set_cmap(cmap, &(fb_display[con].var), kspc,
+ fbhw->setcolreg);
+ else
+ fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+ return 0;
+}
+
+static int
+atari_fb_pan_display(struct fb_var_screeninfo *var, int con)
+{
+ int xoffset = var->xoffset;
+ int yoffset = var->yoffset;
+ int err;
+
+ if ( xoffset < 0 || xoffset + fb_display[con].var.xres > fb_display[con].var.xres_virtual
+ || yoffset < 0 || yoffset + fb_display[con].var.yres > fb_display[con].var.yres_virtual)
+ return -EINVAL;
+
+ if (con == currcon) {
+ if (fbhw->pan_display) {
+ if ((err = fbhw->pan_display(var, &current_par)))
+ return err;
+ }
+ else
+ return -EINVAL;
+ }
+ fb_display[con].var.xoffset = var->xoffset;
+ fb_display[con].var.yoffset = var->yoffset;
+ return 0;
+}
+
+static int
+atari_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg, int con)
+{
+ switch (cmd) {
+#ifdef FBCMD_GET_CURRENTPAR
+ case FBCMD_GET_CURRENTPAR:
+ if (copy_to_user((void *)arg, (void *)&current_par,
+ sizeof(struct atari_fb_par)))
+ return -EFAULT;
+ return 0;
+#endif
+#ifdef FBCMD_SET_CURRENTPAR
+ case FBCMD_SET_CURRENTPAR:
+ if (copy_from_user((void *)&current_par, (void *)arg,
+ sizeof(struct atari_fb_par)))
+ return -EFAULT;
+ atari_fb_set_par(&current_par);
+ return 0;
+#endif
+ }
+ return -EINVAL;
+}
+
+static struct fb_ops atari_fb_ops = {
+ atari_fb_open, atari_fb_release, atari_fb_get_fix, atari_fb_get_var,
+ atari_fb_set_var, atari_fb_get_cmap, atari_fb_set_cmap,
+ atari_fb_pan_display, atari_fb_ioctl
+};
+
+static void
+check_default_par( int detected_mode )
+{
+ char default_name[10];
+ int i;
+ struct fb_var_screeninfo var;
+ unsigned long min_mem;
+
+ /* First try the user supplied mode */
+ if (default_par) {
+ var=atari_fb_predefined[default_par-1];
+ var.activate = FB_ACTIVATE_TEST;
+ if (do_fb_set_var(&var,1))
+ default_par=0; /* failed */
+ }
+ /* Next is the autodetected one */
+ if (! default_par) {
+ var=atari_fb_predefined[detected_mode-1]; /* autodetect */
+ var.activate = FB_ACTIVATE_TEST;
+ if (!do_fb_set_var(&var,1))
+ default_par=detected_mode;
+ }
+ /* If that also failed, try some default modes... */
+ if (! default_par) {
+ /* try default1, default2... */
+ for (i=1 ; i < 10 ; i++) {
+ sprintf(default_name,"default%d",i);
+ default_par=get_video_mode(default_name);
+ if (! default_par)
+ panic("can't set default video mode\n");
+ var=atari_fb_predefined[default_par-1];
+ var.activate = FB_ACTIVATE_TEST;
+ if (! do_fb_set_var(&var,1))
+ break; /* ok */
+ }
+ }
+ min_mem=var.xres_virtual * var.yres_virtual * var.bits_per_pixel/8;
+ if (default_mem_req < min_mem)
+ default_mem_req=min_mem;
+}
+
+static int
+atafb_switch(int con)
+{
+ /* Do we have to save the colormap ? */
+ if (fb_display[currcon].cmap.len)
+ fb_get_cmap(&fb_display[currcon].cmap,
+ &(fb_display[currcon].var), 1, fbhw->getcolreg);
+ do_fb_set_var(&fb_display[con].var,1);
+ currcon=con;
+ /* Install new colormap */
+ do_install_cmap(con);
+ return 0;
+}
+
+/* (un)blank/poweroff
+ * 0 = unblank
+ * 1 = blank
+ * 2 = suspend vsync
+ * 3 = suspend hsync
+ * 4 = off
+ */
+static void
+atafb_blank(int blank)
+{
+ unsigned short black[16];
+ struct fb_cmap cmap;
+ if (fbhw->blank && !fbhw->blank(blank))
+ return;
+ if (blank) {
+ memset(black, 0, 16*sizeof(unsigned short));
+ cmap.red=black;
+ cmap.green=black;
+ cmap.blue=black;
+ cmap.transp=NULL;
+ cmap.start=0;
+ cmap.len=16;
+ fb_set_cmap(&cmap, &(fb_display[currcon].var), 1,
+ fbhw->setcolreg);
+ }
+ else
+ do_install_cmap(currcon);
+}
+
+static int
+atafb_setcmap(struct fb_cmap *cmap, int con)
+{
+ return(atari_fb_set_cmap(cmap, 1, con));
+}
+
+__initfunc(unsigned long atari_fb_init(unsigned long mem_start))
+{
+ int err;
+ int pad;
+ int detected_mode;
+ unsigned long mem_req;
+
+ if (!MACH_IS_ATARI)
+ return mem_start;
+
+ do {
+#ifdef ATAFB_EXT
+ if (external_addr) {
+ fbhw = &ext_switch;
+ break;
+ }
+#endif
+#ifdef ATAFB_TT
+ if (ATARIHW_PRESENT(TT_SHIFTER)) {
+ fbhw = &tt_switch;
+ break;
+ }
+#endif
+#ifdef ATAFB_FALCON
+ if (ATARIHW_PRESENT(VIDEL_SHIFTER)) {
+ fbhw = &falcon_switch;
+ request_irq(IRQ_AUTO_4, falcon_vbl_switcher, IRQ_TYPE_PRIO,
+ "framebuffer/modeswitch", falcon_vbl_switcher);
+ break;
+ }
+#endif
+#ifdef ATAFB_STE
+ if (ATARIHW_PRESENT(STND_SHIFTER) ||
+ ATARIHW_PRESENT(EXTD_SHIFTER)) {
+ fbhw = &st_switch;
+ break;
+ }
+ fbhw = &st_switch;
+ printk("Cannot determine video hardware; defaulting to ST(e)\n");
+#else /* ATAFB_STE */
+ /* no default driver included */
+ /* Nobody will ever see this message :-) */
+ panic("Cannot initialize video hardware\n");
+#endif
+ } while (0);
+ detected_mode = fbhw->detect();
+ check_default_par(detected_mode);
+#ifdef ATAFB_EXT
+ if (!external_addr) {
+#endif /* ATAFB_EXT */
+ mem_req = default_mem_req + ovsc_offset +
+ ovsc_addlen;
+ mem_req = ((mem_req + PAGE_SIZE - 1) & PAGE_MASK) + PAGE_SIZE;
+ screen_base = (unsigned long) atari_stram_alloc(mem_req, &mem_start);
+ memset((char *) screen_base, 0, mem_req);
+ pad = ((screen_base + PAGE_SIZE-1) & PAGE_MASK) - screen_base;
+ screen_base+=pad;
+ real_screen_base=screen_base+ovsc_offset;
+ screen_len = (mem_req - pad - ovsc_offset) & PAGE_MASK;
+ st_ovsc_switch(ovsc_switchmode);
+ if (CPU_IS_040_OR_060) {
+ /* On a '040+, the cache mode of video RAM must be set to
+ * write-through also for internal video hardware! */
+ cache_push( VTOP(screen_base), screen_len );
+ kernel_set_cachemode( screen_base, screen_len,
+ KERNELMAP_NO_COPYBACK );
+ }
+#ifdef ATAFB_EXT
+ }
+ else {
+ /* Map the video memory (physical address given) to somewhere
+ * in the kernel address space.
+ */
+ mem_start = PAGE_ALIGN(mem_start);
+ external_addr = kernel_map(external_addr, external_len,
+ KERNELMAP_NO_COPYBACK, &mem_start);
+ if (external_vgaiobase)
+ external_vgaiobase = kernel_map(external_vgaiobase,
+ 0x10000, KERNELMAP_NOCACHE_SER, &mem_start);
+ mem_start += PAGE_SIZE;
+ screen_base =
+ real_screen_base = external_addr;
+ screen_len = external_len & PAGE_MASK;
+ memset ((char *) screen_base, 0, external_len);
+ }
+#endif /* ATAFB_EXT */
+
+ strcpy(fb_info.modename, "Atari Builtin ");
+ fb_info.changevar = NULL;
+ fb_info.node = -1;
+ fb_info.fbops = &atari_fb_ops;
+ fb_info.fbvar_num = num_atari_fb_predefined;
+ fb_info.fbvar = atari_fb_predefined;
+ fb_info.disp = &disp;
+ fb_info.switch_con = &atafb_switch;
+ fb_info.updatevar = &fb_update_var;
+ fb_info.blank = &atafb_blank;
+ fb_info.setcmap = &atafb_setcmap;
+ do_fb_set_var(&atari_fb_predefined[default_par-1], 1);
+ strcat(fb_info.modename, fb_var_names[default_par-1][0]);
+
+ err=register_framebuffer(&fb_info);
+ if (err < 0)
+ return(err);
+
+ atari_fb_get_var(&disp.var, -1);
+ atari_fb_set_disp(-1);
+ printk("Determined %dx%d, depth %d\n",
+ disp.var.xres, disp.var.yres, disp.var.bits_per_pixel);
+ if ((disp.var.xres != disp.var.xres_virtual) ||
+ (disp.var.yres != disp.var.yres_virtual))
+ printk(" virtual %dx%d\n",
+ disp.var.xres_virtual, disp.var.yres_virtual);
+ do_install_cmap(0);
+ printk("%s frame buffer device, using %dK of video memory\n",
+ fb_info.modename, screen_len>>10);
+
+ /* TODO: This driver cannot be unloaded yet */
+ MOD_INC_USE_COUNT;
+
+ return mem_start;
+}
+
+/* a strtok which returns empty strings, too */
+
+static char * strtoke(char * s,const char * ct)
+{
+ char *sbegin, *send;
+ static char *ssave = NULL;
+
+ sbegin = s ? s : ssave;
+ if (!sbegin) {
+ return NULL;
+ }
+ if (*sbegin == '\0') {
+ ssave = NULL;
+ return NULL;
+ }
+ send = strpbrk(sbegin, ct);
+ if (send && *send != '\0')
+ *send++ = '\0';
+ ssave = send;
+ return sbegin;
+}
+
+__initfunc(void atari_video_setup( char *options, int *ints ))
+{
+ char *this_opt;
+ int temp;
+ char ext_str[80], int_str[100];
+ char mcap_spec[80];
+ char user_mode[80];
+
+ ext_str[0] =
+ int_str[0] =
+ mcap_spec[0] =
+ user_mode[0] =
+ fb_info.fontname[0] = '\0';
+
+ if (!options || !*options)
+ return;
+
+ for(this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,",")) {
+ if (!*this_opt) continue;
+ if ((temp=get_video_mode(this_opt)))
+ default_par=temp;
+ else if (! strcmp(this_opt, "inverse"))
+ inverse=1;
+ else if (!strncmp(this_opt, "font:", 5))
+ strcpy(fb_info.fontname, this_opt+5);
+ else if (! strncmp(this_opt, "hwscroll_",9)) {
+ hwscroll=simple_strtoul(this_opt+9, NULL, 10);
+ if (hwscroll < 0)
+ hwscroll = 0;
+ if (hwscroll > 200)
+ hwscroll = 200;
+ }
+ else if (! strncmp(this_opt, "sw_",3)) {
+ if (! strcmp(this_opt+3, "acia"))
+ ovsc_switchmode = SWITCH_ACIA;
+ else if (! strcmp(this_opt+3, "snd6"))
+ ovsc_switchmode = SWITCH_SND6;
+ else if (! strcmp(this_opt+3, "snd7"))
+ ovsc_switchmode = SWITCH_SND7;
+ else ovsc_switchmode = SWITCH_NONE;
+ }
+#ifdef ATAFB_EXT
+ else if (!strcmp(this_opt,"mv300")) {
+ external_bitspercol = 8;
+ external_card_type = IS_MV300;
+ }
+ else if (!strncmp(this_opt,"external:",9))
+ strcpy(ext_str, this_opt+9);
+#endif
+ else if (!strncmp(this_opt,"internal:",9))
+ strcpy(int_str, this_opt+9);
+#ifdef ATAFB_FALCON
+ else if (!strncmp(this_opt, "eclock:", 7)) {
+ fext.f = simple_strtoul(this_opt+7, NULL, 10);
+ /* external pixelclock in kHz --> ps */
+ fext.t = 1000000000/fext.f;
+ fext.f *= 1000;
+ }
+ else if (!strncmp(this_opt, "monitorcap:", 11))
+ strcpy(mcap_spec, this_opt+11);
+#endif
+ else if (!strcmp(this_opt, "keep"))
+ DontCalcRes = 1;
+ else if (!strncmp(this_opt, "R", 1))
+ strcpy(user_mode, this_opt+1);
+ }
+
+ if (*int_str) {
+ /* Format to config extended internal video hardware like OverScan:
+ "<switch-type>,internal:<xres>;<yres>;<xres_max>;<yres_max>;<offset>"
+ Explanation:
+ <switch-type> type to switch on higher resolution
+ sw_acia : via keyboard ACIA
+ sw_snd6 : via bit 6 of the soundchip port
+ sw_snd7 : via bit 7 of the soundchip port
+ <xres>: x-resolution
+ <yres>: y-resolution
+ The following are only needed if you have an overscan which
+ needs a black border:
+ <xres_max>: max. length of a line in pixels your OverScan hardware would allow
+ <yres_max>: max. number of lines your OverScan hardware would allow
+ <offset>: Offset from physical beginning to visible beginning
+ of screen in bytes
+ */
+ int xres;
+ char *p;
+
+ if (!(p = strtoke(int_str, ";")) ||!*p) goto int_invalid;
+ xres = simple_strtoul(p, NULL, 10);
+ if (!(p = strtoke(NULL, ";")) || !*p) goto int_invalid;
+ sttt_xres=xres;
+ tt_yres=st_yres=simple_strtoul(p, NULL, 10);
+ if ((p=strtoke(NULL, ";")) && *p) {
+ sttt_xres_virtual=simple_strtoul(p, NULL, 10);
+ }
+ if ((p=strtoke(NULL, ";")) && *p) {
+ sttt_yres_virtual=simple_strtoul(p, NULL, 0);
+ }
+ if ((p=strtoke(NULL, ";")) && *p) {
+ ovsc_offset=simple_strtoul(p, NULL, 0);
+ }
+
+ if (ovsc_offset || (sttt_yres_virtual != st_yres))
+ use_hwscroll=0;
+ }
+ else
+ int_invalid: ovsc_switchmode = SWITCH_NONE;
+
+#ifdef ATAFB_EXT
+ if (*ext_str) {
+ int xres, xres_virtual, yres, depth, planes;
+ unsigned long addr, len;
+ char *p;
+
+ /* Format is: <xres>;<yres>;<depth>;<plane organ.>;
+ * <screen mem addr>
+ * [;<screen mem length>[;<vgaiobase>[;<bits-per-col>[;<colorreg-type>
+ * [;<xres-virtual>]]]]]
+ *
+ * 09/23/97 Juergen
+ * <xres_virtual>: hardware's x-resolution (f.e. ProMST)
+ *
+ * Even xres_virtual is available, we neither support panning nor hw-scrolling!
+ */
+ if (!(p = strtoke(ext_str, ";")) ||!*p) goto ext_invalid;
+ xres_virtual = xres = simple_strtoul(p, NULL, 10);
+ if (xres <= 0) goto ext_invalid;
+
+ if (!(p = strtoke(NULL, ";")) ||!*p) goto ext_invalid;
+ yres = simple_strtoul(p, NULL, 10);
+ if (yres <= 0) goto ext_invalid;
+
+ if (!(p = strtoke(NULL, ";")) ||!*p) goto ext_invalid;
+ depth = simple_strtoul(p, NULL, 10);
+ if (depth != 1 && depth != 2 && depth != 4 && depth != 8 &&
+ depth != 16 && depth != 24) goto ext_invalid;
+
+ if (!(p = strtoke(NULL, ";")) ||!*p) goto ext_invalid;
+ if (*p == 'i')
+ planes = FB_TYPE_INTERLEAVED_PLANES;
+ else if (*p == 'p')
+ planes = FB_TYPE_PACKED_PIXELS;
+ else if (*p == 'n')
+ planes = FB_TYPE_PLANES;
+ else if (*p == 't')
+ planes = -1; /* true color */
+ else
+ goto ext_invalid;
+
+
+ if (!(p = strtoke(NULL, ";")) ||!*p) goto ext_invalid;
+ addr = simple_strtoul(p, NULL, 0);
+
+ if (!(p = strtoke(NULL, ";")) ||!*p)
+ len = xres*yres*depth/8;
+ else
+ len = simple_strtoul(p, NULL, 0);
+
+ if ((p = strtoke(NULL, ";")) && *p) {
+ external_vgaiobase=simple_strtoul(p, NULL, 0);
+ }
+
+ if ((p = strtoke(NULL, ";")) && *p) {
+ external_bitspercol = simple_strtoul(p, NULL, 0);
+ if (external_bitspercol > 8)
+ external_bitspercol = 8;
+ else if (external_bitspercol < 1)
+ external_bitspercol = 1;
+ }
+
+ if ((p = strtoke(NULL, ";")) && *p) {
+ if (!strcmp(p, "vga"))
+ external_card_type = IS_VGA;
+ if (!strcmp(p, "mv300"))
+ external_card_type = IS_MV300;
+ }
+
+ if ((p = strtoke(NULL, ";")) && *p) {
+ xres_virtual = simple_strtoul(p, NULL, 10);
+ if (xres_virtual < xres)
+ xres_virtual = xres;
+ if (xres_virtual*yres*depth/8 > len)
+ len=xres_virtual*yres*depth/8;
+ }
+
+ external_xres = xres;
+ external_xres_virtual = xres_virtual;
+ external_yres = yres;
+ external_depth = depth;
+ external_pmode = planes;
+ external_addr = addr;
+ external_len = len;
+
+ if (external_card_type == IS_MV300)
+ switch (external_depth) {
+ case 1:
+ MV300_reg = MV300_reg_1bit;
+ break;
+ case 4:
+ MV300_reg = MV300_reg_4bit;
+ break;
+ case 8:
+ MV300_reg = MV300_reg_8bit;
+ break;
+ }
+
+ ext_invalid:
+ ;
+ }
+#endif /* ATAFB_EXT */
+
+#ifdef ATAFB_FALCON
+ if (*mcap_spec) {
+ char *p;
+ int vmin, vmax, hmin, hmax;
+
+ /* Format for monitor capabilities is: <Vmin>;<Vmax>;<Hmin>;<Hmax>
+ * <V*> vertical freq. in Hz
+ * <H*> horizontal freq. in kHz
+ */
+ if (!(p = strtoke(mcap_spec, ";")) || !*p) goto cap_invalid;
+ vmin = simple_strtoul(p, NULL, 10);
+ if (vmin <= 0) goto cap_invalid;
+ if (!(p = strtoke(NULL, ";")) || !*p) goto cap_invalid;
+ vmax = simple_strtoul(p, NULL, 10);
+ if (vmax <= 0 || vmax <= vmin) goto cap_invalid;
+ if (!(p = strtoke(NULL, ";")) || !*p) goto cap_invalid;
+ hmin = 1000 * simple_strtoul(p, NULL, 10);
+ if (hmin <= 0) goto cap_invalid;
+ if (!(p = strtoke(NULL, "")) || !*p) goto cap_invalid;
+ hmax = 1000 * simple_strtoul(p, NULL, 10);
+ if (hmax <= 0 || hmax <= hmin) goto cap_invalid;
+
+ vfmin = vmin;
+ vfmax = vmax;
+ hfmin = hmin;
+ hfmax = hmax;
+ cap_invalid:
+ ;
+ }
+#endif
+
+ if (*user_mode) {
+ /* Format of user defined video mode is: <xres>;<yres>;<depth>
+ */
+ char *p;
+ int xres, yres, depth, temp;
+
+ if (!(p = strtoke(user_mode, ";")) || !*p) goto user_invalid;
+ xres = simple_strtoul(p, NULL, 10);
+ if (!(p = strtoke(NULL, ";")) || !*p) goto user_invalid;
+ yres = simple_strtoul(p, NULL, 10);
+ if (!(p = strtoke(NULL, "")) || !*p) goto user_invalid;
+ depth = simple_strtoul(p, NULL, 10);
+ if ((temp=get_video_mode("user0"))) {
+ default_par=temp;
+ atari_fb_predefined[default_par-1].xres = xres;
+ atari_fb_predefined[default_par-1].yres = yres;
+ atari_fb_predefined[default_par-1].bits_per_pixel = depth;
+ }
+
+ user_invalid:
+ ;
+ }
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ return(atari_fb_init(NULL));
+}
+
+void cleanup_module(void)
+{
+ /* Not reached because the usecount will never
+ be decremented to zero */
+ unregister_framebuffer(&fb_info);
+ /* TODO: clean up ... */
+}
+#endif /* MODULE */
diff --git a/drivers/video/cyberfb.c b/drivers/video/cyberfb.c
new file mode 100644
index 000000000..ccd83bb01
--- /dev/null
+++ b/drivers/video/cyberfb.c
@@ -0,0 +1,1129 @@
+/*
+ * linux/drivers/video/cyberfb.c -- CyberVision64 frame buffer device
+ *
+ * Copyright (C) 1996 Martin Apel
+ * Geert Uytterhoeven
+ *
+ *
+ * This file is based on the Amiga frame buffer device (amifb.c):
+ *
+ * Copyright (C) 1995 Geert Uytterhoeven
+ *
+ *
+ * History:
+ * - 22 Dec 95: Original version by Martin Apel
+ * - 05 Jan 96: Geert: integration into the current source tree
+ *
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/zorro.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+#include "s3blit.h"
+
+
+#define arraysize(x) (sizeof(x)/sizeof(*(x)))
+
+struct Cyber_fb_par {
+ int xres;
+ int yres;
+ int bpp;
+};
+
+static struct Cyber_fb_par current_par;
+
+static int current_par_valid = 0;
+static int currcon = 0;
+
+static struct display disp;
+static struct fb_info fb_info;
+
+
+/*
+ * Switch for Chipset Independency
+ */
+
+static struct fb_hwswitch {
+
+ /* Initialisation */
+
+ int (*init)(void);
+
+ /* Display Control */
+
+ int (*encode_fix)(struct fb_fix_screeninfo *fix, struct Cyber_fb_par *par);
+ int (*decode_var)(struct fb_var_screeninfo *var, struct Cyber_fb_par *par);
+ int (*encode_var)(struct fb_var_screeninfo *var, struct Cyber_fb_par *par);
+ int (*getcolreg)(u_int regno, u_int *red, u_int *green, u_int *blue,
+ u_int *transp);
+ int (*setcolreg)(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp);
+ void (*blank)(int blank);
+} *fbhw;
+
+
+/*
+ * Frame Buffer Name
+ */
+
+static char Cyber_fb_name[16] = "Cybervision";
+
+
+/*
+ * Cybervision Graphics Board
+ */
+
+#define CYBER8_WIDTH 1152
+#define CYBER8_HEIGHT 886
+#define CYBER8_PIXCLOCK 12500 /* ++Geert: Just a guess */
+
+#if 0
+#define CYBER16_WIDTH 800
+#define CYBER16_HEIGHT 600
+#endif
+#define CYBER16_PIXCLOCK 25000 /* ++Geert: Just a guess */
+
+
+static unsigned int CyberKey = 0;
+static unsigned char Cyber_colour_table [256][4];
+static unsigned long CyberMem;
+static unsigned long CyberSize;
+static volatile char *CyberRegs;
+
+
+/*
+ * Predefined Video Mode Names
+ */
+
+static char *Cyber_fb_modenames[] = {
+
+ /*
+ * Autodetect (Default) Video Mode
+ */
+
+ "default",
+
+ /*
+ * Predefined Video Modes
+ */
+
+ "cyber8", /* Cybervision 8 bpp */
+ "cyber16", /* Cybervision 16 bpp */
+ "800x600x8",
+ "640x480x8",
+
+ /*
+ * Dummy Video Modes
+ */
+
+ "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy",
+ "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy",
+ "dummy", "dummy",
+
+ /*
+ * User Defined Video Modes
+ *
+ * This doesn't work yet!!
+ */
+
+ "user0", "user1", "user2", "user3", "user4", "user5", "user6", "user7"
+};
+
+
+/*
+ * Predefined Video Mode Definitions
+ */
+
+static struct fb_var_screeninfo cyber_fb_predefined[] = {
+
+ /*
+ * Autodetect (Default) Video Mode
+ */
+
+ { 0, },
+
+ /*
+ * Predefined Video Modes
+ */
+
+ {
+ /* Cybervision 8 bpp */
+ CYBER8_WIDTH, CYBER8_HEIGHT, CYBER8_WIDTH, CYBER8_HEIGHT, 0, 0, 8, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
+ FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ }, {
+ /* Cybervision 16 bpp */
+ 800, 600, 800, 600, 0, 0, 16, 0,
+ {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, CYBER16_PIXCLOCK, 64, 96, 35, 12, 112, 2,
+ FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ }, {
+ /* Cybervision 8 bpp */
+ 800, 600, 800, 600, 0, 0, 8, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
+ FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ }, {
+ /* Cybervision 8 bpp */
+ 640, 480, 640, 480, 0, 0, 8, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2,
+ FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ },
+ /*
+ * Dummy Video Modes
+ */
+
+ { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, },
+ { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, },
+
+ /*
+ * User Defined Video Modes
+ */
+
+ { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }
+};
+
+
+#define NUM_TOTAL_MODES arraysize(cyber_fb_predefined)
+#define NUM_PREDEF_MODES (5)
+
+
+static int Cyberfb_inverse = 0;
+#if 0
+static int Cyberfb_Cyber8 = 0; /* Use Cybervision board */
+static int Cyberfb_Cyber16 = 0; /* Use Cybervision board */
+#endif
+static int Cyberfb_mode = 0;
+
+
+/*
+ * Some default modes
+ */
+
+#define CYBER8_DEFMODE (1)
+#define CYBER16_DEFMODE (2)
+
+
+/*
+ * Interface used by the world
+ */
+
+void Cyber_video_setup(char *options, int *ints);
+
+static int Cyber_fb_open(int fbidx);
+static int Cyber_fb_release(int fbidx);
+static int Cyber_fb_get_fix(struct fb_fix_screeninfo *fix, int con);
+static int Cyber_fb_get_var(struct fb_var_screeninfo *var, int con);
+static int Cyber_fb_set_var(struct fb_var_screeninfo *var, int con);
+static int Cyber_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con);
+static int Cyber_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con);
+static int Cyber_fb_pan_display(struct fb_var_screeninfo *var, int con);
+static int Cyber_fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+ u_long arg, int con);
+
+
+/*
+ * Interface to the low level console driver
+ */
+
+unsigned long Cyber_fb_init(unsigned long mem_start);
+static int Cyberfb_switch(int con);
+static int Cyberfb_updatevar(int con);
+static void Cyberfb_blank(int blank);
+static int Cyberfb_setcmap(struct fb_cmap *cmap, int con);
+
+
+/*
+ * Accelerated Functions used by the low level console driver
+ */
+
+void Cyber_WaitQueue(u_short fifo);
+void Cyber_WaitBlit(void);
+void Cyber_BitBLT(u_short curx, u_short cury, u_short destx, u_short desty,
+ u_short width, u_short height, u_short mode);
+void Cyber_RectFill(u_short x, u_short y, u_short width, u_short height,
+ u_short mode, u_short color);
+void Cyber_MoveCursor(u_short x, u_short y);
+
+
+/*
+ * Hardware Specific Routines
+ */
+
+static int Cyber_init(void);
+static int Cyber_encode_fix(struct fb_fix_screeninfo *fix,
+ struct Cyber_fb_par *par);
+static int Cyber_decode_var(struct fb_var_screeninfo *var,
+ struct Cyber_fb_par *par);
+static int Cyber_encode_var(struct fb_var_screeninfo *var,
+ struct Cyber_fb_par *par);
+static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+ u_int *transp);
+static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp);
+static void Cyber_blank(int blank);
+
+
+/*
+ * Internal routines
+ */
+
+static void Cyber_fb_get_par(struct Cyber_fb_par *par);
+static void Cyber_fb_set_par(struct Cyber_fb_par *par);
+static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive);
+static void do_install_cmap(int con);
+static void Cyber_fb_set_disp(int con);
+static int get_video_mode(const char *name);
+
+
+/* -------------------- Hardware specific routines ------------------------- */
+
+
+/*
+ * Initialization
+ *
+ * Set the default video mode for this chipset. If a video mode was
+ * specified on the command line, it will override the default mode.
+ */
+
+static int Cyber_init(void)
+{
+ int i;
+ char size;
+ volatile u_long *CursorBase;
+
+#if 0
+ if (Cyberfb_mode == -1)
+ {
+ if (Cyberfb_Cyber8)
+ Cyberfb_mode = CYBER8_DEFMODE;
+ else
+ Cyberfb_mode = CYBER16_DEFMODE;
+ }
+#endif
+
+ for (i = 0; i < 256; i++)
+
+ for (i = 0; i < 256; i++)
+ {
+ Cyber_colour_table [i][0] = i;
+ Cyber_colour_table [i][1] = i;
+ Cyber_colour_table [i][2] = i;
+ Cyber_colour_table [i][3] = 0;
+ }
+
+ /*
+ * Just clear the thing for the biggest mode.
+ */
+
+ memset ((char*)CyberMem, 0, CYBER8_WIDTH * CYBER8_HEIGHT);
+
+ /* Disable hardware cursor */
+ *(CyberRegs + S3_CRTC_ADR) = S3_REG_LOCK2;
+ *(CyberRegs + S3_CRTC_DATA) = 0xa0;
+ *(CyberRegs + S3_CRTC_ADR) = S3_HGC_MODE;
+ *(CyberRegs + S3_CRTC_DATA) = 0x00;
+ *(CyberRegs + S3_CRTC_ADR) = S3_HWGC_DX;
+ *(CyberRegs + S3_CRTC_DATA) = 0x00;
+ *(CyberRegs + S3_CRTC_ADR) = S3_HWGC_DY;
+ *(CyberRegs + S3_CRTC_DATA) = 0x00;
+
+ /* Get memory size (if not 2MB it is 4MB) */
+ *(CyberRegs + S3_CRTC_ADR) = S3_LAW_CTL;
+ size = *(CyberRegs + S3_CRTC_DATA);
+ if ((size & 0x03) == 0x02)
+ CyberSize = 0x00200000; /* 2 MB */
+ else
+ CyberSize = 0x00400000; /* 4 MB */
+
+ /* Initialize hardware cursor */
+ CursorBase = (u_long *)((char *)(CyberMem) + CyberSize - 0x400);
+ for (i=0; i < 8; i++)
+ {
+ *(CursorBase +(i*4)) = 0xffffff00;
+ *(CursorBase+1+(i*4)) = 0xffff0000;
+ *(CursorBase+2+(i*4)) = 0xffff0000;
+ *(CursorBase+3+(i*4)) = 0xffff0000;
+ }
+ for (i=8; i < 64; i++)
+ {
+ *(CursorBase +(i*4)) = 0xffff0000;
+ *(CursorBase+1+(i*4)) = 0xffff0000;
+ *(CursorBase+2+(i*4)) = 0xffff0000;
+ *(CursorBase+3+(i*4)) = 0xffff0000;
+ }
+
+ Cyber_setcolreg (255, 56, 100, 160, 0);
+ Cyber_setcolreg (254, 0, 0, 0, 0);
+
+ return 0;
+}
+
+
+/*
+ * This function should fill in the `fix' structure based on the
+ * values in the `par' structure.
+ */
+
+static int Cyber_encode_fix(struct fb_fix_screeninfo *fix,
+ struct Cyber_fb_par *par)
+{
+ int i;
+
+ strcpy(fix->id, Cyber_fb_name);
+ fix->smem_start = (caddr_t)CyberMem;
+ fix->smem_len = CyberSize;
+ fix->mmio_start = (unsigned char *)CyberRegs;
+ fix->mmio_len = 0x10000;
+
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->type_aux = 0;
+ if (par->bpp == 8)
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
+ else
+ fix->visual = FB_VISUAL_DIRECTCOLOR;
+
+ fix->xpanstep = 0;
+ fix->ypanstep = 0;
+ fix->ywrapstep = 0;
+ fix->line_length = 0;
+
+ for (i = 0; i < arraysize(fix->reserved); i++)
+ fix->reserved[i] = 0;
+
+ return(0);
+}
+
+
+/*
+ * Get the video params out of `var'. If a value doesn't fit, round
+ * it up, if it's too big, return -EINVAL.
+ */
+
+static int Cyber_decode_var(struct fb_var_screeninfo *var,
+ struct Cyber_fb_par *par)
+{
+#if 1
+ par->xres = var->xres;
+ par->yres = var->yres;
+ par->bpp = var->bits_per_pixel;
+#else
+ if (Cyberfb_Cyber8) {
+ par->xres = CYBER8_WIDTH;
+ par->yres = CYBER8_HEIGHT;
+ par->bpp = 8;
+ } else {
+ par->xres = CYBER16_WIDTH;
+ par->yres = CYBER16_HEIGHT;
+ par->bpp = 16;
+ }
+#endif
+ return(0);
+}
+
+
+/*
+ * Fill the `var' structure based on the values in `par' and maybe
+ * other values read out of the hardware.
+ */
+
+static int Cyber_encode_var(struct fb_var_screeninfo *var,
+ struct Cyber_fb_par *par)
+{
+ int i;
+
+ var->xres = par->xres;
+ var->yres = par->yres;
+ var->xres_virtual = par->xres;
+ var->yres_virtual = par->yres;
+ var->xoffset = 0;
+ var->yoffset = 0;
+
+ var->bits_per_pixel = par->bpp;
+ var->grayscale = 0;
+
+ if (par->bpp == 8) {
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->red.msb_right = 0;
+ var->blue = var->green = var->red;
+ } else {
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->red.msb_right = 0;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->green.msb_right = 0;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->blue.msb_right = 0;
+ }
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ var->transp.msb_right = 0;
+
+ var->nonstd = 0;
+ var->activate = 0;
+
+ var->height = -1;
+ var->width = -1;
+ var->accel = FB_ACCEL_CYBERVISION;
+ var->vmode = FB_VMODE_NONINTERLACED;
+
+ /* Dummy values */
+
+ if (par->bpp == 8)
+ var->pixclock = CYBER8_PIXCLOCK;
+ else
+ var->pixclock = CYBER16_PIXCLOCK;
+ var->sync = 0;
+ var->left_margin = 64;
+ var->right_margin = 96;
+ var->upper_margin = 35;
+ var->lower_margin = 12;
+ var->hsync_len = 112;
+ var->vsync_len = 2;
+
+ for (i = 0; i < arraysize(var->reserved); i++)
+ var->reserved[i] = 0;
+
+ return(0);
+}
+
+
+/*
+ * Set a single color register. The values supplied are already
+ * rounded down to the hardware's capabilities (according to the
+ * entries in the var structure). Return != 0 for invalid regno.
+ */
+
+static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp)
+{
+ if (regno > 255)
+ return (1);
+
+ *(CyberRegs + 0x3c8) = (char)regno;
+ Cyber_colour_table [regno][0] = red & 0xff;
+ Cyber_colour_table [regno][1] = green & 0xff;
+ Cyber_colour_table [regno][2] = blue & 0xff;
+ Cyber_colour_table [regno][3] = transp;
+
+ *(CyberRegs + 0x3c9) = (red & 0xff) >> 2;
+ *(CyberRegs + 0x3c9) = (green & 0xff) >> 2;
+ *(CyberRegs + 0x3c9) = (blue & 0xff) >> 2;
+
+ return (0);
+}
+
+
+/*
+ * Read a single color register and split it into
+ * colors/transparent. Return != 0 for invalid regno.
+ */
+
+static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+ u_int *transp)
+{
+ if (regno >= 256)
+ return (1);
+ *red = Cyber_colour_table [regno][0];
+ *green = Cyber_colour_table [regno][1];
+ *blue = Cyber_colour_table [regno][2];
+ *transp = Cyber_colour_table [regno][3];
+ return (0);
+}
+
+
+/*
+ * (Un)Blank the screen
+ */
+
+void Cyber_blank(int blank)
+{
+ int i;
+
+ if (blank)
+ for (i = 0; i < 256; i++)
+ {
+ *(CyberRegs + 0x3c8) = i;
+ *(CyberRegs + 0x3c9) = 0;
+ *(CyberRegs + 0x3c9) = 0;
+ *(CyberRegs + 0x3c9) = 0;
+ }
+ else
+ for (i = 0; i < 256; i++)
+ {
+ *(CyberRegs + 0x3c8) = i;
+ *(CyberRegs + 0x3c9) = Cyber_colour_table [i][0] >> 2;
+ *(CyberRegs + 0x3c9) = Cyber_colour_table [i][1] >> 2;
+ *(CyberRegs + 0x3c9) = Cyber_colour_table [i][2] >> 2;
+ }
+}
+
+
+/**************************************************************
+ * We are waiting for "fifo" FIFO-slots empty
+ */
+void Cyber_WaitQueue (u_short fifo)
+{
+ u_short status;
+
+ do
+ {
+ status = *((u_short volatile *)(CyberRegs + S3_GP_STAT));
+ }
+ while (status & fifo);
+}
+
+/**************************************************************
+ * We are waiting for Hardware (Graphics Engine) not busy
+ */
+void Cyber_WaitBlit (void)
+{
+ u_short status;
+
+ do
+ {
+ status = *((u_short volatile *)(CyberRegs + S3_GP_STAT));
+ }
+ while (status & S3_HDW_BUSY);
+}
+
+/**************************************************************
+ * BitBLT - Through the Plane
+ */
+void Cyber_BitBLT (u_short curx, u_short cury, u_short destx, u_short desty,
+ u_short width, u_short height, u_short mode)
+{
+ u_short blitcmd = S3_BITBLT;
+
+ /* Set drawing direction */
+ /* -Y, X maj, -X (default) */
+ if (curx > destx)
+ blitcmd |= 0x0020; /* Drawing direction +X */
+ else
+ {
+ curx += (width - 1);
+ destx += (width - 1);
+ }
+
+ if (cury > desty)
+ blitcmd |= 0x0080; /* Drawing direction +Y */
+ else
+ {
+ cury += (height - 1);
+ desty += (height - 1);
+ }
+
+ Cyber_WaitQueue (0x8000);
+
+ *((u_short volatile *)(CyberRegs + S3_PIXEL_CNTL)) = 0xa000;
+ *((u_short volatile *)(CyberRegs + S3_FRGD_MIX)) = (0x0060 | mode);
+
+ *((u_short volatile *)(CyberRegs + S3_CUR_X)) = curx;
+ *((u_short volatile *)(CyberRegs + S3_CUR_Y)) = cury;
+
+ *((u_short volatile *)(CyberRegs + S3_DESTX_DIASTP)) = destx;
+ *((u_short volatile *)(CyberRegs + S3_DESTY_AXSTP)) = desty;
+
+ *((u_short volatile *)(CyberRegs + S3_MIN_AXIS_PCNT)) = height - 1;
+ *((u_short volatile *)(CyberRegs + S3_MAJ_AXIS_PCNT)) = width - 1;
+
+ *((u_short volatile *)(CyberRegs + S3_CMD)) = blitcmd;
+}
+
+/**************************************************************
+ * Rectangle Fill Solid
+ */
+void Cyber_RectFill (u_short x, u_short y, u_short width, u_short height,
+ u_short mode, u_short color)
+{
+ u_short blitcmd = S3_FILLEDRECT;
+
+ Cyber_WaitQueue (0x8000);
+
+ *((u_short volatile *)(CyberRegs + S3_PIXEL_CNTL)) = 0xa000;
+ *((u_short volatile *)(CyberRegs + S3_FRGD_MIX)) = (0x0020 | mode);
+
+ *((u_short volatile *)(CyberRegs + S3_MULT_MISC)) = 0xe000;
+ *((u_short volatile *)(CyberRegs + S3_FRGD_COLOR)) = color;
+
+ *((u_short volatile *)(CyberRegs + S3_CUR_X)) = x;
+ *((u_short volatile *)(CyberRegs + S3_CUR_Y)) = y;
+
+ *((u_short volatile *)(CyberRegs + S3_MIN_AXIS_PCNT)) = height - 1;
+ *((u_short volatile *)(CyberRegs + S3_MAJ_AXIS_PCNT)) = width - 1;
+
+ *((u_short volatile *)(CyberRegs + S3_CMD)) = blitcmd;
+}
+
+
+/**************************************************************
+ * Move cursor to x, y
+ */
+void Cyber_MoveCursor (u_short x, u_short y)
+{
+ *(CyberRegs + S3_CRTC_ADR) = 0x39;
+ *(CyberRegs + S3_CRTC_DATA) = 0xa0;
+
+ *(CyberRegs + S3_CRTC_ADR) = S3_HWGC_ORGX_H;
+ *(CyberRegs + S3_CRTC_DATA) = (char)((x & 0x0700) >> 8);
+ *(CyberRegs + S3_CRTC_ADR) = S3_HWGC_ORGX_L;
+ *(CyberRegs + S3_CRTC_DATA) = (char)(x & 0x00ff);
+
+ *(CyberRegs + S3_CRTC_ADR) = S3_HWGC_ORGY_H;
+ *(CyberRegs + S3_CRTC_DATA) = (char)((y & 0x0700) >> 8);
+ *(CyberRegs + S3_CRTC_ADR) = S3_HWGC_ORGY_L;
+ *(CyberRegs + S3_CRTC_DATA) = (char)(y & 0x00ff);
+}
+
+
+/* -------------------- Interfaces to hardware functions -------------------- */
+
+
+static struct fb_hwswitch Cyber_switch = {
+ Cyber_init, Cyber_encode_fix, Cyber_decode_var, Cyber_encode_var,
+ Cyber_getcolreg, Cyber_setcolreg, Cyber_blank
+};
+
+
+/* -------------------- Generic routines ------------------------------------ */
+
+
+/*
+ * Fill the hardware's `par' structure.
+ */
+
+static void Cyber_fb_get_par(struct Cyber_fb_par *par)
+{
+ if (current_par_valid)
+ *par = current_par;
+ else
+ fbhw->decode_var(&cyber_fb_predefined[Cyberfb_mode], par);
+}
+
+
+static void Cyber_fb_set_par(struct Cyber_fb_par *par)
+{
+ current_par = *par;
+ current_par_valid = 1;
+}
+
+
+static void cyber_set_video(struct fb_var_screeninfo *var)
+{
+ /* Set clipping rectangle to current screen size */
+ *((u_short volatile *)(CyberRegs + 0xbee8)) = 0x1000;
+ *((u_short volatile *)(CyberRegs + 0xbee8)) = 0x2000;
+
+ *((u_short volatile *)(CyberRegs + 0xbee8)) = 0x3000 | (var->yres - 1);
+ *((u_short volatile *)(CyberRegs + 0xbee8)) = 0x4000 | (var->xres - 1);
+}
+
+
+static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive)
+{
+ int err, activate;
+ struct Cyber_fb_par par;
+
+ if ((err = fbhw->decode_var(var, &par)))
+ return(err);
+ activate = var->activate;
+ if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW && isactive)
+ Cyber_fb_set_par(&par);
+ fbhw->encode_var(var, &par);
+ var->activate = activate;
+
+ cyber_set_video(var);
+ return 0;
+}
+
+
+static void do_install_cmap(int con)
+{
+ if (con != currcon)
+ return;
+ if (fb_display[con].cmap.len)
+ fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
+ fbhw->setcolreg);
+ else
+ fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+ &fb_display[con].var, 1, fbhw->setcolreg);
+}
+
+
+/*
+ * Open/Release the frame buffer device
+ */
+
+static int Cyber_fb_open(int fbidx)
+{
+ /*
+ * Nothing, only a usage count for the moment
+ */
+
+ MOD_INC_USE_COUNT;
+ return(0);
+}
+
+static int Cyber_fb_release(int fbidx)
+{
+ MOD_DEC_USE_COUNT;
+ return(0);
+}
+
+
+/*
+ * Get the Fixed Part of the Display
+ */
+
+static int Cyber_fb_get_fix(struct fb_fix_screeninfo *fix, int con)
+{
+ struct Cyber_fb_par par;
+ int error = 0;
+
+ if (con == -1)
+ Cyber_fb_get_par(&par);
+ else
+ error = fbhw->decode_var(&fb_display[con].var, &par);
+ return(error ? error : fbhw->encode_fix(fix, &par));
+}
+
+
+/*
+ * Get the User Defined Part of the Display
+ */
+
+static int Cyber_fb_get_var(struct fb_var_screeninfo *var, int con)
+{
+ struct Cyber_fb_par par;
+ int error = 0;
+
+ if (con == -1) {
+ Cyber_fb_get_par(&par);
+ error = fbhw->encode_var(var, &par);
+ } else
+ *var = fb_display[con].var;
+ return(error);
+}
+
+
+static void Cyber_fb_set_disp(int con)
+{
+ struct fb_fix_screeninfo fix;
+ struct display *display;
+
+ if (con >= 0)
+ display = &fb_display[con];
+ else
+ display = &disp; /* used during initialization */
+
+ Cyber_fb_get_fix(&fix, con);
+ if (con == -1)
+ con = 0;
+ display->screen_base = (u_char *)fix.smem_start;
+ display->visual = fix.visual;
+ display->type = fix.type;
+ display->type_aux = fix.type_aux;
+ display->ypanstep = fix.ypanstep;
+ display->ywrapstep = fix.ywrapstep;
+ display->can_soft_blank = 1;
+ display->inverse = Cyberfb_inverse;
+}
+
+
+/*
+ * Set the User Defined Part of the Display
+ */
+
+static int Cyber_fb_set_var(struct fb_var_screeninfo *var, int con)
+{
+ int err, oldxres, oldyres, oldvxres, oldvyres, oldbpp;
+
+ if ((err = do_fb_set_var(var, con == currcon)))
+ return(err);
+ if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+ oldxres = fb_display[con].var.xres;
+ oldyres = fb_display[con].var.yres;
+ oldvxres = fb_display[con].var.xres_virtual;
+ oldvyres = fb_display[con].var.yres_virtual;
+ oldbpp = fb_display[con].var.bits_per_pixel;
+ fb_display[con].var = *var;
+ if (oldxres != var->xres || oldyres != var->yres ||
+ oldvxres != var->xres_virtual ||
+ oldvyres != var->yres_virtual ||
+ oldbpp != var->bits_per_pixel) {
+ Cyber_fb_set_disp(con);
+ (*fb_info.changevar)(con);
+ fb_alloc_cmap(&fb_display[con].cmap, 0, 0);
+ do_install_cmap(con);
+ }
+ }
+ var->activate = 0;
+ return(0);
+}
+
+
+/*
+ * Get the Colormap
+ */
+
+static int Cyber_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con)
+{
+ if (con == currcon) /* current console? */
+ return(fb_get_cmap(cmap, &fb_display[con].var,
+ kspc, fbhw->getcolreg));
+ else if (fb_display[con].cmap.len) /* non default colormap? */
+ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+ else
+ fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+ cmap, kspc ? 0 : 2);
+ return(0);
+}
+
+
+/*
+ * Set the Colormap
+ */
+
+static int Cyber_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con)
+{
+ int err;
+
+ if (!fb_display[con].cmap.len) { /* no colormap allocated? */
+ if ((err = fb_alloc_cmap(&fb_display[con].cmap,
+ 1<<fb_display[con].var.bits_per_pixel, 0)))
+ return(err);
+ }
+ if (con == currcon) /* current console? */
+ return(fb_set_cmap(cmap, &fb_display[con].var,
+ kspc, fbhw->setcolreg));
+ else
+ fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+ return(0);
+}
+
+
+/*
+ * Pan or Wrap the Display
+ *
+ * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+ */
+
+static int Cyber_fb_pan_display(struct fb_var_screeninfo *var, int con)
+{
+ return(-EINVAL);
+}
+
+
+/*
+ * Cybervision Frame Buffer Specific ioctls
+ */
+
+static int Cyber_fb_ioctl(struct inode *inode, struct file *file,
+ u_int cmd, u_long arg, int con)
+{
+ return(-EINVAL);
+}
+
+
+static struct fb_ops Cyber_fb_ops = {
+ Cyber_fb_open, Cyber_fb_release, Cyber_fb_get_fix, Cyber_fb_get_var,
+ Cyber_fb_set_var, Cyber_fb_get_cmap, Cyber_fb_set_cmap,
+ Cyber_fb_pan_display, Cyber_fb_ioctl
+};
+
+
+__initfunc(void Cyber_video_setup(char *options, int *ints))
+{
+ char *this_opt;
+
+ fb_info.fontname[0] = '\0';
+
+ if (!options || !*options)
+ return;
+
+ for (this_opt = strtok(options, ","); this_opt; this_opt = strtok(NULL, ","))
+ if (!strcmp(this_opt, "inverse")) {
+ Cyberfb_inverse = 1;
+ fb_invert_cmaps();
+ } else if (!strncmp(this_opt, "font:", 5))
+ strcpy(fb_info.fontname, this_opt+5);
+#if 0
+ else if (!strcmp (this_opt, "cyber8"))
+ Cyberfb_Cyber8 = 1;
+ else if (!strcmp (this_opt, "cyber16"))
+ Cyberfb_Cyber16 = 1;
+#endif
+ else
+ Cyberfb_mode = get_video_mode(this_opt);
+}
+
+
+/*
+ * Initialization
+ */
+
+__initfunc(unsigned long Cyber_fb_init(unsigned long mem_start))
+{
+ int err;
+ struct Cyber_fb_par par;
+ unsigned long board_addr;
+ const struct ConfigDev *cd;
+
+ if (!(CyberKey = zorro_find(ZORRO_PROD_PHASE5_CYBERVISION64, 0, 0)))
+ return mem_start;
+
+ cd = zorro_get_board (CyberKey);
+ zorro_config_board (CyberKey, 0);
+ board_addr = (unsigned long)cd->cd_BoardAddr;
+
+ /* This includes the video memory as well as the S3 register set */
+ CyberMem = kernel_map (board_addr + 0x01400000, 0x01000000,
+ KERNELMAP_NOCACHE_SER, &mem_start);
+ CyberRegs = (char*) (CyberMem + 0x00c00000);
+
+ fbhw = &Cyber_switch;
+
+ strcpy(fb_info.modename, Cyber_fb_name);
+ fb_info.changevar = NULL;
+ fb_info.node = -1;
+ fb_info.fbops = &Cyber_fb_ops;
+ fb_info.fbvar_num = NUM_TOTAL_MODES;
+ fb_info.fbvar = cyber_fb_predefined;
+ fb_info.disp = &disp;
+ fb_info.switch_con = &Cyberfb_switch;
+ fb_info.updatevar = &Cyberfb_updatevar;
+ fb_info.blank = &Cyberfb_blank;
+ fb_info.setcmap = &Cyberfb_setcmap;
+
+ err = register_framebuffer(&fb_info);
+ if (err < 0)
+ return mem_start;
+
+ fbhw->init();
+ fbhw->decode_var(&cyber_fb_predefined[Cyberfb_mode], &par);
+ fbhw->encode_var(&cyber_fb_predefined[0], &par);
+
+ do_fb_set_var(&cyber_fb_predefined[0], 1);
+ Cyber_fb_get_var(&fb_display[0].var, -1);
+ Cyber_fb_set_disp(-1);
+ do_install_cmap(0);
+
+ printk("%s frame buffer device, using %ldK of video memory\n",
+ fb_info.modename, CyberSize>>10);
+
+ /* TODO: This driver cannot be unloaded yet */
+ MOD_INC_USE_COUNT;
+
+ return mem_start;
+}
+
+
+static int Cyberfb_switch(int con)
+{
+ /* Do we have to save the colormap? */
+ if (fb_display[currcon].cmap.len)
+ fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1,
+ fbhw->getcolreg);
+
+ do_fb_set_var(&fb_display[con].var, 1);
+ currcon = con;
+ /* Install new colormap */
+ do_install_cmap(con);
+ return(0);
+}
+
+
+/*
+ * Update the `var' structure (called by fbcon.c)
+ *
+ * This call looks only at yoffset and the FB_VMODE_YWRAP flag in `var'.
+ * Since it's called by a kernel driver, no range checking is done.
+ */
+
+static int Cyberfb_updatevar(int con)
+{
+ return(0);
+}
+
+
+/*
+ * Blank the display.
+ */
+
+static void Cyberfb_blank(int blank)
+{
+ fbhw->blank(blank);
+}
+
+
+/*
+ * Set the colormap
+ */
+
+static int Cyberfb_setcmap(struct fb_cmap *cmap, int con)
+{
+ return(Cyber_fb_set_cmap(cmap, 1, con));
+}
+
+
+/*
+ * Get a Video Mode
+ */
+
+static int get_video_mode(const char *name)
+{
+ int i;
+
+ for (i = 1; i < NUM_PREDEF_MODES; i++)
+ if (!strcmp(name, Cyber_fb_modenames[i]))
+ cyber_fb_predefined[0] = cyber_fb_predefined[i];
+ return(i);
+ return(0);
+}
+
+
+#ifdef MODULE
+int init_module(void)
+{
+ return(Cyber_fb_init(NULL));
+}
+
+void cleanup_module(void)
+{
+ /* Not reached because the usecount will never be
+ decremented to zero */
+ unregister_framebuffer(&fb_info);
+ /* TODO: clean up ... */
+}
+#endif /* MODULE */
+
+
+/*
+ * Visible symbols for modules
+ */
+
+EXPORT_SYMBOL(Cyber_BitBLT);
+EXPORT_SYMBOL(Cyber_RectFill);
+EXPORT_SYMBOL(Cyber_WaitBlit);
diff --git a/drivers/video/dn_fb.c b/drivers/video/dn_fb.c
new file mode 100644
index 000000000..f885cdd2c
--- /dev/null
+++ b/drivers/video/dn_fb.c
@@ -0,0 +1,389 @@
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/config.h>
+#include <linux/interrupt.h>
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/apollohw.h>
+#include <linux/fb.h>
+#include <linux/module.h>
+
+/* apollo video HW definitions */
+
+/*
+ * Control Registers. IOBASE + $x
+ *
+ * Note: these are the Memory/IO BASE definitions for a mono card set to the
+ * alternate address
+ *
+ * Control 3A and 3B serve identical functions except that 3A
+ * deals with control 1 and 3b deals with Color LUT reg.
+ */
+
+#define AP_IOBASE 0x5d80 /* Base address of 1 plane board. */
+#define AP_STATUS 0x5d80 /* Status register. Read */
+#define AP_WRITE_ENABLE 0x5d80 /* Write Enable Register Write */
+#define AP_DEVICE_ID 0x5d81 /* Device ID Register. Read */
+#define AP_ROP_1 0x5d82 /* Raster Operation reg. Write Word */
+#define AP_DIAG_MEM_REQ 0x5d84 /* Diagnostic Memory Request. Write Word */
+#define AP_CONTROL_0 0x5d88 /* Control Register 0. Read/Write */
+#define AP_CONTROL_1 0x5d8a /* Control Register 1. Read/Write */
+#define AP_CONTROL_3A 0x5d8e /* Control Register 3a. Read/Write */
+#define AP_CONTROL_2 0x5d8c /* Control Register 2. Read/Write */
+
+
+#define FRAME_BUFFER_START 0x0FA0000
+#define FRAME_BUFFER_LEN 0x40000
+
+/* CREG 0 */
+#define VECTOR_MODE 0x40 /* 010x.xxxx */
+#define DBLT_MODE 0x80 /* 100x.xxxx */
+#define NORMAL_MODE 0xE0 /* 111x.xxxx */
+#define SHIFT_BITS 0x1F /* xxx1.1111 */
+ /* other bits are Shift value */
+
+/* CREG 1 */
+#define AD_BLT 0x80 /* 1xxx.xxxx */
+#define NORMAL 0x80 /* 1xxx.xxxx */ /* What is happening here ?? */
+#define INVERSE 0x00 /* 0xxx.xxxx */ /* Clearing this reverses the screen */
+#define PIX_BLT 0x00 /* 0xxx.xxxx */
+
+#define AD_HIBIT 0x40 /* xIxx.xxxx */
+
+#define ROP_EN 0x10 /* xxx1.xxxx */
+#define DST_EQ_SRC 0x00 /* xxx0.xxxx */
+#define nRESET_SYNC 0x08 /* xxxx.1xxx */
+#define SYNC_ENAB 0x02 /* xxxx.xx1x */
+
+#define BLANK_DISP 0x00 /* xxxx.xxx0 */
+#define ENAB_DISP 0x01 /* xxxx.xxx1 */
+
+#define NORM_CREG1 (nRESET_SYNC | SYNC_ENAB | ENAB_DISP) /* no reset sync */
+
+/* CREG 2 */
+
+/*
+ * Following 3 defines are common to 1, 4 and 8 plane.
+ */
+
+#define S_DATA_1s 0x00 /* 00xx.xxxx */ /* set source to all 1's -- vector drawing */
+#define S_DATA_PIX 0x40 /* 01xx.xxxx */ /* takes source from ls-bits and replicates over 16 bits */
+#define S_DATA_PLN 0xC0 /* 11xx.xxxx */ /* normal, each data access =16-bits in
+ one plane of image mem */
+
+/* CREG 3A/CREG 3B */
+# define RESET_CREG 0x80 /* 1000.0000 */
+
+/* ROP REG - all one nibble */
+/* ********* NOTE : this is used r0,r1,r2,r3 *********** */
+#define ROP(r2,r3,r0,r1) ( (U_SHORT)((r0)|((r1)<<4)|((r2)<<8)|((r3)<<12)) )
+#define DEST_ZERO 0x0
+#define SRC_AND_DEST 0x1
+#define SRC_AND_nDEST 0x2
+#define SRC 0x3
+#define nSRC_AND_DEST 0x4
+#define DEST 0x5
+#define SRC_XOR_DEST 0x6
+#define SRC_OR_DEST 0x7
+#define SRC_NOR_DEST 0x8
+#define SRC_XNOR_DEST 0x9
+#define nDEST 0xA
+#define SRC_OR_nDEST 0xB
+#define nSRC 0xC
+#define nSRC_OR_DEST 0xD
+#define SRC_NAND_DEST 0xE
+#define DEST_ONE 0xF
+
+#define SWAP(A) ((A>>8) | ((A&0xff) <<8))
+
+#if 0
+#define outb(a,d) *(char *)(a)=(d)
+#define outw(a,d) *(unsigned short *)a=d
+#endif
+
+
+void dn_video_setup(char *options, int *ints);
+
+/* frame buffer operations */
+
+static int dn_fb_open(int fbidx);
+static int dn_fb_release(int fbidx);
+static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con);
+static int dn_fb_get_var(struct fb_var_screeninfo *var, int con);
+static int dn_fb_set_var(struct fb_var_screeninfo *var, int isactive);
+static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con);
+static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con);
+static int dn_fb_pan_display(struct fb_var_screeninfo *var, int con);
+static int dn_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg, int con);
+
+static int dnfbcon_switch(int con);
+static int dnfbcon_updatevar(int con);
+static void dnfbcon_blank(int blank);
+
+static void dn_fb_set_disp(int con);
+
+static struct display disp[MAX_NR_CONSOLES];
+static struct fb_info fb_info;
+static struct fb_ops dn_fb_ops = {
+ dn_fb_open,dn_fb_release, dn_fb_get_fix, dn_fb_get_var, dn_fb_set_var,
+ dn_fb_get_cmap, dn_fb_set_cmap, dn_fb_pan_display, dn_fb_ioctl
+};
+
+static int currcon=0;
+
+#define NUM_TOTAL_MODES 1
+struct fb_var_screeninfo dn_fb_predefined[] = {
+
+ { 0, },
+
+};
+
+static char dn_fb_name[]="Apollo ";
+
+static int dn_fb_open(int fbidx)
+{
+ /*
+ * Nothing, only a usage count for the moment
+ */
+
+ MOD_INC_USE_COUNT;
+ return(0);
+}
+
+static int dn_fb_release(int fbidx)
+{
+ MOD_DEC_USE_COUNT;
+ return(0);
+}
+
+static int dn_fb_get_fix(struct fb_fix_screeninfo *fix, int con) {
+
+ memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+ strcpy(fix->id,"Apollo Mono");
+ fix->smem_start=(char*)(FRAME_BUFFER_START+IO_BASE);
+ fix->smem_len=FRAME_BUFFER_LEN;
+ fix->type=FB_TYPE_PACKED_PIXELS;
+ fix->type_aux=0;
+ fix->visual=FB_VISUAL_MONO10;
+ fix->xpanstep=0;
+ fix->ypanstep=0;
+ fix->ywrapstep=0;
+ fix->line_length=256;
+
+ return 0;
+
+}
+
+static int dn_fb_get_var(struct fb_var_screeninfo *var, int con) {
+
+ var->xres=1280;
+ var->yres=1024;
+ var->xres_virtual=2048;
+ var->yres_virtual=1024;
+ var->xoffset=0;
+ var->yoffset=0;
+ var->bits_per_pixel=1;
+ var->grayscale=0;
+ var->nonstd=0;
+ var->activate=0;
+ var->height=-1;
+ var->width=-1;
+ var->pixclock=0;
+ var->left_margin=0;
+ var->right_margin=0;
+ var->hsync_len=0;
+ var->vsync_len=0;
+ var->sync=0;
+ var->vmode=FB_VMODE_NONINTERLACED;
+ var->accel=FB_ACCEL_NONE;
+
+ return 0;
+
+}
+
+static int dn_fb_set_var(struct fb_var_screeninfo *var, int isactive) {
+
+ printk("fb_set_var\n");
+ if(var->xres!=1280)
+ return -EINVAL;
+ if(var->yres!=1024)
+ return -EINVAL;
+ if(var->xres_virtual!=2048)
+ return -EINVAL;
+ if(var->yres_virtual!=1024)
+ return -EINVAL;
+ if(var->xoffset!=0)
+ return -EINVAL;
+ if(var->yoffset!=0)
+ return -EINVAL;
+ if(var->bits_per_pixel!=1)
+ return -EINVAL;
+ if(var->grayscale!=0)
+ return -EINVAL;
+ if(var->nonstd!=0)
+ return -EINVAL;
+ if(var->activate!=0)
+ return -EINVAL;
+ if(var->pixclock!=0)
+ return -EINVAL;
+ if(var->left_margin!=0)
+ return -EINVAL;
+ if(var->right_margin!=0)
+ return -EINVAL;
+ if(var->hsync_len!=0)
+ return -EINVAL;
+ if(var->vsync_len!=0)
+ return -EINVAL;
+ if(var->sync!=0)
+ return -EINVAL;
+ if(var->vmode!=FB_VMODE_NONINTERLACED)
+ return -EINVAL;
+ if(var->accel!=FB_ACCEL_NONE)
+ return -EINVAL;
+
+ return 0;
+
+}
+
+static int dn_fb_get_cmap(struct fb_cmap *cmap,int kspc,int con) {
+
+ printk("get cmap not supported\n");
+
+ return -EINVAL;
+}
+
+static int dn_fb_set_cmap(struct fb_cmap *cmap,int kspc,int con) {
+
+ printk("set cmap not supported\n");
+
+ return -EINVAL;
+
+}
+
+static int dn_fb_pan_display(struct fb_var_screeninfo *var, int con) {
+
+ printk("panning not supported\n");
+
+ return -EINVAL;
+
+}
+
+static int dn_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg, int con) {
+
+ printk("no IOCTLs as of yet.\n");
+
+ return -EINVAL;
+
+}
+
+static void dn_fb_set_disp(int con) {
+
+ struct fb_fix_screeninfo fix;
+
+ dn_fb_get_fix(&fix,con);
+ if(con==-1)
+ con=0;
+
+ disp[con].screen_base = (u_char *)fix.smem_start;
+printk("screenbase: %p\n",fix.smem_start);
+ disp[con].visual = fix.visual;
+ disp[con].type = fix.type;
+ disp[con].type_aux = fix.type_aux;
+ disp[con].ypanstep = fix.ypanstep;
+ disp[con].ywrapstep = fix.ywrapstep;
+ disp[con].can_soft_blank = 1;
+ disp[con].inverse = 0;
+ disp[con].line_length = fix.line_length;
+}
+
+unsigned long dn_fb_init(unsigned long mem_start) {
+
+ int err;
+
+printk("dn_fb_init\n");
+
+ fb_info.changevar=NULL;
+ strcpy(&fb_info.modename[0],dn_fb_name);
+ fb_info.fontname[0]=0;
+ fb_info.disp=disp;
+ fb_info.switch_con=&dnfbcon_switch;
+ fb_info.updatevar=&dnfbcon_updatevar;
+ fb_info.blank=&dnfbcon_blank;
+ fb_info.node = -1;
+ fb_info.fbops = &dn_fb_ops;
+ fb_info.fbvar = dn_fb_predefined;
+ fb_info.fbvar_num = NUM_TOTAL_MODES;
+
+printk("dn_fb_init: register\n");
+ err=register_framebuffer(&fb_info);
+ if(err < 0) {
+ panic("unable to register apollo frame buffer\n");
+ }
+
+ /* now we have registered we can safely setup the hardware */
+
+ outb(RESET_CREG, AP_CONTROL_3A);
+ outw(0x0, AP_WRITE_ENABLE);
+ outb(NORMAL_MODE,AP_CONTROL_0);
+ outb((AD_BLT | DST_EQ_SRC | NORM_CREG1), AP_CONTROL_1);
+ outb(S_DATA_PLN, AP_CONTROL_2);
+ outw(SWAP(0x3),AP_ROP_1);
+
+ printk("apollo frame buffer alive and kicking !\n");
+
+
+ dn_fb_get_var(&disp[0].var,0);
+
+ dn_fb_set_disp(-1);
+
+ return mem_start;
+
+}
+
+
+static int dnfbcon_switch(int con) {
+
+ currcon=con;
+
+ return 0;
+
+}
+
+static int dnfbcon_updatevar(int con) {
+
+ return 0;
+
+}
+
+static void dnfbcon_blank(int blank) {
+
+ printk("dnfbcon_blank: %d\n",blank);
+ if(blank) {
+ outb(0, AP_CONTROL_3A);
+ outb((AD_BLT | DST_EQ_SRC | NORM_CREG1) & ~ENAB_DISP,
+ AP_CONTROL_1);
+ }
+ else {
+ outb(1, AP_CONTROL_3A);
+ outb((AD_BLT | DST_EQ_SRC | NORM_CREG1), AP_CONTROL_1);
+ }
+
+ return ;
+
+}
+
+void dn_video_setup(char *options, int *ints) {
+
+ return;
+
+}
+
diff --git a/drivers/video/fbcmap.c b/drivers/video/fbcmap.c
new file mode 100644
index 000000000..9f3c586c7
--- /dev/null
+++ b/drivers/video/fbcmap.c
@@ -0,0 +1,323 @@
+/*
+ * linux/drivers/video/fbcmap.c -- Colormap handling for frame buffer devices
+ *
+ * Created 15 Jun 1997 by Geert Uytterhoeven
+ *
+ * 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/module.h>
+#include <linux/tty.h>
+#include <linux/fb.h>
+#include <linux/slab.h>
+
+#include <asm/uaccess.h>
+
+
+static void memcpy_fs(int fsfromto, void *to, void *from, int len)
+{
+ switch (fsfromto) {
+ case 0:
+ memcpy(to, from, len);
+ return;
+ case 1:
+ copy_from_user(to, from, len);
+ return;
+ case 2:
+ copy_to_user(to, from, len);
+ return;
+ }
+}
+
+#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7fff-(val))>>16)
+#define CNVT_FROMHW(val,width) (((width) ? ((((val)<<16)-(val)) / \
+ ((1<<(width))-1)) : 0))
+
+static u_short red2[] = {
+ 0x0000, 0xaaaa
+};
+static u_short green2[] = {
+ 0x0000, 0xaaaa
+};
+static u_short blue2[] = {
+ 0x0000, 0xaaaa
+};
+
+static u_short red4[] = {
+ 0x0000, 0xaaaa, 0x5555, 0xffff
+};
+static u_short green4[] = {
+ 0x0000, 0xaaaa, 0x5555, 0xffff
+};
+static u_short blue4[] = {
+ 0x0000, 0xaaaa, 0x5555, 0xffff
+};
+
+static u_short red8[] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa
+};
+static u_short green8[] = {
+ 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x0000, 0x0000, 0xaaaa, 0xaaaa
+};
+static u_short blue8[] = {
+ 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa
+};
+
+static u_short red16[] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa,
+ 0x5555, 0x5555, 0x5555, 0x5555, 0xffff, 0xffff, 0xffff, 0xffff
+};
+static u_short green16[] = {
+ 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x0000, 0x0000, 0xaaaa, 0xaaaa,
+ 0x5555, 0x5555, 0xffff, 0xffff, 0x5555, 0x5555, 0xffff, 0xffff
+};
+static u_short blue16[] = {
+ 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa,
+ 0x5555, 0xffff, 0x5555, 0xffff, 0x5555, 0xffff, 0x5555, 0xffff
+};
+
+static struct fb_cmap default_2_colors = {
+ 0, 2, red2, green2, blue2, NULL
+};
+static struct fb_cmap default_8_colors = {
+ 0, 8, red8, green8, blue8, NULL
+};
+static struct fb_cmap default_4_colors = {
+ 0, 4, red4, green4, blue4, NULL
+};
+static struct fb_cmap default_16_colors = {
+ 0, 16, red16, green16, blue16, NULL
+};
+
+
+ /*
+ * Allocate a colormap
+ */
+
+int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp)
+{
+ int size = len*sizeof(u_short);
+
+ if (cmap->len != len) {
+ if (cmap->red)
+ kfree(cmap->red);
+ if (cmap->green)
+ kfree(cmap->green);
+ if (cmap->blue)
+ kfree(cmap->blue);
+ if (cmap->transp)
+ kfree(cmap->transp);
+ cmap->red = cmap->green = cmap->blue = cmap->transp = NULL;
+ cmap->len = 0;
+ if (!len)
+ return 0;
+ if (!(cmap->red = kmalloc(size, GFP_ATOMIC)))
+ return -1;
+ if (!(cmap->green = kmalloc(size, GFP_ATOMIC)))
+ return -1;
+ if (!(cmap->blue = kmalloc(size, GFP_ATOMIC)))
+ return -1;
+ if (transp) {
+ if (!(cmap->transp = kmalloc(size, GFP_ATOMIC)))
+ return -1;
+ } else
+ cmap->transp = NULL;
+ }
+ cmap->start = 0;
+ cmap->len = len;
+ fb_copy_cmap(fb_default_cmap(len), cmap, 0);
+ return 0;
+}
+
+
+ /*
+ * Copy a colormap
+ */
+
+void fb_copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto)
+{
+ int size;
+ int tooff = 0, fromoff = 0;
+
+ if (to->start > from->start)
+ fromoff = to->start-from->start;
+ else
+ tooff = from->start-to->start;
+ size = to->len-tooff;
+ if (size > from->len-fromoff)
+ size = from->len-fromoff;
+ if (size < 0)
+ return;
+ size *= sizeof(u_short);
+ memcpy_fs(fsfromto, to->red+tooff, from->red+fromoff, size);
+ memcpy_fs(fsfromto, to->green+tooff, from->green+fromoff, size);
+ memcpy_fs(fsfromto, to->blue+tooff, from->blue+fromoff, size);
+ if (from->transp && to->transp)
+ memcpy_fs(fsfromto, to->transp+tooff, from->transp+fromoff, size);
+}
+
+
+ /*
+ * Get the colormap for a screen
+ */
+
+int fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, int kspc,
+ int (*getcolreg)(u_int, u_int *, u_int *, u_int *, u_int *))
+{
+ int i, start;
+ u_short *red, *green, *blue, *transp;
+ u_int hred, hgreen, hblue, htransp;
+
+ red = cmap->red;
+ green = cmap->green;
+ blue = cmap->blue;
+ transp = cmap->transp;
+ start = cmap->start;
+ if (start < 0)
+ return -EINVAL;
+ for (i = 0; i < cmap->len; i++) {
+ if (getcolreg(start++, &hred, &hgreen, &hblue, &htransp))
+ return 0;
+ hred = CNVT_FROMHW(hred, var->red.length);
+ hgreen = CNVT_FROMHW(hgreen, var->green.length);
+ hblue = CNVT_FROMHW(hblue, var->blue.length);
+ htransp = CNVT_FROMHW(htransp, var->transp.length);
+ if (kspc) {
+ *red = hred;
+ *green = hgreen;
+ *blue = hblue;
+ if (transp)
+ *transp = htransp;
+ } else {
+ put_user(hred, red);
+ put_user(hgreen, green);
+ put_user(hblue, blue);
+ if (transp)
+ put_user(htransp, transp);
+ }
+ red++;
+ green++;
+ blue++;
+ if (transp)
+ transp++;
+ }
+ return 0;
+}
+
+
+ /*
+ * Set the colormap for a screen
+ */
+
+int fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, int kspc,
+ int (*setcolreg)(u_int, u_int, u_int, u_int, u_int))
+{
+ int i, start;
+ u_short *red, *green, *blue, *transp;
+ u_int hred, hgreen, hblue, htransp;
+
+ red = cmap->red;
+ green = cmap->green;
+ blue = cmap->blue;
+ transp = cmap->transp;
+ start = cmap->start;
+
+ if (start < 0)
+ return -EINVAL;
+ for (i = 0; i < cmap->len; i++) {
+ if (kspc) {
+ hred = *red;
+ hgreen = *green;
+ hblue = *blue;
+ htransp = transp ? *transp : 0;
+ } else {
+ get_user(hred, red);
+ get_user(hgreen, green);
+ get_user(hblue, blue);
+ if (transp)
+ get_user(htransp, transp);
+ else
+ htransp = 0;
+ }
+ hred = CNVT_TOHW(hred, var->red.length);
+ hgreen = CNVT_TOHW(hgreen, var->green.length);
+ hblue = CNVT_TOHW(hblue, var->blue.length);
+ htransp = CNVT_TOHW(htransp, var->transp.length);
+ red++;
+ green++;
+ blue++;
+ if (transp)
+ transp++;
+ if (setcolreg(start++, hred, hgreen, hblue, htransp))
+ return 0;
+ }
+ return 0;
+}
+
+
+ /*
+ * Get the default colormap for a specific screen depth
+ */
+
+struct fb_cmap *fb_default_cmap(int bpp)
+{
+ switch (bpp) {
+ case 1:
+ return &default_2_colors;
+ break;
+ case 2:
+ return &default_4_colors;
+ break;
+ case 3:
+ return &default_8_colors;
+ break;
+ default:
+ return &default_16_colors;
+ break;
+ }
+}
+
+
+ /*
+ * Invert all default colormaps
+ */
+
+void fb_invert_cmaps(void)
+{
+ u_int i;
+
+ for (i = 0; i < 2; i++) {
+ red2[i] = ~red2[i];
+ green2[i] = ~green2[i];
+ blue2[i] = ~blue2[i];
+ }
+ for (i = 0; i < 4; i++) {
+ red4[i] = ~red4[i];
+ green4[i] = ~green4[i];
+ blue4[i] = ~blue4[i];
+ }
+ for (i = 0; i < 8; i++) {
+ red8[i] = ~red8[i];
+ green8[i] = ~green8[i];
+ blue8[i] = ~blue8[i];
+ }
+ for (i = 0; i < 16; i++) {
+ red16[i] = ~red16[i];
+ green16[i] = ~green16[i];
+ blue16[i] = ~blue16[i];
+ }
+}
+
+
+ /*
+ * Visible symbols for modules
+ */
+
+EXPORT_SYMBOL(fb_alloc_cmap);
+EXPORT_SYMBOL(fb_copy_cmap);
+EXPORT_SYMBOL(fb_get_cmap);
+EXPORT_SYMBOL(fb_set_cmap);
+EXPORT_SYMBOL(fb_default_cmap);
+EXPORT_SYMBOL(fb_invert_cmaps);
diff --git a/drivers/video/fbcon-afb.c b/drivers/video/fbcon-afb.c
new file mode 100644
index 000000000..8cb692f8e
--- /dev/null
+++ b/drivers/video/fbcon-afb.c
@@ -0,0 +1,304 @@
+/*
+ * linux/drivers/video/afb.c -- Low level frame buffer operations for
+ * bitplanes à la Amiga
+ *
+ * Created 5 Apr 1997 by Geert Uytterhoeven
+ *
+ * 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/module.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/fb.h>
+
+#include "fbcon.h"
+
+
+ /*
+ * Prototypes
+ */
+
+static int open_afb(struct display *p);
+static void release_afb(void);
+static void bmove_afb(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width);
+static void clear_afb(struct vc_data *conp, struct display *p, int sy, int sx,
+ int height, int width);
+static void putc_afb(struct vc_data *conp, struct display *p, int c, int yy,
+ int xx);
+static void putcs_afb(struct vc_data *conp, struct display *p, const char *s,
+ int count, int yy, int xx);
+static void rev_char_afb(struct display *p, int xx, int yy);
+
+
+ /*
+ * `switch' for the low level operations
+ */
+
+static struct display_switch dispsw_afb = {
+ open_afb, release_afb, bmove_afb, clear_afb, putc_afb, putcs_afb,
+ rev_char_afb
+};
+
+ /*
+ * Bitplanes à la Amiga
+ */
+
+static int open_afb(struct display *p)
+{
+ if (p->type != FB_TYPE_PLANES)
+ return -EINVAL;
+
+ if (p->line_length)
+ p->next_line = p->line_length;
+ else
+ p->next_line = p->var.xres_virtual>>3;
+ p->next_plane = p->var.yres_virtual*p->next_line;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void release_afb(void)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+static void bmove_afb(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width)
+{
+ u_char *src, *dest, *src0, *dest0;
+ u_int i, rows;
+
+ if (sx == 0 && dx == 0 && width == p->next_line) {
+ src = p->screen_base+sy*p->fontheight*width;
+ dest = p->screen_base+dy*p->fontheight*width;
+ for (i = p->var.bits_per_pixel; i--;) {
+ mymemmove(dest, src, height*p->fontheight*width);
+ src += p->next_plane;
+ dest += p->next_plane;
+ }
+ } else if (dy <= sy) {
+ src0 = p->screen_base+sy*p->fontheight*p->next_line+sx;
+ dest0 = p->screen_base+dy*p->fontheight*p->next_line+dx;
+ for (i = p->var.bits_per_pixel; i--;) {
+ src = src0;
+ dest = dest0;
+ for (rows = height*p->fontheight; rows--;) {
+ mymemmove(dest, src, width);
+ src += p->next_line;
+ dest += p->next_line;
+ }
+ src0 += p->next_plane;
+ dest0 += p->next_plane;
+ }
+ } else {
+ src0 = p->screen_base+(sy+height)*p->fontheight*p->next_line+sx;
+ dest0 = p->screen_base+(dy+height)*p->fontheight*p->next_line+dx;
+ for (i = p->var.bits_per_pixel; i--;) {
+ src = src0;
+ dest = dest0;
+ for (rows = height*p->fontheight; rows--;) {
+ src -= p->next_line;
+ dest -= p->next_line;
+ mymemmove(dest, src, width);
+ }
+ src0 += p->next_plane;
+ dest0 += p->next_plane;
+ }
+ }
+}
+
+static void clear_afb(struct vc_data *conp, struct display *p, int sy, int sx,
+ int height, int width)
+{
+ u_char *dest, *dest0;
+ u_int i, rows;
+ int bg;
+
+ dest0 = p->screen_base+sy*p->fontheight*p->next_line+sx;
+
+ bg = attr_bgcol_ec(p,conp);
+ for (i = p->var.bits_per_pixel; i--; dest0 += p->next_plane) {
+ dest = dest0;
+ for (rows = height*p->fontheight; rows--; dest += p->next_line)
+ if (bg & 1)
+ mymemset(dest, width);
+ else
+ mymemclear(dest, width);
+ bg >>= 1;
+ }
+}
+
+static void putc_afb(struct vc_data *conp, struct display *p, int c, int yy,
+ int xx)
+{
+ u_char *dest, *dest0, *cdat, *cdat0;
+ u_int rows, i;
+ u_char d;
+ int fg, bg;
+
+ c &= 0xff;
+
+ dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx;
+ cdat0 = p->fontdata+c*p->fontheight;
+ fg = attr_fgcol(p,conp);
+ bg = attr_bgcol(p,conp);
+
+ for (i = p->var.bits_per_pixel; i--; dest0 += p->next_plane) {
+ dest = dest0;
+ cdat = cdat0;
+ for (rows = p->fontheight; rows--; dest += p->next_line) {
+ d = *cdat++;
+ if (bg & 1)
+ if (fg & 1)
+ *dest = 0xff;
+ else
+ *dest = ~d;
+ else
+ if (fg & 1)
+ *dest = d;
+ else
+ *dest = 0x00;
+ }
+ bg >>= 1;
+ fg >>= 1;
+ }
+}
+
+ /*
+ * I've split the console character loop in two parts
+ * (cfr. fbcon_putcs_ilbm())
+ */
+
+static void putcs_afb(struct vc_data *conp, struct display *p, const char *s,
+ int count, int yy, int xx)
+{
+ u_char *dest, *dest0, *dest1;
+ u_char *cdat1, *cdat2, *cdat3, *cdat4, *cdat10, *cdat20, *cdat30, *cdat40;
+ u_int rows, i;
+ u_char c1, c2, c3, c4;
+ u_long d;
+ int fg0, bg0, fg, bg;
+
+ dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx;
+ fg0 = attr_fgcol(p,conp);
+ bg0 = attr_bgcol(p,conp);
+
+ while (count--)
+ if (xx&3 || count < 3) { /* Slow version */
+ c1 = *s++;
+ dest1 = dest0++;
+ xx++;
+
+ cdat10 = p->fontdata+c1*p->fontheight;
+ fg = fg0;
+ bg = bg0;
+
+ for (i = p->var.bits_per_pixel; i--; dest1 += p->next_plane) {
+ dest = dest1;
+ cdat1 = cdat10;
+ for (rows = p->fontheight; rows--; dest += p->next_line) {
+ d = *cdat1++;
+ if (bg & 1)
+ if (fg & 1)
+ *dest = 0xff;
+ else
+ *dest = ~d;
+ else
+ if (fg & 1)
+ *dest = d;
+ else
+ *dest = 0x00;
+ }
+ bg >>= 1;
+ fg >>= 1;
+ }
+ } else { /* Fast version */
+ c1 = s[0];
+ c2 = s[1];
+ c3 = s[2];
+ c4 = s[3];
+
+ dest1 = dest0;
+ cdat10 = p->fontdata+c1*p->fontheight;
+ cdat20 = p->fontdata+c2*p->fontheight;
+ cdat30 = p->fontdata+c3*p->fontheight;
+ cdat40 = p->fontdata+c4*p->fontheight;
+ fg = fg0;
+ bg = bg0;
+
+ for (i = p->var.bits_per_pixel; i--; dest1 += p->next_plane) {
+ dest = dest1;
+ cdat1 = cdat10;
+ cdat2 = cdat20;
+ cdat3 = cdat30;
+ cdat4 = cdat40;
+ for (rows = p->fontheight; rows--; dest += p->next_line) {
+ d = *cdat1++<<24 | *cdat2++<<16 | *cdat3++<<8 | *cdat4++;
+ if (bg & 1)
+ if (fg & 1)
+ *(u_long *)dest = 0xffffffff;
+ else
+ *(u_long *)dest = ~d;
+ else
+ if (fg & 1)
+ *(u_long *)dest = d;
+ else
+ *(u_long *)dest = 0x00000000;
+ }
+ bg >>= 1;
+ fg >>= 1;
+ }
+ s += 4;
+ dest0 += 4;
+ xx += 4;
+ count -= 3;
+ }
+}
+
+static void rev_char_afb(struct display *p, int xx, int yy)
+{
+ u_char *dest, *dest0;
+ u_int rows, i;
+ int mask;
+
+ dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx;
+ mask = p->fgcol ^ p->bgcol;
+
+ /*
+ * This should really obey the individual character's
+ * background and foreground colors instead of simply
+ * inverting.
+ */
+
+ for (i = p->var.bits_per_pixel; i--; dest0 += p->next_plane) {
+ if (mask & 1) {
+ dest = dest0;
+ for (rows = p->fontheight; rows--; dest += p->next_line)
+ *dest = ~*dest;
+ }
+ mask >>= 1;
+ }
+}
+
+
+#ifdef MODULE
+int init_module(void)
+#else
+int fbcon_init_afb(void)
+#endif
+{
+ return(fbcon_register_driver(&dispsw_afb, 0));
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ fbcon_unregister_driver(&dispsw_afb);
+}
+#endif /* MODULE */
diff --git a/drivers/video/fbcon-cfb16.c b/drivers/video/fbcon-cfb16.c
new file mode 100644
index 000000000..817322d4a
--- /dev/null
+++ b/drivers/video/fbcon-cfb16.c
@@ -0,0 +1,233 @@
+/*
+ * linux/drivers/video/cfb16.c -- Low level frame buffer operations for 16 bpp
+ * packed pixels
+ *
+ * Created 5 Apr 1997 by Geert Uytterhoeven
+ *
+ * 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/module.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/fb.h>
+
+#include "fbcon.h"
+
+
+ /*
+ * Prototypes
+ */
+
+static int open_cfb16(struct display *p);
+static void release_cfb16(void);
+static void bmove_cfb16(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width);
+static void clear_cfb16(struct vc_data *conp, struct display *p, int sy,
+ int sx, int height, int width);
+static void putc_cfb16(struct vc_data *conp, struct display *p, int c,
+ int yy, int xx);
+static void putcs_cfb16(struct vc_data *conp, struct display *p,
+ const char *s, int count, int yy, int xx);
+static void rev_char_cfb16(struct display *p, int xx, int yy);
+
+
+ /*
+ * `switch' for the low level operations
+ */
+
+static struct display_switch dispsw_cfb16 = {
+ open_cfb16, release_cfb16, bmove_cfb16, clear_cfb16, putc_cfb16,
+ putcs_cfb16, rev_char_cfb16
+};
+
+
+ /*
+ * 16 bpp packed pixels
+ */
+
+u_short packed16_cmap[16];
+
+static u_long tab_cfb16[] = {
+ 0x00000000,0x0000ffff,0xffff0000,0xffffffff
+};
+
+static int open_cfb16(struct display *p)
+{
+ if (p->type != FB_TYPE_PACKED_PIXELS || p->var.bits_per_pixel != 16)
+ return -EINVAL;
+
+ p->next_line = p->var.xres_virtual<<1;
+ p->next_plane = 0;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void release_cfb16(void)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+static void bmove_cfb16(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width)
+{
+ int bytes = p->next_line, linesize = bytes * p->fontheight, rows;
+ u_char *src,*dst;
+
+ if (sx == 0 && dx == 0 && width * 16 == bytes)
+ mymemmove(p->screen_base + dy * linesize,
+ p->screen_base + sy * linesize,
+ height * linesize);
+ else if (dy < sy || (dy == sy && dx < sx)) {
+ src = p->screen_base + sy * linesize + sx * 16;
+ dst = p->screen_base + dy * linesize + dx * 16;
+ for (rows = height * p->fontheight ; rows-- ;) {
+ mymemmove(dst, src, width * 16);
+ src += bytes;
+ dst += bytes;
+ }
+ } else {
+ src = p->screen_base + (sy+height) * linesize + sx * 16 - bytes;
+ dst = p->screen_base + (dy+height) * linesize + dx * 16 - bytes;
+ for (rows = height * p->fontheight ; rows-- ;) {
+ mymemmove(dst, src, width * 16);
+ src -= bytes;
+ dst -= bytes;
+ }
+ }
+}
+
+static void clear_cfb16(struct vc_data *conp, struct display *p, int sy,
+ int sx, int height, int width)
+{
+ u_char *dest0,*dest;
+ int bytes=p->next_line,lines=height * p->fontheight, rows, i;
+ u_long bgx;
+
+ dest = p->screen_base + sy * p->fontheight * bytes + sx * 16;
+
+ bgx = attr_bgcol_ec(p,conp);
+ bgx = packed16_cmap[bgx];
+ bgx |= (bgx << 16);
+
+ if (sx == 0 && width * 16 == bytes)
+ for (i = 0 ; i < lines * width ; i++) {
+ ((u_long *)dest)[0]=bgx;
+ ((u_long *)dest)[1]=bgx;
+ ((u_long *)dest)[2]=bgx;
+ ((u_long *)dest)[3]=bgx;
+ dest+=16;
+ }
+ else {
+ dest0=dest;
+ for (rows = lines; rows-- ; dest0 += bytes) {
+ dest=dest0;
+ for (i = 0 ; i < width ; i++) {
+ ((u_long *)dest)[0]=bgx;
+ ((u_long *)dest)[1]=bgx;
+ ((u_long *)dest)[2]=bgx;
+ ((u_long *)dest)[3]=bgx;
+ dest+=16;
+ }
+ }
+ }
+}
+
+static void putc_cfb16(struct vc_data *conp, struct display *p, int c, int yy,
+ int xx)
+{
+ u_char *dest,*cdat;
+ int bytes=p->next_line,rows;
+ ulong eorx,fgx,bgx;
+
+ c &= 0xff;
+
+ dest = p->screen_base + yy * p->fontheight * bytes + xx * 16;
+ cdat = p->fontdata + c * p->fontheight;
+
+ fgx = attr_fgcol(p,conp);
+ fgx = packed16_cmap[fgx];
+ bgx = attr_bgcol(p,conp);
+ bgx = packed16_cmap[bgx];
+ fgx |= (fgx << 16);
+ bgx |= (bgx << 16);
+ eorx = fgx ^ bgx;
+
+ for (rows = p->fontheight ; rows-- ; dest += bytes) {
+ ((u_long *)dest)[0]= (tab_cfb16[*cdat >> 6] & eorx) ^ bgx;
+ ((u_long *)dest)[1]= (tab_cfb16[*cdat >> 4 & 0x3] & eorx) ^ bgx;
+ ((u_long *)dest)[2]= (tab_cfb16[*cdat >> 2 & 0x3] & eorx) ^ bgx;
+ ((u_long *)dest)[3]= (tab_cfb16[*cdat++ & 0x3] & eorx) ^ bgx;
+ }
+}
+
+static void putcs_cfb16(struct vc_data *conp, struct display *p, const char *s,
+ int count, int yy, int xx)
+{
+ u_char *cdat, c, *dest, *dest0;
+ int rows,bytes=p->next_line;
+ u_long eorx, fgx, bgx;
+
+ dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 16;
+ fgx = attr_fgcol(p,conp);
+ fgx = packed16_cmap[fgx];
+ bgx = attr_bgcol(p,conp);
+ bgx = packed16_cmap[bgx];
+ fgx |= (fgx << 16);
+ bgx |= (bgx << 16);
+ eorx = fgx ^ bgx;
+ while (count--) {
+ c = *s++;
+ cdat = p->fontdata + c * p->fontheight;
+
+ for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) {
+ ((u_long *)dest)[0]= (tab_cfb16[*cdat >> 6] & eorx) ^ bgx;
+ ((u_long *)dest)[1]= (tab_cfb16[*cdat >> 4 & 0x3] & eorx) ^ bgx;
+ ((u_long *)dest)[2]= (tab_cfb16[*cdat >> 2 & 0x3] & eorx) ^ bgx;
+ ((u_long *)dest)[3]= (tab_cfb16[*cdat++ & 0x3] & eorx) ^ bgx;
+ }
+ dest0+=16;
+ }
+}
+
+static void rev_char_cfb16(struct display *p, int xx, int yy)
+{
+ u_char *dest;
+ int bytes=p->next_line, rows;
+
+ dest = p->screen_base + yy * p->fontheight * bytes + xx * 16;
+ for (rows = p->fontheight ; rows-- ; dest += bytes) {
+ ((u_long *)dest)[0] ^= 0xffffffff;
+ ((u_long *)dest)[1] ^= 0xffffffff;
+ ((u_long *)dest)[2] ^= 0xffffffff;
+ ((u_long *)dest)[3] ^= 0xffffffff;
+ }
+}
+
+
+#ifdef MODULE
+int init_module(void)
+#else
+int fbcon_init_cfb16(void)
+#endif
+{
+ return(fbcon_register_driver(&dispsw_cfb16, 0));
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ fbcon_unregister_driver(&dispsw_cfb16);
+}
+#endif /* MODULE */
+
+
+ /*
+ * Visible symbols for modules
+ */
+
+EXPORT_SYMBOL(packed16_cmap);
diff --git a/drivers/video/fbcon-cfb8.c b/drivers/video/fbcon-cfb8.c
new file mode 100644
index 000000000..5c5296b7a
--- /dev/null
+++ b/drivers/video/fbcon-cfb8.c
@@ -0,0 +1,218 @@
+/*
+ * linux/drivers/video/cfb8.c -- Low level frame buffer operations for 8 bpp
+ * packed pixels
+ *
+ * Created 5 Apr 1997 by Geert Uytterhoeven
+ *
+ * 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/module.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/fb.h>
+
+#include "fbcon.h"
+
+
+ /*
+ * Prototypes
+ */
+
+static int open_cfb8(struct display *p);
+static void release_cfb8(void);
+static void bmove_cfb8(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width);
+static void clear_cfb8(struct vc_data *conp, struct display *p, int sy,
+ int sx, int height, int width);
+static void putc_cfb8(struct vc_data *conp, struct display *p, int c, int yy,
+ int xx);
+static void putcs_cfb8(struct vc_data *conp, struct display *p,
+ const char *s, int count, int yy, int xx);
+static void rev_char_cfb8(struct display *p, int xx, int yy);
+
+
+ /*
+ * `switch' for the low level operations
+ */
+
+static struct display_switch dispsw_cfb8 = {
+ open_cfb8, release_cfb8, bmove_cfb8, clear_cfb8, putc_cfb8, putcs_cfb8,
+ rev_char_cfb8
+};
+
+
+ /*
+ * 8 bpp packed pixels
+ */
+
+static u_long nibbletab_cfb8[] = {
+ 0x00000000,0x000000ff,0x0000ff00,0x0000ffff,
+ 0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff,
+ 0xff000000,0xff0000ff,0xff00ff00,0xff00ffff,
+ 0xffff0000,0xffff00ff,0xffffff00,0xffffffff
+};
+
+static int open_cfb8(struct display *p)
+{
+ if (p->type != FB_TYPE_PACKED_PIXELS || p->var.bits_per_pixel != 8)
+ return -EINVAL;
+
+ p->next_line = p->var.xres_virtual;
+ p->next_plane = 0;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void release_cfb8(void)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+static void bmove_cfb8(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width)
+{
+ int bytes = p->next_line, linesize = bytes * p->fontheight, rows;
+ u_char *src,*dst;
+
+ if (sx == 0 && dx == 0 && width * 8 == bytes)
+ mymemmove(p->screen_base + dy * linesize,
+ p->screen_base + sy * linesize,
+ height * linesize);
+ else if (dy < sy || (dy == sy && dx < sx)) {
+ src = p->screen_base + sy * linesize + sx * 8;
+ dst = p->screen_base + dy * linesize + dx * 8;
+ for (rows = height * p->fontheight ; rows-- ;) {
+ mymemmove(dst, src, width * 8);
+ src += bytes;
+ dst += bytes;
+ }
+ } else {
+ src = p->screen_base + (sy+height) * linesize + sx * 8 - bytes;
+ dst = p->screen_base + (dy+height) * linesize + dx * 8 - bytes;
+ for (rows = height * p->fontheight ; rows-- ;) {
+ mymemmove(dst, src, width * 8);
+ src -= bytes;
+ dst -= bytes;
+ }
+ }
+}
+
+static void clear_cfb8(struct vc_data *conp, struct display *p, int sy, int sx,
+ int height, int width)
+{
+ u_char *dest0,*dest;
+ int bytes=p->next_line,lines=height * p->fontheight, rows, i;
+ u_long bgx;
+
+ dest = p->screen_base + sy * p->fontheight * bytes + sx * 8;
+
+ bgx=attr_bgcol_ec(p,conp);
+ bgx |= (bgx << 8);
+ bgx |= (bgx << 16);
+
+ if (sx == 0 && width * 8 == bytes)
+ for (i = 0 ; i < lines * width ; i++) {
+ ((u_long *)dest)[0]=bgx;
+ ((u_long *)dest)[1]=bgx;
+ dest+=8;
+ }
+ else {
+ dest0=dest;
+ for (rows = lines; rows-- ; dest0 += bytes) {
+ dest=dest0;
+ for (i = 0 ; i < width ; i++) {
+ ((u_long *)dest)[0]=bgx;
+ ((u_long *)dest)[1]=bgx;
+ dest+=8;
+ }
+ }
+ }
+}
+
+static void putc_cfb8(struct vc_data *conp, struct display *p, int c, int yy,
+ int xx)
+{
+ u_char *dest,*cdat;
+ int bytes=p->next_line,rows;
+ ulong eorx,fgx,bgx;
+
+ c &= 0xff;
+
+ dest = p->screen_base + yy * p->fontheight * bytes + xx * 8;
+ cdat = p->fontdata + c * p->fontheight;
+
+ fgx=attr_fgcol(p,conp);
+ bgx=attr_bgcol(p,conp);
+ fgx |= (fgx << 8);
+ fgx |= (fgx << 16);
+ bgx |= (bgx << 8);
+ bgx |= (bgx << 16);
+ eorx = fgx ^ bgx;
+
+ for (rows = p->fontheight ; rows-- ; dest += bytes) {
+ ((u_long *)dest)[0]= (nibbletab_cfb8[*cdat >> 4] & eorx) ^ bgx;
+ ((u_long *)dest)[1]= (nibbletab_cfb8[*cdat++ & 0xf] & eorx) ^ bgx;
+ }
+}
+
+static void putcs_cfb8(struct vc_data *conp, struct display *p, const char *s,
+ int count, int yy, int xx)
+{
+ u_char *cdat, c, *dest, *dest0;
+ int rows,bytes=p->next_line;
+ u_long eorx, fgx, bgx;
+
+ dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 8;
+ fgx=attr_fgcol(p,conp);
+ bgx=attr_bgcol(p,conp);
+ fgx |= (fgx << 8);
+ fgx |= (fgx << 16);
+ bgx |= (bgx << 8);
+ bgx |= (bgx << 16);
+ eorx = fgx ^ bgx;
+ while (count--) {
+ c = *s++;
+ cdat = p->fontdata + c * p->fontheight;
+
+ for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) {
+ ((u_long *)dest)[0]= (nibbletab_cfb8[*cdat >> 4] & eorx) ^ bgx;
+ ((u_long *)dest)[1]= (nibbletab_cfb8[*cdat++ & 0xf] & eorx) ^
+ bgx;
+ }
+ dest0+=8;
+ }
+}
+
+static void rev_char_cfb8(struct display *p, int xx, int yy)
+{
+ u_char *dest;
+ int bytes=p->next_line, rows;
+
+ dest = p->screen_base + yy * p->fontheight * bytes + xx * 8;
+ for (rows = p->fontheight ; rows-- ; dest += bytes) {
+ ((u_long *)dest)[0] ^= 0x0f0f0f0f;
+ ((u_long *)dest)[1] ^= 0x0f0f0f0f;
+ }
+}
+
+
+#ifdef MODULE
+int init_module(void)
+#else
+int fbcon_init_cfb8(void)
+#endif
+{
+ return(fbcon_register_driver(&dispsw_cfb8, 0));
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ fbcon_unregister_driver(&dispsw_cfb8);
+}
+#endif /* MODULE */
diff --git a/drivers/video/fbcon-cyber.c b/drivers/video/fbcon-cyber.c
new file mode 100644
index 000000000..d12be1db6
--- /dev/null
+++ b/drivers/video/fbcon-cyber.c
@@ -0,0 +1,230 @@
+/*
+ * linux/drivers/video/cyber.c -- Low level frame buffer operations for the
+ * CyberVision64 (accelerated)
+ *
+ * Created 5 Apr 1997 by Geert Uytterhoeven
+ *
+ * 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/module.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/fb.h>
+
+#include "fbcon.h"
+#include "s3blit.h"
+
+
+ /*
+ * Prototypes
+ */
+
+static int open_cyber(struct display *p);
+static void release_cyber(void);
+static void bmove_cyber(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width);
+static void clear_cyber(struct vc_data *conp, struct display *p, int sy, int sx,
+ int height, int width);
+static void putc_cyber(struct vc_data *conp, struct display *p, int c, int yy,
+ int xx);
+static void putcs_cyber(struct vc_data *conp, struct display *p, const char *s,
+ int count, int yy, int xx);
+static void rev_char_cyber(struct display *p, int xx, int yy);
+
+
+ /*
+ * Acceleration functions in cyberfb.c
+ */
+
+extern void Cyber_WaitQueue(unsigned short fifo);
+extern void Cyber_WaitBlit(void);
+extern void Cyber_BitBLT(unsigned short curx, unsigned short cury,
+ unsigned short destx, unsigned short desty,
+ unsigned short width, unsigned short height,
+ unsigned short mode);
+extern void Cyber_RectFill(unsigned short xx, unsigned short yy,
+ unsigned short width, unsigned short height,
+ unsigned short mode, unsigned short fillcolor);
+extern void Cyber_MoveCursor(unsigned short xx, unsigned short yy);
+
+
+ /*
+ * `switch' for the low level operations
+ */
+
+static struct display_switch dispsw_cyber = {
+ open_cyber, release_cyber, bmove_cyber, clear_cyber, putc_cyber,
+ putcs_cyber, rev_char_cyber
+};
+
+
+ /*
+ * CyberVision64 (accelerated)
+ */
+
+static int open_cyber(struct display *p)
+{
+ if (p->type != FB_TYPE_PACKED_PIXELS ||
+ p->var.accel != FB_ACCEL_CYBERVISION)
+ return -EINVAL;
+
+ p->next_line = p->var.xres_virtual*p->var.bits_per_pixel>>3;
+ p->next_plane = 0;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void release_cyber(void)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+static void bmove_cyber(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width)
+{
+ sx *= 8; dx *= 8; width *= 8;
+ Cyber_BitBLT((u_short)sx, (u_short)(sy*p->fontheight), (u_short)dx,
+ (u_short)(dy*p->fontheight), (u_short)width,
+ (u_short)(height*p->fontheight), (u_short)S3_NEW);
+}
+
+static void clear_cyber(struct vc_data *conp, struct display *p, int
+ sy, int sx, int height, int width)
+{
+ unsigned char bg;
+
+ sx *= 8; width *= 8;
+ bg = attr_bgcol_ec(p,conp);
+ Cyber_RectFill((u_short)sx,
+ (u_short)(sy*p->fontheight),
+ (u_short)width,
+ (u_short)(height*p->fontheight),
+ (u_short)S3_NEW,
+ (u_short)bg);
+}
+
+static void putc_cyber(struct vc_data *conp, struct display *p, int c, int yy,
+ int xx)
+{
+ u_char *dest, *cdat;
+ u_long tmp;
+ u_int rows, revs, underl;
+ u_char d;
+ u_char fg, bg;
+
+ c &= 0xff;
+
+ dest = p->screen_base+yy*p->fontheight*p->next_line+8*xx;
+ cdat = p->fontdata+(c*p->fontheight);
+ fg = p->fgcol;
+ bg = p->bgcol;
+ revs = conp->vc_reverse;
+ underl = conp->vc_underline;
+
+ Cyber_WaitBlit();
+ for (rows = p->fontheight; rows--; dest += p->next_line) {
+ d = *cdat++;
+
+ if (underl && !rows)
+ d = 0xff;
+ if (revs)
+ d = ~d;
+
+ tmp = ((d & 0x80) ? fg : bg) << 24;
+ tmp |= ((d & 0x40) ? fg : bg) << 16;
+ tmp |= ((d & 0x20) ? fg : bg) << 8;
+ tmp |= ((d & 0x10) ? fg : bg);
+ *((u_long*) dest) = tmp;
+ tmp = ((d & 0x8) ? fg : bg) << 24;
+ tmp |= ((d & 0x4) ? fg : bg) << 16;
+ tmp |= ((d & 0x2) ? fg : bg) << 8;
+ tmp |= ((d & 0x1) ? fg : bg);
+ *((u_long*) dest + 1) = tmp;
+ }
+}
+
+static void putcs_cyber(struct vc_data *conp, struct display *p, const char *s,
+ int count, int yy, int xx)
+{
+ u_char *dest, *dest0, *cdat;
+ u_long tmp;
+ u_int rows, underl;
+ u_char c, d;
+ u_char fg, bg;
+
+ dest0 = p->screen_base+yy*p->fontheight*p->next_line+8*xx;
+ fg = p->fgcol;
+ bg = p->bgcol;
+ underl = conp->vc_underline;
+
+ Cyber_WaitBlit();
+ while (count--) {
+ c = *s++;
+ dest = dest0;
+ dest0 += 8;
+ cdat = p->fontdata+(c*p->fontheight);
+ for (rows = p->fontheight; rows--; dest += p->next_line) {
+ d = *cdat++;
+
+ if (underl && !rows)
+ d = 0xff;
+
+ tmp = ((d & 0x80) ? fg : bg) << 24;
+ tmp |= ((d & 0x40) ? fg : bg) << 16;
+ tmp |= ((d & 0x20) ? fg : bg) << 8;
+ tmp |= ((d & 0x10) ? fg : bg);
+ *((u_long*) dest) = tmp;
+ tmp = ((d & 0x8) ? fg : bg) << 24;
+ tmp |= ((d & 0x4) ? fg : bg) << 16;
+ tmp |= ((d & 0x2) ? fg : bg) << 8;
+ tmp |= ((d & 0x1) ? fg : bg);
+ *((u_long*) dest + 1) = tmp;
+ }
+ }
+}
+
+
+static void rev_char_cyber(struct display *p, int xx, int yy)
+{
+ unsigned char *dest;
+ unsigned int rows;
+ unsigned char fg, bg;
+
+ fg = p->fgcol;
+ bg = p->bgcol;
+
+ dest = p->screen_base+yy*p->fontheight*p->next_line+8*xx;
+ Cyber_WaitBlit();
+ for (rows = p->fontheight; rows--; dest += p->next_line) {
+ *dest = (*dest == fg) ? bg : fg;
+ *(dest+1) = (*(dest + 1) == fg) ? bg : fg;
+ *(dest+2) = (*(dest + 2) == fg) ? bg : fg;
+ *(dest+3) = (*(dest + 3) == fg) ? bg : fg;
+ *(dest+4) = (*(dest + 4) == fg) ? bg : fg;
+ *(dest+5) = (*(dest + 5) == fg) ? bg : fg;
+ *(dest+6) = (*(dest + 6) == fg) ? bg : fg;
+ *(dest+7) = (*(dest + 7) == fg) ? bg : fg;
+ }
+}
+
+
+#ifdef MODULE
+int init_module(void)
+#else
+int fbcon_init_cyber(void)
+#endif
+{
+ return(fbcon_register_driver(&dispsw_cyber, 1));
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ fbcon_unregister_driver(&dispsw_cyber);
+}
+#endif /* MODULE */
diff --git a/drivers/video/fbcon-ilbm.c b/drivers/video/fbcon-ilbm.c
new file mode 100644
index 000000000..0bbe3f90b
--- /dev/null
+++ b/drivers/video/fbcon-ilbm.c
@@ -0,0 +1,301 @@
+/*
+ * linux/drivers/video/ilbm.c -- Low level frame buffer operations for
+ * interleaved bitplanes à la Amiga
+ *
+ * Created 5 Apr 1997 by Geert Uytterhoeven
+ *
+ * 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/module.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/fb.h>
+
+#include "fbcon.h"
+
+
+ /*
+ * Prototypes
+ */
+
+static int open_ilbm(struct display *p);
+static void release_ilbm(void);
+static void bmove_ilbm(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width);
+static void clear_ilbm(struct vc_data *conp, struct display *p, int sy, int sx,
+ int height, int width);
+static void putc_ilbm(struct vc_data *conp, struct display *p, int c, int yy,
+ int xx);
+static void putcs_ilbm(struct vc_data *conp, struct display *p, const char *s,
+ int count, int yy, int xx);
+static void rev_char_ilbm(struct display *p, int xx, int yy);
+
+
+ /*
+ * `switch' for the low level operations
+ */
+
+static struct display_switch dispsw_ilbm = {
+ open_ilbm, release_ilbm, bmove_ilbm, clear_ilbm, putc_ilbm, putcs_ilbm,
+ rev_char_ilbm
+};
+
+
+ /*
+ * Interleaved bitplanes à la Amiga
+ *
+ * This code heavily relies on the fact that
+ *
+ * next_line == interleave == next_plane*bits_per_pixel
+ *
+ * But maybe it can be merged with the code for normal bitplanes without
+ * much performance loss?
+ */
+
+static int open_ilbm(struct display *p)
+{
+ if (p->type != FB_TYPE_INTERLEAVED_PLANES || p->type_aux == 2)
+ return -EINVAL;
+
+ if (p->line_length) {
+ p->next_line = p->line_length*p->var.bits_per_pixel;
+ p->next_plane = p->line_length;
+ } else {
+ p->next_line = p->type_aux;
+ p->next_plane = p->type_aux/p->var.bits_per_pixel;
+ }
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void release_ilbm(void)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+static void bmove_ilbm(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width)
+{
+ if (sx == 0 && dx == 0 && width == p->next_plane)
+ mymemmove(p->screen_base+dy*p->fontheight*p->next_line,
+ p->screen_base+sy*p->fontheight*p->next_line,
+ height*p->fontheight*p->next_line);
+ else {
+ u_char *src, *dest;
+ u_int i;
+
+ if (dy <= sy) {
+ src = p->screen_base+sy*p->fontheight*p->next_line+sx;
+ dest = p->screen_base+dy*p->fontheight*p->next_line+dx;
+ for (i = p->var.bits_per_pixel*height*p->fontheight; i--;) {
+ mymemmove(dest, src, width);
+ src += p->next_plane;
+ dest += p->next_plane;
+ }
+ } else {
+ src = p->screen_base+(sy+height)*p->fontheight*p->next_line+sx;
+ dest = p->screen_base+(dy+height)*p->fontheight*p->next_line+dx;
+ for (i = p->var.bits_per_pixel*height*p->fontheight; i--;) {
+ src -= p->next_plane;
+ dest -= p->next_plane;
+ mymemmove(dest, src, width);
+ }
+ }
+ }
+}
+
+static void clear_ilbm(struct vc_data *conp, struct display *p, int sy, int sx,
+ int height, int width)
+{
+ u_char *dest;
+ u_int i, rows;
+ int bg, bg0;
+
+ dest = p->screen_base+sy*p->fontheight*p->next_line+sx;
+
+ bg0 = attr_bgcol_ec(p,conp);
+ for (rows = height*p->fontheight; rows--;) {
+ bg = bg0;
+ for (i = p->var.bits_per_pixel; i--; dest += p->next_plane) {
+ if (bg & 1)
+ mymemset(dest, width);
+ else
+ mymemclear(dest, width);
+ bg >>= 1;
+ }
+ }
+}
+
+static void putc_ilbm(struct vc_data *conp, struct display *p, int c, int yy,
+ int xx)
+{
+ u_char *dest, *cdat;
+ u_int rows, i;
+ u_char d;
+ int fg0, bg0, fg, bg;
+
+ c &= 0xff;
+
+ dest = p->screen_base+yy*p->fontheight*p->next_line+xx;
+ cdat = p->fontdata+c*p->fontheight;
+ fg0 = attr_fgcol(p,conp);
+ bg0 = attr_bgcol(p,conp);
+
+ for (rows = p->fontheight; rows--;) {
+ d = *cdat++;
+ fg = fg0;
+ bg = bg0;
+ for (i = p->var.bits_per_pixel; i--; dest += p->next_plane) {
+ if (bg & 1)
+ if (fg & 1)
+ *dest = 0xff;
+ else
+ *dest = ~d;
+ else
+ if (fg & 1)
+ *dest = d;
+ else
+ *dest = 0x00;
+ bg >>= 1;
+ fg >>= 1;
+ }
+ }
+}
+
+ /*
+ * I've split the console character loop in two parts:
+ *
+ * - slow version: this blits one character at a time
+ *
+ * - fast version: this blits 4 characters at a time at a longword
+ * aligned address, to reduce the number of expensive
+ * Chip RAM accesses.
+ *
+ * Experiments on my A4000/040 revealed that this makes a console switch
+ * on a 640x400 screen with 256 colors about 3 times faster.
+ *
+ * -- Geert
+ */
+
+static void putcs_ilbm(struct vc_data *conp, struct display *p, const char *s,
+ int count, int yy, int xx)
+{
+ u_char *dest0, *dest, *cdat1, *cdat2, *cdat3, *cdat4;
+ u_int rows, i;
+ u_char c1, c2, c3, c4;
+ u_long d;
+ int fg0, bg0, fg, bg;
+
+ dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx;
+ fg0 = attr_fgcol(p,conp);
+ bg0 = attr_bgcol(p,conp);
+
+ while (count--)
+ if (xx&3 || count < 3) { /* Slow version */
+ c1 = *s++;
+ dest = dest0++;
+ xx++;
+
+ cdat1 = p->fontdata+c1*p->fontheight;
+ for (rows = p->fontheight; rows--;) {
+ d = *cdat1++;
+ fg = fg0;
+ bg = bg0;
+ for (i = p->var.bits_per_pixel; i--; dest += p->next_plane) {
+ if (bg & 1)
+ if (fg & 1)
+ *dest = 0xff;
+ else
+ *dest = ~d;
+ else
+ if (fg & 1)
+ *dest = d;
+ else
+ *dest = 0x00;
+ bg >>= 1;
+ fg >>= 1;
+ }
+ }
+ } else { /* Fast version */
+ c1 = s[0];
+ c2 = s[1];
+ c3 = s[2];
+ c4 = s[3];
+
+ dest = dest0;
+ cdat1 = p->fontdata+c1*p->fontheight;
+ cdat2 = p->fontdata+c2*p->fontheight;
+ cdat3 = p->fontdata+c3*p->fontheight;
+ cdat4 = p->fontdata+c4*p->fontheight;
+ for (rows = p->fontheight; rows--;) {
+ d = *cdat1++<<24 | *cdat2++<<16 | *cdat3++<<8 | *cdat4++;
+ fg = fg0;
+ bg = bg0;
+ for (i = p->var.bits_per_pixel; i--; dest += p->next_plane) {
+ if (bg & 1)
+ if (fg & 1)
+ *(u_long *)dest = 0xffffffff;
+ else
+ *(u_long *)dest = ~d;
+ else
+ if (fg & 1)
+ *(u_long *)dest = d;
+ else
+ *(u_long *)dest = 0x00000000;
+ bg >>= 1;
+ fg >>= 1;
+ }
+ }
+ s += 4;
+ dest0 += 4;
+ xx += 4;
+ count -= 3;
+ }
+}
+
+static void rev_char_ilbm(struct display *p, int xx, int yy)
+{
+ u_char *dest, *dest0;
+ u_int rows, i;
+ int mask;
+
+ dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx;
+ mask = p->fgcol ^ p->bgcol;
+
+ /*
+ * This should really obey the individual character's
+ * background and foreground colors instead of simply
+ * inverting.
+ */
+
+ for (i = p->var.bits_per_pixel; i--; dest0 += p->next_plane) {
+ if (mask & 1) {
+ dest = dest0;
+ for (rows = p->fontheight; rows--; dest += p->next_line)
+ *dest = ~*dest;
+ }
+ mask >>= 1;
+ }
+}
+
+
+#ifdef MODULE
+int init_module(void)
+#else
+int fbcon_init_ilbm(void)
+#endif
+{
+ return(fbcon_register_driver(&dispsw_ilbm, 0));
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ fbcon_unregister_driver(&dispsw_ilbm);
+}
+#endif /* MODULE */
diff --git a/drivers/video/fbcon-iplan2p2.c b/drivers/video/fbcon-iplan2p2.c
new file mode 100644
index 000000000..4654b2712
--- /dev/null
+++ b/drivers/video/fbcon-iplan2p2.c
@@ -0,0 +1,443 @@
+/*
+ * linux/drivers/video/iplan2p2.c -- Low level frame buffer operations for
+ * interleaved bitplanes à la Atari (2
+ * planes, 2 bytes interleave)
+ *
+ * Created 5 Apr 1997 by Geert Uytterhoeven
+ *
+ * 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/module.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/fb.h>
+
+#include "fbcon.h"
+
+
+#ifndef __mc68000__
+#error No support for non-m68k yet
+#endif
+
+
+ /*
+ * Prototypes
+ */
+
+static int open_iplan2p2(struct display *p);
+static void release_iplan2p2(void);
+static void bmove_iplan2p2(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width);
+static void clear_iplan2p2(struct vc_data *conp, struct display *p, int sy,
+ int sx, int height, int width);
+static void putc_iplan2p2(struct vc_data *conp, struct display *p, int c,
+ int yy, int xx);
+static void putcs_iplan2p2(struct vc_data *conp, struct display *p,
+ const char *s, int count, int yy, int xx);
+static void rev_char_iplan2p2(struct display *display, int xx, int yy);
+
+
+ /*
+ * `switch' for the low level operations
+ */
+
+static struct display_switch dispsw_iplan2p2 = {
+ open_iplan2p2, release_iplan2p2, bmove_iplan2p2, clear_iplan2p2,
+ putc_iplan2p2, putcs_iplan2p2, rev_char_iplan2p2
+};
+
+
+ /*
+ * Interleaved bitplanes à la Atari (2 planes, 2 bytes interleave)
+ */
+
+/* Increment/decrement 2 plane addresses */
+
+#define INC_2P(p) do { if (!((long)(++(p)) & 1)) (p) += 2; } while(0)
+#define DEC_2P(p) do { if ((long)(--(p)) & 1) (p) -= 2; } while(0)
+
+ /* Convert a standard 4 bit color to our 2 bit color assignment:
+ * If at least two RGB channels are active, the low bit is turned on;
+ * The intensity bit (b3) is shifted into b1.
+ */
+
+#define COLOR_2P(c) (((c & 7) >= 3 && (c & 7) != 4) | (c & 8) >> 2)
+
+
+/* Sets the bytes in the visible column at d, height h, to the value
+ * val for a 2 plane screen. The the bis of the color in 'color' are
+ * moved (8 times) to the respective bytes. This means:
+ *
+ * for(h times; d += bpr)
+ * *d = (color & 1) ? 0xff : 0;
+ * *(d+2) = (color & 2) ? 0xff : 0;
+ */
+
+static __inline__ void memclear_2p_col(void *d, size_t h, u_short val, int bpr)
+{
+#ifdef __mc68000__
+ __asm__ __volatile__ ("1: movepw %4,%0@(0)\n\t"
+ "addal %5,%0\n\t"
+ "dbra %1,1b"
+ : "=a" (d), "=d" (h)
+ : "0" (d), "1" (h - 1), "d" (val), "r" (bpr));
+#else /* !m68k */
+#endif /* !m68k */
+}
+
+/* Sets a 2 plane region from 'd', length 'count' bytes, to the color
+ * in val1. 'd' has to be an even address and count must be divisible
+ * by 8, because only whole words and all planes are accessed. I.e.:
+ *
+ * for(count/4 times)
+ * *d = *(d+1) = (color & 1) ? 0xff : 0;
+ * *(d+2) = *(d+3) = (color & 2) ? 0xff : 0;
+ */
+
+static __inline__ void memset_even_2p(void *d, size_t count, u_long val)
+{
+ u_long *dd = d;
+
+ count /= 4;
+ while (count--)
+ *dd++ = val;
+}
+
+/* Copies a 2 plane column from 's', height 'h', to 'd'. */
+
+static __inline__ void memmove_2p_col (void *d, void *s, int h, int bpr)
+{
+ u_char *dd = d, *ss = s;
+
+ while (h--) {
+ dd[0] = ss[0];
+ dd[2] = ss[2];
+ dd += bpr;
+ ss += bpr;
+ }
+}
+
+
+/* This expands a 2 bit color into a short for movepw (2 plane) operations. */
+
+static __inline__ u_short expand2w(u_char c)
+{
+ u_short rv;
+
+#ifdef __mc68000__
+ __asm__ __volatile__ ("lsrb #1,%2\n\t"
+ "scs %0\n\t"
+ "lsll #8,%0\n\t"
+ "lsrb #1,%2\n\t"
+ "scs %0\n\t"
+ : "=&d" (rv), "=d" (c)
+ : "1" (c));
+#endif /* !m68k */
+ return(rv);
+}
+
+/* This expands a 2 bit color into one long for a movel operation
+ * (2 planes).
+ */
+
+static __inline__ u_long expand2l(u_char c)
+{
+ u_long rv;
+
+#ifdef __mc68000__
+ __asm__ __volatile__ ("lsrb #1,%2\n\t"
+ "scs %0\n\t"
+ "extw %0\n\t"
+ "swap %0\n\t"
+ "lsrb #1,%2\n\t"
+ "scs %0\n\t"
+ "extw %0\n\t"
+ : "=&d" (rv), "=d" (c)
+ : "1" (c));
+#endif /* !m68k */
+ return rv;
+}
+
+
+/* This duplicates a byte 2 times into a short. */
+
+static __inline__ u_short dup2w(u_char c)
+{
+ ushort rv;
+
+#ifdef __mc68000__
+ __asm__ __volatile__ ("moveb %1,%0\n\t"
+ "lslw #8,%0\n\t"
+ "moveb %1,%0\n\t"
+ : "=&d" (rv)
+ : "d" (c));
+#endif /* !m68k */
+ return( rv );
+}
+
+
+static int open_iplan2p2(struct display *p)
+{
+ if (p->type != FB_TYPE_INTERLEAVED_PLANES || p->type_aux != 2 ||
+ p->var.bits_per_pixel != 2)
+ return -EINVAL;
+
+ p->next_line = p->var.xres_virtual>>2;
+ p->next_plane = 2;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void release_iplan2p2(void)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+static void bmove_iplan2p2(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width)
+{
+ /* bmove() has to distinguish two major cases: If both, source and
+ * destination, start at even addresses or both are at odd
+ * addresses, just the first odd and last even column (if present)
+ * require special treatment (memmove_col()). The rest between
+ * then can be copied by normal operations, because all adjacent
+ * bytes are affected and are to be stored in the same order.
+ * The pathological case is when the move should go from an odd
+ * address to an even or vice versa. Since the bytes in the plane
+ * words must be assembled in new order, it seems wisest to make
+ * all movements by memmove_col().
+ */
+
+ if (sx == 0 && dx == 0 && width == p->next_line/2) {
+ /* Special (but often used) case: Moving whole lines can be
+ * done with memmove()
+ */
+ mymemmove(p->screen_base + dy * p->next_line * p->fontheight,
+ p->screen_base + sy * p->next_line * p->fontheight,
+ p->next_line * height * p->fontheight);
+ } else {
+ int rows, cols;
+ u_char *src;
+ u_char *dst;
+ int bytes = p->next_line;
+ int linesize = bytes * p->fontheight;
+ u_int colsize = height * p->fontheight;
+ u_int upwards = (dy < sy) || (dy == sy && dx < sx);
+
+ if ((sx & 1) == (dx & 1)) {
+ /* odd->odd or even->even */
+ if (upwards) {
+ src = p->screen_base + sy * linesize + (sx>>1)*4 + (sx & 1);
+ dst = p->screen_base + dy * linesize + (dx>>1)*4 + (dx & 1);
+ if (sx & 1) {
+ memmove_2p_col(dst, src, colsize, bytes);
+ src += 3;
+ dst += 3;
+ --width;
+ }
+ if (width > 1) {
+ for (rows = colsize; rows > 0; --rows) {
+ mymemmove(dst, src, (width>>1)*4);
+ src += bytes;
+ dst += bytes;
+ }
+ }
+ if (width & 1) {
+ src -= colsize * bytes;
+ dst -= colsize * bytes;
+ memmove_2p_col(dst + (width>>1)*4, src + (width>>1)*4,
+ colsize, bytes);
+ }
+ } else {
+ if (!((sx+width-1) & 1)) {
+ src = p->screen_base + sy * linesize + ((sx+width-1)>>1)*4;
+ dst = p->screen_base + dy * linesize + ((dx+width-1)>>1)*4;
+ memmove_2p_col(dst, src, colsize, bytes);
+ --width;
+ }
+ src = p->screen_base + sy * linesize + (sx>>1)*4 + (sx & 1);
+ dst = p->screen_base + dy * linesize + (dx>>1)*4 + (dx & 1);
+ if (width > 1) {
+ src += colsize * bytes + (sx & 1)*3;
+ dst += colsize * bytes + (sx & 1)*3;
+ for(rows = colsize; rows > 0; --rows) {
+ src -= bytes;
+ dst -= bytes;
+ mymemmove(dst, src, (width>>1)*4);
+ }
+ }
+ if (width & 1)
+ memmove_2p_col(dst-3, src-3, colsize, bytes);
+ }
+ } else {
+ /* odd->even or even->odd */
+ if (upwards) {
+ src = p->screen_base + sy * linesize + (sx>>1)*4 + (sx & 1);
+ dst = p->screen_base + dy * linesize + (dx>>1)*4 + (dx & 1);
+ for (cols = width; cols > 0; --cols) {
+ memmove_2p_col(dst, src, colsize, bytes);
+ INC_2P(src);
+ INC_2P(dst);
+ }
+ } else {
+ sx += width-1;
+ dx += width-1;
+ src = p->screen_base + sy * linesize + (sx>>1)*4 + (sx & 1);
+ dst = p->screen_base + dy * linesize + (dx>>1)*4 + (dx & 1);
+ for(cols = width; cols > 0; --cols) {
+ memmove_2p_col(dst, src, colsize, bytes);
+ DEC_2P(src);
+ DEC_2P(dst);
+ }
+ }
+ }
+ }
+}
+
+static void clear_iplan2p2(struct vc_data *conp, struct display *p, int sy,
+ int sx, int height, int width)
+{
+ ulong offset;
+ u_char *start;
+ int rows;
+ int bytes = p->next_line;
+ int lines = height * p->fontheight;
+ ulong size;
+ u_long cval;
+ u_short pcval;
+
+ cval = expand2l (COLOR_2P (attr_bgcol_ec(p,conp)));
+
+ if (sx == 0 && width == bytes/2) {
+ offset = sy * bytes * p->fontheight;
+ size = lines * bytes;
+ memset_even_2p(p->screen_base+offset, size, cval);
+ } else {
+ offset = (sy * bytes * p->fontheight) + (sx>>1)*4 + (sx & 1);
+ start = p->screen_base + offset;
+ pcval = expand2w(COLOR_2P(attr_bgcol_ec(p,conp)));
+
+ /* Clears are split if the region starts at an odd column or
+ * end at an even column. These extra columns are spread
+ * across the interleaved planes. All in between can be
+ * cleared by normal mymemclear_small(), because both bytes of
+ * the single plane words are affected.
+ */
+
+ if (sx & 1) {
+ memclear_2p_col(start, lines, pcval, bytes);
+ start += 3;
+ width--;
+ }
+ if (width & 1) {
+ memclear_2p_col(start + (width>>1)*4, lines, pcval, bytes);
+ width--;
+ }
+ if (width) {
+ for (rows = lines; rows-- ; start += bytes)
+ memset_even_2p(start, width*2, cval);
+ }
+ }
+}
+
+static void putc_iplan2p2(struct vc_data *conp, struct display *p, int c,
+ int yy, int xx)
+{
+ u_char *dest;
+ u_char *cdat;
+ int rows;
+ int bytes = p->next_line;
+ ulong eorx, fgx, bgx, fdx;
+
+ c &= 0xff;
+
+ dest = p->screen_base + yy * p->fontheight * bytes + (xx>>1)*4 + (xx & 1);
+ cdat = p->fontdata + (c * p->fontheight);
+
+ fgx = expand2w(COLOR_2P(attr_fgcol(p,conp)));
+ bgx = expand2w(COLOR_2P(attr_bgcol(p,conp)));
+ eorx = fgx ^ bgx;
+
+ for (rows = p->fontheight ; rows-- ; dest += bytes) {
+ fdx = dup2w(*cdat++);
+#ifdef __mc68000__
+ __asm__ __volatile__ ("movepw %1,%0@(0)"
+ : /* no outputs */
+ : "a" (dest), "d" ((fdx & eorx) ^ bgx));
+#endif /* !m68k */
+ }
+}
+
+static void putcs_iplan2p2(struct vc_data *conp, struct display *p,
+ const char *s, int count, int yy, int xx)
+{
+ u_char *dest, *dest0;
+ u_char *cdat, c;
+ int rows;
+ int bytes;
+ ulong eorx, fgx, bgx, fdx;
+
+ bytes = p->next_line;
+ dest0 = p->screen_base + yy * p->fontheight * bytes + (xx>>1)*4 + (xx & 1);
+ fgx = expand2w(COLOR_2P(attr_fgcol(p,conp)));
+ bgx = expand2w(COLOR_2P(attr_bgcol(p,conp)));
+ eorx = fgx ^ bgx;
+
+ while (count--) {
+ c = *s++;
+ cdat = p->fontdata + (c * p->fontheight);
+
+ for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) {
+ fdx = dup2w(*cdat++);
+#ifdef __mc68000__
+ __asm__ __volatile__ ("movepw %1,%0@(0)"
+ : /* no outputs */
+ : "a" (dest), "d" ((fdx & eorx) ^ bgx));
+#endif /* !m68k */
+ }
+ INC_2P(dest0);
+ }
+}
+
+static void rev_char_iplan2p2(struct display *p, int xx, int yy)
+{
+ u_char *dest;
+ int j;
+ int bytes;
+
+ dest = p->screen_base + yy * p->fontheight * p->next_line + (xx>>1)*4 +
+ (xx & 1);
+ j = p->fontheight;
+ bytes = p->next_line;
+ while (j--) {
+ /* This should really obey the individual character's
+ * background and foreground colors instead of simply
+ * inverting.
+ */
+ dest[0] = ~dest[0];
+ dest[2] = ~dest[2];
+ dest += bytes;
+ }
+}
+
+
+#ifdef MODULE
+int init_module(void)
+#else
+int fbcon_init_iplan2p2(void)
+#endif
+{
+ return(fbcon_register_driver(&dispsw_iplan2p2, 0));
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ fbcon_unregister_driver(&dispsw_iplan2p2);
+}
+#endif /* MODULE */
diff --git a/drivers/video/fbcon-iplan2p4.c b/drivers/video/fbcon-iplan2p4.c
new file mode 100644
index 000000000..693269f7b
--- /dev/null
+++ b/drivers/video/fbcon-iplan2p4.c
@@ -0,0 +1,474 @@
+/*
+ * linux/drivers/video/iplan2p4.c -- Low level frame buffer operations for
+ * interleaved bitplanes à la Atari (4
+ * planes, 2 bytes interleave)
+ *
+ * Created 5 Apr 1997 by Geert Uytterhoeven
+ *
+ * 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/module.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/fb.h>
+
+#include "fbcon.h"
+
+
+#ifndef __mc68000__
+#error No support for non-m68k yet
+#endif
+
+
+ /*
+ * Prototypes
+ */
+
+static int open_iplan2p4(struct display *p);
+static void release_iplan2p4(void);
+static void bmove_iplan2p4(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width);
+static void clear_iplan2p4(struct vc_data *conp, struct display *p, int sy,
+ int sx, int height, int width);
+static void putc_iplan2p4(struct vc_data *conp, struct display *p, int c,
+ int yy, int xx);
+static void putcs_iplan2p4(struct vc_data *conp, struct display *p,
+ const char *s, int count, int yy, int xx);
+static void rev_char_iplan2p4(struct display *p, int xx, int yy);
+
+
+ /*
+ * `switch' for the low level operations
+ */
+
+static struct display_switch dispsw_iplan2p4 = {
+ open_iplan2p4, release_iplan2p4, bmove_iplan2p4, clear_iplan2p4,
+ putc_iplan2p4, putcs_iplan2p4, rev_char_iplan2p4
+};
+
+
+ /*
+ * Interleaved bitplanes à la Atari (4 planes, 2 bytes interleave)
+ */
+
+/* Increment/decrement 4 plane addresses */
+
+#define INC_4P(p) do { if (!((long)(++(p)) & 1)) (p) += 6; } while(0)
+#define DEC_4P(p) do { if ((long)(--(p)) & 1) (p) -= 6; } while(0)
+
+
+/* Sets the bytes in the visible column at d, height h, to the value
+ * val for a 4 plane screen. The the bis of the color in 'color' are
+ * moved (8 times) to the respective bytes. This means:
+ *
+ * for(h times; d += bpr)
+ * *d = (color & 1) ? 0xff : 0;
+ * *(d+2) = (color & 2) ? 0xff : 0;
+ * *(d+4) = (color & 4) ? 0xff : 0;
+ * *(d+6) = (color & 8) ? 0xff : 0;
+ */
+
+static __inline__ void memclear_4p_col(void *d, size_t h, u_long val, int bpr)
+{
+#ifdef __mc68000__
+ __asm__ __volatile__ ("1: movepl %4,%0@(0)\n\t"
+ "addal %5,%0\n\t"
+ "dbra %1,1b"
+ : "=a" (d), "=d" (h)
+ : "0" (d), "1" (h - 1), "d" (val), "r" (bpr));
+#endif /* !m68k */
+}
+
+/* Sets a 4 plane region from 'd', length 'count' bytes, to the color
+ * in val1/val2. 'd' has to be an even address and count must be divisible
+ * by 8, because only whole words and all planes are accessed. I.e.:
+ *
+ * for(count/8 times)
+ * *d = *(d+1) = (color & 1) ? 0xff : 0;
+ * *(d+2) = *(d+3) = (color & 2) ? 0xff : 0;
+ * *(d+4) = *(d+5) = (color & 4) ? 0xff : 0;
+ * *(d+6) = *(d+7) = (color & 8) ? 0xff : 0;
+ */
+
+static __inline__ void memset_even_4p(void *d, size_t count, u_long val1,
+ u_long val2)
+{
+ u_long *dd = d;
+
+ count /= 8;
+ while (count--) {
+ *dd++ = val1;
+ *dd++ = val2;
+ }
+}
+
+/* Copies a 4 plane column from 's', height 'h', to 'd'. */
+
+static __inline__ void memmove_4p_col (void *d, void *s, int h, int bpr)
+{
+ u_char *dd = d, *ss = s;
+
+ while (h--) {
+ dd[0] = ss[0];
+ dd[2] = ss[2];
+ dd[4] = ss[4];
+ dd[6] = ss[6];
+ dd += bpr;
+ ss += bpr;
+ }
+}
+
+
+/* This expands a 4 bit color into a long for movepl (4 plane) operations. */
+
+static __inline__ u_long expand4l(u_char c)
+{
+ u_long rv;
+
+#ifdef __mc68000__
+ __asm__ __volatile__ ("lsrb #1,%2\n\t"
+ "scs %0\n\t"
+ "lsll #8,%0\n\t"
+ "lsrb #1,%2\n\t"
+ "scs %0\n\t"
+ "lsll #8,%0\n\t"
+ "lsrb #1,%2\n\t"
+ "scs %0\n\t"
+ "lsll #8,%0\n\t"
+ "lsrb #1,%2\n\t"
+ "scs %0\n\t"
+ : "=&d" (rv), "=d" (c)
+ : "1" (c));
+#endif /* !m68k */
+ return(rv);
+}
+
+/* This expands a 4 bit color into two longs for two movel operations
+ * (4 planes).
+ */
+
+static __inline__ void expand4dl(u_char c, u_long *ret1, u_long *ret2)
+{
+ u_long rv1, rv2;
+
+#ifdef __mc68000__
+ __asm__ __volatile__ ("lsrb #1,%3\n\t"
+ "scs %0\n\t"
+ "extw %0\n\t"
+ "swap %0\n\t"
+ "lsrb #1,%3\n\t"
+ "scs %0\n\t"
+ "extw %0\n\t"
+ "lsrb #1,%3\n\t"
+ "scs %1\n\t"
+ "extw %1\n\t"
+ "swap %1\n\t"
+ "lsrb #1,%3\n\t"
+ "scs %1\n\t"
+ "extw %1"
+ : "=&d" (rv1), "=&d" (rv2), "=d" (c)
+ : "2" (c));
+#endif /* !m68k */
+ *ret1 = rv1;
+ *ret2 = rv2;
+}
+
+
+/* This duplicates a byte 4 times into a long. */
+
+static __inline__ u_long dup4l(u_char c)
+{
+ ushort tmp;
+ ulong rv;
+
+#ifdef __mc68000__
+ __asm__ __volatile__ ("moveb %2,%0\n\t"
+ "lslw #8,%0\n\t"
+ "moveb %2,%0\n\t"
+ "movew %0,%1\n\t"
+ "swap %0\n\t"
+ "movew %1,%0"
+ : "=&d" (rv), "=d" (tmp)
+ : "d" (c));
+#endif /* !m68k */
+ return(rv);
+}
+
+
+static int open_iplan2p4(struct display *p)
+{
+ if (p->type != FB_TYPE_INTERLEAVED_PLANES || p->type_aux != 2 ||
+ p->var.bits_per_pixel != 4)
+ return -EINVAL;
+
+ p->next_line = p->var.xres_virtual>>1;
+ p->next_plane = 2;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void release_iplan2p4(void)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+static void bmove_iplan2p4(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width)
+{
+ /* bmove() has to distinguish two major cases: If both, source and
+ * destination, start at even addresses or both are at odd
+ * addresses, just the first odd and last even column (if present)
+ * require special treatment (memmove_col()). The rest between
+ * then can be copied by normal operations, because all adjacent
+ * bytes are affected and are to be stored in the same order.
+ * The pathological case is when the move should go from an odd
+ * address to an even or vice versa. Since the bytes in the plane
+ * words must be assembled in new order, it seems wisest to make
+ * all movements by memmove_col().
+ */
+
+ if (sx == 0 && dx == 0 && width == p->next_line/4) {
+ /* Special (but often used) case: Moving whole lines can be
+ *done with memmove()
+ */
+ mymemmove(p->screen_base + dy * p->next_line * p->fontheight,
+ p->screen_base + sy * p->next_line * p->fontheight,
+ p->next_line * height * p->fontheight);
+ } else {
+ int rows, cols;
+ u_char *src;
+ u_char *dst;
+ int bytes = p->next_line;
+ int linesize = bytes * p->fontheight;
+ u_int colsize = height * p->fontheight;
+ u_int upwards = (dy < sy) || (dy == sy && dx < sx);
+
+ if ((sx & 1) == (dx & 1)) {
+ /* odd->odd or even->even */
+
+ if (upwards) {
+ src = p->screen_base + sy * linesize + (sx>>1)*8 + (sx & 1);
+ dst = p->screen_base + dy * linesize + (dx>>1)*8 + (dx & 1);
+ if (sx & 1) {
+ memmove_4p_col(dst, src, colsize, bytes);
+ src += 7;
+ dst += 7;
+ --width;
+ }
+ if (width > 1) {
+ for(rows = colsize; rows > 0; --rows) {
+ mymemmove(dst, src, (width>>1)*8);
+ src += bytes;
+ dst += bytes;
+ }
+ }
+ if (width & 1) {
+ src -= colsize * bytes;
+ dst -= colsize * bytes;
+ memmove_4p_col(dst + (width>>1)*8, src + (width>>1)*8,
+ colsize, bytes);
+ }
+ } else {
+ if (!((sx+width-1) & 1)) {
+ src = p->screen_base + sy * linesize + ((sx+width-1)>>1)*8;
+ dst = p->screen_base + dy * linesize + ((dx+width-1)>>1)*8;
+ memmove_4p_col(dst, src, colsize, bytes);
+ --width;
+ }
+ src = p->screen_base + sy * linesize + (sx>>1)*8 + (sx & 1);
+ dst = p->screen_base + dy * linesize + (dx>>1)*8 + (dx & 1);
+ if (width > 1) {
+ src += colsize * bytes + (sx & 1)*7;
+ dst += colsize * bytes + (sx & 1)*7;
+ for(rows = colsize; rows > 0; --rows) {
+ src -= bytes;
+ dst -= bytes;
+ mymemmove(dst, src, (width>>1)*8);
+ }
+ }
+ if (width & 1) {
+ memmove_4p_col(dst-7, src-7, colsize, bytes);
+ }
+ }
+ } else {
+ /* odd->even or even->odd */
+
+ if (upwards) {
+ src = p->screen_base + sy * linesize + (sx>>1)*8 + (sx & 1);
+ dst = p->screen_base + dy * linesize + (dx>>1)*8 + (dx & 1);
+ for(cols = width; cols > 0; --cols) {
+ memmove_4p_col(dst, src, colsize, bytes);
+ INC_4P(src);
+ INC_4P(dst);
+ }
+ } else {
+ sx += width-1;
+ dx += width-1;
+ src = p->screen_base + sy * linesize + (sx>>1)*8 + (sx & 1);
+ dst = p->screen_base + dy * linesize + (dx>>1)*8 + (dx & 1);
+ for(cols = width; cols > 0; --cols) {
+ memmove_4p_col(dst, src, colsize, bytes);
+ DEC_4P(src);
+ DEC_4P(dst);
+ }
+ }
+ }
+ }
+}
+
+static void clear_iplan2p4(struct vc_data *conp, struct display *p, int sy,
+ int sx, int height, int width)
+{
+ ulong offset;
+ u_char *start;
+ int rows;
+ int bytes = p->next_line;
+ int lines = height * p->fontheight;
+ ulong size;
+ u_long cval1, cval2, pcval;
+
+ expand4dl(attr_bgcol_ec(p,conp), &cval1, &cval2);
+
+ if (sx == 0 && width == bytes/4) {
+ offset = sy * bytes * p->fontheight;
+ size = lines * bytes;
+ memset_even_4p(p->screen_base+offset, size, cval1, cval2);
+ } else {
+ offset = (sy * bytes * p->fontheight) + (sx>>1)*8 + (sx & 1);
+ start = p->screen_base + offset;
+ pcval = expand4l(attr_bgcol_ec(p,conp));
+
+ /* Clears are split if the region starts at an odd column or
+ * end at an even column. These extra columns are spread
+ * across the interleaved planes. All in between can be
+ * cleared by normal mymemclear_small(), because both bytes of
+ * the single plane words are affected.
+ */
+
+ if (sx & 1) {
+ memclear_4p_col(start, lines, pcval, bytes);
+ start += 7;
+ width--;
+ }
+ if (width & 1) {
+ memclear_4p_col(start + (width>>1)*8, lines, pcval, bytes);
+ width--;
+ }
+ if (width) {
+ for(rows = lines; rows-- ; start += bytes)
+ memset_even_4p(start, width*4, cval1, cval2);
+ }
+ }
+}
+
+static void putc_iplan2p4(struct vc_data *conp, struct display *p, int c,
+ int yy, int xx)
+{
+ u_char *dest;
+ u_char *cdat;
+ int rows;
+ int bytes = p->next_line;
+ ulong eorx, fgx, bgx, fdx;
+
+ c &= 0xff;
+
+ dest = p->screen_base + yy * p->fontheight * bytes + (xx>>1)*8 + (xx & 1);
+ cdat = p->fontdata + (c * p->fontheight);
+
+ fgx = expand4l(attr_fgcol(p,conp));
+ bgx = expand4l(attr_bgcol(p,conp));
+ eorx = fgx ^ bgx;
+
+ for(rows = p->fontheight ; rows-- ; dest += bytes) {
+ fdx = dup4l(*cdat++);
+#ifdef __mc68000__
+ __asm__ __volatile__ ("movepl %1,%0@(0)"
+ : /* no outputs */
+ : "a" (dest), "d" ((fdx & eorx) ^ bgx));
+#endif /* !m68k */
+ }
+}
+
+static void putcs_iplan2p4(struct vc_data *conp, struct display *p,
+ const char *s, int count, int yy, int xx)
+{
+ u_char *dest, *dest0;
+ u_char *cdat, c;
+ int rows;
+ int bytes;
+ ulong eorx, fgx, bgx, fdx;
+
+ bytes = p->next_line;
+ dest0 = p->screen_base + yy * p->fontheight * bytes + (xx>>1)*8 + (xx & 1);
+ fgx = expand4l(attr_fgcol(p,conp));
+ bgx = expand4l(attr_bgcol(p,conp));
+ eorx = fgx ^ bgx;
+
+ while (count--) {
+ /* I think, unrolling the loops like in the 1 plane case isn't
+ * practicable here, because the body is much longer for 4
+ * planes (mostly the dup4l()). I guess, unrolling this would
+ * need more than 256 bytes and so exceed the instruction
+ * cache :-(
+ */
+
+ c = *s++;
+ cdat = p->fontdata + (c * p->fontheight);
+
+ for(rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) {
+ fdx = dup4l(*cdat++);
+#ifdef __mc68000__
+ __asm__ __volatile__ ("movepl %1,%0@(0)"
+ : /* no outputs */
+ : "a" (dest), "d" ((fdx & eorx) ^ bgx));
+#endif /* !m68k */
+ }
+ INC_4P(dest0);
+ }
+}
+
+static void rev_char_iplan2p4(struct display *p, int xx, int yy)
+{
+ u_char *dest;
+ int j;
+ int bytes;
+
+ dest = p->screen_base + yy * p->fontheight * p->next_line + (xx>>1)*8 +
+ (xx & 1);
+ j = p->fontheight;
+ bytes = p->next_line;
+
+ while (j--) {
+ /* This should really obey the individual character's
+ * background and foreground colors instead of simply
+ * inverting.
+ */
+ dest[0] = ~dest[0];
+ dest[2] = ~dest[2];
+ dest[4] = ~dest[4];
+ dest[6] = ~dest[6];
+ dest += bytes;
+ }
+}
+
+
+#ifdef MODULE
+int init_module(void)
+#else
+int fbcon_init_iplan2p4(void)
+#endif
+{
+ return(fbcon_register_driver(&dispsw_iplan2p4, 0));
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ fbcon_unregister_driver(&dispsw_iplan2p4);
+}
+#endif /* MODULE */
diff --git a/drivers/video/fbcon-iplan2p8.c b/drivers/video/fbcon-iplan2p8.c
new file mode 100644
index 000000000..6ad7adb36
--- /dev/null
+++ b/drivers/video/fbcon-iplan2p8.c
@@ -0,0 +1,532 @@
+/*
+ * linux/drivers/video/iplan2p8.c -- Low level frame buffer operations for
+ * interleaved bitplanes à la Atari (8
+ * planes, 2 bytes interleave)
+ *
+ * Created 5 Apr 1997 by Geert Uytterhoeven
+ *
+ * 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/module.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/fb.h>
+
+#include "fbcon.h"
+
+
+#ifndef __mc68000__
+#error No support for non-m68k yet
+#endif
+
+
+ /*
+ * Prototypes
+ */
+
+static int open_iplan2p8(struct display *p);
+static void release_iplan2p8(void);
+static void bmove_iplan2p8(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width);
+static void clear_iplan2p8(struct vc_data *conp, struct display *p, int sy,
+ int sx, int height, int width);
+static void putc_iplan2p8(struct vc_data *conp, struct display *p, int c,
+ int yy, int xx);
+static void putcs_iplan2p8(struct vc_data *conp, struct display *p,
+ const char *s, int count, int yy, int xx);
+static void rev_char_iplan2p8(struct display *display, int xx, int yy);
+
+
+ /*
+ * `switch' for the low level operations
+ */
+
+static struct display_switch dispsw_iplan2p8 = {
+ open_iplan2p8, release_iplan2p8, bmove_iplan2p8, clear_iplan2p8,
+ putc_iplan2p8, putcs_iplan2p8, rev_char_iplan2p8
+};
+
+
+ /*
+ * Interleaved bitplanes à la Atari (8 planes, 2 bytes interleave)
+ *
+ * In 8 plane mode, 256 colors would be possible, but only the first
+ * 16 are used by the console code (the upper 4 bits are
+ * background/unused). For that, the following functions mask off the
+ * higher 4 bits of each color.
+ */
+
+/* Increment/decrement 8 plane addresses */
+
+#define INC_8P(p) do { if (!((long)(++(p)) & 1)) (p) += 14; } while(0)
+#define DEC_8P(p) do { if ((long)(--(p)) & 1) (p) -= 14; } while(0)
+
+
+/* Sets the bytes in the visible column at d, height h, to the value
+ * val1,val2 for a 8 plane screen. The the bis of the color in 'color' are
+ * moved (8 times) to the respective bytes. This means:
+ *
+ * for(h times; d += bpr)
+ * *d = (color & 1) ? 0xff : 0;
+ * *(d+2) = (color & 2) ? 0xff : 0;
+ * *(d+4) = (color & 4) ? 0xff : 0;
+ * *(d+6) = (color & 8) ? 0xff : 0;
+ * *(d+8) = (color & 16) ? 0xff : 0;
+ * *(d+10) = (color & 32) ? 0xff : 0;
+ * *(d+12) = (color & 64) ? 0xff : 0;
+ * *(d+14) = (color & 128) ? 0xff : 0;
+ */
+
+static __inline__ void memclear_8p_col(void *d, size_t h, u_long val1,
+ u_long val2, int bpr)
+{
+#ifdef __mc68000__
+ __asm__ __volatile__ ("1: movepl %4,%0@(0)\n\t"
+ "movepl %5,%0@(8)\n\t"
+ "addal %6,%0\n\t"
+ "dbra %1,1b"
+ : "=a" (d), "=d" (h)
+ : "0" (d), "1" (h - 1), "d" (val1), "d" (val2),
+ "r" (bpr));
+#endif /* !m68k */
+}
+
+/* Sets a 8 plane region from 'd', length 'count' bytes, to the color
+ * val1..val4. 'd' has to be an even address and count must be divisible
+ * by 16, because only whole words and all planes are accessed. I.e.:
+ *
+ * for(count/16 times)
+ * *d = *(d+1) = (color & 1) ? 0xff : 0;
+ * *(d+2) = *(d+3) = (color & 2) ? 0xff : 0;
+ * *(d+4) = *(d+5) = (color & 4) ? 0xff : 0;
+ * *(d+6) = *(d+7) = (color & 8) ? 0xff : 0;
+ * *(d+8) = *(d+9) = (color & 16) ? 0xff : 0;
+ * *(d+10) = *(d+11) = (color & 32) ? 0xff : 0;
+ * *(d+12) = *(d+13) = (color & 64) ? 0xff : 0;
+ * *(d+14) = *(d+15) = (color & 128) ? 0xff : 0;
+ */
+
+static __inline__ void memset_even_8p(void *d, size_t count, u_long val1,
+ u_long val2, u_long val3, u_long val4)
+{
+ u_long *dd = d;
+
+ count /= 16;
+ while (count--) {
+ *dd++ = val1;
+ *dd++ = val2;
+ *dd++ = val3;
+ *dd++ = val4;
+ }
+}
+
+/* Copies a 8 plane column from 's', height 'h', to 'd'. */
+
+static __inline__ void memmove_8p_col (void *d, void *s, int h, int bpr)
+{
+ u_char *dd = d, *ss = s;
+
+ while (h--) {
+ dd[0] = ss[0];
+ dd[2] = ss[2];
+ dd[4] = ss[4];
+ dd[6] = ss[6];
+ dd[8] = ss[8];
+ dd[10] = ss[10];
+ dd[12] = ss[12];
+ dd[14] = ss[14];
+ dd += bpr;
+ ss += bpr;
+ }
+}
+
+
+/* This expands a 8 bit color into two longs for two movepl (8 plane)
+ * operations.
+ */
+
+static __inline__ void expand8dl(u_char c, u_long *ret1, u_long *ret2)
+{
+ u_long rv1, rv2;
+
+#ifdef __mc68000__
+ __asm__ __volatile__ ("lsrb #1,%3\n\t"
+ "scs %0\n\t"
+ "lsll #8,%0\n\t"
+ "lsrb #1,%3\n\t"
+ "scs %0\n\t"
+ "lsll #8,%0\n\t"
+ "lsrb #1,%3\n\t"
+ "scs %0\n\t"
+ "lsll #8,%0\n\t"
+ "lsrb #1,%3\n\t"
+ "scs %0\n\t"
+ "lsrb #1,%3\n\t"
+ "scs %1\n\t"
+ "lsll #8,%1\n\t"
+ "lsrb #1,%3\n\t"
+ "scs %1\n\t"
+ "lsll #8,%1\n\t"
+ "lsrb #1,%3\n\t"
+ "scs %1\n\t"
+ "lsll #8,%1\n\t"
+ "lsrb #1,%3\n\t"
+ "scs %1"
+ : "=&d" (rv1), "=&d" (rv2),"=d" (c)
+ : "2" (c));
+#endif /* !m68k */
+ *ret1 = rv1;
+ *ret2 = rv2;
+}
+
+/* This expands a 8 bit color into four longs for four movel operations
+ * (8 planes).
+ */
+
+#ifdef __mc68000__
+/* ++andreas: use macro to avoid taking address of return values */
+#define expand8ql(c, rv1, rv2, rv3, rv4) \
+ do { \
+ u_char tmp = c; \
+ __asm__ __volatile__ ("lsrb #1,%5\n\t" \
+ "scs %0\n\t" \
+ "extw %0\n\t" \
+ "swap %0\n\t" \
+ "lsrb #1,%5\n\t" \
+ "scs %0\n\t" \
+ "extw %0\n\t" \
+ "lsrb #1,%5\n\t" \
+ "scs %1\n\t" \
+ "extw %1\n\t" \
+ "swap %1\n\t" \
+ "lsrb #1,%5\n\t" \
+ "scs %1\n\t" \
+ "extw %1\n\t" \
+ "lsrb #1,%5\n\t" \
+ "scs %2\n\t" \
+ "extw %2\n\t" \
+ "swap %2\n\t" \
+ "lsrb #1,%5\n\t" \
+ "scs %2\n\t" \
+ "extw %2\n\t" \
+ "lsrb #1,%5\n\t" \
+ "scs %3\n\t" \
+ "extw %3\n\t" \
+ "swap %3\n\t" \
+ "lsrb #1,%5\n\t" \
+ "scs %3\n\t" \
+ "extw %3" \
+ : "=&d" (rv1), "=&d" (rv2), "=&d" (rv3), \
+ "=&d" (rv4), "=d" (tmp) \
+ : "4" (tmp)); \
+ } while (0)
+#endif /* !m68k */
+
+
+/* This duplicates a byte 4 times into a long. */
+
+static __inline__ u_long dup4l(u_char c)
+{
+ ushort tmp;
+ ulong rv;
+
+#ifdef __mc68000__
+ __asm__ __volatile__ ("moveb %2,%0\n\t"
+ "lslw #8,%0\n\t"
+ "moveb %2,%0\n\t"
+ "movew %0,%1\n\t"
+ "swap %0\n\t"
+ "movew %1,%0"
+ : "=&d" (rv), "=d" (tmp)
+ : "d" (c));
+#endif /* !m68k */
+ return(rv);
+}
+
+
+static int open_iplan2p8(struct display *p)
+{
+ if (p->type != FB_TYPE_INTERLEAVED_PLANES || p->type_aux != 2 ||
+ p->var.bits_per_pixel != 8)
+ return -EINVAL;
+
+ p->next_line = p->var.xres_virtual;
+ p->next_plane = 2;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void release_iplan2p8(void)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+static void bmove_iplan2p8(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width)
+{
+ /* bmove() has to distinguish two major cases: If both, source and
+ * destination, start at even addresses or both are at odd
+ * addresses, just the first odd and last even column (if present)
+ * require special treatment (memmove_col()). The rest between
+ * then can be copied by normal operations, because all adjacent
+ * bytes are affected and are to be stored in the same order.
+ * The pathological case is when the move should go from an odd
+ * address to an even or vice versa. Since the bytes in the plane
+ * words must be assembled in new order, it seems wisest to make
+ * all movements by memmove_col().
+ */
+
+ if (sx == 0 && dx == 0 && width == p->next_line/8) {
+ /* Special (but often used) case: Moving whole lines can be
+ * done with memmove()
+ */
+ fast_memmove(p->screen_base + dy * p->next_line * p->fontheight,
+ p->screen_base + sy * p->next_line * p->fontheight,
+ p->next_line * height * p->fontheight);
+ } else {
+ int rows, cols;
+ u_char *src;
+ u_char *dst;
+ int bytes = p->next_line;
+ int linesize = bytes * p->fontheight;
+ u_int colsize = height * p->fontheight;
+ u_int upwards = (dy < sy) || (dy == sy && dx < sx);
+
+ if ((sx & 1) == (dx & 1)) {
+ /* odd->odd or even->even */
+
+ if (upwards) {
+ src = p->screen_base + sy * linesize + (sx>>1)*16 + (sx & 1);
+ dst = p->screen_base + dy * linesize + (dx>>1)*16 + (dx & 1);
+ if (sx & 1) {
+ memmove_8p_col(dst, src, colsize, bytes);
+ src += 15;
+ dst += 15;
+ --width;
+ }
+ if (width > 1) {
+ for(rows = colsize; rows > 0; --rows) {
+ fast_memmove (dst, src, (width >> 1) * 16);
+ src += bytes;
+ dst += bytes;
+ }
+ }
+
+ if (width & 1) {
+ src -= colsize * bytes;
+ dst -= colsize * bytes;
+ memmove_8p_col(dst + (width>>1)*16, src + (width>>1)*16,
+ colsize, bytes);
+ }
+ } else {
+ if (!((sx+width-1) & 1)) {
+ src = p->screen_base + sy * linesize + ((sx+width-1)>>1)*16;
+ dst = p->screen_base + dy * linesize + ((dx+width-1)>>1)*16;
+ memmove_8p_col(dst, src, colsize, bytes);
+ --width;
+ }
+ src = p->screen_base + sy * linesize + (sx>>1)*16 + (sx & 1);
+ dst = p->screen_base + dy * linesize + (dx>>1)*16 + (dx & 1);
+ if (width > 1) {
+ src += colsize * bytes + (sx & 1)*15;
+ dst += colsize * bytes + (sx & 1)*15;
+ for(rows = colsize; rows > 0; --rows) {
+ src -= bytes;
+ dst -= bytes;
+ fast_memmove (dst, src, (width>>1)*16);
+ }
+ }
+ if (width & 1)
+ memmove_8p_col(dst-15, src-15, colsize, bytes);
+ }
+ } else {
+ /* odd->even or even->odd */
+
+ if (upwards) {
+ src = p->screen_base + sy * linesize + (sx>>1)*16 + (sx & 1);
+ dst = p->screen_base + dy * linesize + (dx>>1)*16 + (dx & 1);
+ for(cols = width; cols > 0; --cols) {
+ memmove_8p_col(dst, src, colsize, bytes);
+ INC_8P(src);
+ INC_8P(dst);
+ }
+ } else {
+ sx += width-1;
+ dx += width-1;
+ src = p->screen_base + sy * linesize + (sx>>1)*16 + (sx & 1);
+ dst = p->screen_base + dy * linesize + (dx>>1)*16 + (dx & 1);
+ for(cols = width; cols > 0; --cols) {
+ memmove_8p_col(dst, src, colsize, bytes);
+ DEC_8P(src);
+ DEC_8P(dst);
+ }
+ }
+ }
+ }
+}
+
+static void clear_iplan2p8(struct vc_data *conp, struct display *p, int sy,
+ int sx, int height, int width)
+{
+ ulong offset;
+ u_char *start;
+ int rows;
+ int bytes = p->next_line;
+ int lines = height * p->fontheight;
+ ulong size;
+ u_long cval1, cval2, cval3, cval4, pcval1, pcval2;
+
+ expand8ql(attr_bgcol_ec(p,conp), cval1, cval2, cval3, cval4);
+
+ if (sx == 0 && width == bytes/8) {
+ offset = sy * bytes * p->fontheight;
+ size = lines * bytes;
+ memset_even_8p(p->screen_base+offset, size, cval1, cval2, cval3, cval4);
+ } else {
+ offset = (sy * bytes * p->fontheight) + (sx>>1)*16 + (sx & 1);
+ start = p->screen_base + offset;
+ expand8dl(attr_bgcol_ec(p,conp), &pcval1, &pcval2);
+
+ /* Clears are split if the region starts at an odd column or
+ * end at an even column. These extra columns are spread
+ * across the interleaved planes. All in between can be
+ * cleared by normal mymemclear_small(), because both bytes of
+ * the single plane words are affected.
+ */
+
+ if (sx & 1) {
+ memclear_8p_col(start, lines, pcval1, pcval2, bytes);
+ start += 7;
+ width--;
+ }
+ if (width & 1) {
+ memclear_8p_col(start + (width>>1)*16, lines, pcval1,
+ pcval2, bytes);
+ width--;
+ }
+ if (width)
+ for(rows = lines; rows-- ; start += bytes)
+ memset_even_8p(start, width*8, cval1, cval2, cval3, cval4);
+ }
+}
+
+static void putc_iplan2p8(struct vc_data *conp, struct display *p, int c,
+ int yy, int xx)
+{
+ u_char *dest;
+ u_char *cdat;
+ int rows;
+ int bytes = p->next_line;
+ ulong eorx1, eorx2, fgx1, fgx2, bgx1, bgx2, fdx;
+
+ c &= 0xff;
+
+ dest = p->screen_base + yy * p->fontheight * bytes + (xx>>1)*16 + (xx & 1);
+ cdat = p->fontdata + (c * p->fontheight);
+
+ expand8dl(attr_fgcol(p,conp), &fgx1, &fgx2);
+ expand8dl(attr_bgcol(p,conp), &bgx1, &bgx2);
+ eorx1 = fgx1 ^ bgx1; eorx2 = fgx2 ^ bgx2;
+
+ for(rows = p->fontheight ; rows-- ; dest += bytes) {
+ fdx = dup4l(*cdat++);
+#ifdef __mc68000__
+ __asm__ __volatile__ ("movepl %1,%0@(0)\n\t"
+ "movepl %2,%0@(8)"
+ : /* no outputs */
+ : "a" (dest), "d" ((fdx & eorx1) ^ bgx1),
+ "d" ((fdx & eorx2) ^ bgx2) );
+#endif /* !m68k */
+ }
+}
+
+static void putcs_iplan2p8(struct vc_data *conp, struct display *p,
+ const char *s, int count, int yy, int xx)
+{
+ u_char *dest, *dest0;
+ u_char *cdat, c;
+ int rows;
+ int bytes;
+ ulong eorx1, eorx2, fgx1, fgx2, bgx1, bgx2, fdx;
+
+ bytes = p->next_line;
+ dest0 = p->screen_base + yy * p->fontheight * bytes + (xx>>1)*16 +
+ (xx & 1);
+
+ expand8dl(attr_fgcol(p,conp), &fgx1, &fgx2);
+ expand8dl(attr_bgcol(p,conp), &bgx1, &bgx2);
+ eorx1 = fgx1 ^ bgx1; eorx2 = fgx2 ^ bgx2;
+
+ while (count--) {
+
+ /* I think, unrolling the loops like in the 1 plane case isn't
+ * practicable here, because the body is much longer for 4
+ * planes (mostly the dup4l()). I guess, unrolling this would
+ * need more than 256 bytes and so exceed the instruction
+ * cache :-(
+ */
+
+ c = *s++;
+ cdat = p->fontdata + (c * p->fontheight);
+
+ for(rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) {
+ fdx = dup4l(*cdat++);
+#ifdef __mc68000__
+ __asm__ __volatile__ ("movepl %1,%0@(0)\n\t"
+ "movepl %2,%0@(8)"
+ : /* no outputs */
+ : "a" (dest), "d" ((fdx & eorx1) ^ bgx1),
+ "d" ((fdx & eorx2) ^ bgx2));
+#endif /* !m68k */
+ }
+ INC_8P(dest0);
+ }
+}
+
+static void rev_char_iplan2p8(struct display *p, int xx, int yy)
+{
+ u_char *dest;
+ int j;
+ int bytes;
+
+ dest = p->screen_base + yy * p->fontheight * p->next_line + (xx>>1)*16 +
+ (xx & 1);
+ j = p->fontheight;
+ bytes = p->next_line;
+
+ while (j--) {
+ /* This should really obey the individual character's
+ * background and foreground colors instead of simply
+ * inverting. For 8 plane mode, only the lower 4 bits of the
+ * color are inverted, because only that color registers have
+ * been set up.
+ */
+ dest[0] = ~dest[0];
+ dest[2] = ~dest[2];
+ dest[4] = ~dest[4];
+ dest[6] = ~dest[6];
+ dest += bytes;
+ }
+}
+
+
+#ifdef MODULE
+int init_module(void)
+#else
+int fbcon_init_iplan2p8(void)
+#endif
+{
+ return(fbcon_register_driver(&dispsw_iplan2p8, 0));
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ fbcon_unregister_driver(&dispsw_iplan2p8);
+}
+#endif /* MODULE */
diff --git a/drivers/video/fbcon-mfb.c b/drivers/video/fbcon-mfb.c
new file mode 100644
index 000000000..7992c25b5
--- /dev/null
+++ b/drivers/video/fbcon-mfb.c
@@ -0,0 +1,203 @@
+/*
+ * linux/drivers/video/mfb.c -- Low level frame buffer operations for
+ * monochrome
+ *
+ * Created 5 Apr 1997 by Geert Uytterhoeven
+ *
+ * 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/module.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/fb.h>
+
+#include "fbcon.h"
+
+
+ /*
+ * Prototypes
+ */
+
+static int open_mfb(struct display *p);
+static void release_mfb(void);
+static void bmove_mfb(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width);
+static void clear_mfb(struct vc_data *conp, struct display *p, int sy, int sx,
+ int height, int width);
+static void putc_mfb(struct vc_data *conp, struct display *p, int c, int yy,
+ int xx);
+static void putcs_mfb(struct vc_data *conp, struct display *p, const char *s,
+ int count, int yy, int xx);
+static void rev_char_mfb(struct display *p, int xx, int yy);
+
+
+ /*
+ * `switch' for the low level operations
+ */
+
+static struct display_switch dispsw_mfb = {
+ open_mfb, release_mfb, bmove_mfb, clear_mfb, putc_mfb, putcs_mfb,
+ rev_char_mfb
+};
+
+
+ /*
+ * Monochrome
+ */
+
+static int open_mfb(struct display *p)
+{
+ if (p->var.bits_per_pixel != 1)
+ return -EINVAL;
+
+ if (p->line_length)
+ p->next_line = p->line_length;
+ else
+ p->next_line = p->var.xres_virtual>>3;
+ p->next_plane = 0;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void release_mfb(void)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+static void bmove_mfb(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width)
+{
+ u_char *src, *dest;
+ u_int rows;
+
+ if (sx == 0 && dx == 0 && width == p->next_line) {
+ src = p->screen_base+sy*p->fontheight*width;
+ dest = p->screen_base+dy*p->fontheight*width;
+ mymemmove(dest, src, height*p->fontheight*width);
+ } else if (dy <= sy) {
+ src = p->screen_base+sy*p->fontheight*p->next_line+sx;
+ dest = p->screen_base+dy*p->fontheight*p->next_line+dx;
+ for (rows = height*p->fontheight; rows--;) {
+ mymemmove(dest, src, width);
+ src += p->next_line;
+ dest += p->next_line;
+ }
+ } else {
+ src = p->screen_base+((sy+height)*p->fontheight-1)*p->next_line+sx;
+ dest = p->screen_base+((dy+height)*p->fontheight-1)*p->next_line+dx;
+ for (rows = height*p->fontheight; rows--;) {
+ mymemmove(dest, src, width);
+ src -= p->next_line;
+ dest -= p->next_line;
+ }
+ }
+}
+
+static void clear_mfb(struct vc_data *conp, struct display *p, int sy, int sx,
+ int height, int width)
+{
+ u_char *dest;
+ u_int rows;
+
+ dest = p->screen_base+sy*p->fontheight*p->next_line+sx;
+
+ if (sx == 0 && width == p->next_line) {
+ if (attr_reverse(p,conp))
+ mymemset(dest, height*p->fontheight*width);
+ else
+ mymemclear(dest, height*p->fontheight*width);
+ } else
+ for (rows = height*p->fontheight; rows--; dest += p->next_line)
+ if (attr_reverse(p,conp))
+ mymemset(dest, width);
+ else
+ mymemclear_small(dest, width);
+}
+
+static void putc_mfb(struct vc_data *conp, struct display *p, int c, int yy,
+ int xx)
+{
+ u_char *dest, *cdat;
+ u_int rows, bold, revs, underl;
+ u_char d;
+
+ c &= 0xff;
+
+ dest = p->screen_base+yy*p->fontheight*p->next_line+xx;
+ cdat = p->fontdata+c*p->fontheight;
+ bold = attr_bold(p,conp);
+ revs = attr_reverse(p,conp);
+ underl = attr_underline(p,conp);
+
+ for (rows = p->fontheight; rows--; dest += p->next_line) {
+ d = *cdat++;
+ if (underl && !rows)
+ d = 0xff;
+ else if (bold)
+ d |= d>>1;
+ if (revs)
+ d = ~d;
+ *dest = d;
+ }
+}
+
+static void putcs_mfb(struct vc_data *conp, struct display *p, const char *s,
+ int count, int yy, int xx)
+{
+ u_char *dest, *dest0, *cdat;
+ u_int rows, bold, revs, underl;
+ u_char c, d;
+
+ dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx;
+ bold = attr_bold(p,conp);
+ revs = attr_reverse(p,conp);
+ underl = attr_underline(p,conp);
+
+ while (count--) {
+ c = *s++;
+ dest = dest0++;
+ cdat = p->fontdata+c*p->fontheight;
+ for (rows = p->fontheight; rows--; dest += p->next_line) {
+ d = *cdat++;
+ if (underl && !rows)
+ d = 0xff;
+ else if (bold)
+ d |= d>>1;
+ if (revs)
+ d = ~d;
+ *dest = d;
+ }
+ }
+}
+
+static void rev_char_mfb(struct display *p, int xx, int yy)
+{
+ u_char *dest;
+ u_int rows;
+
+ dest = p->screen_base+yy*p->fontheight*p->next_line+xx;
+ for (rows = p->fontheight; rows--; dest += p->next_line)
+ *dest = ~*dest;
+}
+
+
+#ifdef MODULE
+int init_module(void)
+#else
+int fbcon_init_mfb(void)
+#endif
+{
+ return(fbcon_register_driver(&dispsw_mfb, 0));
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ fbcon_unregister_driver(&dispsw_mfb);
+}
+#endif /* MODULE */
diff --git a/drivers/video/fbcon-retz3.c b/drivers/video/fbcon-retz3.c
new file mode 100644
index 000000000..0b2152108
--- /dev/null
+++ b/drivers/video/fbcon-retz3.c
@@ -0,0 +1,261 @@
+/*
+ * linux/drivers/video/retz3.c -- Low level frame buffer operations for the
+ * RetinaZ3 (accelerated)
+ *
+ * Created 5 Apr 1997 by Geert Uytterhoeven
+ *
+ * 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/module.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/fb.h>
+
+#include "fbcon.h"
+
+
+/*
+ * Prototypes
+ */
+
+static int open_retz3(struct display *p);
+static void release_retz3(void);
+static void bmove_retz3(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width);
+static void clear_retz3(struct vc_data *conp, struct display *p, int
+ sy, int sx, int height, int width);
+static void putc_retz3(struct vc_data *conp, struct display *p, int c,
+ int ypos, int xpos);
+static void putcs_retz3(struct vc_data *conp, struct display *p, const
+ char *s, int count, int ypos, int xpos);
+static void rev_char_retz3(struct display *p, int xpos, int ypos);
+
+
+ /*
+ * Acceleration functions in retz3fb.c
+ */
+
+extern void retz3_bitblt(struct fb_var_screeninfo *scr,
+ unsigned short srcx, unsigned short srcy, unsigned
+ short destx, unsigned short desty, unsigned short
+ width, unsigned short height, unsigned short cmd,
+ unsigned short mask);
+
+#define Z3BLTcopy 0xc0
+#define Z3BLTset 0xf0
+
+
+/*
+ * `switch' for the low level operations
+ */
+
+static struct display_switch dispsw_retz3 = {
+ open_retz3, release_retz3, bmove_retz3, clear_retz3, putc_retz3,
+ putcs_retz3, rev_char_retz3
+};
+
+
+/*
+ * RetinaZ3 (accelerated)
+ */
+
+static int open_retz3(struct display *p)
+{
+ if (p->type != FB_TYPE_PACKED_PIXELS ||
+ p->var.accel != FB_ACCEL_RETINAZ3)
+ return -EINVAL;
+
+ p->next_line = p->var.xres_virtual*p->var.bits_per_pixel>>3;
+ p->next_plane = 0;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void release_retz3(void)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+static void bmove_retz3(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width)
+{
+ int fontwidth = p->fontwidth;
+
+ sx *= fontwidth;
+ dx *= fontwidth;
+ width *= fontwidth;
+
+ retz3_bitblt(&p->var,
+ (unsigned short)sx,
+ (unsigned short)(sy*p->fontheight),
+ (unsigned short)dx,
+ (unsigned short)(dy*p->fontheight),
+ (unsigned short)width,
+ (unsigned short)(height*p->fontheight),
+ Z3BLTcopy,
+ 0xffff);
+}
+
+static void clear_retz3(struct vc_data *conp, struct display *p, int
+ sy, int sx, int height, int width)
+{
+ unsigned short col;
+ int fontwidth = p->fontwidth;
+
+ sx *= fontwidth;
+ width *= fontwidth;
+
+ col = attr_bgcol_ec(p, conp);
+ col &= 0xff;
+ col |= (col << 8);
+
+ retz3_bitblt(&p->var,
+ (unsigned short)sx,
+ (unsigned short)(sy*p->fontheight),
+ (unsigned short)sx,
+ (unsigned short)(sy*p->fontheight),
+ (unsigned short)width,
+ (unsigned short)(height*p->fontheight),
+ Z3BLTset,
+ col);
+}
+
+static void putc_retz3(struct vc_data *conp, struct display *p,
+ int c, int ypos, int xpos)
+{
+ unsigned char *dest, *cdat;
+ unsigned long tmp;
+ unsigned int rows, revs, underl;
+ unsigned char d;
+ unsigned char fg, bg;
+
+ c &= 0xff;
+
+ dest = p->screen_base + ypos * p->fontheight * p->next_line +
+ xpos*p->fontwidth;
+ cdat = p->fontdata + c * p->fontheight;
+
+ fg = p->fgcol;
+ bg = p->bgcol;
+ revs = conp->vc_reverse;
+ underl = conp->vc_underline;
+
+ for (rows = p->fontheight; rows--; dest += p->next_line) {
+ d = *cdat++;
+
+ if (underl && !rows)
+ d = 0xff;
+ if (revs)
+ d = ~d;
+
+ tmp = ((d & 0x80) ? fg : bg) << 24;
+ tmp |= ((d & 0x40) ? fg : bg) << 16;
+ tmp |= ((d & 0x20) ? fg : bg) << 8;
+ tmp |= ((d & 0x10) ? fg : bg);
+ *((unsigned long*) dest) = tmp;
+ tmp = ((d & 0x8) ? fg : bg) << 24;
+ tmp |= ((d & 0x4) ? fg : bg) << 16;
+ tmp |= ((d & 0x2) ? fg : bg) << 8;
+ tmp |= ((d & 0x1) ? fg : bg);
+ *((unsigned long*) dest + 1) = tmp;
+ }
+}
+
+static void putcs_retz3(struct vc_data *conp, struct display *p,
+ const char *s, int count, int ypos, int xpos)
+{
+ unsigned char *dest, *dest0, *cdat;
+ unsigned long tmp;
+ unsigned int rows, revs, underl;
+ unsigned char c, d;
+ unsigned char fg, bg;
+
+ dest0 = p->screen_base + ypos * p->fontheight * p->next_line
+ + xpos * p->fontwidth;
+
+ fg = p->fgcol;
+ bg = p->bgcol;
+ revs = conp->vc_reverse;
+ underl = conp->vc_underline;
+
+ while (count--) {
+ c = *s++;
+ dest = dest0;
+ dest0 += 8;
+
+ cdat = p->fontdata + c * p->fontheight;
+ for (rows = p->fontheight; rows--; dest += p->next_line) {
+ d = *cdat++;
+
+ if (underl && !rows)
+ d = 0xff;
+ if (revs)
+ d = ~d;
+
+ tmp = ((d & 0x80) ? fg : bg) << 24;
+ tmp |= ((d & 0x40) ? fg : bg) << 16;
+ tmp |= ((d & 0x20) ? fg : bg) << 8;
+ tmp |= ((d & 0x10) ? fg : bg);
+ *((unsigned long*) dest) = tmp;
+ tmp = ((d & 0x8) ? fg : bg) << 24;
+ tmp |= ((d & 0x4) ? fg : bg) << 16;
+ tmp |= ((d & 0x2) ? fg : bg) << 8;
+ tmp |= ((d & 0x1) ? fg : bg);
+ *((unsigned long*) dest + 1) = tmp;
+ }
+ }
+}
+
+static void rev_char_retz3(struct display *p, int xpos, int ypos)
+{
+ unsigned char *dest;
+ int bytes=p->next_line, rows;
+ unsigned int bpp, mask;
+
+ bpp = p->var.bits_per_pixel;
+
+ switch (bpp){
+ case 8:
+ mask = 0x0f0f0f0f;
+ break;
+ case 16:
+ mask = 0xffffffff;
+ break;
+ case 24:
+ mask = 0xffffffff; /* ??? */
+ break;
+ default:
+ printk("illegal depth for rev_char_retz3(), bpp = %i\n", bpp);
+ return;
+ }
+
+ dest = p->screen_base + ypos * p->fontheight * bytes +
+ xpos * p->fontwidth;
+
+ for (rows = p->fontheight ; rows-- ; dest += bytes) {
+ ((unsigned long *)dest)[0] ^= mask;
+ ((unsigned long *)dest)[1] ^= mask;
+ }
+}
+
+
+#ifdef MODULE
+int init_module(void)
+#else
+int fbcon_init_retz3(void)
+#endif
+{
+ return(fbcon_register_driver(&dispsw_retz3, 1));
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ fbcon_unregister_driver(&dispsw_retz3);
+}
+#endif /* MODULE */
diff --git a/drivers/video/fbcon.c b/drivers/video/fbcon.c
new file mode 100644
index 000000000..1341d3196
--- /dev/null
+++ b/drivers/video/fbcon.c
@@ -0,0 +1,1475 @@
+/*
+ * linux/drivers/video/fbcon.c -- Low level frame buffer based console driver
+ *
+ * Copyright (C) 1995 Geert Uytterhoeven
+ *
+ *
+ * This file is based on the original Amiga console driver (amicon.c):
+ *
+ * Copyright (C) 1993 Hamish Macdonald
+ * Greg Harp
+ * Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk]
+ *
+ * with work by William Rucklidge (wjr@cs.cornell.edu)
+ * Geert Uytterhoeven
+ * Jes Sorensen (jds@kom.auc.dk)
+ * Martin Apel
+ *
+ * and on the original Atari console driver (atacon.c):
+ *
+ * Copyright (C) 1993 Bjoern Brauel
+ * Roman Hodek
+ *
+ * with work by Guenther Kelleter
+ * Martin Schaller
+ * Andreas Schwab
+ *
+ *
+ * The low level operations for the various display memory organizations are
+ * now in separate source files.
+ *
+ * Currently only the following organizations are supported:
+ *
+ * - non-accelerated:
+ *
+ * o mfb Monochrome
+ * o ilbm Interleaved bitplanes à la Amiga
+ * o afb Bitplanes à la Amiga
+ * o iplan2p[248] Interleaved bitplanes à la Atari (2, 4 and 8 planes)
+ * o cfb{8,16} Packed pixels (8 and 16 bpp)
+ *
+ * - accelerated:
+ *
+ * o cyber CyberVision64 packed pixels (accelerated)
+ * o retz3 Retina Z3 packed pixels (accelerated)
+ * o mach64 ATI Mach 64 packed pixels (accelerated)
+ *
+ * To do:
+ *
+ * - Implement 16 plane mode (iplan2p16)
+ * - Add support for 24/32 bit packed pixels (cfb{24,32})
+ * - Hardware cursor
+ *
+ *
+ * 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.
+ */
+
+#define SUPPORT_SCROLLBACK 0
+#define FLASHING_CURSOR 1
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/kd.h>
+#include <linux/malloc.h>
+#include <linux/fb.h>
+#include <linux/vt_kern.h>
+#include <linux/selection.h>
+#include <linux/init.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#ifdef CONFIG_AMIGA
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#endif /* CONFIG_AMIGA */
+#ifdef CONFIG_ATARI
+#include <asm/atariints.h>
+#endif
+#ifdef __mc68000__
+#include <asm/machdep.h>
+#include <asm/setup.h>
+#endif
+#include <asm/linux_logo.h>
+
+#include "fbcon.h"
+#include "font.h"
+
+
+struct display fb_display[MAX_NR_CONSOLES];
+
+
+/* ++Geert: Sorry, no hardware cursor support at the moment;
+ use Atari alike software cursor */
+
+#if FLASHING_CURSOR
+static int cursor_drawn = 0;
+
+#define CURSOR_DRAW_DELAY (2)
+
+/* # VBL ints between cursor state changes */
+#define AMIGA_CURSOR_BLINK_RATE (20)
+#define ATARI_CURSOR_BLINK_RATE (42)
+#define DEFAULT_CURSOR_BLINK_RATE (20)
+
+static int vbl_cursor_cnt = 0;
+static int cursor_on = 0;
+static int cursor_blink_rate;
+
+static __inline__ int CURSOR_UNDRAWN(void)
+{
+ int cursor_was_drawn;
+ vbl_cursor_cnt = 0;
+ cursor_was_drawn = cursor_drawn;
+ cursor_drawn = 0;
+ return(cursor_was_drawn);
+}
+#endif
+
+/*
+ * Scroll Method
+ */
+
+#define SCROLL_YWRAP (0)
+#define SCROLL_YPAN (1)
+#define SCROLL_YMOVE (2)
+
+#define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1)
+
+
+/*
+ * Interface used by the world
+ */
+
+static unsigned long fbcon_startup(unsigned long kmem_start,
+ const char **display_desc);
+static void fbcon_init(struct vc_data *conp);
+static int fbcon_deinit(struct vc_data *conp);
+static int fbcon_changevar(int con);
+static int fbcon_clear(struct vc_data *conp, int sy, int sx, int height,
+ int width);
+static int fbcon_putc(struct vc_data *conp, int c, int ypos, int xpos);
+static int fbcon_putcs(struct vc_data *conp, const char *s, int count,
+ int ypos, int xpos);
+static int fbcon_cursor(struct vc_data *conp, int mode);
+static int fbcon_scroll(struct vc_data *conp, int t, int b,
+ int dir, int count);
+static int fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
+ int height, int width);
+static int fbcon_switch(struct vc_data *conp);
+static int fbcon_blank(int blank);
+static int fbcon_get_font(struct vc_data *conp, int *w, int *h, char *data);
+static int fbcon_set_font(struct vc_data *conp, int w, int h, char *data);
+static int fbcon_set_palette(struct vc_data *conp, unsigned char *table);
+static int fbcon_scrolldelta(int lines);
+int fbcon_register_driver(struct display_switch *dispsw, int is_accel);
+int fbcon_unregister_driver(struct display_switch *dispsw);
+
+
+/*
+ * Internal routines
+ */
+
+static void fbcon_setup(int con, int setcol, int init);
+static __inline__ int real_y(struct display *p, int ypos);
+#if FLASHING_CURSOR
+static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp);
+#endif
+static __inline__ void updatescrollmode(struct display *p);
+#if SUPPORT_SCROLLBACK
+static __inline__ void ywrap_up(int unit, struct vc_data *conp,
+ struct display *p, int count);
+static __inline__ void ywrap_down(int unit, struct vc_data *conp,
+ struct display *p, int count);
+#else
+static __inline__ void ywrap_up(int unit, struct display *p, int count);
+static __inline__ void ywrap_down(int unit, struct display *p, int count);
+#endif
+static __inline__ void ypan_up(int unit, struct vc_data *conp,
+ struct display *p, int count);
+static __inline__ void ypan_down(int unit, struct vc_data *conp,
+ struct display *p, int count);
+static void fbcon_bmove_rec(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width, u_int y_break);
+static struct display_switch *probe_list(struct display_switch *dispsw,
+ struct display *disp);
+
+#ifdef CONFIG_KERNELD
+static void request_driver(struct display *disp, int is_accel);
+#endif
+static struct display_switch *fbcon_get_driver(struct display *disp);
+static int fbcon_show_logo(void);
+
+#if FLASHING_CURSOR
+static void cursor_timer_handler(unsigned long dev_addr);
+
+static struct timer_list cursor_timer = {
+ NULL, NULL, 0, 0L, cursor_timer_handler
+};
+
+static void cursor_timer_handler(unsigned long dev_addr)
+{
+ fbcon_vbl_handler(0, NULL, NULL);
+ cursor_timer.expires = jiffies+2;
+ cursor_timer.data = 0;
+ cursor_timer.next = cursor_timer.next = NULL;
+ add_timer(&cursor_timer);
+}
+#endif
+
+/*
+ * Low Level Operations
+ */
+
+static struct display_switch dispsw_dummy;
+
+#ifdef CONFIG_FBCON_MFB
+extern int fbcon_init_mfb(void);
+#endif
+#ifdef CONFIG_FBCON_ILBM
+extern int fbcon_init_ilbm(void);
+#endif
+#ifdef CONFIG_FBCON_AFB
+extern int fbcon_init_afb(void);
+#endif
+#ifdef CONFIG_FBCON_IPLAN2P2
+extern int fbcon_init_iplan2p2(void);
+#endif
+#ifdef CONFIG_FBCON_IPLAN2P4
+extern int fbcon_init_iplan2p4(void);
+#endif
+#ifdef CONFIG_FBCON_IPLAN2P8
+extern int fbcon_init_iplan2p8(void);
+#endif
+#ifdef CONFIG_FBCON_CFB8
+extern int fbcon_init_cfb8(void);
+#endif
+#ifdef CONFIG_FBCON_CFB16
+extern int fbcon_init_cfb16(void);
+#endif
+#ifdef CONFIG_FBCON_CFB24
+extern int fbcon_init_cfb24(void);
+#endif
+#ifdef CONFIG_FBCON_CFB32
+extern int fbcon_init_cfb32(void);
+#endif
+#ifdef CONFIG_FBCON_CYBER
+extern int fbcon_init_cyber(void);
+#endif
+#ifdef CONFIG_FBCON_RETINAZ3
+extern int fbcon_init_retz3(void);
+#endif
+#ifdef CONFIG_FBCON_MACH64
+extern int fbcon_init_mach64(void);
+#endif
+
+extern int num_registered_fb;
+
+__initfunc(static unsigned long fbcon_startup(unsigned long kmem_start,
+ const char **display_desc))
+{
+ int irqres = 1;
+
+ /* Probe all frame buffer devices */
+ kmem_start = probe_framebuffers(kmem_start);
+
+ if (!num_registered_fb)
+ return kmem_start;
+
+ /* Initialize all built-in low level drivers */
+#ifdef CONFIG_FBCON_RETINAZ3
+ fbcon_init_retz3();
+#endif
+#ifdef CONFIG_FBCON_MFB
+ fbcon_init_mfb();
+#endif
+#ifdef CONFIG_FBCON_IPLAN2P2
+ fbcon_init_iplan2p2();
+#endif
+#ifdef CONFIG_FBCON_IPLAN2P4
+ fbcon_init_iplan2p4();
+#endif
+#ifdef CONFIG_FBCON_IPLAN2P8
+ fbcon_init_iplan2p8();
+#endif
+#ifdef CONFIG_FBCON_ILBM
+ fbcon_init_ilbm();
+#endif
+#ifdef CONFIG_FBCON_AFB
+ fbcon_init_afb();
+#endif
+#ifdef CONFIG_FBCON_CFB8
+ fbcon_init_cfb8();
+#endif
+#ifdef CONFIG_FBCON_CFB16
+ fbcon_init_cfb16();
+#endif
+#ifdef CONFIG_FBCON_CFB24
+ fbcon_init_cfb24();
+#endif
+#ifdef CONFIG_FBCON_CFB32
+ fbcon_init_cfb32();
+#endif
+#ifdef CONFIG_FBCON_CYBER
+ fbcon_init_cyber();
+#endif
+#ifdef CONFIG_FBCON_MACH64
+ fbcon_init_mach64();
+#endif
+
+ *display_desc = "frame buffer device";
+
+#ifdef CONFIG_AMIGA
+ if (MACH_IS_AMIGA) {
+ cursor_blink_rate = AMIGA_CURSOR_BLINK_RATE;
+ irqres = request_irq(IRQ_AMIGA_VERTB, fbcon_vbl_handler, 0,
+ "console/cursor", fbcon_vbl_handler);
+ }
+#endif /* CONFIG_AMIGA */
+#ifdef CONFIG_ATARI
+ if (MACH_IS_ATARI) {
+ cursor_blink_rate = ATARI_CURSOR_BLINK_RATE;
+ irqres = request_irq(IRQ_AUTO_4, fbcon_vbl_handler, IRQ_TYPE_PRIO,
+ "console/cursor", fbcon_vbl_handler);
+ }
+#endif /* CONFIG_ATARI */
+ if (irqres) {
+ cursor_blink_rate = DEFAULT_CURSOR_BLINK_RATE;
+ cursor_timer.expires = jiffies+2;
+ cursor_timer.data = 0;
+ cursor_timer.next = cursor_timer.prev = NULL;
+ add_timer(&cursor_timer);
+ }
+
+ if (!console_show_logo)
+ console_show_logo = fbcon_show_logo;
+
+ return kmem_start;
+}
+
+
+static void fbcon_init(struct vc_data *conp)
+{
+ int unit = conp->vc_num;
+ struct fb_info *info;
+
+ /* on which frame buffer will we open this console? */
+ info = registered_fb[(int)con2fb_map[unit]];
+
+ info->changevar = &fbcon_changevar;
+ fb_display[unit] = *(info->disp); /* copy from default */
+ fb_display[unit].conp = conp;
+ fb_display[unit].fb_info = info;
+ fbcon_setup(unit, 1, 1);
+}
+
+
+static int fbcon_deinit(struct vc_data *conp)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+
+ if (p->dispsw)
+ p->dispsw->release();
+ p->dispsw = 0;
+ p->conp = 0;
+ return(0);
+}
+
+
+static int fbcon_changevar(int con)
+{
+ if (fb_display[con].conp)
+ fbcon_setup(con, 1, 0);
+ return(0);
+}
+
+
+static __inline__ void updatescrollmode(struct display *p)
+{
+ if (divides(p->ywrapstep, p->fontheight) &&
+ divides(p->fontheight, p->var.yres_virtual))
+ p->scrollmode = SCROLL_YWRAP;
+ else if (divides(p->ypanstep, p->fontheight) &&
+ p->var.yres_virtual >= p->var.yres+p->fontheight)
+ p->scrollmode = SCROLL_YPAN;
+ else
+ p->scrollmode = SCROLL_YMOVE;
+}
+
+
+static void fbcon_setup(int con, int setcol, int init)
+{
+ struct display *p = &fb_display[con];
+ struct vc_data *conp = p->conp;
+ int nr_rows, nr_cols;
+ struct display_switch *old_dispsw, *new_dispsw;
+
+ p->var.xoffset = p->var.yoffset = p->yscroll = 0; /* reset wrap/pan */
+
+ if (!p->fb_info->fontname[0] ||
+ !findsoftfont(p->fb_info->fontname, &p->fontwidth, &p->fontheight,
+ &p->fontdata) || p->fontwidth != 8)
+ getdefaultfont(p->var.xres, p->var.yres, NULL, &p->fontwidth,
+ &p->fontheight, &p->fontdata);
+ if (p->fontwidth != 8) {
+ /* ++Geert: changed from panic() to `correct and continue' */
+ printk(KERN_ERR "fbcon_setup: No support for fontwidth != 8");
+ p->fontwidth = 8;
+ }
+ updatescrollmode(p);
+
+ nr_cols = p->var.xres/p->fontwidth;
+ nr_rows = p->var.yres/p->fontheight;
+ /*
+ * ++guenther: console.c:vc_allocate() relies on initializing
+ * vc_{cols,rows}, but we must not set those if we are only
+ * resizing the console.
+ */
+ if (init) {
+ conp->vc_cols = nr_cols;
+ conp->vc_rows = nr_rows;
+ }
+ p->vrows = p->var.yres_virtual/p->fontheight;
+ conp->vc_can_do_color = p->var.bits_per_pixel != 1;
+
+ new_dispsw = fbcon_get_driver(p);
+ if (!new_dispsw) {
+ printk(KERN_WARNING "fbcon_setup: type %d (aux %d, depth %d) not "
+ "supported\n", p->type, p->type_aux, p->var.bits_per_pixel);
+ dispsw_dummy.open(p);
+ new_dispsw = &dispsw_dummy;
+ }
+ /* Be careful when changing dispsw, it might be the current console. */
+ old_dispsw = p->dispsw;
+ p->dispsw = new_dispsw;
+ if (old_dispsw)
+ old_dispsw->release();
+
+ if (setcol) {
+ p->fgcol = p->var.bits_per_pixel > 2 ? 7 : (1<<p->var.bits_per_pixel)-1;
+ p->bgcol = 0;
+ }
+
+ if (!init)
+ vc_resize_con(nr_rows, nr_cols, con);
+}
+
+
+/* ====================================================================== */
+
+/* fbcon_XXX routines - interface used by the world
+ *
+ * This system is now divided into two levels because of complications
+ * caused by hardware scrolling. Top level functions:
+ *
+ * fbcon_bmove(), fbcon_clear(), fbcon_putc()
+ *
+ * handles y values in range [0, scr_height-1] that correspond to real
+ * screen positions. y_wrap shift means that first line of bitmap may be
+ * anywhere on this display. These functions convert lineoffsets to
+ * bitmap offsets and deal with the wrap-around case by splitting blits.
+ *
+ * fbcon_bmove_physical_8() -- These functions fast implementations
+ * fbcon_clear_physical_8() -- of original fbcon_XXX fns.
+ * fbcon_putc_physical_8() -- (fontwidth != 8) may be added later
+ *
+ * WARNING:
+ *
+ * At the moment fbcon_putc() cannot blit across vertical wrap boundary
+ * Implies should only really hardware scroll in rows. Only reason for
+ * restriction is simplicity & efficiency at the moment.
+ */
+
+static __inline__ int real_y(struct display *p, int ypos)
+{
+ int rows = p->vrows;
+
+ ypos += p->yscroll;
+ return(ypos < rows ? ypos : ypos-rows);
+}
+
+
+static int fbcon_clear(struct vc_data *conp, int sy, int sx, int height,
+ int width)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+ u_int y_break;
+
+ if (!p->can_soft_blank && console_blanked)
+ return(0);
+
+ if ((sy <= p->cursor_y) && (p->cursor_y < sy+height) &&
+ (sx <= p->cursor_x) && (p->cursor_x < sx+width))
+ CURSOR_UNDRAWN();
+
+ /* Split blits that cross physical y_wrap boundary */
+
+ y_break = p->vrows-p->yscroll;
+ if (sy < y_break && sy+height-1 >= y_break) {
+ u_int b = y_break-sy;
+ p->dispsw->clear(conp, p, real_y(p, sy), sx, b, width);
+ p->dispsw->clear(conp, p, real_y(p, sy+b), sx, height-b, width);
+ } else
+ p->dispsw->clear(conp, p, real_y(p, sy), sx, height, width);
+
+ return(0);
+}
+
+
+static int fbcon_putc(struct vc_data *conp, int c, int ypos, int xpos)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+
+ if (!p->can_soft_blank && console_blanked)
+ return 0;
+
+ if ((p->cursor_x == xpos) && (p->cursor_y == ypos))
+ CURSOR_UNDRAWN();
+
+ p->dispsw->putc(conp, p, c, real_y(p, ypos), xpos);
+
+ return 0;
+}
+
+
+static int fbcon_putcs(struct vc_data *conp, const char *s, int count,
+ int ypos, int xpos)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+
+ if (!p->can_soft_blank && console_blanked)
+ return 0;
+
+ if ((p->cursor_y == ypos) && (xpos <= p->cursor_x) &&
+ (p->cursor_x < (xpos + count)))
+ CURSOR_UNDRAWN();
+ p->dispsw->putcs(conp, p, s, count, real_y(p, ypos), xpos);
+
+ return(0);
+}
+
+
+static int fbcon_cursor(struct vc_data *conp, int mode)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+
+ /* Avoid flickering if there's no real change. */
+ if (p->cursor_x == conp->vc_x && p->cursor_y == conp->vc_y &&
+ (mode == CM_ERASE) == !cursor_on)
+ return 0;
+ if (CURSOR_UNDRAWN ())
+ p->dispsw->rev_char(p, p->cursor_x, real_y(p, p->cursor_y));
+ p->cursor_x = conp->vc_x;
+ p->cursor_y = conp->vc_y;
+
+ switch (mode) {
+ case CM_ERASE:
+ cursor_on = 0;
+ break;
+
+ case CM_MOVE:
+ case CM_DRAW:
+ vbl_cursor_cnt = CURSOR_DRAW_DELAY;
+ cursor_on = 1;
+ break;
+ }
+
+ return(0);
+}
+
+
+#if FLASHING_CURSOR
+static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp)
+{
+ struct display *p;
+
+ if (!cursor_on)
+ return;
+
+ if (vbl_cursor_cnt && --vbl_cursor_cnt == 0) {
+ /* Here no check is possible for console changing. The console
+ * switching code should set vbl_cursor_cnt to an appropriate value.
+ */
+ p = &fb_display[fg_console];
+ p->dispsw->rev_char(p, p->cursor_x, real_y(p, p->cursor_y));
+ cursor_drawn ^= 1;
+ vbl_cursor_cnt = cursor_blink_rate;
+ }
+}
+#endif
+
+#if SUPPORT_SCROLLBACK
+static int scrollback_max = 0;
+static int scrollback_current = 0;
+#endif
+
+#if SUPPORT_SCROLLBACK
+static __inline__ void ywrap_up(int unit, struct vc_data *conp,
+ struct display *p, int count)
+#else
+static __inline__ void ywrap_up(int unit, struct display *p, int count)
+#endif
+{
+ p->yscroll += count;
+ if (p->yscroll >= p->vrows) /* Deal with wrap */
+ p->yscroll -= p->vrows;
+ p->var.xoffset = 0;
+ p->var.yoffset = p->yscroll*p->fontheight;
+ p->var.vmode |= FB_VMODE_YWRAP;
+ p->fb_info->updatevar(unit);
+#if SUPPORT_SCROLLBACK
+ scrollback_max += count;
+ if (scrollback_max > p->vrows-conp->vc_rows)
+ scrollback_max = p->vrows-conp->vc_rows;
+ scrollback_current = 0;
+#endif
+}
+
+
+#if SUPPORT_SCROLLBACK
+static __inline__ void ywrap_down(int unit, struct vc_data *conp,
+ struct display *p, int count)
+#else
+static __inline__ void ywrap_down(int unit, struct display *p, int count)
+#endif
+{
+ p->yscroll -= count;
+ if (p->yscroll < 0) /* Deal with wrap */
+ p->yscroll += p->vrows;
+ p->var.xoffset = 0;
+ p->var.yoffset = p->yscroll*p->fontheight;
+ p->var.vmode |= FB_VMODE_YWRAP;
+ p->fb_info->updatevar(unit);
+#if SUPPORT_SCROLLBACK
+ scrollback_max -= count;
+ if (scrollback_max < 0)
+ scrollback_max = 0;
+ scrollback_current = 0;
+#endif
+}
+
+
+static __inline__ void ypan_up(int unit, struct vc_data *conp,
+ struct display *p, int count)
+{
+ p->yscroll += count;
+ if (p->yscroll+conp->vc_rows > p->vrows) {
+ p->dispsw->bmove(p, p->yscroll, 0, 0, 0, conp->vc_rows-count,
+ conp->vc_cols);
+ p->yscroll = 0;
+ }
+ p->var.xoffset = 0;
+ p->var.yoffset = p->yscroll*p->fontheight;
+ p->var.vmode &= ~FB_VMODE_YWRAP;
+ p->fb_info->updatevar(unit);
+}
+
+
+static __inline__ void ypan_down(int unit, struct vc_data *conp,
+ struct display *p, int count)
+{
+ p->yscroll -= count;
+ if (p->yscroll < 0) {
+ p->yscroll = p->vrows-conp->vc_rows;
+ p->dispsw->bmove(p, 0, 0, p->yscroll+count, 0, conp->vc_rows-count,
+ conp->vc_cols);
+ }
+ p->var.xoffset = 0;
+ p->var.yoffset = p->yscroll*p->fontheight;
+ p->var.vmode &= ~FB_VMODE_YWRAP;
+ p->fb_info->updatevar(unit);
+}
+
+
+static int fbcon_scroll(struct vc_data *conp, int t, int b, int dir, int count)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+
+ if (!p->can_soft_blank && console_blanked)
+ return(0);
+
+ fbcon_cursor(conp, CM_ERASE);
+
+ /*
+ * ++Geert: Only use ywrap/ypan if the console is in text mode
+ */
+
+ switch (dir) {
+ case SM_UP:
+ if (count > conp->vc_rows) /* Maximum realistic size */
+ count = conp->vc_rows;
+ if (vt_cons[unit]->vc_mode == KD_TEXT)
+ switch (p->scrollmode) {
+ case SCROLL_YWRAP:
+ if (b-t-count > 3*conp->vc_rows>>2) {
+ if (t > 0)
+ fbcon_bmove(conp, 0, 0, count, 0, t,
+ conp->vc_cols);
+#if SUPPORT_SCROLLBACK
+ ywrap_up(unit, conp, p, count);
+#else
+ ywrap_up(unit, p, count);
+#endif
+ if (conp->vc_rows-b > 0)
+ fbcon_bmove(conp, b-count, 0, b, 0,
+ conp->vc_rows-b, conp->vc_cols);
+ } else
+ fbcon_bmove(conp, t+count, 0, t, 0, b-t-count,
+ conp->vc_cols);
+ fbcon_clear(conp, b-count, 0, count, conp->vc_cols);
+ break;
+
+ case SCROLL_YPAN:
+ if (b-t-count > 3*conp->vc_rows>>2) {
+ if (t > 0)
+ fbcon_bmove(conp, 0, 0, count, 0, t,
+ conp->vc_cols);
+ ypan_up(unit, conp, p, count);
+ if (conp->vc_rows-b > 0)
+ fbcon_bmove(conp, b-count, 0, b, 0,
+ conp->vc_rows-b, conp->vc_cols);
+ } else
+ fbcon_bmove(conp, t+count, 0, t, 0, b-t-count,
+ conp->vc_cols);
+ fbcon_clear(conp, b-count, 0, count, conp->vc_cols);
+ break;
+
+ case SCROLL_YMOVE:
+ p->dispsw->bmove(p, t+count, 0, t, 0, b-t-count,
+ conp->vc_cols);
+ p->dispsw->clear(conp, p, b-count, 0, count,
+ conp->vc_cols);
+ break;
+ }
+ else {
+ fbcon_bmove(conp, t+count, 0, t, 0, b-t-count, conp->vc_cols);
+ fbcon_clear(conp, b-count, 0, count, conp->vc_cols);
+ }
+ break;
+
+ case SM_DOWN:
+ if (count > conp->vc_rows) /* Maximum realistic size */
+ count = conp->vc_rows;
+ if (vt_cons[unit]->vc_mode == KD_TEXT)
+ switch (p->scrollmode) {
+ case SCROLL_YWRAP:
+ if (b-t-count > 3*conp->vc_rows>>2) {
+ if (conp->vc_rows-b > 0)
+ fbcon_bmove(conp, b, 0, b-count, 0,
+ conp->vc_rows-b, conp->vc_cols);
+#if SUPPORT_SCROLLBACK
+ ywrap_down(unit, conp, p, count);
+#else
+ ywrap_down(unit, p, count);
+#endif
+ if (t > 0)
+ fbcon_bmove(conp, count, 0, 0, 0, t,
+ conp->vc_cols);
+ } else
+ fbcon_bmove(conp, t, 0, t+count, 0, b-t-count,
+ conp->vc_cols);
+ fbcon_clear(conp, t, 0, count, conp->vc_cols);
+ break;
+
+ case SCROLL_YPAN:
+ if (b-t-count > 3*conp->vc_rows>>2) {
+ if (conp->vc_rows-b > 0)
+ fbcon_bmove(conp, b, 0, b-count, 0,
+ conp->vc_rows-b, conp->vc_cols);
+ ypan_down(unit, conp, p, count);
+ if (t > 0)
+ fbcon_bmove(conp, count, 0, 0, 0, t,
+ conp->vc_cols);
+ } else
+ fbcon_bmove(conp, t, 0, t+count, 0, b-t-count,
+ conp->vc_cols);
+ fbcon_clear(conp, t, 0, count, conp->vc_cols);
+ break;
+
+ case SCROLL_YMOVE:
+ p->dispsw->bmove(p, t, 0, t+count, 0, b-t-count,
+ conp->vc_cols);
+ p->dispsw->clear(conp, p, t, 0, count, conp->vc_cols);
+ break;
+ }
+ else {
+ /*
+ * Fixed bmove() should end Arno's frustration with copying?
+ * Confucius says:
+ * Man who copies in wrong direction, end up with trashed
+ * data
+ */
+ fbcon_bmove(conp, t, 0, t+count, 0, b-t-count, conp->vc_cols);
+ fbcon_clear(conp, t, 0, count, conp->vc_cols);
+ }
+ break;
+
+ case SM_LEFT:
+ fbcon_bmove(conp, 0, t+count, 0, t, conp->vc_rows, b-t-count);
+ fbcon_clear(conp, 0, b-count, conp->vc_rows, count);
+ break;
+
+ case SM_RIGHT:
+ fbcon_bmove(conp, 0, t, 0, t+count, conp->vc_rows, b-t-count);
+ fbcon_clear(conp, 0, t, conp->vc_rows, count);
+ break;
+ }
+
+ return(0);
+}
+
+
+static int fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
+ int height, int width)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+
+ if (!p->can_soft_blank && console_blanked)
+ return(0);
+
+ if (((sy <= p->cursor_y) && (p->cursor_y < sy+height) &&
+ (sx <= p->cursor_x) && (p->cursor_x < sx+width)) ||
+ ((dy <= p->cursor_y) && (p->cursor_y < dy+height) &&
+ (dx <= p->cursor_x) && (p->cursor_x < dx+width)))
+ fbcon_cursor(conp, CM_ERASE);
+
+ /* Split blits that cross physical y_wrap case.
+ * Pathological case involves 4 blits, better to use recursive
+ * code rather than unrolled case
+ *
+ * Recursive invocations don't need to erase the cursor over and
+ * over again, so we use fbcon_bmove_rec()
+ */
+ fbcon_bmove_rec(p, sy, sx, dy, dx, height, width, p->vrows-p->yscroll);
+
+ return(0);
+}
+
+
+static void fbcon_bmove_rec(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width, u_int y_break)
+{
+ u_int b;
+
+ if (sy < y_break && sy+height > y_break) {
+ b = y_break-sy;
+ if (dy < sy) { /* Avoid trashing self */
+ fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break);
+ fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break);
+ } else {
+ fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break);
+ fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break);
+ }
+ return;
+ }
+
+ if (dy < y_break && dy+height > y_break) {
+ b = y_break-dy;
+ if (dy < sy) { /* Avoid trashing self */
+ fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break);
+ fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break);
+ } else {
+ fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break);
+ fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break);
+ }
+ return;
+ }
+ p->dispsw->bmove(p, real_y(p, sy), sx, real_y(p, dy), dx, height, width);
+}
+
+
+static int fbcon_switch(struct vc_data *conp)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+ struct fb_info *info = p->fb_info;
+
+ if (info && info->switch_con)
+ (*info->switch_con)(conp->vc_num);
+#if SUPPORT_SCROLLBACK
+ scrollback_max = 0;
+ scrollback_current = 0;
+#endif
+ return(0);
+}
+
+
+static int fbcon_blank(int blank)
+{
+ struct display *p = &fb_display[fg_console];
+
+ fbcon_cursor(p->conp, blank ? CM_ERASE : CM_DRAW);
+
+ if (!p->can_soft_blank) {
+ if (blank) {
+ if (p->visual == FB_VISUAL_MONO01)
+ mymemset(p->screen_base,
+ p->var.xres_virtual*p->var.yres_virtual*
+ p->var.bits_per_pixel>>3);
+ else
+ mymemclear(p->screen_base,
+ p->var.xres_virtual*p->var.yres_virtual*
+ p->var.bits_per_pixel>>3);
+ return(0);
+ } else {
+ /* Tell console.c that it has to restore the screen itself */
+ return(1);
+ }
+ }
+ (*p->fb_info->blank)(blank);
+ return(0);
+}
+
+
+static int fbcon_get_font(struct vc_data *conp, int *w, int *h, char *data)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+ int i, j, size, alloc;
+
+ size = (p->fontwidth+7)/8 * p->fontheight * 256;
+ alloc = (*w+7)/8 * *h * 256;
+ *w = p->fontwidth;
+ *h = p->fontheight;
+
+ if (alloc < size)
+ /* allocation length not sufficient */
+ return( -ENAMETOOLONG );
+
+ for (i = 0; i < 256; i++)
+ for (j = 0; j < p->fontheight; j++)
+ data[i*32+j] = p->fontdata[i*p->fontheight+j];
+ return( 0 );
+}
+
+
+#define REFCOUNT(fd) (((int *)(fd))[-1])
+
+static int fbcon_set_font(struct vc_data *conp, int w, int h, char *data)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+ int i, j, size, userspace = 1, resize;
+ char *old_data = NULL, *new_data;
+
+ if (w < 0)
+ w = p->fontwidth;
+ if (h < 0)
+ h = p->fontheight;
+
+ if (w == 0) {
+ /* engage predefined font, name in 'data' */
+ char name[MAX_FONT_NAME+1];
+
+ if ((i = verify_area( VERIFY_READ, (void *)data, MAX_FONT_NAME )))
+ return i;
+ copy_from_user( name, data, MAX_FONT_NAME );
+ name[sizeof(name)-1] = 0;
+
+ if (!findsoftfont( name, &w, &h, (u_char **)&data ))
+ return( -ENOENT );
+ userspace = 0;
+ } else if (w == 1) {
+ /* copy font from some other console in 'h'*/
+ struct display *op;
+
+ if (h < 0 || !vc_cons_allocated( h ))
+ return( -ENOTTY );
+ if (h == unit)
+ return( 0 ); /* nothing to do */
+ op = &fb_display[h];
+ if (op->fontdata == p->fontdata)
+ return( 0 ); /* already the same font... */
+
+ resize = (op->fontwidth != p->fontwidth) ||
+ (op->fontheight != p->fontheight);
+ if (p->userfont)
+ old_data = p->fontdata;
+ p->fontdata = op->fontdata;
+ w = p->fontwidth = op->fontwidth;
+ h = p->fontheight = op->fontheight;
+ if ((p->userfont = op->userfont))
+ REFCOUNT(p->fontdata)++; /* increment usage counter */
+ goto activate;
+ }
+
+ if (w != 8)
+ /* Currently only fontwidth == 8 supported */
+ return( -ENXIO );
+
+ resize = (w != p->fontwidth) || (h != p->fontheight);
+ size = (w+7)/8 * h * 256;
+
+ if (p->userfont)
+ old_data = p->fontdata;
+
+ if (userspace) {
+ if (!(new_data = kmalloc( sizeof(int)+size, GFP_USER )))
+ return( -ENOMEM );
+ new_data += sizeof(int);
+ REFCOUNT(new_data) = 1; /* usage counter */
+
+ for (i = 0; i < 256; i++)
+ for (j = 0; j < h; j++)
+ new_data[i*h+j] = data[i*32+j];
+
+ p->fontdata = new_data;
+ p->userfont = 1;
+ } else {
+ p->fontdata = data;
+ p->userfont = 0;
+ }
+ p->fontwidth = w;
+ p->fontheight = h;
+
+activate:
+ if (resize) {
+ /* reset wrap/pan */
+ p->var.xoffset = p->var.yoffset = p->yscroll = 0;
+ /* Adjust the virtual screen-size to fontheight*rows */
+ p->var.yres_virtual = (p->var.yres/h)*h;
+ p->vrows = p->var.yres_virtual/h;
+ updatescrollmode(p);
+ vc_resize_con( p->var.yres/h, p->var.xres/w, unit );
+ } else if (unit == fg_console)
+ update_screen( unit );
+
+ if (old_data && (--REFCOUNT(old_data) == 0))
+ kfree( old_data - sizeof(int) );
+
+ return( 0 );
+}
+
+static unsigned short palette_red[16];
+static unsigned short palette_green[16];
+static unsigned short palette_blue[16];
+
+static struct fb_cmap palette_cmap = {
+ 0, 16, palette_red, palette_green, palette_blue, NULL
+};
+
+static int fbcon_set_palette(struct vc_data *conp, unsigned char *table)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+ int i, j, k;
+ u_char val;
+
+ if (!conp->vc_can_do_color || (!p->can_soft_blank && console_blanked))
+ return(-EINVAL);
+ for (i = j = 0; i < 16; i++) {
+ k = table[i];
+ val = conp->vc_palette[j++];
+ palette_red[k] = (val<<8)|val;
+ val = conp->vc_palette[j++];
+ palette_green[k] = (val<<8)|val;
+ val = conp->vc_palette[j++];
+ palette_blue[k] = (val<<8)|val;
+ }
+ palette_cmap.len = 1<<p->var.bits_per_pixel;
+ if (palette_cmap.len > 16)
+ palette_cmap.len = 16;
+ return(p->fb_info->setcmap(&palette_cmap, unit));
+}
+
+static int fbcon_scrolldelta(int lines)
+{
+#if SUPPORT_SCROLLBACK
+ int unit = fg_console; /* xxx */
+ struct display *p = &fb_display[unit];
+ int offset;
+
+ if (!p->can_soft_blank && console_blanked ||
+ vt_cons[unit]->vc_mode != KD_TEXT || !lines ||
+ p->scrollmode != SCROLL_YWRAP)
+ return 0;
+
+ fbcon_cursor(conp, CM_ERASE);
+
+ scrollback_current -= lines;
+ if (scrollback_current < 0)
+ scrollback_current = 0;
+ else if (scrollback_current > scrollback_max)
+ scrollback_current = scrollback_max;
+
+ offset = p->yscroll-scrollback_current;
+ if (offset < 0)
+ offset += p->vrows;
+ else if (offset > p->vrows)
+ offset -= p->vrows;
+ p->var.vmode |= FB_VMODE_YWRAP;
+ p->var.xoffset = 0;
+ p->var.yoffset = offset*p->fontheight;
+ p->fb_info->updatevar(unit);
+#else
+ return -ENOSYS;
+#endif
+}
+
+
+#define LOGO_H 80
+#define LOGO_W 80
+#define LOGO_LINE (LOGO_W/8)
+
+__initfunc(static int fbcon_show_logo( void ))
+{
+ struct display *p = &fb_display[fg_console]; /* draw to vt in foreground */
+ int depth = p->var.bits_per_pixel;
+ int line = p->next_line;
+ unsigned char *fb = p->screen_base;
+ unsigned char *logo;
+ unsigned char *dst, *src;
+ int i, j, n, x1, y1;
+ int logo_depth, done = 0;
+
+ /* Set colors if visual is PSEUDOCOLOR and we have enough colors */
+ if (p->visual == FB_VISUAL_PSEUDOCOLOR && depth >= 4) {
+ int first_col = depth >= 8 ? 32 : depth > 4 ? 16 : 0;
+ int num_cols = depth >= 8 ? LINUX_LOGO_COLORS : 16;
+ unsigned char *red, *green, *blue;
+ int old_cmap_len;
+
+ if (depth >= 8) {
+ red = linux_logo_red;
+ green = linux_logo_green;
+ blue = linux_logo_blue;
+ }
+ else {
+ red = linux_logo16_red;
+ green = linux_logo16_green;
+ blue = linux_logo16_blue;
+ }
+
+ /* dirty trick to avoid setcmap calling kmalloc which isn't
+ * initialized yet... */
+ old_cmap_len = fb_display[fg_console].cmap.len;
+ fb_display[fg_console].cmap.len = 1 << depth;
+
+ for( i = 0; i < num_cols; i += n ) {
+ n = num_cols - i;
+ if (n > 16)
+ /* palette_cmap provides space for only 16 colors at once */
+ n = 16;
+ palette_cmap.start = first_col + i;
+ palette_cmap.len = n;
+ for( j = 0; j < n; ++j ) {
+ palette_cmap.red[j] = (red[i+j] << 8) | red[i+j];
+ palette_cmap.green[j] = (green[i+j] << 8) | green[i+j];
+ palette_cmap.blue[j] = (blue[i+j] << 8) | blue[i+j];
+ }
+ p->fb_info->setcmap( &palette_cmap, fg_console );
+ }
+ fb_display[fg_console].cmap.len = old_cmap_len;
+ }
+
+ if (depth >= 8) {
+ logo = linux_logo;
+ logo_depth = 8;
+ }
+ else if (depth >= 4) {
+ logo = linux_logo16;
+ logo_depth = 4;
+ }
+ else {
+ logo = linux_logo_bw;
+ logo_depth = 1;
+ }
+
+#if defined(CONFIG_FBCON_CFB16) || defined(CONFIG_FBCON_CYBER) || \
+ defined(CONFIG_FBCON_RETINAZ3)
+ if ((depth % 8 == 0) && (p->visual == FB_VISUAL_TRUECOLOR ||
+ p->visual == FB_VISUAL_DIRECTCOLOR)) {
+ /* Modes without color mapping, needs special data transformation... */
+ unsigned long val; /* max. depth 32! */
+ int bdepth = depth/8;
+ unsigned char mask[9] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff };
+ unsigned char redmask, greenmask, bluemask;
+ int redshift, greenshift, blueshift;
+
+ /* Bug: Doesn't obey msb_right ... (who needs that?) */
+ redmask = mask[p->var.red.length < 8 ? p->var.red.length : 8];
+ greenmask = mask[p->var.green.length < 8 ? p->var.green.length : 8];
+ bluemask = mask[p->var.blue.length < 8 ? p->var.blue.length : 8];
+ redshift = p->var.red.offset - (8-p->var.red.length);
+ greenshift = p->var.green.offset - (8-p->var.green.length);
+ blueshift = p->var.blue.offset - (8-p->var.blue.length);
+
+ src = logo;
+ for( y1 = 0; y1 < LOGO_H; y1++ ) {
+ dst = fb + y1*line;
+ for( x1 = 0; x1 < LOGO_W; x1++, src++ ) {
+ val = ((linux_logo_red[*src] & redmask) << redshift) |
+ ((linux_logo_green[*src] & greenmask) << greenshift) |
+ ((linux_logo_blue[*src] & bluemask) << blueshift);
+ for( i = 0; i < bdepth; ++i )
+ *dst++ = val >> (i*8);
+ }
+ }
+
+ done = 1;
+ }
+#endif
+#if defined(CONFIG_FBCON_CFB8) || defined(CONFIG_FBCON_CYBER) || \
+ defined(CONFIG_FBCON_RETINAZ3)
+ if (depth == 8 && p->type == FB_TYPE_PACKED_PIXELS) {
+ /* depth 8 or more, packed, with color registers */
+
+ src = logo;
+ for( y1 = 0; y1 < LOGO_H; y1++ ) {
+ dst = fb + y1*line;
+ for( x1 = 0; x1 < LOGO_W; x1++ ) {
+ *dst++ = *src++;
+ }
+ }
+
+ done = 1;
+ }
+#endif
+#if defined(CONFIG_FBCON_AFB) || defined(CONFIG_FBCON_ILBM) || \
+ defined(CONFIG_FBCON_IPLAN2P2) || defined(CONFIG_FBCON_IPLAN2P4) || \
+ defined(CONFIG_FBCON_IPLAN2P8)
+ if (depth >= 2 && (p->type == FB_TYPE_PLANES ||
+ p->type == FB_TYPE_INTERLEAVED_PLANES)) {
+ /* planes (normal or interleaved), with color registers */
+ int bit;
+ unsigned char val, mask;
+ int plane = p->next_plane;
+
+ /* for support of Atari interleaved planes */
+#define MAP_X(x) (plane > line ? x : (x & ~1)*depth + (x & 1))
+ /* extract a bit from the source image */
+#define BIT(p,pix,bit) (p[pix*logo_depth/8] & \
+ (1 << ((8-((pix*logo_depth)&7)-logo_depth) + bit)))
+
+ src = logo;
+ for( y1 = 0; y1 < LOGO_H; y1++ ) {
+ for( x1 = 0; x1 < LOGO_LINE; x1++, src += logo_depth ) {
+ dst = fb + y1*line + MAP_X(x1);
+ for( bit = 0; bit < logo_depth; bit++ ) {
+ val = 0;
+ for( mask = 0x80, i = 0; i < 8; mask >>= 1, i++ ) {
+ if (BIT( src, i, bit ))
+ val |= mask;
+ }
+ *dst = val;
+ dst += plane;
+ }
+ }
+ }
+
+ /* fill remaining planes
+ * special case for logo_depth == 4: we used color registers 16..31,
+ * so fill plane 4 with 1 bits instead of 0 */
+ if (depth > logo_depth) {
+ for( y1 = 0; y1 < LOGO_H; y1++ ) {
+ for( x1 = 0; x1 < LOGO_LINE; x1++ ) {
+ dst = fb + y1*line + MAP_X(x1) + logo_depth*plane;
+ for( i = logo_depth; i < depth; i++, dst += plane )
+ *dst = (i == logo_depth && logo_depth == 4)
+ ? 0xff : 0x00;
+ }
+ }
+ }
+
+ done = 1;
+ }
+#endif
+#if defined(CONFIG_FBCON_MFB) || defined(CONFIG_FBCON_AFB) || \
+ defined(CONFIG_FBCON_ILBM)
+ if (depth == 1) {
+ /* monochrome */
+ unsigned char inverse = p->inverse ? 0x00 : 0xff;
+
+ /* can't use simply memcpy because need to apply inverse */
+ for( y1 = 0; y1 < LOGO_H; y1++ ) {
+ src = logo + y1*LOGO_LINE;
+ dst = fb + y1*line;
+ for( x1 = 0; x1 < LOGO_LINE; ++x1 )
+ *dst++ = *src++ ^ inverse;
+ }
+
+ done = 1;
+ }
+#endif
+ /* Modes not yet supported: packed pixels with depth != 8 (does such a
+ * thing exist in reality?) */
+
+ return( done ? LOGO_H/p->fontheight + 1 : 0 );
+}
+
+
+
+/*
+ * The console `switch' structure for the frame buffer based console
+ */
+
+struct consw fb_con = {
+ fbcon_startup, fbcon_init, fbcon_deinit, fbcon_clear, fbcon_putc,
+ fbcon_putcs, fbcon_cursor, fbcon_scroll, fbcon_bmove, fbcon_switch,
+ fbcon_blank, fbcon_get_font, fbcon_set_font, fbcon_set_palette,
+ fbcon_scrolldelta
+};
+
+
+/*
+ * Driver registration
+ */
+
+static struct display_switch *drivers = NULL, *accel_drivers = NULL;
+
+int fbcon_register_driver(struct display_switch *dispsw, int is_accel)
+{
+ struct display_switch **list;
+
+ list = is_accel ? &accel_drivers : &drivers;
+ dispsw->next = *list;
+ *list = dispsw;
+ return 0;
+}
+
+int fbcon_unregister_driver(struct display_switch *dispsw)
+{
+ struct display_switch **list;
+
+ for (list = &accel_drivers; *list; list = &(*list)->next)
+ if (*list == dispsw) {
+ *list = dispsw->next;
+ dispsw->next = NULL;
+ return 0;
+ }
+ for (list = &drivers; *list; list = &(*list)->next)
+ if (*list == dispsw) {
+ *list = dispsw->next;
+ dispsw->next = NULL;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+
+static struct display_switch *probe_list(struct display_switch *dispsw,
+ struct display *disp)
+{
+ while (dispsw) {
+ if (!dispsw->open(disp))
+ return(dispsw);
+ dispsw = dispsw->next;
+ }
+ return(NULL);
+}
+
+
+#ifdef CONFIG_KERNELD
+static void request_driver(struct display *disp, int is_accel)
+{
+ char modname[30];
+ int len;
+ const char *type;
+
+ if (disp->var.bits_per_pixel == 1)
+ type = "mfb";
+ else
+ switch (disp->type) {
+ case FB_TYPE_INTERLEAVED_PLANES:
+ if (disp->type_aux == 2)
+ type = "iplan2p%d";
+ else
+ type = "ilbm";
+ break;
+ case FB_TYPE_PLANES:
+ type = "afb";
+ break;
+ case FB_TYPE_PACKED_PIXELS:
+ type = "cfb%d";
+ break;
+ default:
+ return;
+ }
+ len = sprintf(modname, "fbcon-");
+ len += sprintf(modname+len, type, disp->var.bits_per_pixel);
+ if (is_accel)
+ len += sprintf(modname+len, "-%d", disp->var.accel);
+ request_module(modname);
+}
+#endif /* CONFIG_KERNELD */
+
+
+static struct display_switch *fbcon_get_driver(struct display *disp)
+{
+ struct display_switch *dispsw;
+
+ if (disp->var.accel != FB_ACCEL_NONE) {
+ /* First try an accelerated driver */
+ dispsw = probe_list(accel_drivers, disp);
+#ifdef CONFIG_KERNELD
+ if (!dispsw) {
+ request_driver(disp, 1);
+ dispsw = probe_list(accel_drivers, disp);
+ }
+#endif
+ if (dispsw)
+ return(dispsw);
+ }
+
+ /* Then try an unaccelerated driver */
+ dispsw = probe_list(drivers, disp);
+#ifdef CONFIG_KERNELD
+ if (!dispsw) {
+ request_driver(disp, 0);
+ dispsw = probe_list(drivers, disp);
+ }
+#endif
+ return(dispsw);
+}
+
+
+/*
+ * Dummy Low Level Operations
+ */
+
+static int open_dummy(struct display *p)
+{
+ if (p->line_length)
+ p->next_line = p->line_length;
+ else
+ p->next_line = p->var.xres_virtual>>3;
+ p->next_plane = 0;
+ p->var.bits_per_pixel = 1;
+ return 0;
+}
+
+static void misc_dummy(void) {}
+
+static struct display_switch dispsw_dummy = {
+ open_dummy,
+ /* release_dummy */
+ misc_dummy,
+ /* bmove_dummy */
+ (void (*)(struct display *, int, int, int, int, int, int))misc_dummy,
+ /* clear_dummy */
+ (void (*)(struct vc_data *, struct display *, int, int, int, int))misc_dummy,
+ /* putc_dummy */
+ (void (*)(struct vc_data *, struct display *, int, int, int))misc_dummy,
+ /* putcs_dummy */
+ (void (*)(struct vc_data *, struct display *, const char *, int, int, int))misc_dummy,
+ /* rev_char_dummy */
+ (void (*)(struct display *, int, int))misc_dummy,
+};
+
+
+/*
+ * Visible symbols for modules
+ */
+
+EXPORT_SYMBOL(fb_display);
+EXPORT_SYMBOL(fbcon_register_driver);
+EXPORT_SYMBOL(fbcon_unregister_driver);
diff --git a/drivers/video/fbcon.h b/drivers/video/fbcon.h
new file mode 100644
index 000000000..b83376d9e
--- /dev/null
+++ b/drivers/video/fbcon.h
@@ -0,0 +1,337 @@
+/*
+ * linux/drivers/video/fbcon.h -- Low level frame buffer based console driver
+ *
+ * Copyright (C) 1997 Geert Uytterhoeven
+ *
+ * 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/console_struct.h>
+
+
+ /*
+ * `switch' for the Low Level Operations
+ */
+
+struct display_switch {
+ int (*open)(struct display *p);
+ void (*release)(void);
+ void (*bmove)(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width);
+ void (*clear)(struct vc_data *conp, struct display *p, int sy, int sx,
+ int height, int width);
+ void (*putc)(struct vc_data *conp, struct display *p, int c, int yy,
+ int xx);
+ void (*putcs)(struct vc_data *conp, struct display *p, const char *s,
+ int count, int yy, int xx);
+ void (*rev_char)(struct display *p, int xx, int yy);
+ struct display_switch *next;
+};
+
+
+ /*
+ * Driver registration
+ */
+
+extern int fbcon_register_driver(struct display_switch *dispsw, int is_accel);
+int fbcon_unregister_driver(struct display_switch *dispsw);
+
+
+ /*
+ * Attribute Decoding
+ */
+
+/* Color */
+#define attr_fgcol(p,conp) \
+ (((conp)->vc_attr >> ((p)->inverse ? 4 : 0)) & 0x0f)
+#define attr_bgcol(p,conp) \
+ (((conp)->vc_attr >> ((p)->inverse ? 0 : 4)) & 0x0f)
+#define attr_bgcol_ec(p,conp) \
+ (((conp)->vc_video_erase_char >> ((p)->inverse ? 8 : 12)) & 0x0f)
+
+/* Monochrome */
+#define attr_bold(p,conp) \
+ (((conp)->vc_attr & 3) == 2)
+#define attr_reverse(p,conp) \
+ (((conp)->vc_attr & 8) ^ ((p)->inverse ? 8 : 0))
+#define attr_underline(p,conp) \
+ (((conp)->vc_attr) & 4)
+
+
+/* ================================================================= */
+/* Utility Assembler Functions */
+/* ================================================================= */
+
+
+#ifdef __mc68000__
+
+/* ====================================================================== */
+
+/* Those of a delicate disposition might like to skip the next couple of
+ * pages.
+ *
+ * These functions are drop in replacements for memmove and
+ * memset(_, 0, _). However their five instances add at least a kilobyte
+ * to the object file. You have been warned.
+ *
+ * Not a great fan of assembler for the sake of it, but I think
+ * that these routines are at least 10 times faster than their C
+ * equivalents for large blits, and that's important to the lowest level of
+ * a graphics driver. Question is whether some scheme with the blitter
+ * would be faster. I suspect not for simple text system - not much
+ * asynchrony.
+ *
+ * Code is very simple, just gruesome expansion. Basic strategy is to
+ * increase data moved/cleared at each step to 16 bytes to reduce
+ * instruction per data move overhead. movem might be faster still
+ * For more than 15 bytes, we try to align the write direction on a
+ * longword boundary to get maximum speed. This is even more gruesome.
+ * Unaligned read/write used requires 68020+ - think this is a problem?
+ *
+ * Sorry!
+ */
+
+
+/* ++roman: I've optimized Robert's original versions in some minor
+ * aspects, e.g. moveq instead of movel, let gcc choose the registers,
+ * use movem in some places...
+ * For other modes than 1 plane, lots of more such assembler functions
+ * were needed (e.g. the ones using movep or expanding color values).
+ */
+
+/* ++andreas: more optimizations:
+ subl #65536,d0 replaced by clrw d0; subql #1,d0 for dbcc
+ addal is faster than addaw
+ movep is rather expensive compared to ordinary move's
+ some functions rewritten in C for clarity, no speed loss */
+
+static __inline__ void *mymemclear_small(void *s, size_t count)
+{
+ if (!count)
+ return(0);
+
+ __asm__ __volatile__(
+ "lsrl #1,%1 ; jcc 1f ; moveb %2,%0@-\n\t"
+ "1: lsrl #1,%1 ; jcc 1f ; movew %2,%0@-\n\t"
+ "1: lsrl #1,%1 ; jcc 1f ; movel %2,%0@-\n\t"
+ "1: lsrl #1,%1 ; jcc 1f ; movel %2,%0@- ; movel %2,%0@-\n\t"
+ "1: subql #1,%1 ; jcs 3f\n\t"
+ "2: moveml %2/%3/%4/%5,%0@-\n\t"
+ "dbra %1,2b\n\t"
+ "3:"
+ : "=a" (s), "=d" (count)
+ : "d" (0), "d" (0), "d" (0), "d" (0),
+ "0" ((char *)s+count), "1" (count)
+ );
+
+ return(0);
+}
+
+
+static __inline__ void *mymemclear(void *s, size_t count)
+{
+ if (!count)
+ return(0);
+
+ if (count < 16) {
+ __asm__ __volatile__(
+ "lsrl #1,%1 ; jcc 1f ; clrb %0@+\n\t"
+ "1: lsrl #1,%1 ; jcc 1f ; clrw %0@+\n\t"
+ "1: lsrl #1,%1 ; jcc 1f ; clrl %0@+\n\t"
+ "1: lsrl #1,%1 ; jcc 1f ; clrl %0@+ ; clrl %0@+\n\t"
+ "1:"
+ : "=a" (s), "=d" (count)
+ : "0" (s), "1" (count)
+ );
+ } else {
+ long tmp;
+ __asm__ __volatile__(
+ "movel %1,%2\n\t"
+ "lsrl #1,%2 ; jcc 1f ; clrb %0@+ ; subqw #1,%1\n\t"
+ "lsrl #1,%2 ; jcs 2f\n\t" /* %0 increased=>bit 2 switched*/
+ "clrw %0@+ ; subqw #2,%1 ; jra 2f\n\t"
+ "1: lsrl #1,%2 ; jcc 2f\n\t"
+ "clrw %0@+ ; subqw #2,%1\n\t"
+ "2: movew %1,%2; lsrl #2,%1 ; jeq 6f\n\t"
+ "lsrl #1,%1 ; jcc 3f ; clrl %0@+\n\t"
+ "3: lsrl #1,%1 ; jcc 4f ; clrl %0@+ ; clrl %0@+\n\t"
+ "4: subql #1,%1 ; jcs 6f\n\t"
+ "5: clrl %0@+; clrl %0@+ ; clrl %0@+ ; clrl %0@+\n\t"
+ "dbra %1,5b ; clrw %1; subql #1,%1; jcc 5b\n\t"
+ "6: movew %2,%1; btst #1,%1 ; jeq 7f ; clrw %0@+\n\t"
+ "7: ; btst #0,%1 ; jeq 8f ; clrb %0@+\n\t"
+ "8:"
+ : "=a" (s), "=d" (count), "=d" (tmp)
+ : "0" (s), "1" (count)
+ );
+ }
+
+ return(0);
+}
+
+
+static __inline__ void *mymemset(void *s, size_t count)
+{
+ if (!count)
+ return(0);
+
+ __asm__ __volatile__(
+ "lsrl #1,%1 ; jcc 1f ; moveb %2,%0@-\n\t"
+ "1: lsrl #1,%1 ; jcc 1f ; movew %2,%0@-\n\t"
+ "1: lsrl #1,%1 ; jcc 1f ; movel %2,%0@-\n\t"
+ "1: lsrl #1,%1 ; jcc 1f ; movel %2,%0@- ; movel %2,%0@-\n\t"
+ "1: subql #1,%1 ; jcs 3f\n\t"
+ "2: moveml %2/%3/%4/%5,%0@-\n\t"
+ "dbra %1,2b\n\t"
+ "3:"
+ : "=a" (s), "=d" (count)
+ : "d" (-1), "d" (-1), "d" (-1), "d" (-1),
+ "0" ((char *) s + count), "1" (count)
+ );
+
+ return(0);
+}
+
+
+static __inline__ void *mymemmove(void *d, const void *s, size_t count)
+{
+ if (d < s) {
+ if (count < 16) {
+ __asm__ __volatile__(
+ "lsrl #1,%2 ; jcc 1f ; moveb %1@+,%0@+\n\t"
+ "1: lsrl #1,%2 ; jcc 1f ; movew %1@+,%0@+\n\t"
+ "1: lsrl #1,%2 ; jcc 1f ; movel %1@+,%0@+\n\t"
+ "1: lsrl #1,%2 ; jcc 1f ; movel %1@+,%0@+ ; movel %1@+,%0@+\n\t"
+ "1:"
+ : "=a" (d), "=a" (s), "=d" (count)
+ : "0" (d), "1" (s), "2" (count)
+ );
+ } else {
+ long tmp;
+ __asm__ __volatile__(
+ "movel %0,%3\n\t"
+ "lsrl #1,%3 ; jcc 1f ; moveb %1@+,%0@+ ; subqw #1,%2\n\t"
+ "lsrl #1,%3 ; jcs 2f\n\t" /* %0 increased=>bit 2 switched*/
+ "movew %1@+,%0@+ ; subqw #2,%2 ; jra 2f\n\t"
+ "1: lsrl #1,%3 ; jcc 2f\n\t"
+ "movew %1@+,%0@+ ; subqw #2,%2\n\t"
+ "2: movew %2,%-; lsrl #2,%2 ; jeq 6f\n\t"
+ "lsrl #1,%2 ; jcc 3f ; movel %1@+,%0@+\n\t"
+ "3: lsrl #1,%2 ; jcc 4f ; movel %1@+,%0@+ ; movel %1@+,%0@+\n\t"
+ "4: subql #1,%2 ; jcs 6f\n\t"
+ "5: movel %1@+,%0@+;movel %1@+,%0@+\n\t"
+ "movel %1@+,%0@+;movel %1@+,%0@+\n\t"
+ "dbra %2,5b ; clrw %2; subql #1,%2; jcc 5b\n\t"
+ "6: movew %+,%2; btst #1,%2 ; jeq 7f ; movew %1@+,%0@+\n\t"
+ "7: ; btst #0,%2 ; jeq 8f ; moveb %1@+,%0@+\n\t"
+ "8:"
+ : "=a" (d), "=a" (s), "=d" (count), "=d" (tmp)
+ : "0" (d), "1" (s), "2" (count)
+ );
+ }
+ } else {
+ if (count < 16) {
+ __asm__ __volatile__(
+ "lsrl #1,%2 ; jcc 1f ; moveb %1@-,%0@-\n\t"
+ "1: lsrl #1,%2 ; jcc 1f ; movew %1@-,%0@-\n\t"
+ "1: lsrl #1,%2 ; jcc 1f ; movel %1@-,%0@-\n\t"
+ "1: lsrl #1,%2 ; jcc 1f ; movel %1@-,%0@- ; movel %1@-,%0@-\n\t"
+ "1:"
+ : "=a" (d), "=a" (s), "=d" (count)
+ : "0" ((char *) d + count), "1" ((char *) s + count), "2" (count)
+ );
+ } else {
+ long tmp;
+ __asm__ __volatile__(
+ "movel %0,%3\n\t"
+ "lsrl #1,%3 ; jcc 1f ; moveb %1@-,%0@- ; subqw #1,%2\n\t"
+ "lsrl #1,%3 ; jcs 2f\n\t" /* %0 increased=>bit 2 switched*/
+ "movew %1@-,%0@- ; subqw #2,%2 ; jra 2f\n\t"
+ "1: lsrl #1,%3 ; jcc 2f\n\t"
+ "movew %1@-,%0@- ; subqw #2,%2\n\t"
+ "2: movew %2,%-; lsrl #2,%2 ; jeq 6f\n\t"
+ "lsrl #1,%2 ; jcc 3f ; movel %1@-,%0@-\n\t"
+ "3: lsrl #1,%2 ; jcc 4f ; movel %1@-,%0@- ; movel %1@-,%0@-\n\t"
+ "4: subql #1,%2 ; jcs 6f\n\t"
+ "5: movel %1@-,%0@-;movel %1@-,%0@-\n\t"
+ "movel %1@-,%0@-;movel %1@-,%0@-\n\t"
+ "dbra %2,5b ; clrw %2; subql #1,%2; jcc 5b\n\t"
+ "6: movew %+,%2; btst #1,%2 ; jeq 7f ; movew %1@-,%0@-\n\t"
+ "7: ; btst #0,%2 ; jeq 8f ; moveb %1@-,%0@-\n\t"
+ "8:"
+ : "=a" (d), "=a" (s), "=d" (count), "=d" (tmp)
+ : "0" ((char *) d + count), "1" ((char *) s + count), "2" (count)
+ );
+ }
+ }
+
+ return(0);
+}
+
+
+/* ++andreas: Simple and fast version of memmove, assumes size is
+ divisible by 16, suitable for moving the whole screen bitplane */
+static __inline__ void fast_memmove(char *dst, const char *src, size_t size)
+{
+ if (!size)
+ return;
+ if (dst < src)
+ __asm__ __volatile__
+ ("1:"
+ " moveml %0@+,%/d0/%/d1/%/a0/%/a1\n"
+ " moveml %/d0/%/d1/%/a0/%/a1,%1@\n"
+ " addql #8,%1; addql #8,%1\n"
+ " dbra %2,1b\n"
+ " clrw %2; subql #1,%2\n"
+ " jcc 1b"
+ : "=a" (src), "=a" (dst), "=d" (size)
+ : "0" (src), "1" (dst), "2" (size / 16 - 1)
+ : "d0", "d1", "a0", "a1", "memory");
+ else
+ __asm__ __volatile__
+ ("1:"
+ " subql #8,%0; subql #8,%0\n"
+ " moveml %0@,%/d0/%/d1/%/a0/%/a1\n"
+ " moveml %/d0/%/d1/%/a0/%/a1,%1@-\n"
+ " dbra %2,1b\n"
+ " clrw %2; subql #1,%2\n"
+ " jcc 1b"
+ : "=a" (src), "=a" (dst), "=d" (size)
+ : "0" (src + size), "1" (dst + size), "2" (size / 16 - 1)
+ : "d0", "d1", "a0", "a1", "memory");
+}
+
+#else /* !m68k */
+
+ /*
+ * Anyone who'd like to write asm functions for other CPUs?
+ */
+
+static __inline__ void *mymemclear_small(void *s, size_t count)
+{
+ return(memset(s, 0, count));
+}
+
+static __inline__ void *mymemclear(void *s, size_t count)
+{
+ return(memset(s, 0, count));
+}
+
+static __inline__ void *mymemset(void *s, size_t count)
+{
+ return(memset(s, 255, count));
+}
+
+static __inline__ void *mymemmove(void *d, const void *s, size_t count)
+{
+ return(memmove(d, s, count));
+}
+
+static __inline__ void fast_memmove(char *dst, const char *src, size_t size)
+{
+ memmove(dst, src, size);
+}
+
+#endif /* !m68k */
diff --git a/drivers/video/font.h b/drivers/video/font.h
new file mode 100644
index 000000000..b020fcdc9
--- /dev/null
+++ b/drivers/video/font.h
@@ -0,0 +1,35 @@
+/*
+ * font.h -- `Soft' font definitions
+ *
+ * Created 1995 by Geert Uytterhoeven
+ *
+ * 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 _FONT_H_
+#define _FONT_H_
+
+#include <linux/types.h>
+
+
+ /*
+ * Find a font with a specific name
+ */
+
+extern int findsoftfont(char *name, int *width, int *height, u_char *data[]);
+
+
+ /*
+ * Get the default font for a specific screen size
+ */
+
+extern void getdefaultfont(int xres, int yres, char *name[], int *width,
+ int *height, u_char *data[]);
+
+
+/* Max. length for the name of a predefined font */
+#define MAX_FONT_NAME 32
+
+#endif /* _FONT_H_ */
diff --git a/drivers/video/font_8x16.c b/drivers/video/font_8x16.c
new file mode 100644
index 000000000..59801b438
--- /dev/null
+++ b/drivers/video/font_8x16.c
@@ -0,0 +1,4625 @@
+/**********************************************/
+/* */
+/* Font file generated by cpi2fnt */
+/* */
+/**********************************************/
+
+#define FONTDATAMAX 4096
+
+char fontname_8x16[] = "VGA8x16";
+
+int fontheight_8x16 = 16;
+int fontwidth_8x16 = 8;
+
+unsigned char fontdata_8x16[FONTDATAMAX] = {
+
+ /* 0 0x00 '^@' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 1 0x01 '^A' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x81, /* 10000001 */
+ 0xa5, /* 10100101 */
+ 0x81, /* 10000001 */
+ 0x81, /* 10000001 */
+ 0xbd, /* 10111101 */
+ 0x99, /* 10011001 */
+ 0x81, /* 10000001 */
+ 0x81, /* 10000001 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 2 0x02 '^B' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xff, /* 11111111 */
+ 0xdb, /* 11011011 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xc3, /* 11000011 */
+ 0xe7, /* 11100111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 3 0x03 '^C' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 4 0x04 '^D' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 5 0x05 '^E' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0xe7, /* 11100111 */
+ 0xe7, /* 11100111 */
+ 0xe7, /* 11100111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 6 0x06 '^F' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 7 0x07 '^G' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 8 0x08 '^H' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xe7, /* 11100111 */
+ 0xc3, /* 11000011 */
+ 0xc3, /* 11000011 */
+ 0xe7, /* 11100111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 9 0x09 '^I' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x42, /* 01000010 */
+ 0x42, /* 01000010 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 10 0x0a '^J' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xc3, /* 11000011 */
+ 0x99, /* 10011001 */
+ 0xbd, /* 10111101 */
+ 0xbd, /* 10111101 */
+ 0x99, /* 10011001 */
+ 0xc3, /* 11000011 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 11 0x0b '^K' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1e, /* 00011110 */
+ 0x0e, /* 00001110 */
+ 0x1a, /* 00011010 */
+ 0x32, /* 00110010 */
+ 0x78, /* 01111000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 12 0x0c '^L' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 13 0x0d '^M' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3f, /* 00111111 */
+ 0x33, /* 00110011 */
+ 0x3f, /* 00111111 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x70, /* 01110000 */
+ 0xf0, /* 11110000 */
+ 0xe0, /* 11100000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 14 0x0e '^N' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7f, /* 01111111 */
+ 0x63, /* 01100011 */
+ 0x7f, /* 01111111 */
+ 0x63, /* 01100011 */
+ 0x63, /* 01100011 */
+ 0x63, /* 01100011 */
+ 0x63, /* 01100011 */
+ 0x67, /* 01100111 */
+ 0xe7, /* 11100111 */
+ 0xe6, /* 11100110 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 15 0x0f '^O' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xdb, /* 11011011 */
+ 0x3c, /* 00111100 */
+ 0xe7, /* 11100111 */
+ 0x3c, /* 00111100 */
+ 0xdb, /* 11011011 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 16 0x10 '^P' */
+ 0x00, /* 00000000 */
+ 0x80, /* 10000000 */
+ 0xc0, /* 11000000 */
+ 0xe0, /* 11100000 */
+ 0xf0, /* 11110000 */
+ 0xf8, /* 11111000 */
+ 0xfe, /* 11111110 */
+ 0xf8, /* 11111000 */
+ 0xf0, /* 11110000 */
+ 0xe0, /* 11100000 */
+ 0xc0, /* 11000000 */
+ 0x80, /* 10000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 17 0x11 '^Q' */
+ 0x00, /* 00000000 */
+ 0x02, /* 00000010 */
+ 0x06, /* 00000110 */
+ 0x0e, /* 00001110 */
+ 0x1e, /* 00011110 */
+ 0x3e, /* 00111110 */
+ 0xfe, /* 11111110 */
+ 0x3e, /* 00111110 */
+ 0x1e, /* 00011110 */
+ 0x0e, /* 00001110 */
+ 0x06, /* 00000110 */
+ 0x02, /* 00000010 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 18 0x12 '^R' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 19 0x13 '^S' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 20 0x14 '^T' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7f, /* 01111111 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0x7b, /* 01111011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 21 0x15 '^U' */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x0c, /* 00001100 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 22 0x16 '^V' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 23 0x17 '^W' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 24 0x18 '^X' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 25 0x19 '^Y' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 26 0x1a '^Z' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0xfe, /* 11111110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 27 0x1b '^[' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xfe, /* 11111110 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 28 0x1c '^\' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 29 0x1d '^]' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x28, /* 00101000 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x28, /* 00101000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 30 0x1e '^^' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0x7c, /* 01111100 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 31 0x1f '^_' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 32 0x20 ' ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 33 0x21 '!' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 34 0x22 '"' */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x24, /* 00100100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 35 0x23 '#' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 36 0x24 '$' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x86, /* 10000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 37 0x25 '%' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc2, /* 11000010 */
+ 0xc6, /* 11000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc6, /* 11000110 */
+ 0x86, /* 10000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 38 0x26 '&' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 39 0x27 ''' */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 40 0x28 '(' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 41 0x29 ')' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 42 0x2a '*' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0xff, /* 11111111 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 43 0x2b '+' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 44 0x2c ',' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 45 0x2d '-' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 46 0x2e '.' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 47 0x2f '/' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x02, /* 00000010 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0x80, /* 10000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 48 0x30 '0' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 49 0x31 '1' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x38, /* 00111000 */
+ 0x78, /* 01111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 50 0x32 '2' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 51 0x33 '3' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x3c, /* 00111100 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 52 0x34 '4' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x1c, /* 00011100 */
+ 0x3c, /* 00111100 */
+ 0x6c, /* 01101100 */
+ 0xcc, /* 11001100 */
+ 0xfe, /* 11111110 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x1e, /* 00011110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 53 0x35 '5' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfc, /* 11111100 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 54 0x36 '6' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfc, /* 11111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 55 0x37 '7' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 56 0x38 '8' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 57 0x39 '9' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 58 0x3a ':' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 59 0x3b ';' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 60 0x3c '<' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 61 0x3d '=' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 62 0x3e '>' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 63 0x3f '?' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 64 0x40 '@' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xde, /* 11011110 */
+ 0xde, /* 11011110 */
+ 0xde, /* 11011110 */
+ 0xdc, /* 11011100 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 65 0x41 'A' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 66 0x42 'B' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfc, /* 11111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xfc, /* 11111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 67 0x43 'C' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc2, /* 11000010 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 68 0x44 'D' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 69 0x45 'E' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x66, /* 01100110 */
+ 0x62, /* 01100010 */
+ 0x68, /* 01101000 */
+ 0x78, /* 01111000 */
+ 0x68, /* 01101000 */
+ 0x60, /* 01100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 70 0x46 'F' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x66, /* 01100110 */
+ 0x62, /* 01100010 */
+ 0x68, /* 01101000 */
+ 0x78, /* 01111000 */
+ 0x68, /* 01101000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 71 0x47 'G' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xde, /* 11011110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x66, /* 01100110 */
+ 0x3a, /* 00111010 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 72 0x48 'H' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 73 0x49 'I' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 74 0x4a 'J' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1e, /* 00011110 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 75 0x4b 'K' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe6, /* 11100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x78, /* 01111000 */
+ 0x78, /* 01111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 76 0x4c 'L' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf0, /* 11110000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 77 0x4d 'M' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xee, /* 11101110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xd6, /* 11010110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 78 0x4e 'N' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xe6, /* 11100110 */
+ 0xf6, /* 11110110 */
+ 0xfe, /* 11111110 */
+ 0xde, /* 11011110 */
+ 0xce, /* 11001110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 79 0x4f 'O' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 80 0x50 'P' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfc, /* 11111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 81 0x51 'Q' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xde, /* 11011110 */
+ 0x7c, /* 01111100 */
+ 0x0c, /* 00001100 */
+ 0x0e, /* 00001110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 82 0x52 'R' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfc, /* 11111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 83 0x53 'S' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x38, /* 00111000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 84 0x54 'T' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x5a, /* 01011010 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 85 0x55 'U' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 86 0x56 'V' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 87 0x57 'W' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xfe, /* 11111110 */
+ 0xee, /* 11101110 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 88 0x58 'X' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 89 0x59 'Y' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 90 0x5a 'Z' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x86, /* 10000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc2, /* 11000010 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 91 0x5b '[' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 92 0x5c '\' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x80, /* 10000000 */
+ 0xc0, /* 11000000 */
+ 0xe0, /* 11100000 */
+ 0x70, /* 01110000 */
+ 0x38, /* 00111000 */
+ 0x1c, /* 00011100 */
+ 0x0e, /* 00001110 */
+ 0x06, /* 00000110 */
+ 0x02, /* 00000010 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 93 0x5d ']' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 94 0x5e '^' */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 95 0x5f '_' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 96 0x60 '`' */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 97 0x61 'a' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 98 0x62 'b' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe0, /* 11100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x78, /* 01111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 99 0x63 'c' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 100 0x64 'd' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1c, /* 00011100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x3c, /* 00111100 */
+ 0x6c, /* 01101100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 101 0x65 'e' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 102 0x66 'f' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1c, /* 00011100 */
+ 0x36, /* 00110110 */
+ 0x32, /* 00110010 */
+ 0x30, /* 00110000 */
+ 0x78, /* 01111000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 103 0x67 'g' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x7c, /* 01111100 */
+ 0x0c, /* 00001100 */
+ 0xcc, /* 11001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+
+ /* 104 0x68 'h' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe0, /* 11100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x6c, /* 01101100 */
+ 0x76, /* 01110110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 105 0x69 'i' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 106 0x6a 'j' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 107 0x6b 'k' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe0, /* 11100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x78, /* 01111000 */
+ 0x78, /* 01111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 108 0x6c 'l' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 109 0x6d 'm' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xec, /* 11101100 */
+ 0xfe, /* 11111110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 110 0x6e 'n' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 111 0x6f 'o' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 112 0x70 'p' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+
+ /* 113 0x71 'q' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x7c, /* 01111100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x1e, /* 00011110 */
+ 0x00, /* 00000000 */
+
+ /* 114 0x72 'r' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x76, /* 01110110 */
+ 0x66, /* 01100110 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 115 0x73 's' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x38, /* 00111000 */
+ 0x0c, /* 00001100 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 116 0x74 't' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0xfc, /* 11111100 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x36, /* 00110110 */
+ 0x1c, /* 00011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 117 0x75 'u' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 118 0x76 'v' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 119 0x77 'w' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 120 0x78 'x' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 121 0x79 'y' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+
+ /* 122 0x7a 'z' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xcc, /* 11001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 123 0x7b '{' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x0e, /* 00001110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 124 0x7c '|' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 125 0x7d '}' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x70, /* 01110000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x0e, /* 00001110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 126 0x7e '~' */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 127 0x7f '' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 128 0x80 '€' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc2, /* 11000010 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 129 0x81 '' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 130 0x82 '‚' */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 131 0x83 'ƒ' */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 132 0x84 '„' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 133 0x85 '…' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 134 0x86 '†' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 135 0x87 '‡' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 136 0x88 'ˆ' */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 137 0x89 '‰' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 138 0x8a 'Š' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 139 0x8b '‹' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 140 0x8c 'Œ' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 141 0x8d '' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 142 0x8e 'Ž' */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 143 0x8f '' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 144 0x90 '' */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x66, /* 01100110 */
+ 0x62, /* 01100010 */
+ 0x68, /* 01101000 */
+ 0x78, /* 01111000 */
+ 0x68, /* 01101000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 145 0x91 '‘' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xec, /* 11101100 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x7e, /* 01111110 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0x6e, /* 01101110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 146 0x92 '’' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3e, /* 00111110 */
+ 0x6c, /* 01101100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xfe, /* 11111110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xce, /* 11001110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 147 0x93 '“' */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 148 0x94 '”' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 149 0x95 '•' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 150 0x96 '–' */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x78, /* 01111000 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 151 0x97 '—' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 152 0x98 '˜' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+
+ /* 153 0x99 '™' */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 154 0x9a 'š' */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 155 0x9b '›' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 156 0x9c 'œ' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x64, /* 01100100 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xe6, /* 11100110 */
+ 0xfc, /* 11111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 157 0x9d '' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 158 0x9e 'ž' */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xf8, /* 11111000 */
+ 0xc4, /* 11000100 */
+ 0xcc, /* 11001100 */
+ 0xde, /* 11011110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 159 0x9f 'Ÿ' */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x1b, /* 00011011 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 160 0xa0 ' ' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 161 0xa1 '¡' */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 162 0xa2 '¢' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 163 0xa3 '£' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 164 0xa4 '¤' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 165 0xa5 '¥' */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xe6, /* 11100110 */
+ 0xf6, /* 11110110 */
+ 0xfe, /* 11111110 */
+ 0xde, /* 11011110 */
+ 0xce, /* 11001110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 166 0xa6 '¦' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x3e, /* 00111110 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 167 0xa7 '§' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 168 0xa8 '¨' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 169 0xa9 '©' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 170 0xaa 'ª' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 171 0xab '«' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0xe0, /* 11100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xdc, /* 11011100 */
+ 0x86, /* 10000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x3e, /* 00111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 172 0xac '¬' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0xe0, /* 11100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x66, /* 01100110 */
+ 0xce, /* 11001110 */
+ 0x9a, /* 10011010 */
+ 0x3f, /* 00111111 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 173 0xad '­' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 174 0xae '®' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x36, /* 00110110 */
+ 0x6c, /* 01101100 */
+ 0xd8, /* 11011000 */
+ 0x6c, /* 01101100 */
+ 0x36, /* 00110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 175 0xaf '¯' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xd8, /* 11011000 */
+ 0x6c, /* 01101100 */
+ 0x36, /* 00110110 */
+ 0x6c, /* 01101100 */
+ 0xd8, /* 11011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 176 0xb0 '°' */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+
+ /* 177 0xb1 '±' */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+
+ /* 178 0xb2 '²' */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+
+ /* 179 0xb3 '³' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 180 0xb4 '´' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 181 0xb5 'µ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 182 0xb6 '¶' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 183 0xb7 '·' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 184 0xb8 '¸' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 185 0xb9 '¹' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x06, /* 00000110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 186 0xba 'º' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 187 0xbb '»' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x06, /* 00000110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 188 0xbc '¼' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x06, /* 00000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 189 0xbd '½' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 190 0xbe '¾' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 191 0xbf '¿' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 192 0xc0 'À' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 193 0xc1 'Á' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 194 0xc2 'Â' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 195 0xc3 'Ã' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 196 0xc4 'Ä' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 197 0xc5 'Å' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 198 0xc6 'Æ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 199 0xc7 'Ç' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 200 0xc8 'È' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x30, /* 00110000 */
+ 0x3f, /* 00111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 201 0xc9 'É' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3f, /* 00111111 */
+ 0x30, /* 00110000 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 202 0xca 'Ê' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf7, /* 11110111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 203 0xcb 'Ë' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xf7, /* 11110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 204 0xcc 'Ì' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x30, /* 00110000 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 205 0xcd 'Í' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 206 0xce 'Î' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf7, /* 11110111 */
+ 0x00, /* 00000000 */
+ 0xf7, /* 11110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 207 0xcf 'Ï' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 208 0xd0 'Ð' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 209 0xd1 'Ñ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 210 0xd2 'Ò' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 211 0xd3 'Ó' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x3f, /* 00111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 212 0xd4 'Ô' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 213 0xd5 'Õ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 214 0xd6 'Ö' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3f, /* 00111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 215 0xd7 '×' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xff, /* 11111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 216 0xd8 'Ø' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 217 0xd9 'Ù' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 218 0xda 'Ú' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 219 0xdb 'Û' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 220 0xdc 'Ü' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 221 0xdd 'Ý' */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+
+ /* 222 0xde 'Þ' */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+
+ /* 223 0xdf 'ß' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 224 0xe0 'à' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xdc, /* 11011100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 225 0xe1 'á' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xd8, /* 11011000 */
+ 0xcc, /* 11001100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 226 0xe2 'â' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 227 0xe3 'ã' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 228 0xe4 'ä' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 229 0xe5 'å' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 230 0xe6 'æ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+
+ /* 231 0xe7 'ç' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 232 0xe8 'è' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 233 0xe9 'é' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 234 0xea 'ê' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0xee, /* 11101110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 235 0xeb 'ë' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1e, /* 00011110 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x3e, /* 00111110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 236 0xec 'ì' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 237 0xed 'í' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x03, /* 00000011 */
+ 0x06, /* 00000110 */
+ 0x7e, /* 01111110 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0xf3, /* 11110011 */
+ 0x7e, /* 01111110 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 238 0xee 'î' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1c, /* 00011100 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x1c, /* 00011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 239 0xef 'ï' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 240 0xf0 'ð' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 241 0xf1 'ñ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 242 0xf2 'ò' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 243 0xf3 'ó' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 244 0xf4 'ô' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 245 0xf5 'õ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 246 0xf6 'ö' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 247 0xf7 '÷' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 248 0xf8 'ø' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 249 0xf9 'ù' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 250 0xfa 'ú' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 251 0xfb 'û' */
+ 0x00, /* 00000000 */
+ 0x0f, /* 00001111 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0xec, /* 11101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x3c, /* 00111100 */
+ 0x1c, /* 00011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 252 0xfc 'ü' */
+ 0x00, /* 00000000 */
+ 0x6c, /* 01101100 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 253 0xfd 'ý' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x32, /* 00110010 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 254 0xfe 'þ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 255 0xff 'ÿ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+};
+
diff --git a/drivers/video/font_8x8.c b/drivers/video/font_8x8.c
new file mode 100644
index 000000000..72a6c3f8f
--- /dev/null
+++ b/drivers/video/font_8x8.c
@@ -0,0 +1,2577 @@
+/**********************************************/
+/* */
+/* Font file generated by cpi2fnt */
+/* */
+/**********************************************/
+
+#define FONTDATAMAX 2048
+
+char fontname_8x8[] = "VGA8x8";
+
+int fontheight_8x8 = 8;
+int fontwidth_8x8 = 8;
+
+unsigned char fontdata_8x8[FONTDATAMAX] = {
+
+ /* 0 0x00 '^@' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 1 0x01 '^A' */
+ 0x7e, /* 01111110 */
+ 0x81, /* 10000001 */
+ 0xa5, /* 10100101 */
+ 0x81, /* 10000001 */
+ 0xbd, /* 10111101 */
+ 0x99, /* 10011001 */
+ 0x81, /* 10000001 */
+ 0x7e, /* 01111110 */
+
+ /* 2 0x02 '^B' */
+ 0x7e, /* 01111110 */
+ 0xff, /* 11111111 */
+ 0xdb, /* 11011011 */
+ 0xff, /* 11111111 */
+ 0xc3, /* 11000011 */
+ 0xe7, /* 11100111 */
+ 0xff, /* 11111111 */
+ 0x7e, /* 01111110 */
+
+ /* 3 0x03 '^C' */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+
+ /* 4 0x04 '^D' */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+
+ /* 5 0x05 '^E' */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xd6, /* 11010110 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+
+ /* 6 0x06 '^F' */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+
+ /* 7 0x07 '^G' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 8 0x08 '^H' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xe7, /* 11100111 */
+ 0xc3, /* 11000011 */
+ 0xc3, /* 11000011 */
+ 0xe7, /* 11100111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 9 0x09 '^I' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x42, /* 01000010 */
+ 0x42, /* 01000010 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 10 0x0a '^J' */
+ 0xff, /* 11111111 */
+ 0xc3, /* 11000011 */
+ 0x99, /* 10011001 */
+ 0xbd, /* 10111101 */
+ 0xbd, /* 10111101 */
+ 0x99, /* 10011001 */
+ 0xc3, /* 11000011 */
+ 0xff, /* 11111111 */
+
+ /* 11 0x0b '^K' */
+ 0x0f, /* 00001111 */
+ 0x07, /* 00000111 */
+ 0x0f, /* 00001111 */
+ 0x7d, /* 01111101 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x78, /* 01111000 */
+
+ /* 12 0x0c '^L' */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+
+ /* 13 0x0d '^M' */
+ 0x3f, /* 00111111 */
+ 0x33, /* 00110011 */
+ 0x3f, /* 00111111 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x70, /* 01110000 */
+ 0xf0, /* 11110000 */
+ 0xe0, /* 11100000 */
+
+ /* 14 0x0e '^N' */
+ 0x7f, /* 01111111 */
+ 0x63, /* 01100011 */
+ 0x7f, /* 01111111 */
+ 0x63, /* 01100011 */
+ 0x63, /* 01100011 */
+ 0x67, /* 01100111 */
+ 0xe6, /* 11100110 */
+ 0xc0, /* 11000000 */
+
+ /* 15 0x0f '^O' */
+ 0x18, /* 00011000 */
+ 0xdb, /* 11011011 */
+ 0x3c, /* 00111100 */
+ 0xe7, /* 11100111 */
+ 0xe7, /* 11100111 */
+ 0x3c, /* 00111100 */
+ 0xdb, /* 11011011 */
+ 0x18, /* 00011000 */
+
+ /* 16 0x10 '^P' */
+ 0x80, /* 10000000 */
+ 0xe0, /* 11100000 */
+ 0xf8, /* 11111000 */
+ 0xfe, /* 11111110 */
+ 0xf8, /* 11111000 */
+ 0xe0, /* 11100000 */
+ 0x80, /* 10000000 */
+ 0x00, /* 00000000 */
+
+ /* 17 0x11 '^Q' */
+ 0x02, /* 00000010 */
+ 0x0e, /* 00001110 */
+ 0x3e, /* 00111110 */
+ 0xfe, /* 11111110 */
+ 0x3e, /* 00111110 */
+ 0x0e, /* 00001110 */
+ 0x02, /* 00000010 */
+ 0x00, /* 00000000 */
+
+ /* 18 0x12 '^R' */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+
+ /* 19 0x13 '^S' */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+
+ /* 20 0x14 '^T' */
+ 0x7f, /* 01111111 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0x7b, /* 01111011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x00, /* 00000000 */
+
+ /* 21 0x15 '^U' */
+ 0x3e, /* 00111110 */
+ 0x61, /* 01100001 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x86, /* 10000110 */
+ 0x7c, /* 01111100 */
+
+ /* 22 0x16 '^V' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+
+ /* 23 0x17 '^W' */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+
+ /* 24 0x18 '^X' */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 25 0x19 '^Y' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 26 0x1a '^Z' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0xfe, /* 11111110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 27 0x1b '^[' */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xfe, /* 11111110 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 28 0x1c '^\' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 29 0x1d '^]' */
+ 0x00, /* 00000000 */
+ 0x24, /* 00100100 */
+ 0x66, /* 01100110 */
+ 0xff, /* 11111111 */
+ 0x66, /* 01100110 */
+ 0x24, /* 00100100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 30 0x1e '^^' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 31 0x1f '^_' */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 32 0x20 ' ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 33 0x21 '!' */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 34 0x22 '"' */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x24, /* 00100100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 35 0x23 '#' */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+
+ /* 36 0x24 '$' */
+ 0x18, /* 00011000 */
+ 0x3e, /* 00111110 */
+ 0x60, /* 01100000 */
+ 0x3c, /* 00111100 */
+ 0x06, /* 00000110 */
+ 0x7c, /* 01111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 37 0x25 '%' */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xcc, /* 11001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x66, /* 01100110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 38 0x26 '&' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 39 0x27 ''' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 40 0x28 '(' */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+
+ /* 41 0x29 ')' */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+
+ /* 42 0x2a '*' */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0xff, /* 11111111 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 43 0x2b '+' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 44 0x2c ',' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+
+ /* 45 0x2d '-' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 46 0x2e '.' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 47 0x2f '/' */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0x80, /* 10000000 */
+ 0x00, /* 00000000 */
+
+ /* 48 0x30 '0' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+
+ /* 49 0x31 '1' */
+ 0x18, /* 00011000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+
+ /* 50 0x32 '2' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x06, /* 00000110 */
+ 0x1c, /* 00011100 */
+ 0x30, /* 00110000 */
+ 0x66, /* 01100110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+
+ /* 51 0x33 '3' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x06, /* 00000110 */
+ 0x3c, /* 00111100 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 52 0x34 '4' */
+ 0x1c, /* 00011100 */
+ 0x3c, /* 00111100 */
+ 0x6c, /* 01101100 */
+ 0xcc, /* 11001100 */
+ 0xfe, /* 11111110 */
+ 0x0c, /* 00001100 */
+ 0x1e, /* 00011110 */
+ 0x00, /* 00000000 */
+
+ /* 53 0x35 '5' */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfc, /* 11111100 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 54 0x36 '6' */
+ 0x38, /* 00111000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0xfc, /* 11111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 55 0x37 '7' */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+
+ /* 56 0x38 '8' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 57 0x39 '9' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+
+ /* 58 0x3a ':' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 59 0x3b ';' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+
+ /* 60 0x3c '<' */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+
+ /* 61 0x3d '=' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 62 0x3e '>' */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+
+ /* 63 0x3f '?' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 64 0x40 '@' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xde, /* 11011110 */
+ 0xde, /* 11011110 */
+ 0xde, /* 11011110 */
+ 0xc0, /* 11000000 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+
+ /* 65 0x41 'A' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 66 0x42 'B' */
+ 0xfc, /* 11111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xfc, /* 11111100 */
+ 0x00, /* 00000000 */
+
+ /* 67 0x43 'C' */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 68 0x44 'D' */
+ 0xf8, /* 11111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+
+ /* 69 0x45 'E' */
+ 0xfe, /* 11111110 */
+ 0x62, /* 01100010 */
+ 0x68, /* 01101000 */
+ 0x78, /* 01111000 */
+ 0x68, /* 01101000 */
+ 0x62, /* 01100010 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+
+ /* 70 0x46 'F' */
+ 0xfe, /* 11111110 */
+ 0x62, /* 01100010 */
+ 0x68, /* 01101000 */
+ 0x78, /* 01111000 */
+ 0x68, /* 01101000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+
+ /* 71 0x47 'G' */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xce, /* 11001110 */
+ 0x66, /* 01100110 */
+ 0x3a, /* 00111010 */
+ 0x00, /* 00000000 */
+
+ /* 72 0x48 'H' */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 73 0x49 'I' */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 74 0x4a 'J' */
+ 0x1e, /* 00011110 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+
+ /* 75 0x4b 'K' */
+ 0xe6, /* 11100110 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x78, /* 01111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+
+ /* 76 0x4c 'L' */
+ 0xf0, /* 11110000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+
+ /* 77 0x4d 'M' */
+ 0xc6, /* 11000110 */
+ 0xee, /* 11101110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xd6, /* 11010110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 78 0x4e 'N' */
+ 0xc6, /* 11000110 */
+ 0xe6, /* 11100110 */
+ 0xf6, /* 11110110 */
+ 0xde, /* 11011110 */
+ 0xce, /* 11001110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 79 0x4f 'O' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 80 0x50 'P' */
+ 0xfc, /* 11111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+
+ /* 81 0x51 'Q' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xce, /* 11001110 */
+ 0x7c, /* 01111100 */
+ 0x0e, /* 00001110 */
+
+ /* 82 0x52 'R' */
+ 0xfc, /* 11111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+
+ /* 83 0x53 'S' */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 84 0x54 'T' */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x5a, /* 01011010 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 85 0x55 'U' */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 86 0x56 'V' */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+
+ /* 87 0x57 'W' */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+
+ /* 88 0x58 'X' */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 89 0x59 'Y' */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 90 0x5a 'Z' */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x8c, /* 10001100 */
+ 0x18, /* 00011000 */
+ 0x32, /* 00110010 */
+ 0x66, /* 01100110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+
+ /* 91 0x5b '[' */
+ 0x3c, /* 00111100 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 92 0x5c '\' */
+ 0xc0, /* 11000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0x02, /* 00000010 */
+ 0x00, /* 00000000 */
+
+ /* 93 0x5d ']' */
+ 0x3c, /* 00111100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 94 0x5e '^' */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 95 0x5f '_' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+
+ /* 96 0x60 '`' */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 97 0x61 'a' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 98 0x62 'b' */
+ 0xe0, /* 11100000 */
+ 0x60, /* 01100000 */
+ 0x7c, /* 01111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+
+ /* 99 0x63 'c' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 100 0x64 'd' */
+ 0x1c, /* 00011100 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 101 0x65 'e' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 102 0x66 'f' */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x60, /* 01100000 */
+ 0xf8, /* 11111000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+
+ /* 103 0x67 'g' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x7c, /* 01111100 */
+ 0x0c, /* 00001100 */
+ 0xf8, /* 11111000 */
+
+ /* 104 0x68 'h' */
+ 0xe0, /* 11100000 */
+ 0x60, /* 01100000 */
+ 0x6c, /* 01101100 */
+ 0x76, /* 01110110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+
+ /* 105 0x69 'i' */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 106 0x6a 'j' */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+
+ /* 107 0x6b 'k' */
+ 0xe0, /* 11100000 */
+ 0x60, /* 01100000 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x78, /* 01111000 */
+ 0x6c, /* 01101100 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+
+ /* 108 0x6c 'l' */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 109 0x6d 'm' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xec, /* 11101100 */
+ 0xfe, /* 11111110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0x00, /* 00000000 */
+
+ /* 110 0x6e 'n' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+
+ /* 111 0x6f 'o' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 112 0x70 'p' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+
+ /* 113 0x71 'q' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x7c, /* 01111100 */
+ 0x0c, /* 00001100 */
+ 0x1e, /* 00011110 */
+
+ /* 114 0x72 'r' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x76, /* 01110110 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+
+ /* 115 0x73 's' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x06, /* 00000110 */
+ 0xfc, /* 11111100 */
+ 0x00, /* 00000000 */
+
+ /* 116 0x74 't' */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0xfc, /* 11111100 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x36, /* 00110110 */
+ 0x1c, /* 00011100 */
+ 0x00, /* 00000000 */
+
+ /* 117 0x75 'u' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 118 0x76 'v' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+
+ /* 119 0x77 'w' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+
+ /* 120 0x78 'x' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 121 0x79 'y' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0xfc, /* 11111100 */
+
+ /* 122 0x7a 'z' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x4c, /* 01001100 */
+ 0x18, /* 00011000 */
+ 0x32, /* 00110010 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+
+ /* 123 0x7b '{' */
+ 0x0e, /* 00001110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x0e, /* 00001110 */
+ 0x00, /* 00000000 */
+
+ /* 124 0x7c '|' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 125 0x7d '}' */
+ 0x70, /* 01110000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x0e, /* 00001110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+
+ /* 126 0x7e '~' */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 127 0x7f '' */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+
+ /* 128 0x80 '€' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x0c, /* 00001100 */
+ 0x78, /* 01111000 */
+
+ /* 129 0x81 '' */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 130 0x82 '‚' */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 131 0x83 'ƒ' */
+ 0x7c, /* 01111100 */
+ 0x82, /* 10000010 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 132 0x84 '„' */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 133 0x85 '…' */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 134 0x86 '†' */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 135 0x87 '‡' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x7e, /* 01111110 */
+ 0x0c, /* 00001100 */
+ 0x38, /* 00111000 */
+
+ /* 136 0x88 'ˆ' */
+ 0x7c, /* 01111100 */
+ 0x82, /* 10000010 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 137 0x89 '‰' */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 138 0x8a 'Š' */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 139 0x8b '‹' */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 140 0x8c 'Œ' */
+ 0x7c, /* 01111100 */
+ 0x82, /* 10000010 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 141 0x8d '' */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 142 0x8e 'Ž' */
+ 0xc6, /* 11000110 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 143 0x8f '' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 144 0x90 '' */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xf8, /* 11111000 */
+ 0xc0, /* 11000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+
+ /* 145 0x91 '‘' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0xd8, /* 11011000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+
+ /* 146 0x92 '’' */
+ 0x3e, /* 00111110 */
+ 0x6c, /* 01101100 */
+ 0xcc, /* 11001100 */
+ 0xfe, /* 11111110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xce, /* 11001110 */
+ 0x00, /* 00000000 */
+
+ /* 147 0x93 '“' */
+ 0x7c, /* 01111100 */
+ 0x82, /* 10000010 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 148 0x94 '”' */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 149 0x95 '•' */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 150 0x96 '–' */
+ 0x78, /* 01111000 */
+ 0x84, /* 10000100 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 151 0x97 '—' */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 152 0x98 '˜' */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0xfc, /* 11111100 */
+
+ /* 153 0x99 '™' */
+ 0xc6, /* 11000110 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+
+ /* 154 0x9a 'š' */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 155 0x9b '›' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 156 0x9c 'œ' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x64, /* 01100100 */
+ 0xf0, /* 11110000 */
+ 0x60, /* 01100000 */
+ 0x66, /* 01100110 */
+ 0xfc, /* 11111100 */
+ 0x00, /* 00000000 */
+
+ /* 157 0x9d '' */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 158 0x9e 'ž' */
+ 0xf8, /* 11111000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xfa, /* 11111010 */
+ 0xc6, /* 11000110 */
+ 0xcf, /* 11001111 */
+ 0xc6, /* 11000110 */
+ 0xc7, /* 11000111 */
+
+ /* 159 0x9f 'Ÿ' */
+ 0x0e, /* 00001110 */
+ 0x1b, /* 00011011 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+
+ /* 160 0xa0 ' ' */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 161 0xa1 '¡' */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 162 0xa2 '¢' */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 163 0xa3 '£' */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 164 0xa4 '¤' */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+
+ /* 165 0xa5 '¥' */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0xe6, /* 11100110 */
+ 0xf6, /* 11110110 */
+ 0xde, /* 11011110 */
+ 0xce, /* 11001110 */
+ 0x00, /* 00000000 */
+
+ /* 166 0xa6 '¦' */
+ 0x3c, /* 00111100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x3e, /* 00111110 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 167 0xa7 '§' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 168 0xa8 '¨' */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x63, /* 01100011 */
+ 0x3e, /* 00111110 */
+ 0x00, /* 00000000 */
+
+ /* 169 0xa9 '©' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 170 0xaa 'ª' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 171 0xab '«' */
+ 0x63, /* 01100011 */
+ 0xe6, /* 11100110 */
+ 0x6c, /* 01101100 */
+ 0x7e, /* 01111110 */
+ 0x33, /* 00110011 */
+ 0x66, /* 01100110 */
+ 0xcc, /* 11001100 */
+ 0x0f, /* 00001111 */
+
+ /* 172 0xac '¬' */
+ 0x63, /* 01100011 */
+ 0xe6, /* 11100110 */
+ 0x6c, /* 01101100 */
+ 0x7a, /* 01111010 */
+ 0x36, /* 00110110 */
+ 0x6a, /* 01101010 */
+ 0xdf, /* 11011111 */
+ 0x06, /* 00000110 */
+
+ /* 173 0xad '­' */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 174 0xae '®' */
+ 0x00, /* 00000000 */
+ 0x33, /* 00110011 */
+ 0x66, /* 01100110 */
+ 0xcc, /* 11001100 */
+ 0x66, /* 01100110 */
+ 0x33, /* 00110011 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 175 0xaf '¯' */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0x66, /* 01100110 */
+ 0x33, /* 00110011 */
+ 0x66, /* 01100110 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 176 0xb0 '°' */
+ 0x22, /* 00100010 */
+ 0x88, /* 10001000 */
+ 0x22, /* 00100010 */
+ 0x88, /* 10001000 */
+ 0x22, /* 00100010 */
+ 0x88, /* 10001000 */
+ 0x22, /* 00100010 */
+ 0x88, /* 10001000 */
+
+ /* 177 0xb1 '±' */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+
+ /* 178 0xb2 '²' */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+
+ /* 179 0xb3 '³' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 180 0xb4 '´' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 181 0xb5 'µ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 182 0xb6 '¶' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 183 0xb7 '·' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 184 0xb8 '¸' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 185 0xb9 '¹' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x06, /* 00000110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 186 0xba 'º' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 187 0xbb '»' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x06, /* 00000110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 188 0xbc '¼' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x06, /* 00000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 189 0xbd '½' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 190 0xbe '¾' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 191 0xbf '¿' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 192 0xc0 'À' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 193 0xc1 'Á' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 194 0xc2 'Â' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 195 0xc3 'Ã' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 196 0xc4 'Ä' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 197 0xc5 'Å' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 198 0xc6 'Æ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 199 0xc7 'Ç' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 200 0xc8 'È' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x30, /* 00110000 */
+ 0x3f, /* 00111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 201 0xc9 'É' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3f, /* 00111111 */
+ 0x30, /* 00110000 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 202 0xca 'Ê' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf7, /* 11110111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 203 0xcb 'Ë' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xf7, /* 11110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 204 0xcc 'Ì' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x30, /* 00110000 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 205 0xcd 'Í' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 206 0xce 'Î' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf7, /* 11110111 */
+ 0x00, /* 00000000 */
+ 0xf7, /* 11110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 207 0xcf 'Ï' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 208 0xd0 'Ð' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 209 0xd1 'Ñ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 210 0xd2 'Ò' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 211 0xd3 'Ó' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x3f, /* 00111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 212 0xd4 'Ô' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 213 0xd5 'Õ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 214 0xd6 'Ö' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3f, /* 00111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 215 0xd7 '×' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xff, /* 11111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 216 0xd8 'Ø' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 217 0xd9 'Ù' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 218 0xda 'Ú' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 219 0xdb 'Û' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 220 0xdc 'Ü' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 221 0xdd 'Ý' */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+
+ /* 222 0xde 'Þ' */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+
+ /* 223 0xdf 'ß' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 224 0xe0 'à' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0xc8, /* 11001000 */
+ 0xdc, /* 11011100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 225 0xe1 'á' */
+ 0x78, /* 01111000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xd8, /* 11011000 */
+ 0xcc, /* 11001100 */
+ 0xc6, /* 11000110 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+
+ /* 226 0xe2 'â' */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+
+ /* 227 0xe3 'ã' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+
+ /* 228 0xe4 'ä' */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+
+ /* 229 0xe5 'å' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+
+ /* 230 0xe6 'æ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0xc0, /* 11000000 */
+
+ /* 231 0xe7 'ç' */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 232 0xe8 'è' */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+
+ /* 233 0xe9 'é' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+
+ /* 234 0xea 'ê' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0xee, /* 11101110 */
+ 0x00, /* 00000000 */
+
+ /* 235 0xeb 'ë' */
+ 0x0e, /* 00001110 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x3e, /* 00111110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 236 0xec 'ì' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 237 0xed 'í' */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x7e, /* 01111110 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0x7e, /* 01111110 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+
+ /* 238 0xee 'î' */
+ 0x1e, /* 00011110 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x7e, /* 01111110 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x1e, /* 00011110 */
+ 0x00, /* 00000000 */
+
+ /* 239 0xef 'ï' */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 240 0xf0 'ð' */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 241 0xf1 'ñ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+
+ /* 242 0xf2 'ò' */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+
+ /* 243 0xf3 'ó' */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+
+ /* 244 0xf4 'ô' */
+ 0x0e, /* 00001110 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 245 0xf5 'õ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+
+ /* 246 0xf6 'ö' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 247 0xf7 '÷' */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 248 0xf8 'ø' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 249 0xf9 'ù' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 250 0xfa 'ú' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 251 0xfb 'û' */
+ 0x0f, /* 00001111 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0xec, /* 11101100 */
+ 0x6c, /* 01101100 */
+ 0x3c, /* 00111100 */
+ 0x1c, /* 00011100 */
+
+ /* 252 0xfc 'ü' */
+ 0x6c, /* 01101100 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 253 0xfd 'ý' */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 254 0xfe 'þ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 255 0xff 'ÿ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+};
+
diff --git a/drivers/video/fonts.c b/drivers/video/fonts.c
new file mode 100644
index 000000000..f67890cfc
--- /dev/null
+++ b/drivers/video/fonts.c
@@ -0,0 +1,113 @@
+/*
+ * linux/drivers/video/fonts.c -- `Soft' font definitions
+ *
+ * Created 1995 by Geert Uytterhoeven
+ *
+ * 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/types.h>
+#include <linux/string.h>
+#ifdef __mc68000__
+#include <asm/setup.h>
+#endif
+#include "font.h"
+
+
+ /*
+ * External Font Definitions
+ */
+
+/* VGA8x8 */
+extern char fontname_8x8[];
+extern int fontwidth_8x8, fontheight_8x8;
+extern u_char fontdata_8x8[];
+
+/* VGA8x16 */
+extern char fontname_8x16[];
+extern int fontwidth_8x16, fontheight_8x16;
+extern u_char fontdata_8x16[];
+
+/* PEARL8x8 */
+extern char fontname_pearl8x8[];
+extern int fontwidth_pearl8x8, fontheight_pearl8x8;
+extern u_char fontdata_pearl8x8[];
+
+
+ /*
+ * Font Descriptor Array
+ */
+
+struct softfontdesc {
+ char *name;
+ int *width;
+ int *height;
+ u_char *data;
+};
+
+#define VGA8x8_IDX 0
+#define VGA8x16_IDX 1
+#define PEARL8x8_IDX 2
+
+static struct softfontdesc softfonts[] = {
+ { fontname_8x8, &fontwidth_8x8, &fontheight_8x8, fontdata_8x8 },
+ { fontname_8x16, &fontwidth_8x16, &fontheight_8x16, fontdata_8x16 },
+ { fontname_pearl8x8, &fontwidth_pearl8x8, &fontheight_pearl8x8,
+ fontdata_pearl8x8 },
+};
+
+static unsigned int numsoftfonts = sizeof(softfonts)/sizeof(*softfonts);
+
+
+ /*
+ * Find a font with a specific name
+ */
+
+int findsoftfont(char *name, int *width, int *height, u_char *data[])
+{
+ unsigned int i;
+
+ for (i = 0; i < numsoftfonts; i++)
+ if (!strcmp(softfonts[i].name, name)) {
+ if (width)
+ *width = *softfonts[i].width;
+ if (height)
+ *height = *softfonts[i].height;
+ if (data)
+ *data = softfonts[i].data;
+ return(1);
+ }
+ return(0);
+}
+
+
+ /*
+ * Get the default font for a specific screen size
+ */
+
+void getdefaultfont(int xres, int yres, char *name[], int *width, int *height,
+ u_char *data[])
+{
+ int i;
+
+ if (yres < 400) {
+ i = VGA8x8_IDX;
+#ifdef CONFIG_AMIGA
+ if (MACH_IS_AMIGA)
+ i = PEARL8x8_IDX;
+#endif
+ } else
+ i = VGA8x16_IDX;
+
+ if (name)
+ *name = softfonts[i].name;
+ if (width)
+ *width = *softfonts[i].width;
+ if (height)
+ *height = *softfonts[i].height;
+ if (data)
+ *data = softfonts[i].data;
+}
diff --git a/drivers/video/offb.c b/drivers/video/offb.c
new file mode 100644
index 000000000..cc521899a
--- /dev/null
+++ b/drivers/video/offb.c
@@ -0,0 +1,454 @@
+/*
+ * linux/drivers/video/offb.c -- Open Firmware based frame buffer device
+ *
+ * Copyright (C) 1997 Geert Uytterhoeven
+ *
+ * This driver is partly based on the PowerMac console driver:
+ *
+ * Copyright (C) 1996 Paul Mackerras
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+
+
+#define arraysize(x) (sizeof(x)/sizeof(*(x)))
+
+static int currcon = 0;
+static struct display disp;
+static struct fb_info fb_info;
+static struct { u_char red, green, blue, pad; } palette[256];
+static char offb_name[16] = "OFfb ";
+
+static volatile unsigned char *unknown_cmap_adr = NULL;
+static volatile unsigned char *unknown_cmap_data = NULL;
+
+static struct fb_fix_screeninfo fb_fix = { 0, };
+static struct fb_var_screeninfo fb_var = { 0, };
+
+
+ /*
+ * Interface used by the world
+ */
+
+void offb_video_setup(char *options, int *ints);
+
+static int offb_open(int fbidx);
+static int offb_release(int fbidx);
+static int offb_get_fix(struct fb_fix_screeninfo *fix, int con);
+static int offb_get_var(struct fb_var_screeninfo *var, int con);
+static int offb_set_var(struct fb_var_screeninfo *var, int con);
+static int offb_pan_display(struct fb_var_screeninfo *var, int con);
+static int offb_get_cmap(struct fb_cmap *cmap, int kspc, int con);
+static int offb_set_cmap(struct fb_cmap *cmap, int kspc, int con);
+static int offb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+ u_long arg, int con);
+
+
+ /*
+ * Interface to the low level console driver
+ */
+
+unsigned long offb_init(unsigned long mem_start);
+static int offbcon_switch(int con);
+static int offbcon_updatevar(int con);
+static void offbcon_blank(int blank);
+static int offbcon_setcmap(struct fb_cmap *cmap, int con);
+
+
+ /*
+ * Internal routines
+ */
+
+static int offb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+ u_int *transp);
+static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp);
+static void do_install_cmap(int con);
+
+
+static struct fb_ops offb_ops = {
+ offb_open, offb_release, offb_get_fix, offb_get_var, offb_set_var,
+ offb_get_cmap, offb_set_cmap, offb_pan_display, offb_ioctl
+};
+
+
+ /*
+ * Open/Release the frame buffer device
+ */
+
+static int offb_open(int fbidx)
+{
+ /*
+ * Nothing, only a usage count for the moment
+ */
+
+ MOD_INC_USE_COUNT;
+ return(0);
+}
+
+static int offb_release(int fbidx)
+{
+ MOD_DEC_USE_COUNT;
+ return(0);
+}
+
+
+ /*
+ * Get the Fixed Part of the Display
+ */
+
+static int offb_get_fix(struct fb_fix_screeninfo *fix, int con)
+{
+ memcpy(fix, &fb_fix, sizeof(fb_fix));
+ return 0;
+}
+
+
+ /*
+ * Get the User Defined Part of the Display
+ */
+
+static int offb_get_var(struct fb_var_screeninfo *var, int con)
+{
+ memcpy(var, &fb_var, sizeof(fb_var));
+ return 0;
+}
+
+
+ /*
+ * Set the User Defined Part of the Display
+ */
+
+static int offb_set_var(struct fb_var_screeninfo *var, int con)
+{
+ struct display *display;
+ int oldbpp = -1, err;
+
+ if (con >= 0)
+ display = &fb_display[con];
+ else
+ display = &disp; /* used during initialization */
+
+ if (var->xres > fb_var.xres || var->yres > fb_var.yres ||
+ var->xres_virtual > fb_var.xres_virtual ||
+ var->yres_virtual > fb_var.yres_virtual ||
+ var->bits_per_pixel > fb_var.bits_per_pixel ||
+ var->nonstd ||
+ (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
+ return -EINVAL;
+ memcpy(var, &fb_var, sizeof(fb_var));
+
+ if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+ oldbpp = display->var.bits_per_pixel;
+ display->var = *var;
+ }
+ if (oldbpp != var->bits_per_pixel) {
+ if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
+ return err;
+ do_install_cmap(con);
+ }
+ return 0;
+}
+
+
+ /*
+ * Pan or Wrap the Display
+ *
+ * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+ */
+
+static int offb_pan_display(struct fb_var_screeninfo *var, int con)
+{
+ if (var->xoffset || var->yoffset)
+ return -EINVAL;
+ else
+ return 0;
+}
+
+ /*
+ * Get the Colormap
+ */
+
+static int offb_get_cmap(struct fb_cmap *cmap, int kspc, int con)
+{
+ if (con == currcon) /* current console? */
+ return fb_get_cmap(cmap, &fb_display[con].var, kspc, offb_getcolreg);
+ else if (fb_display[con].cmap.len) /* non default colormap? */
+ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+ else
+ fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+ cmap, kspc ? 0 : 2);
+ return 0;
+}
+
+ /*
+ * Set the Colormap
+ */
+
+static int offb_set_cmap(struct fb_cmap *cmap, int kspc, int con)
+{
+ int err;
+
+ if (!unknown_cmap_adr)
+ return -ENOSYS;
+
+ if (!fb_display[con].cmap.len) { /* no colormap allocated? */
+ if ((err = fb_alloc_cmap(&fb_display[con].cmap,
+ 1<<fb_display[con].var.bits_per_pixel, 0)))
+ return err;
+ }
+ if (con == currcon) /* current console? */
+ return fb_set_cmap(cmap, &fb_display[con].var, kspc, offb_setcolreg);
+ else
+ fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+ return 0;
+}
+
+
+static int offb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+ u_long arg, int con)
+{
+ return -EINVAL;
+}
+
+
+ /*
+ * Initialisation
+ */
+
+__initfunc(unsigned long offb_init(unsigned long mem_start))
+{
+ struct device_node *dp;
+ int i, err, *pp, len;
+ unsigned *up, address;
+
+ if (!prom_display_path[0])
+ return mem_start;
+ if (!(dp = find_path_device(prom_display_path)))
+ return mem_start;
+
+ strncat(offb_name, dp->name, sizeof(offb_name));
+ offb_name[sizeof(offb_name)-1] = '\0';
+ strcpy(fb_fix.id, offb_name);
+
+ if ((pp = (int *)get_property(dp, "depth", &len)) != NULL
+ && len == sizeof(int) && *pp != 8) {
+ printk("%s: can't use depth = %d\n", dp->full_name, *pp);
+ return mem_start;
+ }
+ if ((pp = (int *)get_property(dp, "width", &len)) != NULL
+ && len == sizeof(int))
+ fb_var.xres = fb_var.xres_virtual = *pp;
+ if ((pp = (int *)get_property(dp, "height", &len)) != NULL
+ && len == sizeof(int))
+ fb_var.yres = fb_var.yres_virtual = *pp;
+ if ((pp = (int *)get_property(dp, "linebytes", &len)) != NULL
+ && len == sizeof(int))
+ fb_fix.line_length = *pp;
+ else
+ fb_fix.line_length = fb_var.xres_virtual;
+ fb_fix.smem_len = fb_fix.line_length*fb_var.yres;
+ if ((up = (unsigned *)get_property(dp, "address", &len)) != NULL
+ && len == sizeof(unsigned))
+ address = (u_long)*up;
+ else {
+ for (i = 0; i < dp->n_addrs; ++i)
+ if (dp->addrs[i].size >= len)
+ break;
+ if (i >= dp->n_addrs) {
+ printk("no framebuffer address found for %s\n", dp->full_name);
+ return mem_start;
+ }
+ address = (u_long)dp->addrs[i].address;
+ }
+ fb_fix.smem_start = ioremap(address, fb_fix.smem_len);
+ fb_fix.type = FB_TYPE_PACKED_PIXELS;
+ fb_fix.type_aux = 0;
+
+ /* XXX kludge for ati */
+ if (strncmp(dp->name, "ATY,", 4) == 0) {
+ unknown_cmap_adr = ioremap(address + 0x7ff000, 0x1000) + 0xcc0;
+ unknown_cmap_data = unknown_cmap_adr + 1;
+ }
+
+ fb_fix.visual = unknown_cmap_adr ? FB_VISUAL_PSEUDOCOLOR :
+ FB_VISUAL_STATIC_PSEUDOCOLOR;
+
+ fb_var.xoffset = fb_var.yoffset = 0;
+ fb_var.bits_per_pixel = 8;
+ fb_var.grayscale = 0;
+ fb_var.red.offset = fb_var.green.offset = fb_var.blue.offset = 0;
+ fb_var.red.length = fb_var.green.length = fb_var.blue.length = 8;
+ fb_var.red.msb_right = fb_var.green.msb_right = fb_var.blue.msb_right = 0;
+ fb_var.transp.offset = fb_var.transp.length = fb_var.transp.msb_right = 0;
+ fb_var.nonstd = 0;
+ fb_var.activate = 0;
+ fb_var.height = fb_var.width = -1;
+ fb_var.accel = FB_ACCEL_NONE;
+ fb_var.pixclock = 10000;
+ fb_var.left_margin = fb_var.right_margin = 16;
+ fb_var.upper_margin = fb_var.lower_margin = 16;
+ fb_var.hsync_len = fb_var.vsync_len = 8;
+ fb_var.sync = 0;
+ fb_var.vmode = FB_VMODE_NONINTERLACED;
+
+ disp.var = fb_var;
+ disp.cmap.start = 0;
+ disp.cmap.len = 0;
+ disp.cmap.red = disp.cmap.green = disp.cmap.blue = disp.cmap.transp = NULL;
+ disp.screen_base = fb_fix.smem_start;
+ disp.visual = fb_fix.visual;
+ disp.type = fb_fix.type;
+ disp.type_aux = fb_fix.type_aux;
+ disp.ypanstep = 0;
+ disp.ywrapstep = 0;
+ disp.line_length = fb_fix.line_length;
+ disp.can_soft_blank = 1;
+ disp.inverse = 0;
+
+ strcpy(fb_info.modename, "OFfb ");
+ strncat(fb_info.modename, dp->full_name, sizeof(fb_info.modename));
+ fb_info.node = -1;
+ fb_info.fbops = &offb_ops;
+ fb_info.fbvar_num = 1;
+ fb_info.fbvar = &fb_var;
+ fb_info.disp = &disp;
+ fb_info.fontname[0] = '\0';
+ fb_info.changevar = NULL;
+ fb_info.switch_con = &offbcon_switch;
+ fb_info.updatevar = &offbcon_updatevar;
+ fb_info.blank = &offbcon_blank;
+ fb_info.setcmap = &offbcon_setcmap;
+
+ err = register_framebuffer(&fb_info);
+ if (err < 0)
+ return mem_start;
+
+ offb_set_var(&fb_var, -1);
+
+ printk("Open Firmware frame buffer device on %s\n", dp->full_name);
+ return mem_start;
+}
+
+
+static int offbcon_switch(int con)
+{
+ /* Do we have to save the colormap? */
+ if (fb_display[currcon].cmap.len)
+ fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1,
+ offb_getcolreg);
+
+ currcon = con;
+ /* Install new colormap */
+ do_install_cmap(con);
+ return 0;
+}
+
+ /*
+ * Update the `var' structure (called by fbcon.c)
+ */
+
+static int offbcon_updatevar(int con)
+{
+ /* Nothing */
+ return 0;
+}
+
+ /*
+ * Blank the display.
+ */
+
+static void offbcon_blank(int blank)
+{
+ /* Nothing */
+}
+
+ /*
+ * Set the colormap
+ */
+
+static int offbcon_setcmap(struct fb_cmap *cmap, int con)
+{
+ return(offb_set_cmap(cmap, 1, con));
+}
+
+
+ /*
+ * Read a single color register and split it into
+ * colors/transparent. Return != 0 for invalid regno.
+ */
+
+static int offb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+ u_int *transp)
+{
+ if (!unknown_cmap_adr || regno > 255)
+ return 1;
+ *red = palette[regno].red;
+ *green = palette[regno].green;
+ *blue = palette[regno].blue;
+ return 0;
+}
+
+
+ /*
+ * Set a single color register. The values supplied are already
+ * rounded down to the hardware's capabilities (according to the
+ * entries in the var structure). Return != 0 for invalid regno.
+ */
+
+static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp)
+{
+ if (!unknown_cmap_adr || regno > 255)
+ return 1;
+ palette[regno].red = red;
+ palette[regno].green = green;
+ palette[regno].blue = blue;
+ *unknown_cmap_adr = regno;
+#ifdef __powerpc__
+ eieio();
+#endif
+ *unknown_cmap_data = red;
+#ifdef __powerpc__
+ eieio();
+#endif
+ *unknown_cmap_data = green;
+#ifdef __powerpc__
+ eieio();
+#endif
+ *unknown_cmap_data = blue;
+#ifdef __powerpc__
+ eieio();
+#endif
+ return 0;
+}
+
+
+static void do_install_cmap(int con)
+{
+ if (con != currcon)
+ return;
+ if (fb_display[con].cmap.len)
+ fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
+ offb_setcolreg);
+ else
+ fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+ &fb_display[con].var, 1, offb_setcolreg);
+}
diff --git a/drivers/video/pearl_8x8.c b/drivers/video/pearl_8x8.c
new file mode 100644
index 000000000..a48c702d2
--- /dev/null
+++ b/drivers/video/pearl_8x8.c
@@ -0,0 +1,2582 @@
+/**********************************************/
+/* */
+/* Font file generated by cpi2fnt */
+/* ------------------------------ */
+/* Combined with the alpha-numeric */
+/* portion of Greg Harp's old PEARL */
+/* font (from earlier versions of */
+/* linux-m86k) by John Shifflett */
+/* */
+/**********************************************/
+
+#define FONTDATAMAX 2048
+
+char fontname_pearl8x8[] = "PEARL8x8";
+
+int fontheight_pearl8x8 = 8;
+int fontwidth_pearl8x8 = 8;
+
+unsigned char fontdata_pearl8x8[FONTDATAMAX] = {
+
+ /* 0 0x00 '^@' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 1 0x01 '^A' */
+ 0x7e, /* 01111110 */
+ 0x81, /* 10000001 */
+ 0xa5, /* 10100101 */
+ 0x81, /* 10000001 */
+ 0xbd, /* 10111101 */
+ 0x99, /* 10011001 */
+ 0x81, /* 10000001 */
+ 0x7e, /* 01111110 */
+
+ /* 2 0x02 '^B' */
+ 0x7e, /* 01111110 */
+ 0xff, /* 11111111 */
+ 0xdb, /* 11011011 */
+ 0xff, /* 11111111 */
+ 0xc3, /* 11000011 */
+ 0xe7, /* 11100111 */
+ 0xff, /* 11111111 */
+ 0x7e, /* 01111110 */
+
+ /* 3 0x03 '^C' */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+
+ /* 4 0x04 '^D' */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+
+ /* 5 0x05 '^E' */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xd6, /* 11010110 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+
+ /* 6 0x06 '^F' */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+
+ /* 7 0x07 '^G' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 8 0x08 '^H' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xe7, /* 11100111 */
+ 0xc3, /* 11000011 */
+ 0xc3, /* 11000011 */
+ 0xe7, /* 11100111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 9 0x09 '^I' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x42, /* 01000010 */
+ 0x42, /* 01000010 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 10 0x0a '^J' */
+ 0xff, /* 11111111 */
+ 0xc3, /* 11000011 */
+ 0x99, /* 10011001 */
+ 0xbd, /* 10111101 */
+ 0xbd, /* 10111101 */
+ 0x99, /* 10011001 */
+ 0xc3, /* 11000011 */
+ 0xff, /* 11111111 */
+
+ /* 11 0x0b '^K' */
+ 0x0f, /* 00001111 */
+ 0x07, /* 00000111 */
+ 0x0f, /* 00001111 */
+ 0x7d, /* 01111101 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x78, /* 01111000 */
+
+ /* 12 0x0c '^L' */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+
+ /* 13 0x0d '^M' */
+ 0x3f, /* 00111111 */
+ 0x33, /* 00110011 */
+ 0x3f, /* 00111111 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x70, /* 01110000 */
+ 0xf0, /* 11110000 */
+ 0xe0, /* 11100000 */
+
+ /* 14 0x0e '^N' */
+ 0x7f, /* 01111111 */
+ 0x63, /* 01100011 */
+ 0x7f, /* 01111111 */
+ 0x63, /* 01100011 */
+ 0x63, /* 01100011 */
+ 0x67, /* 01100111 */
+ 0xe6, /* 11100110 */
+ 0xc0, /* 11000000 */
+
+ /* 15 0x0f '^O' */
+ 0x18, /* 00011000 */
+ 0xdb, /* 11011011 */
+ 0x3c, /* 00111100 */
+ 0xe7, /* 11100111 */
+ 0xe7, /* 11100111 */
+ 0x3c, /* 00111100 */
+ 0xdb, /* 11011011 */
+ 0x18, /* 00011000 */
+
+ /* 16 0x10 '^P' */
+ 0x80, /* 10000000 */
+ 0xe0, /* 11100000 */
+ 0xf8, /* 11111000 */
+ 0xfe, /* 11111110 */
+ 0xf8, /* 11111000 */
+ 0xe0, /* 11100000 */
+ 0x80, /* 10000000 */
+ 0x00, /* 00000000 */
+
+ /* 17 0x11 '^Q' */
+ 0x02, /* 00000010 */
+ 0x0e, /* 00001110 */
+ 0x3e, /* 00111110 */
+ 0xfe, /* 11111110 */
+ 0x3e, /* 00111110 */
+ 0x0e, /* 00001110 */
+ 0x02, /* 00000010 */
+ 0x00, /* 00000000 */
+
+ /* 18 0x12 '^R' */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+
+ /* 19 0x13 '^S' */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+
+ /* 20 0x14 '^T' */
+ 0x7f, /* 01111111 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0x7b, /* 01111011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x00, /* 00000000 */
+
+ /* 21 0x15 '^U' */
+ 0x3e, /* 00111110 */
+ 0x61, /* 01100001 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x86, /* 10000110 */
+ 0x7c, /* 01111100 */
+
+ /* 22 0x16 '^V' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+
+ /* 23 0x17 '^W' */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+
+ /* 24 0x18 '^X' */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 25 0x19 '^Y' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 26 0x1a '^Z' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0xfe, /* 11111110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 27 0x1b '^[' */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xfe, /* 11111110 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 28 0x1c '^\' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 29 0x1d '^]' */
+ 0x00, /* 00000000 */
+ 0x24, /* 00100100 */
+ 0x66, /* 01100110 */
+ 0xff, /* 11111111 */
+ 0x66, /* 01100110 */
+ 0x24, /* 00100100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 30 0x1e '^^' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 31 0x1f '^_' */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 32 0x20 ' ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 33 0x21 '!' */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 34 0x22 '"' */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 35 0x23 '#' */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+
+ /* 36 0x24 '$' */
+ 0x18, /* 00011000 */
+ 0x3e, /* 00111110 */
+ 0x60, /* 01100000 */
+ 0x3c, /* 00111100 */
+ 0x06, /* 00000110 */
+ 0x7c, /* 01111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 37 0x25 '%' */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xcc, /* 11001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x66, /* 01100110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 38 0x26 '&' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x68, /* 01101000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 39 0x27 ''' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 40 0x28 '(' */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+
+ /* 41 0x29 ')' */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+
+ /* 42 0x2a '*' */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0xff, /* 11111111 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 43 0x2b '+' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 44 0x2c ',' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+
+ /* 45 0x2d '-' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 46 0x2e '.' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 47 0x2f '/' */
+ 0x03, /* 00000011 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+
+ /* 48 0x30 '0' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xde, /* 11011110 */
+ 0xfe, /* 11111110 */
+ 0xf6, /* 11110110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 49 0x31 '1' */
+ 0x18, /* 00011000 */
+ 0x78, /* 01111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 50 0x32 '2' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+
+ /* 51 0x33 '3' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x06, /* 00000110 */
+ 0x1c, /* 00011100 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 52 0x34 '4' */
+ 0x1c, /* 00011100 */
+ 0x3c, /* 00111100 */
+ 0x6c, /* 01101100 */
+ 0xcc, /* 11001100 */
+ 0xfe, /* 11111110 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+
+ /* 53 0x35 '5' */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xfc, /* 11111100 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 54 0x36 '6' */
+ 0x38, /* 00111000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0xfc, /* 11111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 55 0x37 '7' */
+ 0xfe, /* 11111110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+
+ /* 56 0x38 '8' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 57 0x39 '9' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+
+ /* 58 0x3a ':' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 59 0x3b ';' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+
+ /* 60 0x3c '<' */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+
+ /* 61 0x3d '=' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 62 0x3e '>' */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+
+ /* 63 0x3f '?' */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 64 0x40 '@' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xde, /* 11011110 */
+ 0xde, /* 11011110 */
+ 0xde, /* 11011110 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 65 0x41 'A' */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 66 0x42 'B' */
+ 0xfc, /* 11111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfc, /* 11111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfc, /* 11111100 */
+ 0x00, /* 00000000 */
+
+ /* 67 0x43 'C' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 68 0x44 'D' */
+ 0xfc, /* 11111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfc, /* 11111100 */
+ 0x00, /* 00000000 */
+
+ /* 69 0x45 'E' */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xf8, /* 11111000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+
+ /* 70 0x46 'F' */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xf8, /* 11111000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+
+ /* 71 0x47 'G' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xce, /* 11001110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 72 0x48 'H' */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 73 0x49 'I' */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+
+ /* 74 0x4a 'J' */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 75 0x4b 'K' */
+ 0xc6, /* 11000110 */
+ 0xcc, /* 11001100 */
+ 0xd8, /* 11011000 */
+ 0xf0, /* 11110000 */
+ 0xd8, /* 11011000 */
+ 0xcc, /* 11001100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 76 0x4c 'L' */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+
+ /* 77 0x4d 'M' */
+ 0x82, /* 10000010 */
+ 0xc6, /* 11000110 */
+ 0xee, /* 11101110 */
+ 0xfe, /* 11111110 */
+ 0xd6, /* 11010110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 78 0x4e 'N' */
+ 0xc6, /* 11000110 */
+ 0xe6, /* 11100110 */
+ 0xf6, /* 11110110 */
+ 0xde, /* 11011110 */
+ 0xce, /* 11001110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 79 0x4f 'O' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 80 0x50 'P' */
+ 0xfc, /* 11111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfc, /* 11111100 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+
+ /* 81 0x51 'Q' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xf6, /* 11110110 */
+ 0xde, /* 11011110 */
+ 0x7c, /* 01111100 */
+ 0x06, /* 00000110 */
+
+ /* 82 0x52 'R' */
+ 0xfc, /* 11111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfc, /* 11111100 */
+ 0xd8, /* 11011000 */
+ 0xcc, /* 11001100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 83 0x53 'S' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x38, /* 00111000 */
+ 0x0c, /* 00001100 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 84 0x54 'T' */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 85 0x55 'U' */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 86 0x56 'V' */
+ 0xc3, /* 11000011 */
+ 0xc3, /* 11000011 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 87 0x57 'W' */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xfe, /* 11111110 */
+ 0xee, /* 11101110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 88 0x58 'X' */
+ 0xc3, /* 11000011 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0xc3, /* 11000011 */
+ 0x00, /* 00000000 */
+
+ /* 89 0x59 'Y' */
+ 0xc3, /* 11000011 */
+ 0xc3, /* 11000011 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 90 0x5a 'Z' */
+ 0xfe, /* 11111110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+
+ /* 91 0x5b '[' */
+ 0x3c, /* 00111100 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 92 0x5c '\' */
+ 0xc0, /* 11000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0x03, /* 00000011 */
+ 0x00, /* 00000000 */
+
+ /* 93 0x5d ']' */
+ 0x3c, /* 00111100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 94 0x5e '^' */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 95 0x5f '_' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+
+ /* 96 0x60 '`' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 97 0x61 'a' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0x06, /* 00000110 */
+ 0x7e, /* 01111110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+
+ /* 98 0x62 'b' */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfc, /* 11111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfc, /* 11111100 */
+ 0x00, /* 00000000 */
+
+ /* 99 0x63 'c' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 100 0x64 'd' */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x7e, /* 01111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+
+ /* 101 0x65 'e' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 102 0x66 'f' */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+
+ /* 103 0x67 'g' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0x7c, /* 01111100 */
+
+ /* 104 0x68 'h' */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfc, /* 11111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 105 0x69 'i' */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 106 0x6a 'j' */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+
+ /* 107 0x6b 'k' */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xcc, /* 11001100 */
+ 0xd8, /* 11011000 */
+ 0xf0, /* 11110000 */
+ 0xd8, /* 11011000 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+
+ /* 108 0x6c 'l' */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 109 0x6d 'm' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xec, /* 11101100 */
+ 0xfe, /* 11111110 */
+ 0xd6, /* 11010110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 110 0x6e 'n' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfc, /* 11111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 111 0x6f 'o' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 112 0x70 'p' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfc, /* 11111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfc, /* 11111100 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+
+ /* 113 0x71 'q' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+
+ /* 114 0x72 'r' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0xe6, /* 11100110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+
+ /* 115 0x73 's' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x06, /* 00000110 */
+ 0xfc, /* 11111100 */
+ 0x00, /* 00000000 */
+
+ /* 116 0x74 't' */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x7c, /* 01111100 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x36, /* 00110110 */
+ 0x1c, /* 00011100 */
+ 0x00, /* 00000000 */
+
+ /* 117 0x75 'u' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 118 0x76 'v' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+
+ /* 119 0x77 'w' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+
+ /* 120 0x78 'x' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 121 0x79 'y' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc3, /* 11000011 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+
+ /* 122 0x7a 'z' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x0c, /* 00001100 */
+ 0x38, /* 00111000 */
+ 0x60, /* 01100000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+
+ /* 123 0x7b '{' */
+ 0x0e, /* 00001110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x0e, /* 00001110 */
+ 0x00, /* 00000000 */
+
+ /* 124 0x7c '|' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 125 0x7d '}' */
+ 0x70, /* 01110000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x0e, /* 00001110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+
+ /* 126 0x7e '~' */
+ 0x72, /* 01110010 */
+ 0x9c, /* 10011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 127 0x7f '' */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+
+ /* 128 0x80 '€' */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x0c, /* 00001100 */
+ 0x78, /* 01111000 */
+
+ /* 129 0x81 '' */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 130 0x82 '‚' */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 131 0x83 'ƒ' */
+ 0x7c, /* 01111100 */
+ 0x82, /* 10000010 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 132 0x84 '„' */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 133 0x85 '…' */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 134 0x86 '†' */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 135 0x87 '‡' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x7e, /* 01111110 */
+ 0x0c, /* 00001100 */
+ 0x38, /* 00111000 */
+
+ /* 136 0x88 'ˆ' */
+ 0x7c, /* 01111100 */
+ 0x82, /* 10000010 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 137 0x89 '‰' */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 138 0x8a 'Š' */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 139 0x8b '‹' */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 140 0x8c 'Œ' */
+ 0x7c, /* 01111100 */
+ 0x82, /* 10000010 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 141 0x8d '' */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 142 0x8e 'Ž' */
+ 0xc6, /* 11000110 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 143 0x8f '' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 144 0x90 '' */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xf8, /* 11111000 */
+ 0xc0, /* 11000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+
+ /* 145 0x91 '‘' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0xd8, /* 11011000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+
+ /* 146 0x92 '’' */
+ 0x3e, /* 00111110 */
+ 0x6c, /* 01101100 */
+ 0xcc, /* 11001100 */
+ 0xfe, /* 11111110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xce, /* 11001110 */
+ 0x00, /* 00000000 */
+
+ /* 147 0x93 '“' */
+ 0x7c, /* 01111100 */
+ 0x82, /* 10000010 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 148 0x94 '”' */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 149 0x95 '•' */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 150 0x96 '–' */
+ 0x78, /* 01111000 */
+ 0x84, /* 10000100 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 151 0x97 '—' */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 152 0x98 '˜' */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0xfc, /* 11111100 */
+
+ /* 153 0x99 '™' */
+ 0xc6, /* 11000110 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+
+ /* 154 0x9a 'š' */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 155 0x9b '›' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 156 0x9c 'œ' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x64, /* 01100100 */
+ 0xf0, /* 11110000 */
+ 0x60, /* 01100000 */
+ 0x66, /* 01100110 */
+ 0xfc, /* 11111100 */
+ 0x00, /* 00000000 */
+
+ /* 157 0x9d '' */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 158 0x9e 'ž' */
+ 0xf8, /* 11111000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xfa, /* 11111010 */
+ 0xc6, /* 11000110 */
+ 0xcf, /* 11001111 */
+ 0xc6, /* 11000110 */
+ 0xc7, /* 11000111 */
+
+ /* 159 0x9f 'Ÿ' */
+ 0x0e, /* 00001110 */
+ 0x1b, /* 00011011 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+
+ /* 160 0xa0 ' ' */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 161 0xa1 '¡' */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 162 0xa2 '¢' */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+
+ /* 163 0xa3 '£' */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 164 0xa4 '¤' */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+
+ /* 165 0xa5 '¥' */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0xe6, /* 11100110 */
+ 0xf6, /* 11110110 */
+ 0xde, /* 11011110 */
+ 0xce, /* 11001110 */
+ 0x00, /* 00000000 */
+
+ /* 166 0xa6 '¦' */
+ 0x3c, /* 00111100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x3e, /* 00111110 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 167 0xa7 '§' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 168 0xa8 '¨' */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x63, /* 01100011 */
+ 0x3e, /* 00111110 */
+ 0x00, /* 00000000 */
+
+ /* 169 0xa9 '©' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 170 0xaa 'ª' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 171 0xab '«' */
+ 0x63, /* 01100011 */
+ 0xe6, /* 11100110 */
+ 0x6c, /* 01101100 */
+ 0x7e, /* 01111110 */
+ 0x33, /* 00110011 */
+ 0x66, /* 01100110 */
+ 0xcc, /* 11001100 */
+ 0x0f, /* 00001111 */
+
+ /* 172 0xac '¬' */
+ 0x63, /* 01100011 */
+ 0xe6, /* 11100110 */
+ 0x6c, /* 01101100 */
+ 0x7a, /* 01111010 */
+ 0x36, /* 00110110 */
+ 0x6a, /* 01101010 */
+ 0xdf, /* 11011111 */
+ 0x06, /* 00000110 */
+
+ /* 173 0xad '­' */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 174 0xae '®' */
+ 0x00, /* 00000000 */
+ 0x33, /* 00110011 */
+ 0x66, /* 01100110 */
+ 0xcc, /* 11001100 */
+ 0x66, /* 01100110 */
+ 0x33, /* 00110011 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 175 0xaf '¯' */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0x66, /* 01100110 */
+ 0x33, /* 00110011 */
+ 0x66, /* 01100110 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 176 0xb0 '°' */
+ 0x22, /* 00100010 */
+ 0x88, /* 10001000 */
+ 0x22, /* 00100010 */
+ 0x88, /* 10001000 */
+ 0x22, /* 00100010 */
+ 0x88, /* 10001000 */
+ 0x22, /* 00100010 */
+ 0x88, /* 10001000 */
+
+ /* 177 0xb1 '±' */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+
+ /* 178 0xb2 '²' */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+
+ /* 179 0xb3 '³' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 180 0xb4 '´' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 181 0xb5 'µ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 182 0xb6 '¶' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 183 0xb7 '·' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 184 0xb8 '¸' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 185 0xb9 '¹' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x06, /* 00000110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 186 0xba 'º' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 187 0xbb '»' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x06, /* 00000110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 188 0xbc '¼' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x06, /* 00000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 189 0xbd '½' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 190 0xbe '¾' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 191 0xbf '¿' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 192 0xc0 'À' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 193 0xc1 'Á' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 194 0xc2 'Â' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 195 0xc3 'Ã' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 196 0xc4 'Ä' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 197 0xc5 'Å' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 198 0xc6 'Æ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 199 0xc7 'Ç' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 200 0xc8 'È' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x30, /* 00110000 */
+ 0x3f, /* 00111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 201 0xc9 'É' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3f, /* 00111111 */
+ 0x30, /* 00110000 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 202 0xca 'Ê' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf7, /* 11110111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 203 0xcb 'Ë' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xf7, /* 11110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 204 0xcc 'Ì' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x30, /* 00110000 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 205 0xcd 'Í' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 206 0xce 'Î' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf7, /* 11110111 */
+ 0x00, /* 00000000 */
+ 0xf7, /* 11110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 207 0xcf 'Ï' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 208 0xd0 'Ð' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 209 0xd1 'Ñ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 210 0xd2 'Ò' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 211 0xd3 'Ó' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x3f, /* 00111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 212 0xd4 'Ô' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 213 0xd5 'Õ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 214 0xd6 'Ö' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3f, /* 00111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 215 0xd7 '×' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xff, /* 11111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 216 0xd8 'Ø' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 217 0xd9 'Ù' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 218 0xda 'Ú' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 219 0xdb 'Û' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 220 0xdc 'Ü' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 221 0xdd 'Ý' */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+
+ /* 222 0xde 'Þ' */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+
+ /* 223 0xdf 'ß' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 224 0xe0 'à' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0xc8, /* 11001000 */
+ 0xdc, /* 11011100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+
+ /* 225 0xe1 'á' */
+ 0x78, /* 01111000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xd8, /* 11011000 */
+ 0xcc, /* 11001100 */
+ 0xc6, /* 11000110 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+
+ /* 226 0xe2 'â' */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+
+ /* 227 0xe3 'ã' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+
+ /* 228 0xe4 'ä' */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+
+ /* 229 0xe5 'å' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+
+ /* 230 0xe6 'æ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0xc0, /* 11000000 */
+
+ /* 231 0xe7 'ç' */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+
+ /* 232 0xe8 'è' */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+
+ /* 233 0xe9 'é' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+
+ /* 234 0xea 'ê' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0xee, /* 11101110 */
+ 0x00, /* 00000000 */
+
+ /* 235 0xeb 'ë' */
+ 0x0e, /* 00001110 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x3e, /* 00111110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 236 0xec 'ì' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 237 0xed 'í' */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x7e, /* 01111110 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0x7e, /* 01111110 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+
+ /* 238 0xee 'î' */
+ 0x1e, /* 00011110 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x7e, /* 01111110 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x1e, /* 00011110 */
+ 0x00, /* 00000000 */
+
+ /* 239 0xef 'ï' */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+
+ /* 240 0xf0 'ð' */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 241 0xf1 'ñ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+
+ /* 242 0xf2 'ò' */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+
+ /* 243 0xf3 'ó' */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+
+ /* 244 0xf4 'ô' */
+ 0x0e, /* 00001110 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 245 0xf5 'õ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+
+ /* 246 0xf6 'ö' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 247 0xf7 '÷' */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 248 0xf8 'ø' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 249 0xf9 'ù' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 250 0xfa 'ú' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 251 0xfb 'û' */
+ 0x0f, /* 00001111 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0xec, /* 11101100 */
+ 0x6c, /* 01101100 */
+ 0x3c, /* 00111100 */
+ 0x1c, /* 00011100 */
+
+ /* 252 0xfc 'ü' */
+ 0x6c, /* 01101100 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 253 0xfd 'ý' */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 254 0xfe 'þ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 255 0xff 'ÿ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+};
+
diff --git a/drivers/video/retz3fb.c b/drivers/video/retz3fb.c
new file mode 100644
index 000000000..18a410614
--- /dev/null
+++ b/drivers/video/retz3fb.c
@@ -0,0 +1,1646 @@
+/*
+ * Linux/drivers/video/retz3fb.c -- RetinaZ3 frame buffer device
+ *
+ * Copyright (C) 1997 Jes Sorensen
+ *
+ * This file is based on the CyberVision64 frame buffer device and
+ * the generic Cirrus Logic driver.
+ *
+ * cyberfb.c: Copyright (C) 1996 Martin Apel,
+ * Geert Uytterhoeven
+ * clgen.c: Copyright (C) 1996 Frank Neumann
+ *
+ * History:
+ * - 22 Jan 97: Initial work
+ * - 14 Feb 97: Screen initialization works somewhat, still only
+ * 8-bit packed pixel is supported.
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/zorro.h>
+#include <linux/init.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+
+#include "retz3fb.h"
+
+/* #define DEBUG if(1) */
+#define DEBUG if(0)
+
+/*
+ * Reserve space for one pattern line.
+ *
+ * For the time being we only support 4MB boards!
+ */
+
+#define PAT_MEM_SIZE 16*3
+#define PAT_MEM_OFF (4*1024*1024 - PAT_MEM_SIZE)
+
+#define arraysize(x) (sizeof(x)/sizeof(*(x)))
+
+struct retz3_fb_par {
+ int xres;
+ int yres;
+ int xres_vir;
+ int yres_vir;
+ int xoffset;
+ int yoffset;
+ int bpp;
+
+ struct fb_bitfield red;
+ struct fb_bitfield green;
+ struct fb_bitfield blue;
+ struct fb_bitfield transp;
+
+ int pixclock;
+ int left_margin; /* time from sync to picture */
+ int right_margin; /* time from picture to sync */
+ int upper_margin; /* time from sync to picture */
+ int lower_margin;
+ int hsync_len; /* length of horizontal sync */
+ int vsync_len; /* length of vertical sync */
+ int vmode;
+};
+
+struct display_data {
+ long h_total; /* Horizontal Total */
+ long h_sstart; /* Horizontal Sync Start */
+ long h_sstop; /* Horizontal Sync Stop */
+ long h_bstart; /* Horizontal Blank Start */
+ long h_bstop; /* Horizontal Blank Stop */
+ long h_dispend; /* Horizontal Display End */
+ long v_total; /* Vertical Total */
+ long v_sstart; /* Vertical Sync Start */
+ long v_sstop; /* Vertical Sync Stop */
+ long v_bstart; /* Vertical Blank Start */
+ long v_bstop; /* Vertical Blank Stop */
+ long v_dispend; /* Horizontal Display End */
+};
+
+static struct retz3_fb_par current_par;
+
+static int current_par_valid = 0;
+static int currcon = 0;
+
+static struct display disp;
+static struct fb_info fb_info;
+
+
+/*
+ * Switch for Chipset Independency
+ */
+
+static struct fb_hwswitch {
+
+ /* Initialisation */
+
+ int (*init)(void);
+
+ /* Display Control */
+
+ int (*encode_fix)(struct fb_fix_screeninfo *fix, struct retz3_fb_par *par);
+ int (*decode_var)(struct fb_var_screeninfo *var, struct retz3_fb_par *par);
+ int (*encode_var)(struct fb_var_screeninfo *var, struct retz3_fb_par *par);
+ int (*getcolreg)(unsigned int regno, unsigned int *red, unsigned
+ int *green, unsigned int *blue, unsigned int *transp);
+ int (*setcolreg)(unsigned int regno, unsigned int red, unsigned int
+ green, unsigned int blue, unsigned int transp);
+ void (*blank)(int blank);
+} *fbhw;
+
+
+/*
+ * Frame Buffer Name
+ */
+
+static char retz3_fb_name[16] = "RetinaZ3";
+
+
+static unsigned char retz3_color_table [256][4];
+static unsigned long z3_mem;
+static unsigned long z3_fbmem;
+static unsigned long z3_size;
+static volatile unsigned char *z3_regs;
+
+
+/*
+ * Predefined Video Mode Names
+ */
+
+static char *retz3_fb_modenames[] = {
+
+ /*
+ * Autodetect (Default) Video Mode
+ */
+
+ "default",
+
+ /*
+ * Predefined Video Modes
+ */
+
+ "640x480", /* RetinaZ3 8 bpp */
+ "800x600", /* RetinaZ3 8 bpp */
+ "1024x768i",
+ "640x480-16", /* RetinaZ3 16 bpp */
+ "640x480-24", /* RetinaZ3 24 bpp */
+
+ /*
+ * Dummy Video Modes
+ */
+
+ "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy",
+ "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy",
+ "dummy", "dummy", "dummy", "dummy", "dummy", "dummy",
+
+ /*
+ * User Defined Video Modes
+ *
+ * This doesn't work yet!!
+ */
+
+ "user0", "user1", "user2", "user3",
+ "user4", "user5", "user6", "user7"
+};
+
+/*
+ * A small info on how to convert XFree86 timing values into fb
+ * timings - by Frank Neumann:
+ *
+An XFree86 mode line consists of the following fields:
+ "800x600" 50 800 856 976 1040 600 637 643 666
+ < name > DCF HR SH1 SH2 HFL VR SV1 SV2 VFL
+
+The fields in the fb_var_screeninfo structure are:
+ unsigned long pixclock; * pixel clock in ps (pico seconds) *
+ unsigned long left_margin; * time from sync to picture *
+ unsigned long right_margin; * time from picture to sync *
+ unsigned long upper_margin; * time from sync to picture *
+ unsigned long lower_margin;
+ unsigned long hsync_len; * length of horizontal sync *
+ unsigned long vsync_len; * length of vertical sync *
+
+1) Pixelclock:
+ xfree: in MHz
+ fb: In Picoseconds (ps)
+
+ pixclock = 1000000 / DCF
+
+2) horizontal timings:
+ left_margin = HFL - SH2
+ right_margin = SH1 - HR
+ hsync_len = SH2 - SH1
+
+3) vertical timings:
+ upper_margin = VFL - SV2
+ lower_margin = SV1 - VR
+ vsync_len = SV2 - SV1
+
+Good examples for VESA timings can be found in the XFree86 source tree,
+under "programs/Xserver/hw/xfree86/doc/modeDB.txt".
+*/
+
+/*
+ * Predefined Video Mode Definitions
+ */
+
+static struct fb_var_screeninfo retz3_fb_predefined[] = {
+
+ /*
+ * Autodetect (Default) Video Mode
+ */
+
+ { 0, },
+
+ /*
+ * Predefined Video Modes
+ */
+
+ /*
+ * NB: it is very important to adjust the pixel-clock to the color-depth.
+ */
+
+ {
+ 640, 480, 640, 480, 0, 0, 8, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_RETINAZ3, 38461, 28, 32, 12, 10, 96, 2,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ },
+ /*
+ ModeLine "800x600" 36 800 824 896 1024 600 601 603 625
+ < name > DCF HR SH1 SH2 HFL VR SV1 SV2 VFL
+ */
+ {
+ /* 800 x 600, 8 bpp */
+ 800, 600, 800, 600, 0, 0, 8, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_RETINAZ3, 27778, 64, 24, 22, 1, 120, 2,
+ FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ },
+ /*
+ ModeLine "1024x768i" 45 1024 1064 1224 1264 768 777 785 817 interlace
+ < name > DCF HR SH1 SH2 HFL VR SV1 SV2 VFL
+ */
+ {
+ /* 1024 x 768, 8 bpp, interlaced */
+ 1024, 768, 1024, 768, 0, 0, 8, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_RETINAZ3, 22222, 40, 40, 32, 9, 160, 8,
+ FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_INTERLACED
+ },
+ {
+ 640, 480, 640, 480, 0, 0, 16, 0,
+ {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_RETINAZ3, 38461/2, 28, 32, 12, 10, 96, 2,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ },
+ {
+ 640, 480, 640, 480, 0, 0, 24, 0,
+ {8, 8, 8}, {8, 8, 8}, {8, 8, 8}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_RETINAZ3, 38461/3, 28, 32, 12, 10, 96, 2,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ },
+
+ /*
+ * Dummy Video Modes
+ */
+
+ { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, },
+ { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, },
+ { 0, }, { 0, },
+
+ /*
+ * User Defined Video Modes
+ */
+
+ { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }
+};
+
+
+#define NUM_TOTAL_MODES arraysize(retz3_fb_predefined)
+#define NUM_PREDEF_MODES 5
+
+
+static int z3fb_inverse = 0;
+static int z3fb_mode = 0;
+
+
+/*
+ * Interface used by the world
+ */
+
+void retz3_video_setup(char *options, int *ints);
+
+static int retz3_fb_open(int fbidx);
+static int retz3_fb_release(int fbidx);
+static int retz3_fb_get_fix(struct fb_fix_screeninfo *fix, int con);
+static int retz3_fb_get_var(struct fb_var_screeninfo *var, int con);
+static int retz3_fb_set_var(struct fb_var_screeninfo *var, int con);
+static int retz3_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con);
+static int retz3_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con);
+static int retz3_fb_pan_display(struct fb_var_screeninfo *var, int con);
+static int retz3_fb_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg, int con);
+
+
+/*
+ * Interface to the low level console driver
+ */
+
+unsigned long retz3_fb_init(unsigned long mem_start);
+static int z3fb_switch(int con);
+static int z3fb_updatevar(int con);
+static void z3fb_blank(int blank);
+static int z3fb_setcmap(struct fb_cmap *cmap, int con);
+
+
+/*
+ * Accelerated Functions used by the low level console driver
+ */
+
+void retz3_bitblt(struct fb_var_screeninfo *scr,
+ unsigned short curx, unsigned short cury, unsigned
+ short destx, unsigned short desty, unsigned short
+ width, unsigned short height, unsigned short cmd,
+ unsigned short mask);
+void retz3_fill(unsigned short x, unsigned short y, unsigned short
+ width, unsigned short height, unsigned short mode,
+ unsigned short color);
+
+/*
+ * Hardware Specific Routines
+ */
+
+static int retz3_init(void);
+static int retz3_encode_fix(struct fb_fix_screeninfo *fix,
+ struct retz3_fb_par *par);
+static int retz3_decode_var(struct fb_var_screeninfo *var,
+ struct retz3_fb_par *par);
+static int retz3_encode_var(struct fb_var_screeninfo *var,
+ struct retz3_fb_par *par);
+static int retz3_getcolreg(unsigned int regno, unsigned int *red,
+ unsigned int *green, unsigned int *blue,
+ unsigned int *transp);
+static int retz3_setcolreg(unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue,
+ unsigned int transp);
+static void retz3_blank(int blank);
+
+
+/*
+ * Internal routines
+ */
+
+static void retz3_fb_get_par(struct retz3_fb_par *par);
+static void retz3_fb_set_par(struct retz3_fb_par *par);
+static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive);
+static void do_install_cmap(int con);
+/*
+static void retz3_fb_set_disp(int con);
+*/
+static int get_video_mode(const char *name);
+
+
+/* -------------------- Hardware specific routines -------------------------- */
+
+static unsigned short find_fq(unsigned int freq)
+{
+ unsigned long f;
+ long tmp;
+ long prev = 0x7fffffff;
+ long n2, n1 = 3;
+ unsigned long m;
+ unsigned short res = 0;
+
+ if (freq <= 31250000)
+ n2 = 3;
+ else if (freq <= 62500000)
+ n2 = 2;
+ else if (freq <= 125000000)
+ n2 = 1;
+ else if (freq <= 250000000)
+ n2 = 0;
+ else
+ return(0);
+
+
+ do {
+ f = freq >> (10 - n2);
+
+ m = (f * n1) / (14318180/1024);
+
+ if (m > 129)
+ break;
+
+ tmp = (((m * 14318180) >> n2) / n1) - freq;
+ if (tmp < 0)
+ tmp = -tmp;
+
+ if (tmp < prev) {
+ prev = tmp;
+ res = (((n2 << 5) | (n1-2)) << 8) | (m-2);
+ }
+
+ } while ( (++n1) <= 21);
+
+ return res;
+}
+
+
+static int retz3_set_video(struct fb_var_screeninfo *var,
+ struct retz3_fb_par *par)
+{
+ float freq_f;
+ long freq;
+
+ int xres, hfront, hsync, hback;
+ int yres, vfront, vsync, vback;
+ unsigned char tmp;
+ unsigned short best_freq;
+ struct display_data data;
+
+ short clocksel = 0; /* Apparantly this is always zero */
+
+ int bpp = var->bits_per_pixel;
+
+ /*
+ * XXX
+ */
+ if (bpp == 24)
+ return 0;
+
+ if ((bpp != 8) && (bpp != 16) && (bpp != 24))
+ return -EFAULT;
+
+ par->xoffset = 0;
+ par->yoffset = 0;
+
+ xres = var->xres * bpp / 4;
+ hfront = var->right_margin * bpp / 4;
+ hsync = var->hsync_len * bpp / 4;
+ hback = var->left_margin * bpp / 4;
+
+ if (var->vmode & FB_VMODE_DOUBLE)
+ {
+ yres = var->yres * 2;
+ vfront = var->lower_margin * 2;
+ vsync = var->vsync_len * 2;
+ vback = var->upper_margin * 2;
+ }
+ else if (var->vmode & FB_VMODE_INTERLACED)
+ {
+ yres = (var->yres + 1) / 2;
+ vfront = (var->lower_margin + 1) / 2;
+ vsync = (var->vsync_len + 1) / 2;
+ vback = (var->upper_margin + 1) / 2;
+ }
+ else
+ {
+ yres = var->yres; /* -1 ? */
+ vfront = var->lower_margin;
+ vsync = var->vsync_len;
+ vback = var->upper_margin;
+ }
+
+ data.h_total = (hback / 8) + (xres / 8)
+ + (hfront / 8) + (hsync / 8) - 1 /* + 1 */;
+ data.h_dispend = ((xres + bpp - 1)/ 8) - 1;
+ data.h_bstart = xres / 8 /* + 1 */;
+
+ data.h_bstop = data.h_total+1 + 2 + 1;
+ data.h_sstart = (xres / 8) + (hfront / 8) + 1;
+ data.h_sstop = (xres / 8) + (hfront / 8) + (hsync / 8) + 1;
+
+ data.v_total = yres + vfront + vsync + vback - 1;
+
+ data.v_dispend = yres - 1;
+ data.v_bstart = yres;
+
+ data.v_bstop = data.v_total;
+ data.v_sstart = yres + vfront - 1 - 2;
+ data.v_sstop = yres + vfront + vsync - 1;
+
+#if 0 /* testing */
+
+ printk("HBS: %i\n", data.h_bstart);
+ printk("HSS: %i\n", data.h_sstart);
+ printk("HSE: %i\n", data.h_sstop);
+ printk("HBE: %i\n", data.h_bstop);
+ printk("HT: %i\n", data.h_total);
+
+ printk("hsync: %i\n", hsync);
+ printk("hfront: %i\n", hfront);
+ printk("hback: %i\n", hback);
+
+ printk("VBS: %i\n", data.v_bstart);
+ printk("VSS: %i\n", data.v_sstart);
+ printk("VSE: %i\n", data.v_sstop);
+ printk("VBE: %i\n", data.v_bstop);
+ printk("VT: %i\n", data.v_total);
+
+ printk("vsync: %i\n", vsync);
+ printk("vfront: %i\n", vfront);
+ printk("vback: %i\n", vback);
+#endif
+
+ if (data.v_total >= 1024)
+ printk("MAYDAY: v_total >= 1024; bailing out!\n");
+
+ reg_w(GREG_MISC_OUTPUT_W, 0xe3 | ((clocksel & 3) * 0x04));
+ reg_w(GREG_FEATURE_CONTROL_W, 0x00);
+
+ seq_w(SEQ_RESET, 0x00);
+ seq_w(SEQ_RESET, 0x03); /* reset sequencer logic */
+
+ /*
+ * CLOCKING_MODE bits:
+ * 2: This one is only set for certain text-modes, wonder if
+ * it may be for EGA-lines? (it was referred to as CLKDIV2)
+ * (The CL drivers sets it to 0x21 with the comment:
+ * FullBandwidth (video off) and 8/9 dot clock)
+ */
+ seq_w(SEQ_CLOCKING_MODE, 0x01 | 0x00 /* 0x08 */);
+
+ seq_w(SEQ_MAP_MASK, 0x0f); /* enable writing to plane 0-3 */
+ seq_w(SEQ_CHAR_MAP_SELECT, 0x00); /* doesn't matter in gfx-mode */
+ seq_w(SEQ_MEMORY_MODE, 0x06); /* CL driver says 0x0e for 256 col mode*/
+ seq_w(SEQ_RESET, 0x01);
+ seq_w(SEQ_RESET, 0x03);
+
+ seq_w(SEQ_EXTENDED_ENABLE, 0x05);
+
+ seq_w(SEQ_CURSOR_CONTROL, 0x00); /* disable cursor */
+ seq_w(SEQ_PRIM_HOST_OFF_HI, 0x00);
+ seq_w(SEQ_PRIM_HOST_OFF_HI, 0x00);
+ seq_w(SEQ_LINEAR_0, 0x4a);
+ seq_w(SEQ_LINEAR_1, 0x00);
+
+ seq_w(SEQ_SEC_HOST_OFF_HI, 0x00);
+ seq_w(SEQ_SEC_HOST_OFF_LO, 0x00);
+ seq_w(SEQ_EXTENDED_MEM_ENA, 0x3 | 0x4 | 0x10 | 0x40);
+
+ /*
+ * The lower 4 bits (0-3) are used to set the font-width for
+ * text-mode - DON'T try to set this for gfx-mode.
+ */
+ seq_w(SEQ_EXT_CLOCK_MODE, 0x10);
+ seq_w(SEQ_EXT_VIDEO_ADDR, 0x03);
+
+ /*
+ * Extended Pixel Control:
+ * bit 0: text-mode=0, gfx-mode=1 (Graphics Byte ?)
+ * bit 1: (Packed/Nibble Pixel Format ?)
+ * bit 4-5: depth, 0=1-8bpp, 1=9-16bpp, 2=17-24bpp
+ */
+ seq_w(SEQ_EXT_PIXEL_CNTL, 0x01 | (((bpp / 8) - 1) << 4));
+
+ seq_w(SEQ_BUS_WIDTH_FEEDB, 0x04);
+ seq_w(SEQ_COLOR_EXP_WFG, 0x01);
+ seq_w(SEQ_COLOR_EXP_WBG, 0x00);
+ seq_w(SEQ_EXT_RW_CONTROL, 0x00);
+ seq_w(SEQ_MISC_FEATURE_SEL, (0x51 | (clocksel & 8)));
+ seq_w(SEQ_COLOR_KEY_CNTL, 0x40);
+ seq_w(SEQ_COLOR_KEY_MATCH0, 0x00);
+ seq_w(SEQ_COLOR_KEY_MATCH1, 0x00);
+ seq_w(SEQ_COLOR_KEY_MATCH2, 0x00);
+ seq_w(SEQ_CRC_CONTROL, 0x00);
+ seq_w(SEQ_PERF_SELECT, 0x10);
+ seq_w(SEQ_ACM_APERTURE_1, 0x00);
+ seq_w(SEQ_ACM_APERTURE_2, 0x30);
+ seq_w(SEQ_ACM_APERTURE_3, 0x00);
+ seq_w(SEQ_MEMORY_MAP_CNTL, 0x03);
+
+
+ /* unlock register CRT0..CRT7 */
+ crt_w(CRT_END_VER_RETR, (data.v_sstop & 0x0f) | 0x20);
+
+ /* Zuerst zu schreibende Werte nur per printk ausgeben */
+ DEBUG printk("CRT_HOR_TOTAL: %ld\n", data.h_total);
+ crt_w(CRT_HOR_TOTAL, data.h_total & 0xff);
+
+ DEBUG printk("CRT_HOR_DISP_ENA_END: %ld\n", data.h_dispend);
+ crt_w(CRT_HOR_DISP_ENA_END, (data.h_dispend) & 0xff);
+
+ DEBUG printk("CRT_START_HOR_BLANK: %ld\n", data.h_bstart);
+ crt_w(CRT_START_HOR_BLANK, data.h_bstart & 0xff);
+
+ DEBUG printk("CRT_END_HOR_BLANK: 128+%ld\n", data.h_bstop % 32);
+ crt_w(CRT_END_HOR_BLANK, 0x80 | (data.h_bstop & 0x1f));
+
+ DEBUG printk("CRT_START_HOR_RETR: %ld\n", data.h_sstart);
+ crt_w(CRT_START_HOR_RETR, data.h_sstart & 0xff);
+
+ tmp = (data.h_sstop & 0x1f);
+ if (data.h_bstop & 0x20)
+ tmp |= 0x80;
+ DEBUG printk("CRT_END_HOR_RETR: %d\n", tmp);
+ crt_w(CRT_END_HOR_RETR, tmp);
+
+ DEBUG printk("CRT_VER_TOTAL: %ld\n", data.v_total & 0xff);
+ crt_w(CRT_VER_TOTAL, (data.v_total & 0xff));
+
+ tmp = 0x10; /* LineCompare bit #9 */
+ if (data.v_total & 256)
+ tmp |= 0x01;
+ if (data.v_dispend & 256)
+ tmp |= 0x02;
+ if (data.v_sstart & 256)
+ tmp |= 0x04;
+ if (data.v_bstart & 256)
+ tmp |= 0x08;
+ if (data.v_total & 512)
+ tmp |= 0x20;
+ if (data.v_dispend & 512)
+ tmp |= 0x40;
+ if (data.v_sstart & 512)
+ tmp |= 0x80;
+ DEBUG printk("CRT_OVERFLOW: %d\n", tmp);
+ crt_w(CRT_OVERFLOW, tmp);
+
+ crt_w(CRT_PRESET_ROW_SCAN, 0x00); /* not CL !!! */
+
+ tmp = 0x40; /* LineCompare bit #8 */
+ if (data.v_bstart & 512)
+ tmp |= 0x20;
+ if (var->vmode & FB_VMODE_DOUBLE)
+ tmp |= 0x80;
+ DEBUG printk("CRT_MAX_SCAN_LINE: %d\n", tmp);
+ crt_w(CRT_MAX_SCAN_LINE, tmp);
+
+ crt_w(CRT_CURSOR_START, 0x00);
+ crt_w(CRT_CURSOR_END, 8 & 0x1f); /* font height */
+
+ crt_w(CRT_START_ADDR_HIGH, 0x00);
+ crt_w(CRT_START_ADDR_LOW, 0x00);
+
+ crt_w(CRT_CURSOR_LOC_HIGH, 0x00);
+ crt_w(CRT_CURSOR_LOC_LOW, 0x00);
+
+ DEBUG printk("CRT_START_VER_RETR: %ld\n", data.v_sstart & 0xff);
+ crt_w(CRT_START_VER_RETR, (data.v_sstart & 0xff));
+
+#if 1
+ /* 5 refresh cycles per scanline */
+ DEBUG printk("CRT_END_VER_RETR: 64+32+%ld\n", data.v_sstop % 16);
+ crt_w(CRT_END_VER_RETR, ((data.v_sstop & 0x0f) | 0x40 | 0x20));
+#else
+ DEBUG printk("CRT_END_VER_RETR: 128+32+%ld\n", data.v_sstop % 16);
+ crt_w(CRT_END_VER_RETR, ((data.v_sstop & 0x0f) | 128 | 32));
+#endif
+ DEBUG printk("CRT_VER_DISP_ENA_END: %ld\n", data.v_dispend & 0xff);
+ crt_w(CRT_VER_DISP_ENA_END, (data.v_dispend & 0xff));
+
+ DEBUG printk("CRT_START_VER_BLANK: %ld\n", data.v_bstart & 0xff);
+ crt_w(CRT_START_VER_BLANK, (data.v_bstart & 0xff));
+
+ DEBUG printk("CRT_END_VER_BLANK: %ld\n", data.v_bstop & 0xff);
+ crt_w(CRT_END_VER_BLANK, (data.v_bstop & 0xff));
+
+ DEBUG printk("CRT_MODE_CONTROL: 0xe3\n");
+ crt_w(CRT_MODE_CONTROL, 0xe3);
+
+ DEBUG printk("CRT_LINE_COMPARE: 0xff\n");
+ crt_w(CRT_LINE_COMPARE, 0xff);
+
+ tmp = (var->xres_virtual / 8) * (bpp / 8);
+ crt_w(CRT_OFFSET, tmp);
+
+ crt_w(CRT_UNDERLINE_LOC, 0x07); /* probably font-height - 1 */
+
+ tmp = 0x20; /* Enable extended end bits */
+ if (data.h_total & 0x100)
+ tmp |= 0x01;
+ if ((data.h_dispend) & 0x100)
+ tmp |= 0x02;
+ if (data.h_bstart & 0x100)
+ tmp |= 0x04;
+ if (data.h_sstart & 0x100)
+ tmp |= 0x08;
+ if (var->vmode & FB_VMODE_INTERLACED)
+ tmp |= 0x10;
+ DEBUG printk("CRT_EXT_HOR_TIMING1: %d\n", tmp);
+ crt_w(CRT_EXT_HOR_TIMING1, tmp);
+
+ tmp = 0x00;
+ if (((var->xres_virtual / 8) * (bpp / 8)) & 0x100)
+ tmp |= 0x10;
+ crt_w(CRT_EXT_START_ADDR, tmp);
+
+ tmp = 0x00;
+ if (data.h_total & 0x200)
+ tmp |= 0x01;
+ if ((data.h_dispend) & 0x200)
+ tmp |= 0x02;
+ if (data.h_bstart & 0x200)
+ tmp |= 0x04;
+ if (data.h_sstart & 0x200)
+ tmp |= 0x08;
+ tmp |= ((data.h_bstop & 0xc0) >> 2);
+ tmp |= ((data.h_sstop & 0x60) << 1);
+ crt_w(CRT_EXT_HOR_TIMING2, tmp);
+ DEBUG printk("CRT_EXT_HOR_TIMING2: %d\n", tmp);
+
+ tmp = 0x10; /* Line compare bit 10 */
+ if (data.v_total & 0x400)
+ tmp |= 0x01;
+ if ((data.v_dispend) & 0x400)
+ tmp |= 0x02;
+ if (data.v_bstart & 0x400)
+ tmp |= 0x04;
+ if (data.v_sstart & 0x400)
+ tmp |= 0x08;
+ tmp |= ((data.v_bstop & 0x300) >> 3);
+ if (data.v_sstop & 0x10)
+ tmp |= 0x80;
+ crt_w(CRT_EXT_VER_TIMING, tmp);
+ DEBUG printk("CRT_EXT_VER_TIMING: %d\n", tmp);
+
+ crt_w(CRT_MONITOR_POWER, 0x00);
+
+ /*
+ * Convert from ps to Hz.
+ */
+ freq_f = (1.0/(float)var->pixclock) * 1000000000;
+ freq = ((long)freq_f) * 1000;
+
+ best_freq = find_fq(freq);
+ pll_w(0x02, best_freq);
+ best_freq = find_fq(61000000);
+ pll_w(0x0a, best_freq);
+ pll_w(0x0e, 0x22);
+
+ gfx_w(GFX_SET_RESET, 0x00);
+ gfx_w(GFX_ENABLE_SET_RESET, 0x00);
+ gfx_w(GFX_COLOR_COMPARE, 0x00);
+ gfx_w(GFX_DATA_ROTATE, 0x00);
+ gfx_w(GFX_READ_MAP_SELECT, 0x00);
+ gfx_w(GFX_GRAPHICS_MODE, 0x00);
+ gfx_w(GFX_MISC, 0x05);
+ gfx_w(GFX_COLOR_XCARE, 0x0f);
+ gfx_w(GFX_BITMASK, 0xff);
+
+ reg_r(ACT_ADDRESS_RESET);
+ attr_w(ACT_PALETTE0 , 0x00);
+ attr_w(ACT_PALETTE1 , 0x01);
+ attr_w(ACT_PALETTE2 , 0x02);
+ attr_w(ACT_PALETTE3 , 0x03);
+ attr_w(ACT_PALETTE4 , 0x04);
+ attr_w(ACT_PALETTE5 , 0x05);
+ attr_w(ACT_PALETTE6 , 0x06);
+ attr_w(ACT_PALETTE7 , 0x07);
+ attr_w(ACT_PALETTE8 , 0x08);
+ attr_w(ACT_PALETTE9 , 0x09);
+ attr_w(ACT_PALETTE10, 0x0a);
+ attr_w(ACT_PALETTE11, 0x0b);
+ attr_w(ACT_PALETTE12, 0x0c);
+ attr_w(ACT_PALETTE13, 0x0d);
+ attr_w(ACT_PALETTE14, 0x0e);
+ attr_w(ACT_PALETTE15, 0x0f);
+ reg_r(ACT_ADDRESS_RESET);
+
+ attr_w(ACT_ATTR_MODE_CNTL, 0x09); /* 0x01 for CL */
+
+ attr_w(ACT_OVERSCAN_COLOR, 0x00);
+ attr_w(ACT_COLOR_PLANE_ENA, 0x0f);
+ attr_w(ACT_HOR_PEL_PANNING, 0x00);
+ attr_w(ACT_COLOR_SELECT, 0x00);
+
+ reg_r(ACT_ADDRESS_RESET);
+ reg_w(ACT_DATA, 0x20);
+
+ reg_w(VDAC_MASK, 0xff);
+
+ /*
+ * Extended palette adressing ???
+ */
+ switch (bpp){
+ case 8:
+ reg_w(0x83c6, 0x00);
+ break;
+ case 16:
+ reg_w(0x83c6, 0x60);
+ break;
+ case 24:
+ reg_w(0x83c6, 0xe0);
+ break;
+ default:
+ printk("Illegal color-depth: %i\n", bpp);
+ }
+
+ reg_w(VDAC_ADDRESS, 0x00);
+
+ seq_w(SEQ_MAP_MASK, 0x0f );
+
+ return 0;
+}
+
+/*
+ * Initialization
+ *
+ * Set the default video mode for this chipset. If a video mode was
+ * specified on the command line, it will override the default mode.
+ */
+
+static int retz3_init(void)
+{
+ short i;
+#if 0
+ volatile unsigned long *CursorBase;
+#endif
+
+ for (i = 0; i < 256; i++){
+ for (i = 0; i < 256; i++){
+ retz3_color_table [i][0] = i;
+ retz3_color_table [i][1] = i;
+ retz3_color_table [i][2] = i;
+ retz3_color_table [i][3] = 0;
+ }
+ }
+
+ /* Disable hardware cursor */
+
+ seq_w(SEQ_CURSOR_Y_INDEX, 0x00);
+
+#if 0
+ /* Initialize hardware cursor */
+ CursorBase = (unsigned long *)((char *)(z3_mem) + z3_size - 0x400);
+ for (i=0; i < 8; i++){
+ *(CursorBase +(i*4)) = 0xffffff00;
+ *(CursorBase+1+(i*4)) = 0xffff0000;
+ *(CursorBase+2+(i*4)) = 0xffff0000;
+ *(CursorBase+3+(i*4)) = 0xffff0000;
+ }
+ for (i=8; i < 64; i++){
+ *(CursorBase +(i*4)) = 0xffff0000;
+ *(CursorBase+1+(i*4)) = 0xffff0000;
+ *(CursorBase+2+(i*4)) = 0xffff0000;
+ *(CursorBase+3+(i*4)) = 0xffff0000;
+ }
+#endif
+
+ retz3_setcolreg (255, 56, 100, 160, 0);
+ retz3_setcolreg (254, 0, 0, 0, 0);
+
+ return 0;
+}
+
+
+/*
+ * This function should fill in the `fix' structure based on the
+ * values in the `par' structure.
+ */
+
+static int retz3_encode_fix(struct fb_fix_screeninfo *fix,
+ struct retz3_fb_par *par)
+{
+ short i;
+
+ strcpy(fix->id, retz3_fb_name);
+ fix->smem_start = (char *)z3_fbmem;
+ fix->smem_len = z3_size;
+ fix->mmio_start = (unsigned char *)z3_regs;
+ fix->mmio_len = 0x00c00000;
+
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->type_aux = 0;
+ if (par->bpp == 8)
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
+ else
+ fix->visual = FB_VISUAL_DIRECTCOLOR;
+
+ fix->xpanstep = 0;
+ fix->ypanstep = 0;
+ fix->ywrapstep = 0;
+ fix->line_length = 0;
+
+ for (i = 0; i < arraysize(fix->reserved); i++)
+ fix->reserved[i] = 0;
+
+ return 0;
+}
+
+
+/*
+ * Get the video params out of `var'. If a value doesn't fit, round
+ * it up, if it's too big, return -EINVAL.
+ */
+
+static int retz3_decode_var(struct fb_var_screeninfo *var,
+ struct retz3_fb_par *par)
+{
+ par->xres = var->xres;
+ par->yres = var->yres;
+ par->xres_vir = var->xres_virtual;
+ par->yres_vir = var->yres_virtual;
+ par->bpp = var->bits_per_pixel;
+ par->pixclock = var->pixclock;
+ par->vmode = var->vmode;
+
+ par->red = var->red;
+ par->green = var->green;
+ par->blue = var->blue;
+ par->transp = var->transp;
+
+ par->left_margin = var->left_margin;
+ par->right_margin = var->right_margin;
+ par->upper_margin = var->upper_margin;
+ par->lower_margin = var->lower_margin;
+ par->hsync_len = var->hsync_len;
+ par->vsync_len = var->vsync_len;
+
+ return 0;
+}
+
+
+/*
+ * Fill the `var' structure based on the values in `par' and maybe
+ * other values read out of the hardware.
+ */
+
+static int retz3_encode_var(struct fb_var_screeninfo *var,
+ struct retz3_fb_par *par)
+{
+ short i;
+
+ var->xres = par->xres;
+ var->yres = par->yres;
+ var->xres_virtual = par->xres_vir;
+ var->yres_virtual = par->yres_vir;
+ var->xoffset = 0;
+ var->yoffset = 0;
+
+ var->bits_per_pixel = par->bpp;
+ var->grayscale = 0;
+
+ var->red = par->red;
+ var->green = par->green;
+ var->blue = par->blue;
+ var->transp = par->transp;
+
+ var->nonstd = 0;
+ var->activate = 0;
+
+ var->height = -1;
+ var->width = -1;
+
+ var->accel = FB_ACCEL_RETINAZ3;
+
+ var->pixclock = par->pixclock;
+
+ var->sync = 0; /* ??? */
+ var->left_margin = par->left_margin;
+ var->right_margin = par->right_margin;
+ var->upper_margin = par->upper_margin;
+ var->lower_margin = par->lower_margin;
+ var->hsync_len = par->hsync_len;
+ var->vsync_len = par->vsync_len;
+
+ for (i = 0; i < arraysize(var->reserved); i++)
+ var->reserved[i] = 0;
+
+ var->vmode = par->vmode;
+ return 0;
+}
+
+
+/*
+ * Set a single color register. The values supplied are already
+ * rounded down to the hardware's capabilities (according to the
+ * entries in the var structure). Return != 0 for invalid regno.
+ */
+
+static int retz3_setcolreg(unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue,
+ unsigned int transp)
+{
+ /* We'll get to this */
+
+ if (regno > 255)
+ return 1;
+
+ retz3_color_table [regno][0] = red & 0xff;
+ retz3_color_table [regno][1] = green & 0xff;
+ retz3_color_table [regno][2] = blue & 0xff;
+ retz3_color_table [regno][3] = transp;
+
+ reg_w(VDAC_ADDRESS_W, regno);
+ reg_w(VDAC_DATA, (red & 0xff) >> 2);
+ reg_w(VDAC_DATA, (green & 0xff) >> 2);
+ reg_w(VDAC_DATA, (blue & 0xff) >> 2);
+
+ return 0;
+}
+
+
+/*
+ * Read a single color register and split it into
+ * colors/transparent. Return != 0 for invalid regno.
+ */
+
+static int retz3_getcolreg(unsigned int regno, unsigned int *red,
+ unsigned int *green, unsigned int *blue,
+ unsigned int *transp)
+{
+ if (regno > 255)
+ return 1;
+ *red = retz3_color_table [regno][0];
+ *green = retz3_color_table [regno][1];
+ *blue = retz3_color_table [regno][2];
+ *transp = retz3_color_table [regno][3];
+ return 0;
+}
+
+
+/*
+ * (Un)Blank the screen
+ */
+
+void retz3_blank(int blank)
+{
+ short i;
+
+ if (blank)
+ for (i = 0; i < 256; i++){
+ reg_w(VDAC_ADDRESS_W, i);
+ reg_w(VDAC_DATA, 0);
+ reg_w(VDAC_DATA, 0);
+ reg_w(VDAC_DATA, 0);
+ }
+ else
+ for (i = 0; i < 256; i++){
+ reg_w(VDAC_ADDRESS_W, i);
+ reg_w(VDAC_DATA, retz3_color_table [i][0] >> 2);
+ reg_w(VDAC_DATA, retz3_color_table [i][1] >> 2);
+ reg_w(VDAC_DATA, retz3_color_table [i][2] >> 2);
+ }
+}
+
+
+void retz3_bitblt (struct fb_var_screeninfo *var,
+ unsigned short srcx, unsigned short srcy, unsigned
+ short destx, unsigned short desty, unsigned short
+ width, unsigned short height, unsigned short cmd,
+ unsigned short mask)
+{
+
+ volatile unsigned long *acm = (unsigned long *) (z3_mem + ACM_OFFSET);
+ unsigned long *pattern = (unsigned long *)(z3_fbmem + PAT_MEM_OFF);
+
+ unsigned short mod;
+ unsigned long tmp;
+ unsigned long pat, src, dst;
+ unsigned char blt_status;
+
+ int i, xres_virtual = var->xres_virtual;
+ short bpp = (var->bits_per_pixel & 0xff);
+
+ if (bpp < 8)
+ bpp = 8;
+
+ tmp = mask | (mask << 16);
+
+#if 0
+ /*
+ * Check for blitter finished before we start messing with the
+ * pattern.
+ */
+ do{
+ blt_status = *(((volatile unsigned char *)acm) +
+ (ACM_START_STATUS + 2));
+ }while ((blt_status & 1) == 0);
+#endif
+
+ i = 0;
+ do{
+ *pattern++ = tmp;
+ }while(i++ < bpp/4);
+
+ tmp = cmd << 8;
+ *(acm + ACM_RASTEROP_ROTATION/4) = tmp;
+
+ mod = 0xc0c2;
+
+ pat = 8 * PAT_MEM_OFF;
+ dst = bpp * (destx + desty * xres_virtual);
+
+ /*
+ * Source is not set for clear.
+ */
+ if ((cmd != Z3BLTclear) && (cmd != Z3BLTset)) {
+ src = bpp * (srcx + srcy * xres_virtual);
+
+ if (destx > srcx) {
+ mod &= ~0x8000;
+ src += bpp * (width - 1);
+ dst += bpp * (width - 1);
+ pat += bpp * 2;
+ }
+ if (desty > srcy) {
+ mod &= ~0x4000;
+ src += bpp * (height - 1) * xres_virtual;
+ dst += bpp * (height - 1) * xres_virtual;
+ pat += bpp * 4;
+ }
+
+ *(acm + ACM_SOURCE/4) = cpu_to_le32(src);
+ }
+
+ *(acm + ACM_PATTERN/4) = cpu_to_le32(pat);
+
+ *(acm + ACM_DESTINATION/4) = cpu_to_le32(dst);
+
+ tmp = mod << 16;
+ *(acm + ACM_CONTROL/4) = tmp;
+
+ tmp = width | (height << 16);
+
+ *(acm + ACM_BITMAP_DIMENSION/4) = cpu_to_le32(tmp);
+
+ *(((volatile unsigned char *)acm) + ACM_START_STATUS) = 0x00;
+ *(((volatile unsigned char *)acm) + ACM_START_STATUS) = 0x01;
+
+ /*
+ * No reason to wait for the blitter to finish, it is better
+ * just to check if it has finished before we use it again.
+ */
+#if 1
+#if 0
+ while ((*(((volatile unsigned char *)acm) +
+ (ACM_START_STATUS + 2)) & 1) == 0);
+#else
+ do{
+ blt_status = *(((volatile unsigned char *)acm) +
+ (ACM_START_STATUS + 2));
+ }
+ while ((blt_status & 1) == 0);
+#endif
+#endif
+}
+
+#if 0
+void retz3_fill (unsigned short x, unsigned short y, unsigned
+ short width, unsigned short height,
+ unsigned short mode, unsigned short color)
+{
+
+}
+#endif
+
+
+#if 0
+/*
+ * Move cursor to x, y
+ */
+void retz3_MoveCursor (unsigned short x, unsigned short y)
+{
+ /* Guess we gotta deal with the cursor at some point */
+}
+#endif
+
+/* -------------------- Interfaces to hardware functions -------------------- */
+
+
+static struct fb_hwswitch retz3_switch = {
+ retz3_init, retz3_encode_fix, retz3_decode_var, retz3_encode_var,
+ retz3_getcolreg, retz3_setcolreg, retz3_blank
+};
+
+
+/* -------------------- Generic routines ------------------------------------ */
+
+
+/*
+ * Fill the hardware's `par' structure.
+ */
+
+static void retz3_fb_get_par(struct retz3_fb_par *par)
+{
+ if (current_par_valid)
+ *par = current_par;
+ else
+ fbhw->decode_var(&retz3_fb_predefined[z3fb_mode], par);
+}
+
+
+static void retz3_fb_set_par(struct retz3_fb_par *par)
+{
+ current_par = *par;
+ current_par_valid = 1;
+}
+
+
+static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive)
+{
+ int err, activate;
+ struct retz3_fb_par par;
+
+ if ((err = fbhw->decode_var(var, &par)))
+ return err;
+ activate = var->activate;
+
+ /* XXX ... what to do about isactive ? */
+
+ if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW && isactive)
+ retz3_fb_set_par(&par);
+ fbhw->encode_var(var, &par);
+ var->activate = activate;
+
+ retz3_set_video(var, &current_par);
+
+ return 0;
+}
+
+
+static void do_install_cmap(int con)
+{
+ if (con != currcon)
+ return;
+ if (fb_display[con].cmap.len)
+ fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
+ fbhw->setcolreg);
+ else
+ fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+ &fb_display[con].var, 1,
+ fbhw->setcolreg);
+}
+
+
+/*
+ * Open/Release the frame buffer device
+ */
+
+static int retz3_fb_open(int fbidx)
+{
+ /*
+ * Nothing, only a usage count for the moment
+ */
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int retz3_fb_release(int fbidx)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+
+/*
+ * Get the Fixed Part of the Display
+ */
+
+static int retz3_fb_get_fix(struct fb_fix_screeninfo *fix, int con)
+{
+ struct retz3_fb_par par;
+ int error = 0;
+
+ if (con == -1)
+ retz3_fb_get_par(&par);
+ else
+ error = fbhw->decode_var(&fb_display[con].var, &par);
+ return(error ? error : fbhw->encode_fix(fix, &par));
+}
+
+
+/*
+ * Get the User Defined Part of the Display
+ */
+
+static int retz3_fb_get_var(struct fb_var_screeninfo *var, int con)
+{
+ struct retz3_fb_par par;
+ int error = 0;
+
+ if (con == -1) {
+ retz3_fb_get_par(&par);
+ error = fbhw->encode_var(var, &par);
+ } else
+ *var = fb_display[con].var;
+ return error;
+}
+
+
+#if 1
+static void retz3_fb_set_disp(int con)
+{
+ struct fb_fix_screeninfo fix;
+ struct display *display;
+
+ if (con >= 0)
+ display = &fb_display[con];
+ else
+ display = &disp; /* used during initialization */
+
+ retz3_fb_get_fix(&fix, con);
+
+ if (con == -1)
+ con = 0;
+
+ display->screen_base = (unsigned char *)fix.smem_start;
+ display->visual = fix.visual;
+ display->type = fix.type;
+ display->type_aux = fix.type_aux;
+ display->ypanstep = fix.ypanstep;
+ display->ywrapstep = fix.ywrapstep;
+ display->can_soft_blank = 1;
+ display->inverse = z3fb_inverse;
+}
+#endif
+
+/*
+ * Set the User Defined Part of the Display
+ */
+
+static int retz3_fb_set_var(struct fb_var_screeninfo *var, int con)
+{
+ int err, oldxres, oldyres, oldvxres, oldvyres, oldbpp;
+ struct display *display;
+
+ if (con >= 0)
+ display = &fb_display[con];
+ else
+ display = &disp; /* used during initialization */
+
+#if 0
+ if (con == -1)
+ con = 0;
+#endif
+
+ if ((err = do_fb_set_var(var, con == currcon)))
+ return err;
+ if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+ oldxres = display->var.xres;
+ oldyres = display->var.yres;
+ oldvxres = display->var.xres_virtual;
+ oldvyres = display->var.yres_virtual;
+ oldbpp = display->var.bits_per_pixel;
+ display->var = *var;
+
+ if (oldxres != var->xres || oldyres != var->yres ||
+ oldvxres != var->xres_virtual ||
+ oldvyres != var->yres_virtual ||
+ oldbpp != var->bits_per_pixel) {
+
+ struct fb_fix_screeninfo fix;
+ retz3_fb_get_fix(&fix, con);
+
+ display->screen_base = fix.smem_start;
+ display->visual = fix.visual;
+ display->type = fix.type;
+ display->type_aux = fix.type_aux;
+ display->ypanstep = fix.ypanstep;
+ display->ywrapstep = fix.ywrapstep;
+ display->line_length = fix.line_length;
+ display->can_soft_blank = 1;
+ display->inverse = z3fb_inverse;
+/*
+ retz3_fb_set_disp(con);
+*/
+ if (fb_info.changevar)
+ (*fb_info.changevar)(con);
+ }
+
+ if (oldbpp != var->bits_per_pixel) {
+ if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
+ return err;
+ do_install_cmap(con);
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * Get the Colormap
+ */
+
+static int retz3_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con)
+{
+ if (con == currcon) /* current console? */
+ return(fb_get_cmap(cmap, &fb_display[con].var, kspc,
+ fbhw->getcolreg));
+ else if (fb_display[con].cmap.len) /* non default colormap? */
+ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+ else
+ fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+ cmap, kspc ? 0 : 2);
+ return 0;
+}
+
+
+/*
+ * Set the Colormap
+ */
+
+static int retz3_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con)
+{
+ int err;
+
+ if (!fb_display[con].cmap.len) { /* no colormap allocated? */
+ if ((err = fb_alloc_cmap(&fb_display[con].cmap,
+ 1<<fb_display[con].var.bits_per_pixel,
+ 0)))
+ return err;
+ }
+ if (con == currcon) /* current console? */
+ return(fb_set_cmap(cmap, &fb_display[con].var, kspc,
+ fbhw->setcolreg));
+ else
+ fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+ return 0;
+}
+
+
+/*
+ * Pan or Wrap the Display
+ *
+ * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+ */
+
+static int retz3_fb_pan_display(struct fb_var_screeninfo *var, int con)
+{
+ return -EINVAL;
+}
+
+
+/*
+ * RetinaZ3 Frame Buffer Specific ioctls
+ */
+
+static int retz3_fb_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg, int con)
+{
+ return -EINVAL;
+}
+
+
+static struct fb_ops retz3_fb_ops = {
+ retz3_fb_open, retz3_fb_release, retz3_fb_get_fix, retz3_fb_get_var,
+ retz3_fb_set_var, retz3_fb_get_cmap, retz3_fb_set_cmap,
+ retz3_fb_pan_display, retz3_fb_ioctl
+};
+
+
+__initfunc(void retz3_video_setup(char *options, int *ints))
+{
+ char *this_opt;
+
+ fb_info.fontname[0] = '\0';
+
+ if (!options || !*options)
+ return;
+
+ for (this_opt = strtok(options, ","); this_opt;
+ this_opt = strtok(NULL, ",")){
+ if (!strcmp(this_opt, "inverse")) {
+ z3fb_inverse = 1;
+ fb_invert_cmaps();
+ } else if (!strncmp(this_opt, "font:", 5))
+ strcpy(fb_info.fontname, this_opt+5);
+ else
+ z3fb_mode = get_video_mode(this_opt);
+ }
+}
+
+
+/*
+ * Initialization
+ */
+
+__initfunc(unsigned long retz3_fb_init(unsigned long mem_start))
+{
+ int err;
+ unsigned long board_addr, board_size;
+ unsigned int key;
+ const struct ConfigDev *cd;
+
+ struct retz3_fb_par par;
+
+ if (!(key = zorro_find(ZORRO_PROD_MACROSYSTEMS_RETINA_Z3, 0, 0)))
+ return mem_start;
+
+ cd = zorro_get_board (key);
+ zorro_config_board (key, 0);
+ board_addr = (unsigned long)cd->cd_BoardAddr;
+ board_size = (unsigned long)cd->cd_BoardSize;
+
+ z3_mem = kernel_map (board_addr, board_size,
+ KERNELMAP_NOCACHE_SER, &mem_start);
+
+ z3_regs = (char*) z3_mem;
+ z3_fbmem = z3_mem + VIDEO_MEM_OFFSET;
+
+ /* Get memory size - for now we asume its a 4MB board */
+
+ z3_size = 0x00400000; /* 4 MB */
+
+ memset ((char*)z3_fbmem, 0, z3_size);
+
+ fbhw = &retz3_switch;
+
+ fbhw->init();
+
+ strcpy(fb_info.modename, retz3_fb_name);
+ fb_info.changevar = NULL;
+ fb_info.node = -1;
+ fb_info.fbops = &retz3_fb_ops;
+ fb_info.fbvar_num = NUM_TOTAL_MODES;
+ fb_info.fbvar = retz3_fb_predefined;
+ fb_info.disp = &disp;
+ fb_info.switch_con = &z3fb_switch;
+ fb_info.updatevar = &z3fb_updatevar;
+ fb_info.blank = &z3fb_blank;
+ fb_info.setcmap = &z3fb_setcmap;
+
+ err = register_framebuffer(&fb_info);
+ if (err < 0)
+ return mem_start;
+
+ if (z3fb_mode == -1)
+ z3fb_mode = 1;
+
+ fbhw->decode_var(&retz3_fb_predefined[z3fb_mode], &par);
+ fbhw->encode_var(&retz3_fb_predefined[0], &par);
+
+ do_fb_set_var(&retz3_fb_predefined[0], 0);
+ retz3_fb_get_var(&disp.var, -1);
+
+ retz3_fb_set_disp(-1);
+
+ do_install_cmap(0);
+
+ printk("%s frame buffer device, using %ldK of video memory\n",
+ fb_info.modename, z3_size>>10);
+
+ /* TODO: This driver cannot be unloaded yet */
+ MOD_INC_USE_COUNT;
+
+ return mem_start;
+}
+
+
+static int z3fb_switch(int con)
+{
+ /* Do we have to save the colormap? */
+ if (fb_display[currcon].cmap.len)
+ fb_get_cmap(&fb_display[currcon].cmap,
+ &fb_display[currcon].var, 1, fbhw->getcolreg);
+
+ do_fb_set_var(&fb_display[con].var, 1);
+ currcon = con;
+ /* Install new colormap */
+ do_install_cmap(con);
+ return 0;
+}
+
+
+/*
+ * Update the `var' structure (called by fbcon.c)
+ *
+ * This call looks only at yoffset and the FB_VMODE_YWRAP flag in `var'.
+ * Since it's called by a kernel driver, no range checking is done.
+ */
+
+static int z3fb_updatevar(int con)
+{
+ return 0;
+}
+
+
+/*
+ * Blank the display.
+ */
+
+static void z3fb_blank(int blank)
+{
+ fbhw->blank(blank);
+}
+
+
+/*
+ * Set the colormap
+ */
+
+static int z3fb_setcmap(struct fb_cmap *cmap, int con)
+{
+ return(retz3_fb_set_cmap(cmap, 1, con));
+}
+
+
+/*
+ * Get a Video Mode
+ */
+
+static int get_video_mode(const char *name)
+{
+ short i;
+
+ for (i = 1; i <= NUM_PREDEF_MODES; i++)
+ if (!strcmp(name, retz3_fb_modenames[i])){
+ retz3_fb_predefined[0] = retz3_fb_predefined[i];
+ return i;
+ }
+ return -1;
+}
+
+
+#ifdef MODULE
+int init_module(void)
+{
+ return(retz3_fb_init(NULL));
+}
+
+void cleanup_module(void)
+{
+ /*
+ * Not reached because the usecount will never
+ * be decremented to zero
+ */
+ unregister_framebuffer(&fb_info);
+ /* TODO: clean up ... */
+}
+#endif /* MODULE */
+
+
+/*
+ * Visible symbols for modules
+ */
+
+EXPORT_SYMBOL(retz3_bitblt);
diff --git a/drivers/video/retz3fb.h b/drivers/video/retz3fb.h
new file mode 100644
index 000000000..149842860
--- /dev/null
+++ b/drivers/video/retz3fb.h
@@ -0,0 +1,286 @@
+/*
+ * linux/drivers/video/retz3fb.h -- Defines and macros for the RetinaZ3 frame
+ * buffer device
+ *
+ * Copyright (C) 1997 Jes Sorensen
+ *
+ * History:
+ * - 22 Jan 97: Initial work
+ *
+ *
+ * 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.
+ */
+
+/*
+ * Macros to read and write to registers.
+ */
+#define reg_w(reg,dat) (*(z3_regs + reg) = dat)
+#define reg_r(reg) (*(z3_regs + reg))
+
+/*
+ * Macro to access the sequencer.
+ */
+#define seq_w(sreg,sdat) \
+ do{ reg_w(SEQ_IDX, sreg); reg_w(SEQ_DATA, sdat); } while(0)
+
+/*
+ * Macro to access the CRT controller.
+ */
+#define crt_w(creg,cdat) \
+ do{ reg_w(CRT_IDX, creg); reg_w(CRT_DATA, cdat); } while(0)
+
+/*
+ * Macro to access the graphics controller.
+ */
+#define gfx_w(greg,gdat) \
+ do{ reg_w(GFX_IDX, greg); reg_w(GFX_DATA, gdat); } while(0)
+
+/*
+ * Macro to access the attribute controller.
+ */
+#define attr_w(areg,adat) \
+ do{ reg_w(ACT_IDX, areg); reg_w(ACT_DATA, adat); } while(0)
+
+/*
+ * Macro to access the pll.
+ */
+#define pll_w(preg,pdat) \
+ do{ reg_w(PLL_IDX, preg); \
+ reg_w(PLL_DATA, (pdat & 0xff)); \
+ reg_w(PLL_DATA, (pdat >> 8));\
+ } while(0)
+
+/*
+ * Offsets
+ */
+#define VIDEO_MEM_OFFSET 0x00c00000
+#define ACM_OFFSET 0x00b00000
+
+/*
+ * Accelerator Control Menu
+ */
+#define ACM_PRIMARY_OFFSET 0x00
+#define ACM_SECONDARY_OFFSET 0x04
+#define ACM_MODE_CONTROL 0x08
+#define ACM_CURSOR_POSITION 0x0c
+#define ACM_START_STATUS 0x30
+#define ACM_CONTROL 0x34
+#define ACM_RASTEROP_ROTATION 0x38
+#define ACM_BITMAP_DIMENSION 0x3c
+#define ACM_DESTINATION 0x40
+#define ACM_SOURCE 0x44
+#define ACM_PATTERN 0x48
+#define ACM_FOREGROUND 0x4c
+#define ACM_BACKGROUND 0x50
+
+/*
+ * Video DAC addresses
+ */
+#define VDAC_ADDRESS 0x03c8
+#define VDAC_ADDRESS_W 0x03c8
+#define VDAC_ADDRESS_R 0x03c7
+#define VDAC_STATE 0x03c7
+#define VDAC_DATA 0x03c9
+#define VDAC_MASK 0x03c6
+
+/*
+ * Sequencer
+ */
+#define SEQ_IDX 0x03c4 /* Sequencer Index */
+#define SEQ_DATA 0x03c5
+#define SEQ_RESET 0x00
+#define SEQ_CLOCKING_MODE 0x01
+#define SEQ_MAP_MASK 0x02
+#define SEQ_CHAR_MAP_SELECT 0x03
+#define SEQ_MEMORY_MODE 0x04
+#define SEQ_EXTENDED_ENABLE 0x05 /* NCR extensions */
+#define SEQ_UNKNOWN1 0x06
+#define SEQ_UNKNOWN2 0x07
+#define SEQ_CHIP_ID 0x08
+#define SEQ_UNKNOWN3 0x09
+#define SEQ_CURSOR_COLOR1 0x0a
+#define SEQ_CURSOR_COLOR0 0x0b
+#define SEQ_CURSOR_CONTROL 0x0c
+#define SEQ_CURSOR_X_LOC_HI 0x0d
+#define SEQ_CURSOR_X_LOC_LO 0x0e
+#define SEQ_CURSOR_Y_LOC_HI 0x0f
+#define SEQ_CURSOR_Y_LOC_LO 0x10
+#define SEQ_CURSOR_X_INDEX 0x11
+#define SEQ_CURSOR_Y_INDEX 0x12
+#define SEQ_CURSOR_STORE_HI 0x13
+#define SEQ_CURSOR_STORE_LO 0x14
+#define SEQ_CURSOR_ST_OFF_HI 0x15
+#define SEQ_CURSOR_ST_OFF_LO 0x16
+#define SEQ_CURSOR_PIXELMASK 0x17
+#define SEQ_PRIM_HOST_OFF_HI 0x18
+#define SEQ_PRIM_HOST_OFF_LO 0x19
+#define SEQ_LINEAR_0 0x1a
+#define SEQ_LINEAR_1 0x1b
+#define SEQ_SEC_HOST_OFF_HI 0x1c
+#define SEQ_SEC_HOST_OFF_LO 0x1d
+#define SEQ_EXTENDED_MEM_ENA 0x1e
+#define SEQ_EXT_CLOCK_MODE 0x1f
+#define SEQ_EXT_VIDEO_ADDR 0x20
+#define SEQ_EXT_PIXEL_CNTL 0x21
+#define SEQ_BUS_WIDTH_FEEDB 0x22
+#define SEQ_PERF_SELECT 0x23
+#define SEQ_COLOR_EXP_WFG 0x24
+#define SEQ_COLOR_EXP_WBG 0x25
+#define SEQ_EXT_RW_CONTROL 0x26
+#define SEQ_MISC_FEATURE_SEL 0x27
+#define SEQ_COLOR_KEY_CNTL 0x28
+#define SEQ_COLOR_KEY_MATCH0 0x29
+#define SEQ_COLOR_KEY_MATCH1 0x2a
+#define SEQ_COLOR_KEY_MATCH2 0x2b
+#define SEQ_UNKNOWN6 0x2c
+#define SEQ_CRC_CONTROL 0x2d
+#define SEQ_CRC_DATA_LOW 0x2e
+#define SEQ_CRC_DATA_HIGH 0x2f
+#define SEQ_MEMORY_MAP_CNTL 0x30
+#define SEQ_ACM_APERTURE_1 0x31
+#define SEQ_ACM_APERTURE_2 0x32
+#define SEQ_ACM_APERTURE_3 0x33
+#define SEQ_BIOS_UTILITY_0 0x3e
+#define SEQ_BIOS_UTILITY_1 0x3f
+
+/*
+ * Graphics Controller
+ */
+#define GFX_IDX 0x03ce
+#define GFX_DATA 0x03cf
+#define GFX_SET_RESET 0x00
+#define GFX_ENABLE_SET_RESET 0x01
+#define GFX_COLOR_COMPARE 0x02
+#define GFX_DATA_ROTATE 0x03
+#define GFX_READ_MAP_SELECT 0x04
+#define GFX_GRAPHICS_MODE 0x05
+#define GFX_MISC 0x06
+#define GFX_COLOR_XCARE 0x07
+#define GFX_BITMASK 0x08
+
+/*
+ * CRT Controller
+ */
+#define CRT_IDX 0x03d4
+#define CRT_DATA 0x03d5
+#define CRT_HOR_TOTAL 0x00
+#define CRT_HOR_DISP_ENA_END 0x01
+#define CRT_START_HOR_BLANK 0x02
+#define CRT_END_HOR_BLANK 0x03
+#define CRT_START_HOR_RETR 0x04
+#define CRT_END_HOR_RETR 0x05
+#define CRT_VER_TOTAL 0x06
+#define CRT_OVERFLOW 0x07
+#define CRT_PRESET_ROW_SCAN 0x08
+#define CRT_MAX_SCAN_LINE 0x09
+#define CRT_CURSOR_START 0x0a
+#define CRT_CURSOR_END 0x0b
+#define CRT_START_ADDR_HIGH 0x0c
+#define CRT_START_ADDR_LOW 0x0d
+#define CRT_CURSOR_LOC_HIGH 0x0e
+#define CRT_CURSOR_LOC_LOW 0x0f
+#define CRT_START_VER_RETR 0x10
+#define CRT_END_VER_RETR 0x11
+#define CRT_VER_DISP_ENA_END 0x12
+#define CRT_OFFSET 0x13
+#define CRT_UNDERLINE_LOC 0x14
+#define CRT_START_VER_BLANK 0x15
+#define CRT_END_VER_BLANK 0x16
+#define CRT_MODE_CONTROL 0x17
+#define CRT_LINE_COMPARE 0x18
+#define CRT_UNKNOWN1 0x19
+#define CRT_UNKNOWN2 0x1a
+#define CRT_UNKNOWN3 0x1b
+#define CRT_UNKNOWN4 0x1c
+#define CRT_UNKNOWN5 0x1d
+#define CRT_UNKNOWN6 0x1e
+#define CRT_UNKNOWN7 0x1f
+#define CRT_UNKNOWN8 0x20
+#define CRT_UNKNOWN9 0x21
+#define CRT_UNKNOWN10 0x22
+#define CRT_UNKNOWN11 0x23
+#define CRT_UNKNOWN12 0x24
+#define CRT_UNKNOWN13 0x25
+#define CRT_UNKNOWN14 0x26
+#define CRT_UNKNOWN15 0x27
+#define CRT_UNKNOWN16 0x28
+#define CRT_UNKNOWN17 0x29
+#define CRT_UNKNOWN18 0x2a
+#define CRT_UNKNOWN19 0x2b
+#define CRT_UNKNOWN20 0x2c
+#define CRT_UNKNOWN21 0x2d
+#define CRT_UNKNOWN22 0x2e
+#define CRT_UNKNOWN23 0x2f
+#define CRT_EXT_HOR_TIMING1 0x30 /* NCR crt extensions */
+#define CRT_EXT_START_ADDR 0x31
+#define CRT_EXT_HOR_TIMING2 0x32
+#define CRT_EXT_VER_TIMING 0x33
+#define CRT_MONITOR_POWER 0x34
+
+/*
+ * General Registers
+ */
+#define GREG_STATUS0_R 0x03c2
+#define GREG_STATUS1_R 0x03da
+#define GREG_MISC_OUTPUT_R 0x03cc
+#define GREG_MISC_OUTPUT_W 0x03c2
+#define GREG_FEATURE_CONTROL_R 0x03ca
+#define GREG_FEATURE_CONTROL_W 0x03da
+#define GREG_POS 0x0102
+
+/*
+ * Attribute Controller
+ */
+#define ACT_IDX 0x03C0
+#define ACT_ADDRESS_R 0x03C0
+#define ACT_DATA 0x03C0
+#define ACT_ADDRESS_RESET 0x03DA
+#define ACT_PALETTE0 0x00
+#define ACT_PALETTE1 0x01
+#define ACT_PALETTE2 0x02
+#define ACT_PALETTE3 0x03
+#define ACT_PALETTE4 0x04
+#define ACT_PALETTE5 0x05
+#define ACT_PALETTE6 0x06
+#define ACT_PALETTE7 0x07
+#define ACT_PALETTE8 0x08
+#define ACT_PALETTE9 0x09
+#define ACT_PALETTE10 0x0A
+#define ACT_PALETTE11 0x0B
+#define ACT_PALETTE12 0x0C
+#define ACT_PALETTE13 0x0D
+#define ACT_PALETTE14 0x0E
+#define ACT_PALETTE15 0x0F
+#define ACT_ATTR_MODE_CNTL 0x10
+#define ACT_OVERSCAN_COLOR 0x11
+#define ACT_COLOR_PLANE_ENA 0x12
+#define ACT_HOR_PEL_PANNING 0x13
+#define ACT_COLOR_SELECT 0x14
+
+/*
+ * PLL
+ */
+#define PLL_IDX 0x83c8
+#define PLL_DATA 0x83c9
+
+/*
+ * Blitter operations
+ */
+#define Z3BLTclear 0x00 /* 0 */
+#define Z3BLTand 0x80 /* src AND dst */
+#define Z3BLTandReverse 0x40 /* src AND NOT dst */
+#define Z3BLTcopy 0xc0 /* src */
+#define Z3BLTandInverted 0x20 /* NOT src AND dst */
+#define Z3BLTnoop 0xa0 /* dst */
+#define Z3BLTxor 0x60 /* src XOR dst */
+#define Z3BLTor 0xe0 /* src OR dst */
+#define Z3BLTnor 0x10 /* NOT src AND NOT dst */
+#define Z3BLTequiv 0x90 /* NOT src XOR dst */
+#define Z3BLTinvert 0x50 /* NOT dst */
+#define Z3BLTorReverse 0xd0 /* src OR NOT dst */
+#define Z3BLTcopyInverted 0x30 /* NOT src */
+#define Z3BLTorInverted 0xb0 /* NOT src OR dst */
+#define Z3BLTnand 0x70 /* NOT src OR NOT dst */
+#define Z3BLTset 0xf0 /* 1 */
diff --git a/drivers/video/s3blit.h b/drivers/video/s3blit.h
new file mode 100644
index 000000000..ef9340770
--- /dev/null
+++ b/drivers/video/s3blit.h
@@ -0,0 +1,74 @@
+/* s3 commands */
+#define S3_BITBLT 0xc011
+#define S3_TWOPOINTLINE 0x2811
+#define S3_FILLEDRECT 0x40b1
+
+#define S3_FIFO_EMPTY 0x0400
+#define S3_HDW_BUSY 0x0200
+
+/* Enhanced register mapping (MMIO mode) */
+
+#define S3_READ_SEL 0xbee8 /* offset f */
+#define S3_MULT_MISC 0xbee8 /* offset e */
+#define S3_ERR_TERM 0x92e8
+#define S3_FRGD_COLOR 0xa6e8
+#define S3_BKGD_COLOR 0xa2e8
+#define S3_PIXEL_CNTL 0xbee8 /* offset a */
+#define S3_FRGD_MIX 0xbae8
+#define S3_BKGD_MIX 0xb6e8
+#define S3_CUR_Y 0x82e8
+#define S3_CUR_X 0x86e8
+#define S3_DESTY_AXSTP 0x8ae8
+#define S3_DESTX_DIASTP 0x8ee8
+#define S3_MIN_AXIS_PCNT 0xbee8 /* offset 0 */
+#define S3_MAJ_AXIS_PCNT 0x96e8
+#define S3_CMD 0x9ae8
+#define S3_GP_STAT 0x9ae8
+#define S3_ADVFUNC_CNTL 0x4ae8
+#define S3_WRT_MASK 0xaae8
+#define S3_RD_MASK 0xaee8
+
+/* Enhanced register mapping (Packed MMIO mode, write only) */
+#define S3_ALT_CURXY 0x8100
+#define S3_ALT_CURXY2 0x8104
+#define S3_ALT_STEP 0x8108
+#define S3_ALT_STEP2 0x810c
+#define S3_ALT_ERR 0x8110
+#define S3_ALT_CMD 0x8118
+#define S3_ALT_MIX 0x8134
+#define S3_ALT_PCNT 0x8148
+#define S3_ALT_PAT 0x8168
+
+/* Drawing modes */
+#define S3_NOTCUR 0x0000
+#define S3_LOGICALZERO 0x0001
+#define S3_LOGICALONE 0x0002
+#define S3_LEAVEASIS 0x0003
+#define S3_NOTNEW 0x0004
+#define S3_CURXORNEW 0x0005
+#define S3_NOT_CURXORNEW 0x0006
+#define S3_NEW 0x0007
+#define S3_NOTCURORNOTNEW 0x0008
+#define S3_CURORNOTNEW 0x0009
+#define S3_NOTCURORNEW 0x000a
+#define S3_CURORNEW 0x000b
+#define S3_CURANDNEW 0x000c
+#define S3_NOTCURANDNEW 0x000d
+#define S3_CURANDNOTNEW 0x000e
+#define S3_NOTCURANDNOTNEW 0x000f
+
+#define S3_CRTC_ADR 0x03d4
+#define S3_CRTC_DATA 0x03d5
+
+#define S3_REG_LOCK2 0x39
+#define S3_HGC_MODE 0x45
+
+#define S3_HWGC_ORGX_H 0x46
+#define S3_HWGC_ORGX_L 0x47
+#define S3_HWGC_ORGY_H 0x48
+#define S3_HWGC_ORGY_L 0x49
+#define S3_HWGC_DX 0x4e
+#define S3_HWGC_DY 0x4f
+
+
+#define S3_LAW_CTL 0x58
diff --git a/drivers/video/tgafb.c b/drivers/video/tgafb.c
new file mode 100644
index 000000000..ac3746109
--- /dev/null
+++ b/drivers/video/tgafb.c
@@ -0,0 +1,938 @@
+/*
+ * linux/drivers/video/tgafb.c -- DEC 21030 TGA frame buffer device
+ *
+ * Copyright (C) 1997 Geert Uytterhoeven
+ *
+ * This driver is partly based on the original TGA console driver
+ *
+ * Copyright (C) 1995 Jay Estabrook
+ *
+ * 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.
+ */
+
+
+/* KNOWN PROBLEMS/TO DO ===================================================== *
+ *
+ * - How to set a single color register?
+ *
+ * - We don't have support for CFB32 yet (fbcon-cfb32.c)
+ *
+ * - Hardware cursor (useful for other graphics boards too)
+ *
+ * KNOWN PROBLEMS/TO DO ==================================================== */
+
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/selection.h>
+#include <asm/io.h>
+
+
+/* TGA hardware description (minimal) */
+/*
+ * Offsets within Memory Space
+ */
+#define TGA_ROM_OFFSET 0x0000000
+#define TGA_REGS_OFFSET 0x0100000
+#define TGA_8PLANE_FB_OFFSET 0x0200000
+#define TGA_24PLANE_FB_OFFSET 0x0804000
+#define TGA_24PLUSZ_FB_OFFSET 0x1004000
+
+#define TGA_PLANEMASK_REG 0x0028
+#define TGA_MODE_REG 0x0030
+#define TGA_RASTEROP_REG 0x0034
+#define TGA_DEEP_REG 0x0050
+#define TGA_PIXELMASK_REG 0x005c
+#define TGA_CURSOR_BASE_REG 0x0060
+#define TGA_HORIZ_REG 0x0064
+#define TGA_VERT_REG 0x0068
+#define TGA_BASE_ADDR_REG 0x006c
+#define TGA_VALID_REG 0x0070
+#define TGA_CURSOR_XY_REG 0x0074
+#define TGA_INTR_STAT_REG 0x007c
+#define TGA_RAMDAC_SETUP_REG 0x00c0
+#define TGA_BLOCK_COLOR0_REG 0x0140
+#define TGA_BLOCK_COLOR1_REG 0x0144
+#define TGA_CLOCK_REG 0x01e8
+#define TGA_RAMDAC_REG 0x01f0
+#define TGA_CMD_STAT_REG 0x01f8
+
+/*
+ * useful defines for managing the BT485 on the 8-plane TGA
+ */
+#define BT485_READ_BIT 0x01
+#define BT485_WRITE_BIT 0x00
+
+#define BT485_ADDR_PAL_WRITE 0x00
+#define BT485_DATA_PAL 0x02
+#define BT485_PIXEL_MASK 0x04
+#define BT485_ADDR_PAL_READ 0x06
+#define BT485_ADDR_CUR_WRITE 0x08
+#define BT485_DATA_CUR 0x0a
+#define BT485_CMD_0 0x0c
+#define BT485_ADDR_CUR_READ 0x0e
+#define BT485_CMD_1 0x10
+#define BT485_CMD_2 0x12
+#define BT485_STATUS 0x14
+#define BT485_CMD_3 0x14
+#define BT485_CUR_RAM 0x16
+#define BT485_CUR_LOW_X 0x18
+#define BT485_CUR_HIGH_X 0x1a
+#define BT485_CUR_LOW_Y 0x1c
+#define BT485_CUR_HIGH_Y 0x1e
+
+/*
+ * useful defines for managing the BT463 on the 24-plane TGAs
+ */
+#define BT463_ADDR_LO 0x0
+#define BT463_ADDR_HI 0x1
+#define BT463_REG_ACC 0x2
+#define BT463_PALETTE 0x3
+
+#define BT463_CUR_CLR_0 0x0100
+#define BT463_CUR_CLR_1 0x0101
+
+#define BT463_CMD_REG_0 0x0201
+#define BT463_CMD_REG_1 0x0202
+#define BT463_CMD_REG_2 0x0203
+
+#define BT463_READ_MASK_0 0x0205
+#define BT463_READ_MASK_1 0x0206
+#define BT463_READ_MASK_2 0x0207
+#define BT463_READ_MASK_3 0x0208
+
+#define BT463_BLINK_MASK_0 0x0209
+#define BT463_BLINK_MASK_1 0x020a
+#define BT463_BLINK_MASK_2 0x020b
+#define BT463_BLINK_MASK_3 0x020c
+
+#define BT463_WINDOW_TYPE_BASE 0x0300
+
+
+int tga_type;
+unsigned int tga_mem_base;
+unsigned long tga_fb_base;
+unsigned long tga_regs_base;
+
+static unsigned int fb_offset_presets[4] __initdata = {
+ TGA_8PLANE_FB_OFFSET,
+ TGA_24PLANE_FB_OFFSET,
+ 0xffffffff,
+ TGA_24PLUSZ_FB_OFFSET
+};
+
+static unsigned int deep_presets[4] __initdata = {
+ 0x00014000,
+ 0x0001440d,
+ 0xffffffff,
+ 0x0001441d
+};
+
+static unsigned int rasterop_presets[4] __initdata = {
+ 0x00000003,
+ 0x00000303,
+ 0xffffffff,
+ 0x00000303
+};
+
+static unsigned int mode_presets[4] __initdata = {
+ 0x00002000,
+ 0x00002300,
+ 0xffffffff,
+ 0x00002300
+};
+
+static unsigned int base_addr_presets[4] __initdata = {
+ 0x00000000,
+ 0x00000001,
+ 0xffffffff,
+ 0x00000001
+};
+
+#define TGA_WRITE_REG(v,r) \
+ { writel((v), tga_regs_base+(r)); mb(); }
+
+#define TGA_READ_REG(r) readl(tga_regs_base+(r))
+
+#define BT485_WRITE(v,r) \
+ TGA_WRITE_REG((r),TGA_RAMDAC_SETUP_REG); \
+ TGA_WRITE_REG(((v)&0xff)|((r)<<8),TGA_RAMDAC_REG);
+
+#define BT463_LOAD_ADDR(a) \
+ TGA_WRITE_REG(BT463_ADDR_LO<<2, TGA_RAMDAC_SETUP_REG); \
+ TGA_WRITE_REG((BT463_ADDR_LO<<10)|((a)&0xff), TGA_RAMDAC_REG); \
+ TGA_WRITE_REG(BT463_ADDR_HI<<2, TGA_RAMDAC_SETUP_REG); \
+ TGA_WRITE_REG((BT463_ADDR_HI<<10)|(((a)>>8)&0xff), TGA_RAMDAC_REG);
+
+#define BT463_WRITE(m,a,v) \
+ BT463_LOAD_ADDR((a)); \
+ TGA_WRITE_REG(((m)<<2),TGA_RAMDAC_SETUP_REG); \
+ TGA_WRITE_REG(((m)<<10)|((v)&0xff),TGA_RAMDAC_REG);
+
+
+unsigned char PLLbits[7] __initdata = { 0x80, 0x04, 0x00, 0x24, 0x44, 0x80, 0xb8 };
+
+const unsigned long bt485_cursor_source[64] __initdata = {
+ 0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,
+ 0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,
+ 0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,
+ 0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+
+const unsigned int bt463_cursor_source[256] __initdata = {
+ 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
+ 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
+ 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
+ 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
+ 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
+ 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
+ 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
+ 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
+ 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
+ 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
+ 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
+ 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
+ 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
+ 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
+ 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
+ 0xffff0000, 0x00000000, 0x00000000, 0x00000000,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+
+
+#define arraysize(x) (sizeof(x)/sizeof(*(x)))
+
+static int currcon = 0;
+static struct display disp;
+static struct fb_info fb_info;
+static struct { u_char red, green, blue, pad; } palette[256];
+static char tgafb_name[16] = "DEC TGA ";
+
+static struct fb_fix_screeninfo fb_fix;
+static struct fb_var_screeninfo fb_var = { 0, };
+
+
+ /*
+ * Interface used by the world
+ */
+
+void tgafb_video_setup(char *options, int *ints);
+
+static int tgafb_open(int fbidx);
+static int tgafb_release(int fbidx);
+static int tgafb_get_fix(struct fb_fix_screeninfo *fix, int con);
+static int tgafb_get_var(struct fb_var_screeninfo *var, int con);
+static int tgafb_set_var(struct fb_var_screeninfo *var, int con);
+static int tgafb_pan_display(struct fb_var_screeninfo *var, int con);
+static int tgafb_get_cmap(struct fb_cmap *cmap, int kspc, int con);
+static int tgafb_set_cmap(struct fb_cmap *cmap, int kspc, int con);
+static int tgafb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+ u_long arg, int con);
+
+
+ /*
+ * Interface to the low level console driver
+ */
+
+unsigned long tgafb_init(unsigned long mem_start);
+static int tgafbcon_switch(int con);
+static int tgafbcon_updatevar(int con);
+static void tgafbcon_blank(int blank);
+static int tgafbcon_setcmap(struct fb_cmap *cmap, int con);
+
+
+ /*
+ * Internal routines
+ */
+
+static int tgafb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+ u_int *transp);
+static int tgafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp);
+#if 1
+static void tga_update_palette(void);
+#endif
+static void do_install_cmap(int con);
+
+
+static struct fb_ops tgafb_ops = {
+ tgafb_open, tgafb_release, tgafb_get_fix, tgafb_get_var, tgafb_set_var,
+ tgafb_get_cmap, tgafb_set_cmap, tgafb_pan_display, tgafb_ioctl
+};
+
+
+ /*
+ * Open/Release the frame buffer device
+ */
+
+static int tgafb_open(int fbidx)
+{
+ /*
+ * Nothing, only a usage count for the moment
+ */
+
+ MOD_INC_USE_COUNT;
+ return(0);
+}
+
+static int tgafb_release(int fbidx)
+{
+ MOD_DEC_USE_COUNT;
+ return(0);
+}
+
+
+ /*
+ * Get the Fixed Part of the Display
+ */
+
+static int tgafb_get_fix(struct fb_fix_screeninfo *fix, int con)
+{
+ memcpy(fix, &fb_fix, sizeof(fb_fix));
+ return 0;
+}
+
+
+ /*
+ * Get the User Defined Part of the Display
+ */
+
+static int tgafb_get_var(struct fb_var_screeninfo *var, int con)
+{
+ memcpy(var, &fb_var, sizeof(fb_var));
+ return 0;
+}
+
+
+ /*
+ * Set the User Defined Part of the Display
+ */
+
+static int tgafb_set_var(struct fb_var_screeninfo *var, int con)
+{
+ struct display *display;
+ int oldbpp = -1, err;
+
+ if (con >= 0)
+ display = &fb_display[con];
+ else
+ display = &disp; /* used during initialization */
+
+ if (var->xres > fb_var.xres || var->yres > fb_var.yres ||
+ var->xres_virtual > fb_var.xres_virtual ||
+ var->yres_virtual > fb_var.yres_virtual ||
+ var->bits_per_pixel > fb_var.bits_per_pixel ||
+ var->nonstd ||
+ (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
+ return -EINVAL;
+ memcpy(var, &fb_var, sizeof(fb_var));
+
+ if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+ oldbpp = display->var.bits_per_pixel;
+ display->var = *var;
+ }
+ if (oldbpp != var->bits_per_pixel) {
+ if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
+ return err;
+ do_install_cmap(con);
+ }
+ return 0;
+}
+
+
+ /*
+ * Pan or Wrap the Display
+ *
+ * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+ */
+
+static int tgafb_pan_display(struct fb_var_screeninfo *var, int con)
+{
+ if (var->xoffset || var->yoffset)
+ return -EINVAL;
+ else
+ return 0;
+}
+
+ /*
+ * Get the Colormap
+ */
+
+static int tgafb_get_cmap(struct fb_cmap *cmap, int kspc, int con)
+{
+ if (con == currcon) /* current console? */
+ return fb_get_cmap(cmap, &fb_display[con].var, kspc, tgafb_getcolreg);
+ else if (fb_display[con].cmap.len) /* non default colormap? */
+ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+ else
+ fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+ cmap, kspc ? 0 : 2);
+ return 0;
+}
+
+ /*
+ * Set the Colormap
+ */
+
+static int tgafb_set_cmap(struct fb_cmap *cmap, int kspc, int con)
+{
+ int err;
+
+ if (!fb_display[con].cmap.len) { /* no colormap allocated? */
+ if ((err = fb_alloc_cmap(&fb_display[con].cmap,
+ 1<<fb_display[con].var.bits_per_pixel, 0)))
+ return err;
+ }
+ if (con == currcon) { /* current console? */
+ err = fb_set_cmap(cmap, &fb_display[con].var, kspc, tgafb_setcolreg);
+#if 1
+ tga_update_palette();
+#endif
+ return err;
+ } else
+ fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+ return 0;
+}
+
+
+static int tgafb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+ u_long arg, int con)
+{
+ return -EINVAL;
+}
+
+
+ /*
+ * Initialisation
+ */
+
+__initfunc(unsigned long tgafb_init(unsigned long mem_start))
+{
+ unsigned char pci_bus, pci_devfn;
+ int status;
+ int i, j, temp, err;
+ unsigned char *cbp;
+
+ status = pcibios_find_device (PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA,
+ 0, &pci_bus, &pci_devfn);
+ if (status == PCIBIOS_DEVICE_NOT_FOUND)
+ return mem_start;
+
+ /*
+ * read BASE_REG_0 for memory address
+ */
+ pcibios_read_config_dword(pci_bus, pci_devfn, PCI_BASE_ADDRESS_0,
+ &tga_mem_base);
+ tga_mem_base &= ~15;
+#ifdef DEBUG
+ printk("tgafb_init: mem_base 0x%x\n", tga_mem_base);
+#endif /* DEBUG */
+
+ tga_type = (readl((unsigned long)tga_mem_base) >> 12) & 0x0f;
+ switch (tga_type) {
+ case 0:
+ strcat(tgafb_name, "8plane");
+ break;
+ case 1:
+ strcat(tgafb_name, "24plane");
+ break;
+ case 3:
+ strcat(tgafb_name, "24plusZ");
+ break;
+ default:
+ printk("TGA type (0x%x) unrecognized!\n", tga_type);
+ return mem_start;
+ }
+ strcpy(fb_fix.id, tgafb_name);
+
+ tga_regs_base = ((unsigned long)tga_mem_base + TGA_REGS_OFFSET);
+ tga_fb_base = ((unsigned long)tga_mem_base + fb_offset_presets[tga_type]);
+
+ /* first, disable video timing */
+ TGA_WRITE_REG(0x03, TGA_VALID_REG); /* SCANNING and BLANK */
+
+ /* write the DEEP register */
+ while (TGA_READ_REG(TGA_CMD_STAT_REG) & 1) /* wait for not busy */
+ continue;
+
+ mb();
+ TGA_WRITE_REG(deep_presets[tga_type], TGA_DEEP_REG);
+ while (TGA_READ_REG(TGA_CMD_STAT_REG) & 1) /* wait for not busy */
+ continue;
+ mb();
+
+ /* write some more registers */
+ TGA_WRITE_REG(rasterop_presets[tga_type], TGA_RASTEROP_REG);
+ TGA_WRITE_REG(mode_presets[tga_type], TGA_MODE_REG);
+ TGA_WRITE_REG(base_addr_presets[tga_type], TGA_BASE_ADDR_REG);
+
+ /* write the PLL for 640x480 @ 60Hz */
+ for (i = 0; i <= 6; i++) {
+ for (j = 0; j <= 7; j++) {
+ temp = (PLLbits[i] >> (7-j)) & 1;
+ if (i == 6 && j == 7)
+ temp |= 2;
+ TGA_WRITE_REG(temp, TGA_CLOCK_REG);
+ }
+ }
+
+ /* write some more registers */
+ TGA_WRITE_REG(0xffffffff, TGA_PLANEMASK_REG);
+ TGA_WRITE_REG(0xffffffff, TGA_PIXELMASK_REG);
+ TGA_WRITE_REG(0x12345678, TGA_BLOCK_COLOR0_REG);
+ TGA_WRITE_REG(0x12345678, TGA_BLOCK_COLOR1_REG);
+
+ /* init video timing regs for 640x480 @ 60 Hz */
+ TGA_WRITE_REG(0x018608a0, TGA_HORIZ_REG);
+ TGA_WRITE_REG(0x084251e0, TGA_VERT_REG);
+
+ if (tga_type == 0) { /* 8-plane */
+
+ fb_var.bits_per_pixel = 8;
+ fb_fix.visual = FB_VISUAL_PSEUDOCOLOR;
+
+ /* init BT485 RAMDAC registers */
+ BT485_WRITE(0xa2, BT485_CMD_0);
+ BT485_WRITE(0x01, BT485_ADDR_PAL_WRITE);
+ BT485_WRITE(0x14, BT485_CMD_3); /* cursor 64x64 */
+ BT485_WRITE(0x40, BT485_CMD_1);
+ BT485_WRITE(0x22, BT485_CMD_2); /* WIN cursor type */
+ BT485_WRITE(0xff, BT485_PIXEL_MASK);
+
+ /* fill palette registers */
+ BT485_WRITE(0x00, BT485_ADDR_PAL_WRITE);
+ TGA_WRITE_REG(BT485_DATA_PAL, TGA_RAMDAC_SETUP_REG);
+
+ for (i = 0; i < 16; i++) {
+ j = color_table[i];
+ TGA_WRITE_REG(default_red[j]|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(default_grn[j]|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(default_blu[j]|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG);
+ }
+ for (i = 0; i < 240*3; i += 4) {
+ TGA_WRITE_REG(0x55|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(0x00|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(0x00|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(0x00|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG);
+ }
+
+ /* initialize RAMDAC cursor colors */
+ BT485_WRITE(0, BT485_ADDR_CUR_WRITE);
+
+ BT485_WRITE(0xaa, BT485_DATA_CUR); /* overscan WHITE */
+ BT485_WRITE(0xaa, BT485_DATA_CUR); /* overscan WHITE */
+ BT485_WRITE(0xaa, BT485_DATA_CUR); /* overscan WHITE */
+
+ BT485_WRITE(0x00, BT485_DATA_CUR); /* color 1 BLACK */
+ BT485_WRITE(0x00, BT485_DATA_CUR); /* color 1 BLACK */
+ BT485_WRITE(0x00, BT485_DATA_CUR); /* color 1 BLACK */
+
+ BT485_WRITE(0x00, BT485_DATA_CUR); /* color 2 BLACK */
+ BT485_WRITE(0x00, BT485_DATA_CUR); /* color 2 BLACK */
+ BT485_WRITE(0x00, BT485_DATA_CUR); /* color 2 BLACK */
+
+ BT485_WRITE(0x00, BT485_DATA_CUR); /* color 3 BLACK */
+ BT485_WRITE(0x00, BT485_DATA_CUR); /* color 3 BLACK */
+ BT485_WRITE(0x00, BT485_DATA_CUR); /* color 3 BLACK */
+
+ /* initialize RAMDAC cursor RAM */
+ BT485_WRITE(0x00, BT485_ADDR_PAL_WRITE);
+ cbp = (unsigned char *)bt485_cursor_source;
+ for (i = 0; i < 512; i++) {
+ BT485_WRITE(*cbp++, BT485_CUR_RAM);
+ }
+ for (i = 0; i < 512; i++) {
+ BT485_WRITE(0xff, BT485_CUR_RAM);
+ }
+
+ } else { /* 24-plane or 24plusZ */
+
+ fb_var.bits_per_pixel = 32;
+ fb_fix.visual = FB_VISUAL_TRUECOLOR;
+
+ TGA_WRITE_REG(0x01, TGA_VALID_REG); /* SCANNING */
+
+ /*
+ * init some registers
+ */
+ BT463_WRITE(BT463_REG_ACC, BT463_CMD_REG_0, 0x40);
+ BT463_WRITE(BT463_REG_ACC, BT463_CMD_REG_1, 0x08);
+ BT463_WRITE(BT463_REG_ACC, BT463_CMD_REG_2, 0x40);
+
+ BT463_WRITE(BT463_REG_ACC, BT463_READ_MASK_0, 0xff);
+ BT463_WRITE(BT463_REG_ACC, BT463_READ_MASK_1, 0xff);
+ BT463_WRITE(BT463_REG_ACC, BT463_READ_MASK_2, 0xff);
+ BT463_WRITE(BT463_REG_ACC, BT463_READ_MASK_3, 0x0f);
+
+ BT463_WRITE(BT463_REG_ACC, BT463_BLINK_MASK_0, 0x00);
+ BT463_WRITE(BT463_REG_ACC, BT463_BLINK_MASK_1, 0x00);
+ BT463_WRITE(BT463_REG_ACC, BT463_BLINK_MASK_2, 0x00);
+ BT463_WRITE(BT463_REG_ACC, BT463_BLINK_MASK_3, 0x00);
+
+ /*
+ * fill the palette
+ */
+ BT463_LOAD_ADDR(0x0000);
+ TGA_WRITE_REG((BT463_PALETTE<<2), TGA_RAMDAC_REG);
+
+ for (i = 0; i < 16; i++) {
+ j = color_table[i];
+ TGA_WRITE_REG(default_red[j]|(BT463_PALETTE<<10), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(default_grn[j]|(BT463_PALETTE<<10), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(default_blu[j]|(BT463_PALETTE<<10), TGA_RAMDAC_REG);
+ }
+ for (i = 0; i < 512*3; i += 4) {
+ TGA_WRITE_REG(0x55|(BT463_PALETTE<<10), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(0x00|(BT463_PALETTE<<10), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(0x00|(BT463_PALETTE<<10), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(0x00|(BT463_PALETTE<<10), TGA_RAMDAC_REG);
+ }
+
+ /*
+ * fill window type table after start of vertical retrace
+ */
+ while (!(TGA_READ_REG(TGA_INTR_STAT_REG) & 0x01))
+ continue;
+ TGA_WRITE_REG(0x01, TGA_INTR_STAT_REG);
+ mb();
+ while (!(TGA_READ_REG(TGA_INTR_STAT_REG) & 0x01))
+ continue;
+ TGA_WRITE_REG(0x01, TGA_INTR_STAT_REG);
+
+ BT463_LOAD_ADDR(BT463_WINDOW_TYPE_BASE);
+ TGA_WRITE_REG((BT463_REG_ACC<<2), TGA_RAMDAC_SETUP_REG);
+
+ for (i = 0; i < 16; i++) {
+ TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(0x01|(BT463_REG_ACC<<10), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(0x80|(BT463_REG_ACC<<10), TGA_RAMDAC_REG);
+ }
+
+ /*
+ * init cursor colors
+ */
+ BT463_LOAD_ADDR(BT463_CUR_CLR_0);
+
+ TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); /* background */
+ TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); /* background */
+ TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); /* background */
+
+ TGA_WRITE_REG(0xff|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); /* foreground */
+ TGA_WRITE_REG(0xff|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); /* foreground */
+ TGA_WRITE_REG(0xff|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); /* foreground */
+
+ TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG);
+
+ TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG);
+
+ /*
+ * finally, init the cursor shape
+ */
+ temp = tga_fb_base - 1024; /* this assumes video starts at base
+ and base is beyond memory start*/
+
+ for (i = 0; i < 256; i++) {
+ writel(bt463_cursor_source[i], temp + i*4);
+ }
+ TGA_WRITE_REG(temp & 0x000fffff, TGA_CURSOR_BASE_REG);
+ }
+
+ /* finally, enable video scan & cursor
+ (and pray for the monitor... :-) */
+ TGA_WRITE_REG(0x05, TGA_VALID_REG); /* SCANNING and CURSOR */
+
+ fb_var.xres = fb_var.xres_virtual = 640;
+ fb_var.yres = fb_var.yres_virtual = 480;
+ fb_fix.line_length = 80*fb_var.bits_per_pixel;
+ fb_fix.smem_start = (char *)tga_fb_base;
+ fb_fix.smem_len = fb_fix.line_length*fb_var.yres;
+ fb_fix.type = FB_TYPE_PACKED_PIXELS;
+ fb_fix.type_aux = 0;
+ fb_fix.mmio_start = (unsigned char *)tga_regs_base;
+ fb_fix.mmio_len = 0x1000; /* Is this sufficient? */
+
+ fb_var.xoffset = fb_var.yoffset = 0;
+ fb_var.grayscale = 0;
+ fb_var.red.offset = fb_var.green.offset = fb_var.blue.offset = 0;
+ fb_var.red.length = fb_var.green.length = fb_var.blue.length = 8;
+ fb_var.red.msb_right = fb_var.green.msb_right = fb_var.blue.msb_right = 0;
+ fb_var.transp.offset = fb_var.transp.length = fb_var.transp.msb_right = 0;
+ fb_var.nonstd = 0;
+ fb_var.activate = 0;
+ fb_var.height = fb_var.width = -1;
+ fb_var.accel = FB_ACCEL_TGA;
+ fb_var.pixclock = 39722;
+ fb_var.left_margin = 40;
+ fb_var.right_margin = 24;
+ fb_var.upper_margin = 32;
+ fb_var.lower_margin = 11;
+ fb_var.hsync_len = 96;
+ fb_var.vsync_len = 2;
+ fb_var.sync = 0;
+ fb_var.vmode = FB_VMODE_NONINTERLACED;
+
+ disp.var = fb_var;
+ disp.cmap.start = 0;
+ disp.cmap.len = 0;
+ disp.cmap.red = disp.cmap.green = disp.cmap.blue = disp.cmap.transp = NULL;
+ disp.screen_base = fb_fix.smem_start;
+ disp.visual = fb_fix.visual;
+ disp.type = fb_fix.type;
+ disp.type_aux = fb_fix.type_aux;
+ disp.ypanstep = 0;
+ disp.ywrapstep = 0;
+ disp.line_length = fb_fix.line_length;
+ disp.can_soft_blank = 1;
+ disp.inverse = 0;
+
+ strcpy(fb_info.modename, tgafb_name);
+ fb_info.node = -1;
+ fb_info.fbops = &tgafb_ops;
+ fb_info.fbvar_num = 1;
+ fb_info.fbvar = &fb_var;
+ fb_info.disp = &disp;
+ fb_info.fontname[0] = '\0';
+ fb_info.changevar = NULL;
+ fb_info.switch_con = &tgafbcon_switch;
+ fb_info.updatevar = &tgafbcon_updatevar;
+ fb_info.blank = &tgafbcon_blank;
+ fb_info.setcmap = &tgafbcon_setcmap;
+
+ err = register_framebuffer(&fb_info);
+ if (err < 0)
+ return mem_start;
+
+ tgafb_set_var(&fb_var, -1);
+
+ printk("%s frame buffer device\n", tgafb_name);
+ return mem_start;
+}
+
+
+static int tgafbcon_switch(int con)
+{
+ /* Do we have to save the colormap? */
+ if (fb_display[currcon].cmap.len)
+ fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1,
+ tgafb_getcolreg);
+
+ currcon = con;
+ /* Install new colormap */
+ do_install_cmap(con);
+ return 0;
+}
+
+ /*
+ * Update the `var' structure (called by fbcon.c)
+ */
+
+static int tgafbcon_updatevar(int con)
+{
+ /* Nothing */
+ return 0;
+}
+
+ /*
+ * Blank the display.
+ */
+
+static void tgafbcon_blank(int blank)
+{
+ /* Nothing */
+}
+
+ /*
+ * Set the colormap
+ */
+
+static int tgafbcon_setcmap(struct fb_cmap *cmap, int con)
+{
+ return(tgafb_set_cmap(cmap, 1, con));
+}
+
+
+ /*
+ * Read a single color register and split it into
+ * colors/transparent. Return != 0 for invalid regno.
+ */
+
+static int tgafb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+ u_int *transp)
+{
+ if (regno > 255)
+ return 1;
+ *red = palette[regno].red;
+ *green = palette[regno].green;
+ *blue = palette[regno].blue;
+ return 0;
+}
+
+
+ /*
+ * Set a single color register. The values supplied are already
+ * rounded down to the hardware's capabilities (according to the
+ * entries in the var structure). Return != 0 for invalid regno.
+ */
+
+static int tgafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp)
+{
+ if (regno > 255)
+ return 1;
+ palette[regno].red = red;
+ palette[regno].green = green;
+ palette[regno].blue = blue;
+
+ /* How to set a single color register?? */
+
+ return 0;
+}
+
+
+#if 1
+ /*
+ * FIXME: since I don't know how to set a single arbitrary color register,
+ * all color palette registers have to be updated
+ */
+
+static void tga_update_palette(void)
+{
+ int i;
+
+ if (tga_type == 0) { /* 8-plane */
+ BT485_WRITE(0x00, BT485_ADDR_PAL_WRITE);
+ TGA_WRITE_REG(BT485_DATA_PAL, TGA_RAMDAC_SETUP_REG);
+ for (i = 0; i < 256; i++) {
+ TGA_WRITE_REG(palette[i].red|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
+ TGA_WRITE_REG(palette[i].green|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
+ TGA_WRITE_REG(palette[i].blue|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
+ }
+ } else {
+ BT463_LOAD_ADDR(0x0000);
+ TGA_WRITE_REG((BT463_PALETTE<<2), TGA_RAMDAC_REG);
+
+ for (i = 0; i < 256; i++) {
+ TGA_WRITE_REG(palette[i].red|(BT463_PALETTE<<10), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(palette[i].green|(BT463_PALETTE<<10), TGA_RAMDAC_REG);
+ TGA_WRITE_REG(palette[i].blue|(BT463_PALETTE<<10), TGA_RAMDAC_REG);
+ }
+ }
+}
+#endif
+
+static void do_install_cmap(int con)
+{
+ if (con != currcon)
+ return;
+ if (fb_display[con].cmap.len)
+ fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
+ tgafb_setcolreg);
+ else
+ fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+ &fb_display[con].var, 1, tgafb_setcolreg);
+#if 1
+ tga_update_palette();
+#endif
+}
+
+
+#if 0 /* No cursor stuff yet */
+
+/*
+ * Hide the cursor from view, during blanking, usually...
+ */
+void
+hide_cursor(void)
+{
+ unsigned long flags;
+ save_flags(flags); cli();
+
+ if (tga_type == 0) {
+ BT485_WRITE(0x20, BT485_CMD_2);
+ } else {
+ TGA_WRITE_REG(0x03, TGA_VALID_REG); /* SCANNING and BLANK */
+ }
+
+ restore_flags(flags);
+}
+
+void
+set_cursor(int currcons)
+{
+ unsigned int idx, xt, yt, row, col;
+ unsigned long flags;
+
+ if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
+ return;
+
+ if (__real_origin != __origin)
+ __set_origin(__real_origin);
+
+ save_flags(flags); cli();
+
+ if (deccm) {
+ idx = (pos - video_mem_base) >> 1;
+ col = idx % 80;
+ row = (idx - col) / 80;
+
+ if (tga_type == 0) { /* 8-plane */
+
+ xt = col * TGA_F_WIDTH + 64;
+ yt = row * TGA_F_HEIGHT_PADDED + 64;
+
+ /* make sure it's enabled */
+ BT485_WRITE(0x22, BT485_CMD_2); /* WIN cursor type */
+
+ BT485_WRITE(xt, BT485_CUR_LOW_X);
+ BT485_WRITE((xt >> 8), BT485_CUR_HIGH_X);
+ BT485_WRITE(yt, BT485_CUR_LOW_Y);
+ BT485_WRITE((yt >> 8), BT485_CUR_HIGH_Y);
+
+ } else {
+
+ xt = col * TGA_F_WIDTH + 144;
+ yt = row * TGA_F_HEIGHT_PADDED + 35;
+
+ TGA_WRITE_REG(0x05, TGA_VALID_REG); /* SCANNING and CURSOR */
+ TGA_WRITE_REG(xt | (yt << 12), TGA_CURSOR_XY_REG);
+ }
+
+ } else
+ hide_cursor();
+ restore_flags(flags);
+}
+
+#endif
diff --git a/drivers/video/txtcon.c b/drivers/video/txtcon.c
new file mode 100644
index 000000000..cdecc08b0
--- /dev/null
+++ b/drivers/video/txtcon.c
@@ -0,0 +1,153 @@
+/*
+ * linux/drivers/video/txtcon.c -- Low level text mode based console driver
+ *
+ * Copyright (C) 1995 Geert Uytterhoeven
+ *
+ *
+ * This file is currently only a skeleton, since all Amigas and Ataris have
+ * bitmapped graphics.
+ *
+ *
+ * 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/types.h>
+#include <linux/console.h>
+
+
+ /*
+ * Interface used by the world
+ */
+
+static int txtcon_startup(u_long *kmem_start, const char **display_desc);
+static void txtcon_init(struct vc_data *conp);
+static int txtcon_deinit(struct vc_data *conp);
+static int txtcon_clear(struct vc_data *conp, int sy, int sx, int height,
+ int width);
+static int txtcon_putc(struct vc_data *conp, int c, int y, int x);
+static int txtcon_putcs(struct vc_data *conp, const char *s, int count, int y,
+ int x);
+static int txtcon_cursor(struct vc_data *conp, int mode);
+static int txtcon_scroll(struct vc_data *conp, int t, int b, int dir, int count);
+static int txtcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
+ int height, int width);
+static int txtcon_switch(struct vc_data *conp);
+static int txtcon_blank(int blank);
+static int txtcon_get_font(struct vc_data *conp, int *w, int *h, char *data);
+static int txtcon_set_font(struct vc_data *conp, int w, int h, char *data);
+static int txtcon_set_palette(struct vc_data *conp, unsigned char *table);
+static int txtcon_scrolldelta(int lines);
+
+
+static int txtcon_startup(u_long *kmem_start, const char **display_desc)
+{
+ return -ENODEV;
+}
+
+
+static void txtcon_init(struct vc_data *conp)
+{
+}
+
+
+static int txtcon_deinit(struct vc_data *conp)
+{
+ return 0;
+}
+
+
+/* ====================================================================== */
+
+/* txtcon_XXX routines - interface used by the world */
+
+
+static int txtcon_clear(struct vc_data *conp, int sy, int sx, int height,
+ int width)
+{
+ return -ENOSYS;
+}
+
+
+static int txtcon_putc(struct vc_data *conp, int c, int y, int x)
+{
+ return -ENOSYS;
+}
+
+
+static int txtcon_putcs(struct vc_data *conp, const char *s, int count, int y,
+ int x)
+{
+ return -ENOSYS;
+}
+
+
+static int txtcon_cursor(struct vc_data *conp, int mode)
+{
+ return -ENOSYS;
+}
+
+
+static int txtcon_scroll(struct vc_data *conp, int t, int b, int dir, int count)
+{
+ return -ENOSYS;
+}
+
+
+static int txtcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
+ int height, int width)
+{
+ return -ENOSYS;
+}
+
+
+static int txtcon_switch(struct vc_data *conp)
+{
+ return -ENOSYS;
+}
+
+
+static int txtcon_blank(int blank)
+{
+ return -ENOSYS;
+}
+
+
+static int txtcon_get_font(struct vc_data *conp, int *w, int *h, char *data)
+{
+ return -ENOSYS;
+}
+
+
+static int txtcon_set_font(struct vc_data *conp, int w, int h, char *data)
+{
+ return -ENOSYS;
+}
+
+
+static int txtcon_set_palette(struct vc_data *conp, unsigned char *table)
+{
+ return -ENOSYS;
+}
+
+
+static int txtcon_scrolldelta(int lines)
+{
+ return -ENOSYS;
+}
+
+
+/* ====================================================================== */
+
+ /*
+ * The console `switch' structure for the text mode based console
+ */
+
+struct consw txt_con = {
+ txtcon_startup, txtcon_init, txtcon_deinit, txtcon_clear, txtcon_putc,
+ txtcon_putcs, txtcon_cursor, txtcon_scroll, txtcon_bmove, txtcon_switch,
+ txtcon_blank, txtcon_get_font, txtcon_set_font, txtcon_set_palette,
+ txtcon_scrolldelta
+};
diff --git a/drivers/video/vfb.c b/drivers/video/vfb.c
new file mode 100644
index 000000000..63e7f7ca2
--- /dev/null
+++ b/drivers/video/vfb.c
@@ -0,0 +1,601 @@
+/*
+ * linux/drivers/video/vfb.c -- Virtual frame buffer device
+ *
+ * Copyright (C) 1997 Geert Uytterhoeven
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <asm/uaccess.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+
+
+#define arraysize(x) (sizeof(x)/sizeof(*(x)))
+
+
+ /*
+ * RAM we reserve for the frame buffer. This defines the maximum screen
+ * size
+ *
+ * The default can be overridden if the driver is compiled as a module
+ */
+
+#define VIDEOMEMSIZE (1*1024*1024) /* 1 MB */
+
+static u_long videomemory, videomemorysize = VIDEOMEMSIZE;
+MODULE_PARM(videomemorysize, "l");
+static int currcon = 0;
+static struct display disp;
+static struct fb_info fb_info;
+static struct { u_char red, green, blue, pad; } palette[256];
+static char virtual_fb_name[16] = "Virtual FB";
+
+static struct fb_var_screeninfo virtual_fb_predefined[] = {
+
+ /*
+ * Autodetect (Default) Video Mode
+ */
+
+ {
+ /* 640x480, 8 bpp */
+ 640, 480, 640, 480, 0, 0, 8, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, FB_ACCEL_NONE, 20000, 64, 64, 32, 32, 64, 2,
+ 0, FB_VMODE_NONINTERLACED
+ },
+
+ /*
+ * User Defined Video Modes (8)
+ */
+
+ { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }
+};
+
+#define NUM_USER_MODES (8)
+#define NUM_TOTAL_MODES arraysize(virtual_fb_predefined)
+#define NUM_PREDEF_MODES (1)
+
+
+ /*
+ * Interface used by the world
+ */
+
+void vfb_video_setup(char *options, int *ints);
+
+static int virtual_fb_open(int fbidx);
+static int virtual_fb_release(int fbidx);
+static int virtual_fb_get_fix(struct fb_fix_screeninfo *fix, int con);
+static int virtual_fb_get_var(struct fb_var_screeninfo *var, int con);
+static int virtual_fb_set_var(struct fb_var_screeninfo *var, int con);
+static int virtual_fb_pan_display(struct fb_var_screeninfo *var, int con);
+static int virtual_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con);
+static int virtual_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con);
+static int virtual_fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+ u_long arg, int con);
+
+
+ /*
+ * Interface to the low level console driver
+ */
+
+unsigned long virtual_fb_init(unsigned long mem_start);
+static int vfbcon_switch(int con);
+static int vfbcon_updatevar(int con);
+static void vfbcon_blank(int blank);
+static int vfbcon_setcmap(struct fb_cmap *cmap, int con);
+
+
+ /*
+ * Internal routines
+ */
+
+static u_long get_line_length(int xres_virtual, int bpp);
+static void vfb_encode_fix(struct fb_fix_screeninfo *fix,
+ struct fb_var_screeninfo *var);
+static void set_color_bitfields(struct fb_var_screeninfo *var);
+static int vfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+ u_int *transp);
+static int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp);
+static void do_install_cmap(int con);
+
+
+static struct fb_ops virtual_fb_ops = {
+ virtual_fb_open, virtual_fb_release, virtual_fb_get_fix,
+ virtual_fb_get_var, virtual_fb_set_var, virtual_fb_get_cmap,
+ virtual_fb_set_cmap, virtual_fb_pan_display, virtual_fb_ioctl
+};
+
+
+ /*
+ * Open/Release the frame buffer device
+ */
+
+static int virtual_fb_open(int fbidx)
+{
+ /*
+ * Nothing, only a usage count for the moment
+ */
+
+ MOD_INC_USE_COUNT;
+ return(0);
+}
+
+static int virtual_fb_release(int fbidx)
+{
+ MOD_DEC_USE_COUNT;
+ return(0);
+}
+
+
+ /*
+ * Get the Fixed Part of the Display
+ */
+
+static int virtual_fb_get_fix(struct fb_fix_screeninfo *fix, int con)
+{
+ struct fb_var_screeninfo *var;
+
+ if (con == -1)
+ var = &virtual_fb_predefined[0];
+ else
+ var = &fb_display[con].var;
+ vfb_encode_fix(fix, var);
+ return 0;
+}
+
+
+ /*
+ * Get the User Defined Part of the Display
+ */
+
+static int virtual_fb_get_var(struct fb_var_screeninfo *var, int con)
+{
+ if (con == -1)
+ *var = virtual_fb_predefined[0];
+ else
+ *var = fb_display[con].var;
+ set_color_bitfields(var);
+ return 0;
+}
+
+
+ /*
+ * Set the User Defined Part of the Display
+ */
+
+static int virtual_fb_set_var(struct fb_var_screeninfo *var, int con)
+{
+ int err, activate = var->activate;
+ int oldxres, oldyres, oldvxres, oldvyres, oldbpp;
+ u_long line_length;
+
+ struct display *display;
+ if (con >= 0)
+ display = &fb_display[con];
+ else
+ display = &disp; /* used during initialization */
+
+ /*
+ * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
+ * as FB_VMODE_SMOOTH_XPAN is only used internally
+ */
+
+ if (var->vmode & FB_VMODE_CONUPDATE) {
+ var->vmode |= FB_VMODE_YWRAP;
+ var->xoffset = display->var.xoffset;
+ var->yoffset = display->var.yoffset;
+ }
+
+ /*
+ * Some very basic checks
+ */
+ if (!var->xres)
+ var->xres = 1;
+ if (!var->yres)
+ var->yres = 1;
+ if (var->xres > var->xres_virtual)
+ var->xres_virtual = var->xres;
+ if (var->yres > var->yres_virtual)
+ var->yres_virtual = var->yres;
+ if (var->bits_per_pixel <= 1)
+ var->bits_per_pixel = 1;
+ else if (var->bits_per_pixel <= 8)
+ var->bits_per_pixel = 8;
+ else if (var->bits_per_pixel <= 16)
+ var->bits_per_pixel = 16;
+#if 0
+ /* fbcon doesn't support this (yet) */
+ else if (var->bits_per_pixel <= 24)
+ var->bits_per_pixel = 24;
+ else if (var->bits_per_pixel <= 32)
+ var->bits_per_pixel = 32;
+#endif
+ else
+ return -EINVAL;
+
+ /*
+ * Memory limit
+ */
+ line_length = get_line_length(var->xres_virtual, var->bits_per_pixel);
+ if (line_length*var->yres_virtual > videomemorysize)
+ return -ENOMEM;
+
+ set_color_bitfields(var);
+
+ if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+ oldxres = display->var.xres;
+ oldyres = display->var.yres;
+ oldvxres = display->var.xres_virtual;
+ oldvyres = display->var.yres_virtual;
+ oldbpp = display->var.bits_per_pixel;
+ display->var = *var;
+ if (oldxres != var->xres || oldyres != var->yres ||
+ oldvxres != var->xres_virtual || oldvyres != var->yres_virtual ||
+ oldbpp != var->bits_per_pixel) {
+ struct fb_fix_screeninfo fix;
+
+ vfb_encode_fix(&fix, var);
+ display->screen_base = (u_char *)fix.smem_start;
+ display->visual = fix.visual;
+ display->type = fix.type;
+ display->type_aux = fix.type_aux;
+ display->ypanstep = fix.ypanstep;
+ display->ywrapstep = fix.ywrapstep;
+ display->line_length = fix.line_length;
+ display->can_soft_blank = 1;
+ display->inverse = 0;
+ if (fb_info.changevar)
+ (*fb_info.changevar)(con);
+ }
+ if (oldbpp != var->bits_per_pixel) {
+ if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
+ return err;
+ do_install_cmap(con);
+ }
+ }
+ return 0;
+}
+
+
+ /*
+ * Pan or Wrap the Display
+ *
+ * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+ */
+
+static int virtual_fb_pan_display(struct fb_var_screeninfo *var, int con)
+{
+ if (var->vmode & FB_VMODE_YWRAP) {
+ if (var->yoffset < 0 ||
+ var->yoffset >= fb_display[con].var.yres_virtual ||
+ var->xoffset)
+ return -EINVAL;
+ } else {
+ if (var->xoffset+fb_display[con].var.xres >
+ fb_display[con].var.xres_virtual ||
+ var->yoffset+fb_display[con].var.yres >
+ fb_display[con].var.yres_virtual)
+ return -EINVAL;
+ }
+ fb_display[con].var.xoffset = var->xoffset;
+ fb_display[con].var.yoffset = var->yoffset;
+ if (var->vmode & FB_VMODE_YWRAP)
+ fb_display[con].var.vmode |= FB_VMODE_YWRAP;
+ else
+ fb_display[con].var.vmode &= ~FB_VMODE_YWRAP;
+ return 0;
+}
+
+ /*
+ * Get the Colormap
+ */
+
+static int virtual_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con)
+{
+ if (con == currcon) /* current console? */
+ return fb_get_cmap(cmap, &fb_display[con].var, kspc, vfb_getcolreg);
+ else if (fb_display[con].cmap.len) /* non default colormap? */
+ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+ else
+ fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+ cmap, kspc ? 0 : 2);
+ return 0;
+}
+
+ /*
+ * Set the Colormap
+ */
+
+static int virtual_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con)
+{
+ int err;
+
+ if (!fb_display[con].cmap.len) { /* no colormap allocated? */
+ if ((err = fb_alloc_cmap(&fb_display[con].cmap,
+ 1<<fb_display[con].var.bits_per_pixel, 0)))
+ return err;
+ }
+ if (con == currcon) /* current console? */
+ return fb_set_cmap(cmap, &fb_display[con].var, kspc, vfb_setcolreg);
+ else
+ fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+ return 0;
+}
+
+
+ /*
+ * Virtual Frame Buffer Specific ioctls
+ */
+
+static int virtual_fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+ u_long arg, int con)
+{
+ return -EINVAL;
+}
+
+
+__initfunc(void vfb_video_setup(char *options, int *ints))
+{
+ char *this_opt;
+
+ fb_info.fontname[0] = '\0';
+
+ if (!options || !*options)
+ return;
+
+ for (this_opt = strtok(options, ","); this_opt;
+ this_opt = strtok(NULL, ",")) {
+ if (!strncmp(this_opt, "font:", 5))
+ strcpy(fb_info.fontname, this_opt+5);
+ }
+}
+
+
+ /*
+ * Initialisation
+ */
+
+__initfunc(unsigned long virtual_fb_init(unsigned long mem_start))
+{
+ int err;
+
+ if (mem_start) {
+ videomemory = mem_start;
+ mem_start += videomemorysize;
+ } else
+ videomemory = (u_long)vmalloc(videomemorysize);
+
+ if (!videomemory)
+ return mem_start;
+
+ strcpy(fb_info.modename, virtual_fb_name);
+ fb_info.changevar = NULL;
+ fb_info.node = -1;
+ fb_info.fbops = &virtual_fb_ops;
+ fb_info.fbvar_num = NUM_TOTAL_MODES;
+ fb_info.fbvar = virtual_fb_predefined;
+ fb_info.disp = &disp;
+ fb_info.switch_con = &vfbcon_switch;
+ fb_info.updatevar = &vfbcon_updatevar;
+ fb_info.blank = &vfbcon_blank;
+ fb_info.setcmap = &vfbcon_setcmap;
+
+ err = register_framebuffer(&fb_info);
+ if (err < 0)
+ return mem_start;
+
+ virtual_fb_set_var(&virtual_fb_predefined[0], -1);
+
+ printk("Virtual frame buffer device, using %ldK of video memory\n",
+ videomemorysize>>10);
+ return mem_start;
+}
+
+
+static int vfbcon_switch(int con)
+{
+ /* Do we have to save the colormap? */
+ if (fb_display[currcon].cmap.len)
+ fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1,
+ vfb_getcolreg);
+
+ currcon = con;
+ /* Install new colormap */
+ do_install_cmap(con);
+ return 0;
+}
+
+ /*
+ * Update the `var' structure (called by fbcon.c)
+ */
+
+static int vfbcon_updatevar(int con)
+{
+ /* Nothing */
+ return 0;
+}
+
+ /*
+ * Blank the display.
+ */
+
+static void vfbcon_blank(int blank)
+{
+ /* Nothing */
+}
+
+ /*
+ * Set the colormap
+ */
+
+static int vfbcon_setcmap(struct fb_cmap *cmap, int con)
+{
+ return(virtual_fb_set_cmap(cmap, 1, con));
+}
+
+
+static u_long get_line_length(int xres_virtual, int bpp)
+{
+ u_long length;
+
+ length = (xres_virtual+bpp-1)/bpp;
+ length = (length+31)&-32;
+ length >>= 3;
+ return(length);
+}
+
+static void vfb_encode_fix(struct fb_fix_screeninfo *fix,
+ struct fb_var_screeninfo *var)
+{
+ memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+ strcpy(fix->id, virtual_fb_name);
+ fix->smem_start = (caddr_t)videomemory;
+ fix->smem_len = videomemorysize;
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->type_aux = 0;
+ switch (var->bits_per_pixel) {
+ case 1:
+ fix->visual = FB_VISUAL_MONO01;
+ break;
+ case 8:
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
+ break;
+ case 16:
+ case 24:
+ case 32:
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ break;
+ }
+ fix->ywrapstep = 1;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+ fix->line_length = get_line_length(var->xres_virtual, var->bits_per_pixel);
+}
+
+static void set_color_bitfields(struct fb_var_screeninfo *var)
+{
+ switch (var->bits_per_pixel) {
+ case 1:
+ case 8:
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 0;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ case 16: /* RGB 565 */
+ var->red.offset = 0;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 11;
+ var->blue.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ case 24: /* RGB 888 */
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 16;
+ var->blue.length = 8;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ case 32: /* RGBA 8888 */
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 16;
+ var->blue.length = 8;
+ var->transp.offset = 24;
+ var->transp.length = 8;
+ break;
+ }
+ var->red.msb_right = 0;
+ var->green.msb_right = 0;
+ var->blue.msb_right = 0;
+ var->transp.msb_right = 0;
+}
+
+
+ /*
+ * Read a single color register and split it into
+ * colors/transparent. Return != 0 for invalid regno.
+ */
+
+static int vfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+ u_int *transp)
+{
+ if (regno > 255)
+ return 1;
+ *red = palette[regno].red;
+ *green = palette[regno].green;
+ *blue = palette[regno].blue;
+ return 0;
+}
+
+
+ /*
+ * Set a single color register. The values supplied are already
+ * rounded down to the hardware's capabilities (according to the
+ * entries in the var structure). Return != 0 for invalid regno.
+ */
+
+static int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp)
+{
+ if (regno > 255)
+ return 1;
+ palette[regno].red = red;
+ palette[regno].green = green;
+ palette[regno].blue = blue;
+ return 0;
+}
+
+
+static void do_install_cmap(int con)
+{
+ if (con != currcon)
+ return;
+ if (fb_display[con].cmap.len)
+ fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
+ vfb_setcolreg);
+ else
+ fb_set_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+ &fb_display[con].var, 1, vfb_setcolreg);
+}
+
+
+#ifdef MODULE
+int init_module(void)
+{
+ return(virtual_fb_init(NULL));
+}
+
+void cleanup_module(void)
+{
+ unregister_framebuffer(&fb_info);
+ vfree(videomemory);
+}
+
+#endif /* MODULE */
diff --git a/drivers/video/vgacon.c b/drivers/video/vgacon.c
new file mode 100644
index 000000000..83a908a09
--- /dev/null
+++ b/drivers/video/vgacon.c
@@ -0,0 +1,591 @@
+/*
+ * linux/drivers/video/vgacon.c -- Low level VGA based console driver
+ *
+ * Created 28 Sep 1997 by Geert Uytterhoeven
+ *
+ * This file is based on the old console.c, vga.c and vesa_blank.c drivers.
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * 1995 Jay Estabrook
+ *
+ * User definable mapping table and font loading by Eugene G. Crosser,
+ * <crosser@pccross.msk.su>
+ *
+ * Improved loadable font/UTF-8 support by H. Peter Anvin
+ * Feb-Sep 1995 <peter.anvin@linux.org>
+ *
+ * Colour palette handling, by Simon Tatham
+ * 17-Jun-95 <sgt20@cam.ac.uk>
+ *
+ * if 512 char mode is already enabled don't re-enable it,
+ * because it causes screen to flicker, by Mitja Horvat
+ * 5-May-96 <mitja.horvat@guest.arnes.si>
+ *
+ * Use 2 outw instead of 4 outb_p to reduce erroneous text
+ * flashing on RHS of screen during heavy console scrolling .
+ * Oct 1996, Paul Gortmaker.
+ *
+ *
+ * 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.
+ */
+
+
+
+
+/* KNOWN PROBLEMS/TO DO ===================================================== *
+ *
+ * - monochrome attribute encoding (convert abscon <-> VGA style)
+ *
+ * - speed up scrolling by changing the screen origin
+ *
+ * - add support for palette, loadable fonts and VESA blanking
+ *
+ * KNOWN PROBLEMS/TO DO ==================================================== */
+
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/console_struct.h>
+#include <linux/string.h>
+#include <linux/kd.h>
+#include <linux/malloc.h>
+#include <linux/vt_kern.h>
+#include <linux/selection.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/linux_logo.h>
+
+
+#define BLANK 0x0020
+
+#define CAN_LOAD_EGA_FONTS /* undefine if the user must not do this */
+#define CAN_LOAD_PALETTE /* undefine if the user must not do this */
+
+#define dac_reg (0x3c8)
+#define dac_val (0x3c9)
+
+#ifdef __powerpc__
+#define VGA_OFFSET _ISA_MEM_BASE;
+#else
+#define VGA_OFFSET 0x0
+#endif
+
+
+/*
+ * Interface used by the world
+ */
+
+static unsigned long vgacon_startup(unsigned long kmem_start,
+ const char **display_desc);
+static void vgacon_init(struct vc_data *conp);
+static int vgacon_deinit(struct vc_data *conp);
+static int vgacon_clear(struct vc_data *conp, int sy, int sx, int height,
+ int width);
+static int vgacon_putc(struct vc_data *conp, int c, int ypos, int xpos);
+static int vgacon_putcs(struct vc_data *conp, const char *s, int count,
+ int ypos, int xpos);
+static int vgacon_cursor(struct vc_data *conp, int mode);
+static int vgacon_scroll(struct vc_data *conp, int t, int b,
+ int dir, int count);
+static int vgacon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
+ int height, int width);
+static int vgacon_switch(struct vc_data *conp);
+static int vgacon_blank(int blank);
+static int vgacon_get_font(struct vc_data *conp, int *w, int *h, char *data);
+static int vgacon_set_font(struct vc_data *conp, int w, int h, char *data);
+static int vgacon_set_palette(struct vc_data *conp, unsigned char *table);
+static int vgacon_scrolldelta(int lines);
+
+
+/*
+ * Internal routines
+ */
+
+static int vgacon_show_logo(void);
+
+
+/* Description of the hardware situation */
+static unsigned long vga_video_mem_base; /* Base of video memory */
+static unsigned long vga_video_mem_term; /* End of video memory */
+static unsigned short vga_video_port_reg; /* Video register select port */
+static unsigned short vga_video_port_val; /* Video register value port */
+static unsigned long vga_video_num_columns; /* Number of text columns */
+static unsigned long vga_video_num_lines; /* Number of text lines */
+static unsigned long vga_video_size_row;
+static unsigned long vga_video_screen_size;
+
+static int vga_can_do_color = 0;
+static unsigned long vga_default_font_height; /* Height of default screen font */
+
+static unsigned char vga_video_type;
+static unsigned char vga_has_wrapped; /* all of videomem is data of fg_console */
+static unsigned char vga_hardscroll_enabled;
+static unsigned char vga_hardscroll_disabled_by_init = 0;
+
+
+
+ /*
+ * VGA screen access
+ */
+
+static inline void vga_writew(unsigned short val, unsigned short * addr)
+{
+#ifdef __powerpc__
+ st_le16(addr, val);
+#else
+ writew(val, (unsigned long) addr);
+#endif /* !__powerpc__ */
+}
+
+static inline unsigned short vga_readw(unsigned short * addr)
+{
+#ifdef __powerpc__
+ return ld_le16(addr);
+#else
+ return readw((unsigned long) addr);
+#endif /* !__powerpc__ */
+}
+
+static inline void vga_memsetw(void * s, unsigned short c, unsigned int count)
+{
+ unsigned short * addr = (unsigned short *) s;
+
+ while (count) {
+ count--;
+ vga_writew(c, addr++);
+ }
+}
+
+static inline void vga_memmovew(unsigned short *to, unsigned short *from,
+ unsigned int count)
+{
+ if (to < from) {
+ while (count) {
+ count--;
+ vga_writew(vga_readw(from++), to++);
+ }
+ } else {
+ from += count;
+ to += count;
+ while (count) {
+ count--;
+ vga_writew(vga_readw(--from), --to);
+ }
+ }
+}
+
+
+/*
+ * By replacing the four outb_p with two back to back outw, we can reduce
+ * the window of opportunity to see text mislocated to the RHS of the
+ * console during heavy scrolling activity. However there is the remote
+ * possibility that some pre-dinosaur hardware won't like the back to back
+ * I/O. Since the Xservers get away with it, we should be able to as well.
+ */
+static inline void write_vga(unsigned char reg, unsigned int val)
+{
+#ifndef SLOW_VGA
+ unsigned int v1, v2;
+
+ v1 = reg + (val & 0xff00);
+ v2 = reg + 1 + ((val << 8) & 0xff00);
+ outw(v1, vga_video_port_reg);
+ outw(v2, vga_video_port_reg);
+#else
+ outb_p(reg, vga_video_port_reg);
+ outb_p(val >> 8, vga_video_port_val);
+ outb_p(reg+1, vga_video_port_reg);
+ outb_p(val & 0xff, vga_video_port_val);
+#endif
+}
+
+
+__initfunc(static unsigned long vgacon_startup(unsigned long kmem_start,
+ const char **display_desc))
+{
+ unsigned short saved;
+ unsigned short *p;
+
+ /*
+ * Find out if there is a graphics card present.
+ * Are there smarter methods around?
+ */
+ p = (unsigned short *)(((ORIG_VIDEO_MODE == 7) ? 0xb0000 : 0xb8000) +
+ + VGA_OFFSET);
+ saved = vga_readw(p);
+ vga_writew(0xAA55, p);
+ if (vga_readw(p) != 0xAA55) {
+ vga_writew(saved, p);
+ return kmem_start;
+ }
+ vga_writew(0x55AA, p);
+ if (vga_readw(p) != 0x55AA) {
+ vga_writew(saved, p);
+ return kmem_start;
+ }
+ vga_writew(saved, p);
+
+ vga_video_num_lines = ORIG_VIDEO_LINES;
+ vga_video_num_columns = ORIG_VIDEO_COLS;
+ vga_video_size_row = 2 * ORIG_VIDEO_COLS;
+ vga_video_screen_size = vga_video_num_lines * vga_video_size_row;
+
+ if (ORIG_VIDEO_MODE == 7) /* Is this a monochrome display? */
+ {
+ vga_video_mem_base = 0xb0000 + VGA_OFFSET;
+ vga_video_port_reg = 0x3b4;
+ vga_video_port_val = 0x3b5;
+ if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)
+ {
+ vga_video_type = VIDEO_TYPE_EGAM;
+ vga_video_mem_term = 0xb8000 + VGA_OFFSET;
+ *display_desc = "EGA+";
+ request_region(0x3b0,16,"ega");
+ }
+ else
+ {
+ vga_video_type = VIDEO_TYPE_MDA;
+ vga_video_mem_term = 0xb2000 + VGA_OFFSET;
+ *display_desc = "*MDA";
+ request_region(0x3b0,12,"mda");
+ request_region(0x3bf, 1,"mda");
+ }
+ }
+ else /* If not, it is color. */
+ {
+ vga_can_do_color = 1;
+ vga_video_mem_base = 0xb8000 + VGA_OFFSET;
+ vga_video_port_reg = 0x3d4;
+ vga_video_port_val = 0x3d5;
+ if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)
+ {
+ int i ;
+
+ vga_video_mem_term = 0xc0000 + VGA_OFFSET;
+
+ if (!ORIG_VIDEO_ISVGA) {
+ vga_video_type = VIDEO_TYPE_EGAC;
+ *display_desc = "EGA";
+ request_region(0x3c0,32,"ega");
+ } else {
+ vga_video_type = VIDEO_TYPE_VGAC;
+ *display_desc = "VGA+";
+ request_region(0x3c0,32,"vga+");
+
+#ifdef VGA_CAN_DO_64KB
+ /*
+ * get 64K rather than 32K of video RAM.
+ * This doesn't actually work on all "VGA"
+ * controllers (it seems like setting MM=01
+ * and COE=1 isn't necessarily a good idea)
+ */
+ vga_video_mem_base = 0xa0000 + VGA_OFFSET;
+ vga_video_mem_term = 0xb0000 + VGA_OFFSET;
+ outb_p (6, 0x3ce) ;
+ outb_p (6, 0x3cf) ;
+#endif
+
+ /*
+ * Normalise the palette registers, to point
+ * the 16 screen colours to the first 16
+ * DAC entries.
+ */
+
+ for (i=0; i<16; i++) {
+ inb_p (0x3da) ;
+ outb_p (i, 0x3c0) ;
+ outb_p (i, 0x3c0) ;
+ }
+ outb_p (0x20, 0x3c0) ;
+
+ /* now set the DAC registers back to their
+ * default values */
+
+ for (i=0; i<16; i++) {
+ outb_p (color_table[i], 0x3c8) ;
+ outb_p (default_red[i], 0x3c9) ;
+ outb_p (default_grn[i], 0x3c9) ;
+ outb_p (default_blu[i], 0x3c9) ;
+ }
+ }
+ }
+ else
+ {
+ vga_video_type = VIDEO_TYPE_CGA;
+ vga_video_mem_term = 0xba000 + VGA_OFFSET;
+ *display_desc = "*CGA";
+ request_region(0x3d4,2,"cga");
+ }
+ }
+
+ vga_hardscroll_enabled = (vga_hardscroll_disabled_by_init ? 0 :
+ (vga_video_type == VIDEO_TYPE_EGAC
+ || vga_video_type == VIDEO_TYPE_VGAC
+ || vga_video_type == VIDEO_TYPE_EGAM));
+ vga_has_wrapped = 0;
+
+ if (vga_video_type == VIDEO_TYPE_VGAC
+ || vga_video_type == VIDEO_TYPE_EGAC
+ || vga_video_type == VIDEO_TYPE_EGAM)
+ {
+ vga_default_font_height = ORIG_VIDEO_POINTS;
+ video_font_height = ORIG_VIDEO_POINTS;
+ /* This may be suboptimal but is a safe bet - go with it */
+ video_scan_lines =
+ video_font_height * vga_video_num_lines;
+ }
+
+ if (!console_show_logo)
+ console_show_logo = vgacon_show_logo;
+
+ return kmem_start;
+}
+
+
+static void vgacon_init(struct vc_data *conp)
+{
+ conp->vc_cols = vga_video_num_columns;
+ conp->vc_rows = vga_video_num_lines;
+ conp->vc_can_do_color = vga_can_do_color;
+}
+
+static int vgacon_deinit(struct vc_data *conp)
+{
+ return 0;
+}
+
+
+/* ====================================================================== */
+
+static int vgacon_clear(struct vc_data *conp, int sy, int sx, int height,
+ int width)
+{
+ int rows;
+ unsigned long dest;
+
+ if (console_blanked)
+ return 0;
+
+ dest = vga_video_mem_base + sy*vga_video_size_row + sx*2;
+ if (sx == 0 && width == vga_video_num_columns)
+ vga_memsetw((void *)dest, conp->vc_video_erase_char, height * width);
+ else
+ for (rows = height; rows-- ; dest += vga_video_size_row)
+ vga_memsetw((void *)dest, conp->vc_video_erase_char, width);
+ return 0;
+}
+
+
+static int vgacon_putc(struct vc_data *conp, int c, int ypos, int xpos)
+{
+ u_short *p;
+
+ if (console_blanked)
+ return 0;
+
+ p = (u_short *)(vga_video_mem_base+ypos*vga_video_size_row+xpos*2);
+ vga_writew(conp->vc_attr << 8 | c, p);
+ return 0;
+}
+
+
+static int vgacon_putcs(struct vc_data *conp, const char *s, int count,
+ int ypos, int xpos)
+{
+ u_short *p;
+ u_short sattr;
+
+ if (console_blanked)
+ return 0;
+
+ p = (u_short *)(vga_video_mem_base+ypos*vga_video_size_row+xpos*2);
+ sattr = conp->vc_attr << 8;
+ while (count--)
+ vga_writew(sattr | *s++, p++);
+ return 0;
+}
+
+
+static int vgacon_cursor(struct vc_data *conp, int mode)
+{
+ switch (mode) {
+ case CM_ERASE:
+ write_vga(14, (vga_video_mem_term - vga_video_mem_base - 1)>>1);
+ break;
+
+ case CM_MOVE:
+ case CM_DRAW:
+ write_vga(14, conp->vc_y*vga_video_num_columns+conp->vc_x);
+ break;
+ }
+ return 0;
+}
+
+
+static int vgacon_scroll(struct vc_data *conp, int t, int b, int dir, int count)
+{
+ if (console_blanked)
+ return 0;
+
+ vgacon_cursor(conp, CM_ERASE);
+
+ switch (dir) {
+ case SM_UP:
+ if (count > conp->vc_rows) /* Maximum realistic size */
+ count = conp->vc_rows;
+ vgacon_bmove(conp, t+count, 0, t, 0, b-t-count, conp->vc_cols);
+ vgacon_clear(conp, b-count, 0, count, conp->vc_cols);
+ break;
+
+ case SM_DOWN:
+ if (count > conp->vc_rows) /* Maximum realistic size */
+ count = conp->vc_rows;
+ /*
+ * Fixed bmove() should end Arno's frustration with copying?
+ * Confucius says:
+ * Man who copies in wrong direction, end up with trashed
+ * data
+ */
+ vgacon_bmove(conp, t, 0, t+count, 0, b-t-count, conp->vc_cols);
+ vgacon_clear(conp, t, 0, count, conp->vc_cols);
+ break;
+
+ case SM_LEFT:
+ vgacon_bmove(conp, 0, t+count, 0, t, conp->vc_rows, b-t-count);
+ vgacon_clear(conp, 0, b-count, conp->vc_rows, count);
+ break;
+
+ case SM_RIGHT:
+ vgacon_bmove(conp, 0, t, 0, t+count, conp->vc_rows, b-t-count);
+ vgacon_clear(conp, 0, t, conp->vc_rows, count);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int vgacon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
+ int height, int width)
+{
+ unsigned long src, dst;
+ int rows;
+
+ if (console_blanked)
+ return 0;
+
+ if (sx == 0 && dx == 0 && width == vga_video_num_columns) {
+ src = vga_video_mem_base + sy * vga_video_size_row;
+ dst = vga_video_mem_base + dy * vga_video_size_row;
+ vga_memmovew((unsigned short *)dst, (unsigned short *)src,
+ height * width);
+ } else if (dy < sy || (dy == sy && dx < sx)) {
+ src = vga_video_mem_base + sy * vga_video_size_row + sx * 2;
+ dst = vga_video_mem_base + dy * vga_video_size_row + dx * 2;
+ for (rows = height; rows-- ;) {
+ vga_memmovew((unsigned short *)dst, (unsigned short *)src, width);
+ src += vga_video_size_row;
+ dst += vga_video_size_row;
+ }
+ } else {
+ src = vga_video_mem_base + (sy+height-1) * vga_video_size_row + sx * 2;
+ dst = vga_video_mem_base + (dy+height-1) * vga_video_size_row + dx * 2;
+ for (rows = height; rows-- ;) {
+ vga_memmovew((unsigned short *)dst, (unsigned short *)src, width);
+ src -= vga_video_size_row;
+ dst -= vga_video_size_row;
+ }
+ }
+ return 0;
+}
+
+
+static int vgacon_switch(struct vc_data *conp)
+{
+ return 0;
+}
+
+
+static int vgacon_blank(int blank)
+{
+ if (blank) {
+ vga_memsetw((void *)vga_video_mem_base, BLANK, vga_video_screen_size/2);
+ return 0;
+ } else {
+ /* Tell console.c that it has to restore the screen itself */
+ return(1);
+ }
+ return 0;
+}
+
+
+static int vgacon_get_font(struct vc_data *conp, int *w, int *h, char *data)
+{
+ /* TODO */
+ return -ENOSYS;
+}
+
+
+static int vgacon_set_font(struct vc_data *conp, int w, int h, char *data)
+{
+ /* TODO */
+ return -ENOSYS;
+}
+
+static int vgacon_set_palette(struct vc_data *conp, unsigned char *table)
+{
+ int i, j ;
+
+ if (vga_video_type != VIDEO_TYPE_VGAC || console_blanked ||
+ vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+ return -EINVAL;
+
+ for (i=j=0; i<16; i++) {
+ outb_p (table[i], dac_reg) ;
+ outb_p (vc_cons[fg_console].d->vc_palette[j++]>>2, dac_val) ;
+ outb_p (vc_cons[fg_console].d->vc_palette[j++]>>2, dac_val) ;
+ outb_p (vc_cons[fg_console].d->vc_palette[j++]>>2, dac_val) ;
+ }
+ return 0;
+}
+
+static int vgacon_scrolldelta(int lines)
+{
+ /* TODO */
+ return -ENOSYS;
+}
+
+
+__initfunc(static int vgacon_show_logo( void ))
+{
+ int height = 0;
+ char *p;
+
+ printk(linux_serial_image);
+ for (p = linux_serial_image; *p; p++)
+ if (*p == '\n')
+ height++;
+ return height;
+}
+
+
+
+/*
+ * The console `switch' structure for the VGA based console
+ */
+
+struct consw vga_con = {
+ vgacon_startup, vgacon_init, vgacon_deinit, vgacon_clear, vgacon_putc,
+ vgacon_putcs, vgacon_cursor, vgacon_scroll, vgacon_bmove, vgacon_switch,
+ vgacon_blank, vgacon_get_font, vgacon_set_font, vgacon_set_palette,
+ vgacon_scrolldelta
+};